La CLI cmssy

init génère le câblage dans une application existante, add block échafaude et enregistre un bloc, link connecte l'application à un espace de travail et prouve que ça marche, et types génère le TypeScript des modèles de l'espace de travail.

Les commandes ci-dessous couvrent chacune un moment qui relevait autrefois du travail manuel : câbler une nouvelle application, ajouter un bloc, se connecter à un espace de travail.

cmssy init

cmssy n'échafaude jamais votre application. Le framework est un adaptateur, jamais la fondation : vous créez l'application avec la CLI de Next.js, et init y ajoute le câblage cmssy.

npx create-next-app@latest my-site
cd my-site
npx @cmssy/cli init

La CLI vise Next.js avec l'App Router et requiert Node 18.18+. Astro, Remix et React Router sont pris en charge par le SDK lui-même : ceux-là, vous les câblez à la main plutôt que via init.

Elle est idempotente : un fichier déjà présent est ignoré et signalé comme tel, jamais écrasé. Lancée deux fois, la seconde exécution ne change rien.

Ce qu'elle écrit

cmssy.config.ts
proxy.ts
next.config.mjs
env.example
cmssy/blocks.ts          registre de blocs
cmssy/editor.tsx         pont d'édition
blocks/hero/block.ts
blocks/hero/Hero.tsx
blocks/hero/Hero.module.css
app/[[...path]]/page.tsx route catch-all
app/api/draft/route.ts   aperçu brouillon

Remarquez ce qui n'y est pas : ni app/sitemap.ts, ni app/robots.ts, ni route /cmssy-edit, ni layout racine, ni layout éditable.

Ce n'est pas un oubli mais un choix. Ces fichiers encodent des décisions propres à votre application - quelles URL vous exposez, à quoi ressemble votre habillage, comment vous formez un sitemap - et un modèle qui les devinerait serait faux pour la plupart des projets. init vous donne une page qui s'affiche et une route de brouillon ; le reste, vous l'ajoutez selon vos besoins - voir routes et pages, layouts et SEO.

Options : --dir <chemin> vise une application hors du répertoire courant, --force écrase le câblage existant.

cmssy add block

Chaque bloc après le hero généré signifiait recopier des fichiers à la main et penser à modifier le registre.

cmssy add block pricing-table
cmssy add block faq-list --dir ../my-site

Tous les noms dérivent de l'argument en kebab-case : pricing-table donne le type pricing-table, le libellé Pricing Table, le composant PricingTable et l'export pricingTableBlock.

Il écrit les fichiers du bloc et l'enregistre dans cmssy/blocks.ts - ajout de l'import et ajout au tableau blocks, en préservant votre formatage et vos entrées existantes.

Il refuse de toucher à tout ce qui est ambigu : un nom invalide, un bloc déjà enregistré, des fichiers existants, ou un registre sans tableau export const blocks = [...]. Chaque cas est un échec explicite avec l'étape manuelle détaillée - jamais une écriture partielle silencieuse.

Le bloc généré démarre avec un heading requis et un text optionnel. Modifiez les props et le markup, redémarrez le serveur de développement, et l'éditeur récupère le nouveau type via la poignée de main du manifeste.

Connecter une application à un espace de travail signifiait recopier cinq valeurs entre le tableau de bord et .env.local - et l'éditeur restait mort tant que toutes n'étaient pas bonnes.

npx @cmssy/cli link
cmssy link --token cs_... --workspace acme/shop --preview-url https://shop.example.com

Il s'authentifie avec un jeton API (depuis --token ou CMSSY_API_TOKEN ; .env.local et .env sont lus d'abord, sans jamais écraser les variables du shell), choisit un espace de travail, lit le secret de brouillon, écrit CMSSY_ORG_SLUG, CMSSY_WORKSPACE_SLUG et CMSSY_DRAFT_SECRET dans .env.local - en fusionnant, donc lignes et commentaires existants survivent - puis lance le preflight.

Lire le secret de brouillon exige la permission PAGES_EDIT. Une permission manquante est signalée exactement comme telle.

L'URL de prévisualisation est partagée - localhost est refusé

--preview-url définit l'origine où l'éditeur encadre votre application pour tout le monde dans l'espace de travail. Une valeur localhost est refusée exprès : pointer l'aperçu partagé vers votre machine casserait l'éditeur de chaque collègue.

