CLI cmssy

init generuje okablowanie w istniejącej aplikacji, add block tworzy i rejestruje blok, link łączy aplikację z workspace'em i dowodzi, że działa, a types generuje TypeScript dla modeli workspace'u.

Każda z poniższych komend obsługuje moment, który kiedyś był robotą ręczną: okablowanie nowej aplikacji, dodanie bloku i podłączenie do workspace'u.

cmssy init

cmssy nigdy nie scaffolduje Twojej aplikacji. Framework jest adapterem, nigdy fundamentem - aplikację tworzysz własnym CLI Next.js, a init dokłada do niej okablowanie cmssy.

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

CLI celuje w Next.js z App Routerem i wymaga Node 18.18+. Astro, Remix i React Router są wspierane przez samo SDK - te podpinasz ręcznie, a nie przez init.

Jest idempotentne: plik, który już istnieje, jest pomijany i raportowany jako pominięty, nigdy nadpisywany. Uruchom dwa razy, a drugi przebieg niczego nie zmieni.

Co zapisuje

cmssy.config.ts
proxy.ts
next.config.mjs
env.example
cmssy/blocks.ts          rejestr bloków
cmssy/editor.tsx         most edytora
blocks/hero/block.ts
blocks/hero/Hero.tsx
blocks/hero/Hero.module.css
app/[[...path]]/page.tsx catch-all route
app/api/draft/route.ts   podgląd roboczy

Zwróć uwagę, czego tam nie ma: żadnego app/sitemap.ts, żadnego app/robots.ts, żadnego route'u /cmssy-edit, żadnego root layoutu ani edytowalnego layoutu.

To jest celowe, a nie brakujące. Te pliki kodują decyzje o Twojej aplikacji - jakie URL-e wystawiasz, jak wygląda Twoje chrome, jak kształtujesz sitemapę - a szablon, który by je zgadywał, byłby zły dla większości projektów. init daje Ci renderującą stronę i route roboczy; resztę dokładasz sam, według route'ów i stron, layoutów i SEO.

Flagi: --dir <ścieżka> celuje w aplikację poza katalogiem roboczym, --force nadpisuje istniejące okablowanie.

cmssy add block

Każdy blok po wygenerowanym hero oznaczał kiedyś ręczne kopiowanie plików i pamiętanie o edycji rejestru.

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

Wszystkie nazwy wyprowadza z argumentu w kebab-case: pricing-table daje typ pricing-table, etykietę Pricing Table, komponent PricingTable i eksport pricingTableBlock.

Zapisuje pliki bloku i rejestruje go w cmssy/blocks.ts - dodaje import i dopisuje do tablicy blocks, zachowując Twoje formatowanie i istniejące wpisy.

Odmawia tknięcia czegokolwiek niejednoznacznego: nieprawidłowej nazwy, już zarejestrowanego bloku, istniejących plików albo rejestru bez tablicy export const blocks = [...]. Każdy z tych przypadków to głośny błąd z wypisanym krokiem ręcznym - nigdy cichy częściowy zapis.

Wygenerowany blok startuje z wymaganym heading i opcjonalnym text. Zmień propsy i markup, zrestartuj dev server, a edytor podchwyci nowy typ z handshake'u manifestu.

Podłączenie aplikacji do workspace'u oznaczało kiedyś ręczne przeklejanie pięciu wartości między panelem a .env.local - a edytor pozostawał martwy, dopóki każda nie była poprawna.

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

Uwierzytelnia się tokenem API (z --token albo CMSSY_API_TOKEN; .env.local i .env są czytane najpierw, nigdy nie nadpisując zmiennych z powłoki), wybiera workspace, odczytuje draft secret, zapisuje CMSSY_ORG_SLUG, CMSSY_WORKSPACE_SLUG i CMSSY_DRAFT_SECRET do .env.local - scalając, więc istniejące linie i komentarze przeżywają - po czym odpala preflight i wypisuje deep link do edytora.

Odczyt draft secretu wymaga uprawnienia PAGES_EDIT. Jego brak jest raportowany dokładnie jako to.

Preview URL jest współdzielony - localhost jest odrzucany

--preview-url ustawia origin, pod którym edytor osadza Twoją aplikację dla wszystkich w workspace. Wartość localhost jest odrzucana celowo: wskazanie współdzielonego podglądu na Twoją maszynę zepsułoby edytor każdemu współpracownikowi.