Pour le développement local, activez le mode dev dans l'éditeur cmssy et saisissez-y votre hôte local. Cette cible est propre à chaque utilisateur.

Les vérifications

  • Espace de travail joignable - public.siteConfig répond. Distingue slugs erronés, problèmes réseau et espace au-delà de sa limite de diffusion.
  • Secret de brouillon - le backend confirme que le secret écrit correspond. Sur une plateforme sans ce champ, il signale ? et continue.
  • Lien profond éditeur - toujours affiché.
  • Lien d'aperçu brouillon - affiché quand l'espace déclare une URL d'aperçu. Quitter : /api/draft?disable=1.

Il y a trois statuts, pas deux : ✓ vérifié, ✗ cassé avec le correctif à la ligne suivante et code de sortie 1, et ? non vérifiable - qui ne bloque jamais. Ce troisième état existe parce que « inconnu » et « cassé » sont différents, et qu'un outil qui les confond soit crie au loup, soit masque une vraie panne.

Le preflight est aussi une API

Chaque vérification est une fonction pure de @cmssy/core, exposée sous un sous-chemin pour que l'outillage de développement n'entre jamais dans votre bundle de production :

import {
  checkWorkspaceReachable,
  checkDraftSecret,
  checkPreviewUrl,
  checkFrameAncestors,
  buildEditorUrl,
} from "@cmssy/core/preflight";

Chacune renvoie { status: "ok" | "fail" | "unknown", message, fix? }. Les deux premières parlent à l'API de diffusion ; le reste est de la logique de chaînes pure. Aucune n'importe de framework ni de module Node : elles tournent partout.

Quand le câblage est cassé

En développement, les mêmes vérifications gardent la route d'édition. Une requête éditeur qui échoue à la vérification ne renvoie pas 404 : l'adaptateur affiche une page de diagnostic dans l'iframe de l'éditeur, une ligne par vérification.

Elle montre le slug de l'espace de travail et la vérification en échec, jamais une valeur de secret.

cmssy types

Génère du TypeScript pour les modèles de l'espace de travail, pour que le data d'un enregistrement cesse d'être unknown.

npx @cmssy/cli types                  # cmssy/models.ts + cmssy/operations.graphql
npx @cmssy/cli types --out types/cms.ts
npx @cmssy/cli types --check          # CI : échoue si l'un des fichiers n'est plus à jour

Elle lit les définitions de modèles via l'API publique de diffusion, avec les mêmes CMSSY_ORG_SLUG / CMSSY_WORKSPACE_SLUG que votre application (depuis .env.local / .env). Aucun jeton API : elle tourne donc en CI.

Options : --out <chemin>, --operations-out <chemin>, --no-operations, --check, --org <slug>, --workspace <slug>.

Pour chaque modèle, elle écrit <Model>Data et <Model>Record (un CmssyRecordOf<<Model>Data>) :

  • les champs requis ne sont pas optionnels, les autres le sont
  • les champs localisés sont des CmssyLocalized
  • select, radio et multiselect deviennent l'union de leurs valeurs d'option : une faute de frappe dans un filtre est une erreur de compilation
  • une relation est le ou les id d'enregistrement qu'elle stocke, le modèle cible étant nommé dans le commentaire de documentation
  • les champs object et repeater sont développés sur place ; les champs masqués sont omis
  • les commentaires de documentation portent le libellé de chaque champ dans la langue par défaut de l'espace de travail

Elle écrit aussi cmssy/operations.graphql - les requêtes de diffusion qu'envoie @cmssy/core lui-même - pour votre codegen GraphQL : ajoutez "cmssy/**/*.graphql" à documents. Le fichier est réécrit à chaque exécution. Si votre application déclare déjà une opération du même nom, la commande s'arrête et nomme le fichier : supprimez votre copie, ou passez --no-operations pour la conserver.

Commitez les deux fichiers et relancez la commande après avoir modifié un modèle dans le CMS - un champ supprimé devient une erreur de compilation. Requiert @cmssy/cli 19.6.0 ou plus récent ; les versions antérieures échouent face à l'API actuelle.

Étapes suivantes