Do pracy lokalnej przełącz tryb dev w edytorze cmssy i wpisz tam swój lokalny host. Ten cel jest per użytkownik i nie dotyka niczego współdzielonego. Bez flagi wartość pozostaje niezmieniona.

Sprawdzenia

  • Workspace osiągalny - public.siteConfig odpowiada dla podłączonej organizacji i workspace'u. Rozróżnia złe slugi, problemy sieciowe i workspace ponad limitem.
  • Draft secret - backend potwierdza, że zapisany sekret się zgadza. Na platformie bez tego pola raportuje ? i idzie dalej.
  • Deep link do edytora - zawsze wypisywany.
  • Link podglądu roboczego - wypisywany, gdy workspace zgłasza preview URL. Wyjście z trybu roboczego: /api/draft?disable=1.

Statusy są trzy, nie dwa: ✓ zweryfikowane, ✗ zepsute z poprawką w następnej linii i kodem wyjścia 1, oraz ? nie dało się zweryfikować - co nigdy nie blokuje. Ten trzeci stan istnieje, bo "nieznane" i "zepsute" to różne rzeczy, a narzędzie, które je sklei, albo krzyczy bez powodu, albo ukrywa prawdziwą awarię.

Preflight jest też API

Każde sprawdzenie to czysta funkcja w @cmssy/core, wystawiona pod podsceżką, żeby narzędzia deweloperskie nigdy nie trafiły do Twojego bundla produkcyjnego:

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

Każda zwraca { status: "ok" | "fail" | "unknown", message, fix? }. Dwie pierwsze rozmawiają z API dostawczym; reszta to czysta logika na stringach. Żadna nie importuje frameworka ani wbudowanego modułu Node'a, więc działają wszędzie - w endpoincie health, w kroku CI, w dashboardzie.

Gdy okablowanie jest zepsute

W developmencie te same sprawdzenia pilnują route'u edycyjnego. Żądanie edytora, które nie przejdzie weryfikacji, nie daje 404 - adapter renderuje stronę diagnostyczną wewnątrz ramki edytora, linia na sprawdzenie: brakujące zmienne środowiskowe i skąd je wziąć, nieosiągalny workspace, niezgodność draft secretu, porównanie preview URL i originy, które musi dopuszczać frame-ancestors.

Pokazuje slug workspace'u i które sprawdzenie padło, nigdy wartości sekretu.

cmssy types

Generuje TypeScript dla modeli workspace'u, więc data rekordu przestaje być 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: pada, gdy któryś plik jest nieaktualny

Czyta definicje modeli przez publiczne API dostawcze, z tymi samymi CMSSY_ORG_SLUG / CMSSY_WORKSPACE_SLUG, których używa Twoja aplikacja (z .env.local / .env). Bez tokenu API, więc działa w CI.

Flagi: --out <ścieżka>, --operations-out <ścieżka>, --no-operations, --check, --org <slug>, --workspace <slug>.

Dla każdego modelu zapisuje <Model>Data i <Model>Record (czyli CmssyRecordOf<<Model>Data>):

  • pola wymagane są nieopcjonalne, reszta opcjonalna
  • pola lokalizowane mają typ CmssyLocalized
  • select, radio i multiselect stają się unią swoich wartości opcji, więc literówka w filtrze to błąd kompilacji
  • relacja to id rekordu (lub rekordów), które przechowuje, a model docelowy jest nazwany w komentarzu dokumentacyjnym
  • pola object i repeater są rozwijane w miejscu; pola ukryte są pomijane
  • komentarze dokumentacyjne niosą etykietę każdego pola w domyślnym języku workspace'u

Zapisuje też cmssy/operations.graphql - zapytania dostawcze, które wysyła sam @cmssy/core - dla Twojego codegenu GraphQL: dodaj "cmssy/**/*.graphql" do documents. Plik jest nadpisywany przy każdym uruchomieniu. Jeśli Twoja aplikacja już deklaruje operację o tej samej nazwie, komenda się zatrzymuje i wskazuje plik: usuń swoją kopię albo przekaż --no-operations, żeby ją zachować.

Commituj oba pliki i uruchom ponownie po zmianie modelu w CMS-ie - usunięte pole staje się błędem kompilacji. Wymaga @cmssy/cli 19.6.0 lub nowszego; starsze wersje padają na obecnym API.

Następne kroki