@elytracms/studio 0.0.88 → 0.0.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/convex/asset-revalidate.test.ts +49 -43
- package/convex/cms.ts +3 -4
- package/convex/delivery.ts +8 -11
- package/convex/document-revalidate.test.ts +188 -0
- package/convex/redirect-revalidate.test.ts +50 -37
- package/convex/revalidate.ts +18 -30
- package/convex/triggers.ts +47 -38
- package/convex/webhooks.ts +77 -62
- package/dist/components/account/self-account-view-source.js +2 -1
- package/dist/components/account/self-account-view-source.js.map +1 -1
- package/dist/components/ai/ai-chat-panel.js +1 -1
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/app-views/default-modules.js +3 -3
- package/dist/components/app-views/default-modules.js.map +1 -1
- package/dist/components/assets/asset-grid.js +1 -1
- package/dist/components/assets/asset-grid.js.map +1 -1
- package/dist/components/assets/asset-selection.d.ts +8 -9
- package/dist/components/assets/asset-selection.js +26 -16
- package/dist/components/assets/asset-selection.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.js +3 -2
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/builder/blocks-model.d.ts +7 -5
- package/dist/components/builder/blocks-model.js +11 -9
- package/dist/components/builder/blocks-model.js.map +1 -1
- package/dist/components/builder/blocks-panel.js +1 -1
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/builder-store.d.ts +11 -3
- package/dist/components/builder/builder-store.js +12 -5
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-dnd.d.ts +7 -1
- package/dist/components/builder/canvas-dnd.js +6 -3
- package/dist/components/builder/canvas-dnd.js.map +1 -1
- package/dist/components/builder/canvas-render-context.d.ts +13 -1
- package/dist/components/builder/canvas-render-context.js +28 -18
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/canvas.js +1 -1
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/clipboard-actions.d.ts +0 -6
- package/dist/components/builder/clipboard-actions.js +9 -4
- package/dist/components/builder/clipboard-actions.js.map +1 -1
- package/dist/components/builder/composition-field-editor.d.ts +22 -0
- package/dist/components/builder/composition-field-editor.js +47 -8
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/inline-edit.d.ts +7 -4
- package/dist/components/builder/inline-edit.js +11 -8
- package/dist/components/builder/inline-edit.js.map +1 -1
- package/dist/components/builder/insert-context.js +9 -5
- package/dist/components/builder/insert-context.js.map +1 -1
- package/dist/components/builder/layout-inspector.d.ts +2 -2
- package/dist/components/builder/layout-inspector.js +8 -6
- package/dist/components/builder/layout-inspector.js.map +1 -1
- package/dist/components/builder/node-factory.d.ts +31 -14
- package/dist/components/builder/node-factory.js +73 -93
- package/dist/components/builder/node-factory.js.map +1 -1
- package/dist/components/builder/page-tree.d.ts +3 -1
- package/dist/components/builder/page-tree.js +6 -4
- package/dist/components/builder/page-tree.js.map +1 -1
- package/dist/components/builder/property-inspector.js +2 -1
- package/dist/components/builder/property-inspector.js.map +1 -1
- package/dist/components/builder/reuse-context.js +2 -2
- package/dist/components/builder/reuse-context.js.map +1 -1
- package/dist/components/builder/reuse.d.ts +7 -3
- package/dist/components/builder/reuse.js +19 -11
- package/dist/components/builder/reuse.js.map +1 -1
- package/dist/components/builder/selection-overlay.d.ts +2 -2
- package/dist/components/builder/selection-overlay.js +3 -3
- package/dist/components/builder/selection-overlay.js.map +1 -1
- package/dist/components/builder/spacing-handles.d.ts +2 -2
- package/dist/components/builder/spacing-handles.js +3 -3
- package/dist/components/builder/spacing-handles.js.map +1 -1
- package/dist/components/cms/asset-field.d.ts +1 -1
- package/dist/components/cms/asset-field.js +15 -13
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/asset-picker-core.js +3 -4
- package/dist/components/cms/asset-picker-core.js.map +1 -1
- package/dist/components/cms/cell-edit.d.ts +0 -6
- package/dist/components/cms/cell-edit.js +5 -23
- package/dist/components/cms/cell-edit.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +3 -2
- package/dist/components/cms/cms-store.js +23 -24
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +14 -4
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/detail-editor.js +28 -24
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/discard-draft.d.ts +1 -2
- package/dist/components/cms/discard-draft.js +1 -1
- package/dist/components/cms/discard-draft.js.map +1 -1
- package/dist/components/cms/duplicate-document.d.ts +3 -2
- package/dist/components/cms/duplicate-document.js +5 -5
- package/dist/components/cms/duplicate-document.js.map +1 -1
- package/dist/components/cms/editable-cell.js +11 -7
- package/dist/components/cms/editable-cell.js.map +1 -1
- package/dist/components/cms/field-control-lib.d.ts +10 -0
- package/dist/components/cms/field-control-lib.js +20 -0
- package/dist/components/cms/field-control-lib.js.map +1 -1
- package/dist/components/cms/field-control.js +10 -9
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/object-array-field.js +3 -5
- package/dist/components/cms/object-array-field.js.map +1 -1
- package/dist/components/cms/prop-fields.d.ts +16 -3
- package/dist/components/cms/prop-fields.js +57 -12
- package/dist/components/cms/prop-fields.js.map +1 -1
- package/dist/components/cms/publish-clean.d.ts +13 -6
- package/dist/components/cms/publish-clean.js +14 -6
- package/dist/components/cms/publish-clean.js.map +1 -1
- package/dist/components/cms/rich-text/embed-props.d.ts +6 -1
- package/dist/components/cms/rich-text/embed-props.js +7 -12
- package/dist/components/cms/rich-text/embed-props.js.map +1 -1
- package/dist/components/cms/rich-text/insert.d.ts +13 -6
- package/dist/components/cms/rich-text/insert.js +16 -17
- package/dist/components/cms/rich-text/insert.js.map +1 -1
- package/dist/components/cms/use-cell-edit.js +7 -12
- package/dist/components/cms/use-cell-edit.js.map +1 -1
- package/dist/components/cms/use-discard-controller.d.ts +1 -2
- package/dist/components/cms/use-discard-controller.js +1 -2
- package/dist/components/cms/use-discard-controller.js.map +1 -1
- package/dist/components/cms/use-publish-controller.js +3 -2
- package/dist/components/cms/use-publish-controller.js.map +1 -1
- package/dist/components/cms/versions-pane.js +2 -2
- package/dist/components/cms/versions-pane.js.map +1 -1
- package/dist/components/components/component-detail.d.ts +1 -1
- package/dist/components/components/component-detail.js +2 -4
- package/dist/components/components/component-detail.js.map +1 -1
- package/dist/components/components/component-preview.js +9 -4
- package/dist/components/components/component-preview.js.map +1 -1
- package/dist/components/components/components-index.d.ts +2 -2
- package/dist/components/components/components-index.js +3 -6
- package/dist/components/components/components-index.js.map +1 -1
- package/dist/components/components/field-inspector.js +5 -2
- package/dist/components/components/field-inspector.js.map +1 -1
- package/dist/components/components/registry-browser.d.ts +6 -5
- package/dist/components/components/registry-browser.js +11 -37
- package/dist/components/components/registry-browser.js.map +1 -1
- package/dist/components/components/taxonomy.d.ts +14 -21
- package/dist/components/components/taxonomy.js +5 -29
- package/dist/components/components/taxonomy.js.map +1 -1
- package/dist/components/components/transfer-dialog.js +1 -0
- package/dist/components/components/transfer-dialog.js.map +1 -1
- package/dist/components/components/transfer.d.ts +20 -12
- package/dist/components/components/transfer.js +27 -21
- package/dist/components/components/transfer.js.map +1 -1
- package/dist/components/delivery-health-banner.d.ts +3 -2
- package/dist/components/delivery-health-banner.js +3 -2
- package/dist/components/delivery-health-banner.js.map +1 -1
- package/dist/components/redirects/redirects-collection-view-source.js +3 -2
- package/dist/components/redirects/redirects-collection-view-source.js.map +1 -1
- package/dist/components/users/users-collection-view-source.js +2 -1
- package/dist/components/users/users-collection-view-source.js.map +1 -1
- package/dist/lib/ai/apply-content.js +32 -28
- package/dist/lib/ai/apply-content.js.map +1 -1
- package/dist/lib/ai/constraints.d.ts +16 -14
- package/dist/lib/ai/constraints.js +64 -48
- package/dist/lib/ai/constraints.js.map +1 -1
- package/dist/lib/ai/translate-document.js +11 -7
- package/dist/lib/ai/translate-document.js.map +1 -1
- package/dist/lib/ai/types.d.ts +9 -5
- package/dist/lib/ai/types.js.map +1 -1
- package/dist/lib/ai/update-fields.js +15 -32
- package/dist/lib/ai/update-fields.js.map +1 -1
- package/dist/lib/ai/vite.d.ts +22 -2
- package/dist/lib/ai/vite.js +12 -3
- package/dist/lib/ai/vite.js.map +1 -1
- package/dist/lib/assets/asset-placements.d.ts +3 -10
- package/dist/lib/assets/asset-placements.js +17 -8
- package/dist/lib/assets/asset-placements.js.map +1 -1
- package/dist/lib/clipboard/paste-target.js +3 -3
- package/dist/lib/clipboard/paste-target.js.map +1 -1
- package/dist/lib/cms/published-documents.d.ts +14 -3
- package/dist/lib/cms/published-documents.js +12 -3
- package/dist/lib/cms/published-documents.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +4 -4
- package/dist/lib/cms-views/view-model.js +26 -20
- package/dist/lib/cms-views/view-model.js.map +1 -1
- package/dist/lib/data/collection-content.d.ts +10 -7
- package/dist/lib/data/collection-content.js +70 -29
- package/dist/lib/data/collection-content.js.map +1 -1
- package/dist/lib/delivery/instant-publish.d.ts +0 -3
- package/dist/lib/delivery/instant-publish.js +0 -1
- package/dist/lib/delivery/instant-publish.js.map +1 -1
- package/dist/lib/dnd/drop-rules.d.ts +22 -7
- package/dist/lib/dnd/drop-rules.js +21 -14
- package/dist/lib/dnd/drop-rules.js.map +1 -1
- package/dist/lib/engine-version.d.ts +1 -1
- package/dist/lib/engine-version.js +1 -1
- package/dist/lib/engine-version.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +25 -9
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +21 -7
- package/dist/lib/i18n/messages.en.js +28 -9
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/locale/fallback.d.ts +5 -19
- package/dist/lib/locale/fallback.js +18 -39
- package/dist/lib/locale/fallback.js.map +1 -1
- package/dist/lib/locale/locale-store.js +2 -1
- package/dist/lib/locale/locale-store.js.map +1 -1
- package/dist/lib/operations/cms-persistence.js +7 -4
- package/dist/lib/operations/cms-persistence.js.map +1 -1
- package/dist/lib/operations/studio-operations.js +4 -6
- package/dist/lib/operations/studio-operations.js.map +1 -1
- package/dist/lib/persistence/backend-mode.d.ts +5 -25
- package/dist/lib/persistence/backend-mode.js +13 -30
- package/dist/lib/persistence/backend-mode.js.map +1 -1
- package/dist/lib/persona/labels.d.ts +4 -9
- package/dist/lib/persona/labels.js +5 -12
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/references/reference-model.d.ts +17 -5
- package/dist/lib/references/reference-model.js +20 -28
- package/dist/lib/references/reference-model.js.map +1 -1
- package/dist/lib/references/references-store.js +7 -0
- package/dist/lib/references/references-store.js.map +1 -1
- package/dist/lib/registry/__fixtures__/demo-components.d.ts +32 -0
- package/dist/lib/registry/{project-components.js → __fixtures__/demo-components.js} +18 -5
- package/dist/lib/registry/__fixtures__/demo-components.js.map +1 -0
- package/dist/lib/registry/index.d.ts +9 -13
- package/dist/lib/registry/index.js +34 -61
- package/dist/lib/registry/index.js.map +1 -1
- package/dist/lib/registry/preview.d.ts +22 -7
- package/dist/lib/registry/preview.js +26 -89
- package/dist/lib/registry/preview.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +0 -7
- package/dist/lib/workspace/index.js +0 -9
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/previews/enumerate.d.ts +23 -27
- package/dist/previews/enumerate.js +27 -48
- package/dist/previews/enumerate.js.map +1 -1
- package/dist/previews/harness.js +1 -1
- package/dist/previews/harness.js.map +1 -1
- package/package.json +4 -7
- package/dist/lib/registry/project-components.d.ts +0 -16
- package/dist/lib/registry/project-components.js.map +0 -1
- package/dist/lib/webhooks/config.d.ts +0 -15
- package/dist/lib/webhooks/config.js +0 -65
- package/dist/lib/webhooks/config.js.map +0 -1
- package/dist/lib/webhooks/dispatcher.d.ts +0 -70
- package/dist/lib/webhooks/dispatcher.js +0 -61
- package/dist/lib/webhooks/dispatcher.js.map +0 -1
- package/dist/lib/webhooks/index.d.ts +0 -11
- package/dist/lib/webhooks/index.js +0 -10
- package/dist/lib/webhooks/index.js.map +0 -1
- package/dist/lib/webhooks/signing.d.ts +0 -15
- package/dist/lib/webhooks/signing.js +0 -24
- package/dist/lib/webhooks/signing.js.map +0 -1
- package/dist/lib/webhooks/tags.d.ts +0 -25
- package/dist/lib/webhooks/tags.js +0 -289
- package/dist/lib/webhooks/tags.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.js","sourceRoot":"","sources":["../../../src/lib/registry/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;
|
|
1
|
+
{"version":3,"file":"preview.js","sourceRoot":"","sources":["../../../src/lib/registry/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAG3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAA;AAQ9E;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAC3B,QAA2B,EAC3B,MAAiC,EACjC,EAAU;IAEV,MAAM,KAAK,GAA2B,EAAE,CAAA;IACxC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAA;QAC1B,IAAI,KAAK,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAA;IAC1D,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,CAAA;AAChD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,QAA2B,EAAE,QAA2B;IACnF,IAAI,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtD,OAAO,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YAC5C,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,IAAI,EAAE,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE,EAAE,WAAW,QAAQ,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC;SAClF,CAAC,CAAC,CAAA;IACL,CAAC;IACD,+EAA+E;IAC/E,mFAAmF;IACnF,MAAM,IAAI,GAAG,WAAW,QAAQ,CAAC,EAAE,IAAI,CAAA;IACvC,IAAI,CAAC,GAAG,CAAC,CAAA;IACT,MAAM,IAAI,GAAG,eAAe,CAAC,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;IACvH,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;AACpC,CAAC","sourcesContent":["import { cloneWithNewIds, staticProp } from '@elytracms/core/project-graph'\nimport type { ComponentNode, JsonValue } from '@elytracms/core/project-graph'\nimport type { ComponentManifest, ComponentRegistry } from '@elytracms/core/component-registry'\nimport { seededNodeFromManifest } from '../../components/builder/node-factory'\n\n/** A named, renderable preview state for a component. */\nexport interface PreviewSpec {\n name: string\n node: ComponentNode\n}\n\n/**\n * A node carrying exactly the given prop values (EC-591 / ratified 2f): a prop\n * the caller does not set stays ABSENT — the implementation's own default applies\n * — and the manifest `default` is never copied in (it is an authoring hint). Until\n * EC-591 this overlaid the overrides on every manifest default, so a fixture\n * insert stored — and rendered — something other than the same fixture pasted\n * from \"copy exact configuration\" (COMPOSITION #19).\n *\n * Node ids are derived deterministically from a stable base (manifest id +\n * fixture index) so previews are byte-identical across SSR and client hydration\n * and reproducible across runs — no module-level counter, no time/random.\n */\nexport function nodeFromProps(\n manifest: ComponentManifest,\n values: Record<string, JsonValue>,\n id: string,\n): ComponentNode {\n const props: ComponentNode['props'] = {}\n for (const name of Object.keys(manifest.props)) {\n const value = values[name]\n if (value !== undefined) props[name] = staticProp(value)\n }\n return { id, componentId: manifest.id, props }\n}\n\n/**\n * Resolve the preview states for a component (brief §7.2, §8.3, EC-054): the\n * declared manifest fixtures; otherwise the single `Default` — the SAME seeded\n * node the blocks panel inserts and `elytra previews` renders for a fixture-less\n * component (a layout container comes pre-filled through its role, EC-383).\n * EC-591: there are no authored previews keyed by engine ids any more — a\n * component that wants a richer preview declares `fixtures`, like any other.\n * Deterministic ids throughout.\n */\nexport function previewSpecs(manifest: ComponentManifest, registry: ComponentRegistry): PreviewSpec[] {\n if (manifest.fixtures && manifest.fixtures.length > 0) {\n return manifest.fixtures.map((fixture, i) => ({\n name: fixture.name,\n node: nodeFromProps(manifest, fixture.props ?? {}, `preview-${manifest.id}-${i}`),\n }))\n }\n // The seed's placeholder ids are re-minted deterministically (root `…-0`, then\n // `…-0-1`, `…-0-2` …) so every node in the preview tree has a distinct, stable id.\n const base = `preview-${manifest.id}-0`\n let n = 0\n const node = cloneWithNewIds(seededNodeFromManifest(manifest, registry), () => (n++ === 0 ? base : `${base}-${n - 1}`))\n return [{ name: 'Default', node }]\n}\n"]}
|
|
@@ -163,13 +163,6 @@ export declare function resolveDeploymentReuse(): StudioConfig['reuse'];
|
|
|
163
163
|
* declares none, or when the studio is wired by env alone with no config.
|
|
164
164
|
*/
|
|
165
165
|
export declare function resolveDeploymentRoutes(): RouteRecord[];
|
|
166
|
-
/**
|
|
167
|
-
* The configured locales of the active config project (AD-8). The same locale
|
|
168
|
-
* set the host stamps onto its per-locale cache tags — used by the revalidation
|
|
169
|
-
* webhook layer (EC-192) so a publish always invalidates EVERY configured locale
|
|
170
|
-
* (e.g. `/de`, `/fr`), never just the default. `[]` when wired by env alone.
|
|
171
|
-
*/
|
|
172
|
-
export declare function resolveDeploymentLocales(): string[];
|
|
173
166
|
/**
|
|
174
167
|
* The active config project's `LocaleConfig` (EC-357 finding #3). Locales are
|
|
175
168
|
* structure and structure is code: when `elytra.config.ts` declares them, they
|
|
@@ -242,15 +242,6 @@ export function resolveDeploymentRoutes() {
|
|
|
242
242
|
// Redirects are editorial DATA now (EC-303), managed in the studio Redirects rail
|
|
243
243
|
// and served dynamically — no longer resolved from config. `resolveDeploymentRedirects`
|
|
244
244
|
// is retired; static/infra redirects are the dev's job via native next.config/vercel.
|
|
245
|
-
/**
|
|
246
|
-
* The configured locales of the active config project (AD-8). The same locale
|
|
247
|
-
* set the host stamps onto its per-locale cache tags — used by the revalidation
|
|
248
|
-
* webhook layer (EC-192) so a publish always invalidates EVERY configured locale
|
|
249
|
-
* (e.g. `/de`, `/fr`), never just the default. `[]` when wired by env alone.
|
|
250
|
-
*/
|
|
251
|
-
export function resolveDeploymentLocales() {
|
|
252
|
-
return activeConfigProject()?.locales.locales ?? [];
|
|
253
|
-
}
|
|
254
245
|
/**
|
|
255
246
|
* The active config project's `LocaleConfig` (EC-357 finding #3). Locales are
|
|
256
247
|
* structure and structure is code: when `elytra.config.ts` declares them, they
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/workspace/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAA;AAUvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAG1D,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAE1D,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,iBAAiB,EACjB,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,mBAAmB,CAAA;AAG1B,IAAI,MAAM,GAA0B,IAAI,CAAA;AACxC,IAAI,YAAY,GAAwB,IAAI,CAAA;AA8B5C,MAAM,UAAU,YAAY,CAC1B,MAAyB,EACzB,MAAqB,EACrB,OAA4B;IAE5B,gBAAgB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;IAC9B,MAAM,GAAG,qBAAqB,CAAC;QAC7B,UAAU,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;QAC/B,QAAQ,EAAE,kBAAkB;KAC7B,CAAC,CAAA;IACF,YAAY,GAAG,MAAM,IAAI,IAAI,CAAA;AAC/B,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,uBAAuB,CAAC,IAAyB;IAC/D,YAAY,GAAG,IAAI,CAAA;AACrB,CAAC;AAED;;;;;;;;;GASG;AACH,IAAI,iBAAiB,GAAkB,IAAI,CAAA;AAE3C,oFAAoF;AACpF,MAAM,UAAU,oBAAoB,CAAC,IAAmB;IACtD,MAAM,MAAM,GAAG,oBAAoB,CAAC,EAAE,CAAC,CAAA;IACvC,iBAAiB,GAAG,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;IAC5D,OAAO,oBAAoB,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;AAC5C,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,iBAAiB,CAAA;AAC1B,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,4BAA4B,CAAC,IAAmB;IAC9D,iBAAiB,GAAG,IAAI,CAAA;AAC1B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,sBAAsB,GAAwB,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAA;AAEtF,8EAA8E;AAC9E,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAC3C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IACvB,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAA;IAC7C,OAAO,sBAAsB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAA;AAC7D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAgB,EAAE,QAAiB;IACvE,MAAM,MAAM,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAA;IAC5C,IAAI,MAAM;QAAE,OAAO,MAAM,CAAA;IACzB,IAAI,QAAQ,KAAK,UAAU,IAAI,QAAQ;QAAE,OAAO,mBAAmB,CAAC,QAAQ,CAAC,CAAA;IAC7E,OAAO,IAAI,CAAA;AACb,CAAC;AAED,4EAA4E;AAC5E,SAAS,mBAAmB;IAC1B,MAAM,KAAK,GAAG,iBAAiB,EAAE,CAAA;IACjC,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAA;IAC7C,IAAI,iBAAiB,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,iBAAiB,CAAC,CAAA;QACtE,IAAI,KAAK;YAAE,OAAO,KAAK,CAAA;IACzB,CAAC;IACD,OAAO,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB;IAC/B,gFAAgF;IAChF,mFAAmF;IACnF,yDAAyD;IACzD,IAAI,CAAC,MAAM;QAAE,MAAM,GAAG,qBAAqB,CAAC,EAAE,CAAC,CAAA;IAC/C,OAAO,MAAM,CAAA;AACf,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,yBAAyB,CAAC,IAA2B;IACnE,MAAM,GAAG,IAAI,CAAA;AACf,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,WAAmB;IACtD,OAAO,oBAAoB,CAAC,mBAAmB,EAAE,EAAE,WAAW,CAAC,CAAA;AACjE,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAE,WAAmB;IACpE,MAAM,KAAK,GAAG,iBAAiB,EAAE,CAAA;IACjC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACjG,OAAO,oBAAoB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;AACnD,CAAC;AAED,sFAAsF;AACtF,SAAS,oBAAoB,CAC3B,OAA6C,EAC7C,WAAmB;IAEnB,IAAI,CAAC,OAAO;QAAE,OAAO,WAAW,CAAA;IAChC,MAAM,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,UAAU,CAAA;IAChG,OAAO,aAAa,IAAI,OAAO,CAAC,MAAM,EAAE,UAAU,IAAI,WAAW,CAAA;AACnE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CAAC,UAAkB;IAC3D,OAAO,mBAAmB,EAAE,EAAE,IAAI,IAAI,UAAU,CAAA;AAClD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B;IAC1C,OAAO,mBAAmB,EAAE,EAAE,WAAW,IAAI,EAAE,CAAA;AACjD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B;IAC1C,OAAO,mBAAmB,EAAE,EAAE,WAAW,CAAA;AAC3C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,+BAA+B;IAC7C,OAAO,YAAY,EAAE,cAAc,CAAA;AACrC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB;IACnC,OAAO,YAAY,EAAE,IAAI,CAAA;AAC3B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,YAAY,EAAE,EAAE,CAAA;AACzB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kCAAkC;IAChD,OAAO,YAAY,EAAE,QAAQ,EAAE,SAAS,CAAA;AAC1C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO,YAAY,EAAE,OAAO,CAAA;AAC9B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B;IAC1C,OAAO,YAAY,EAAE,MAAM,CAAA;AAC7B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO,YAAY,EAAE,KAAK,CAAA;AAC5B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO,mBAAmB,EAAE,EAAE,MAAM,IAAI,EAAE,CAAA;AAC5C,CAAC;AAED,kFAAkF;AAClF,wFAAwF;AACxF,sFAAsF;AAEtF;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO,mBAAmB,EAAE,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,CAAA;AACrD,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,6BAA6B;IAC3C,OAAO,mBAAmB,EAAE,EAAE,OAAO,CAAA;AACvC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,2BAA2B;IACzC,OAAO,mBAAmB,EAAE,EAAE,UAAU,CAAA;AAC1C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,4BAA4B;IAC1C,MAAM,OAAO,GAAG,mBAAmB,EAAE,CAAA;IACrC,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAA;IAC9B,MAAM,QAAQ,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACvE,OAAO,yBAAyB,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAA;AAC1D,CAAC","sourcesContent":["import { frontendUrlForEnvironment } from '@elytracms/core/studio-core'\nimport type {\n BreakpointConfig,\n ElytraConfigInput,\n ResolvedWorkspaceProject,\n} from '@elytracms/core/studio-core'\nimport type { CollectionDef, LocaleConfig, RouteRecord } from '@elytracms/core/cms-core'\nimport type { SidebarNode } from '../../components/app-views/module'\nimport type { CustomFieldRenderer, StudioConfig } from '../../components/app-views/studio-config'\nimport type { GeneratedPreviewLookup } from '../../components/builder/blocks-model'\nimport { provideStudioEnv } from '../persistence/backend-mode'\nimport { resolveWorkspaceState } from './workspace-source'\nimport type { WorkspaceState } from './workspace-source'\n\nexport {\n PROJECT_CONFIG_FILE,\n PROJECT_CONFIG_ISSUE_KEYS,\n PROJECT_CONFIG_POINT,\n projectConfigIssues,\n} from './config-issues'\nexport { resolveWorkspaceState } from './workspace-source'\nexport type { WorkspaceSources, WorkspaceState } from './workspace-source'\nexport {\n isSingleProjectWorkspace,\n launcherAutoForwardTarget,\n persistedProjects,\n selectSwitcherProjects,\n soleWorkspaceProject,\n unprovisionedProjects,\n} from './switcher-source'\nexport type { SwitcherEntry, SwitcherSourceInput } from './switcher-source'\n\nlet cached: WorkspaceState | null = null\nlet studioConfig: StudioConfig | null = null\n\n/**\n * Mount the studio against a project config (EC-223 mount contract) and an\n * optional studio-only customization config (EC-236). The consumer's entry point\n * does `import config from './elytra.config'` + `import studio from\n * './studio.config'` and calls this — an explicit init, NOT an `import.meta.glob`\n * autoload that reaches into the consumer's filesystem (Oli, 2026-06-19: \"there's\n * always an entry point that can trigger a function call\").\n *\n * Two configs, deliberately: `config` (`elytra.config.ts`) is the project\n * definition, shared with the `frontend/` delivery bundle and projected to data;\n * `studio` (`studio.config.tsx` via `defineStudio`) is live builder UI code — the\n * section rail, custom field renderers, branding — that must NEVER reach delivery.\n * Keeping them separate is the customization boundary principle in the mount\n * signature. Seeds the workspace state + the studio-config holder every later\n * query (`getActiveProjectSlug`, `resolveDeployment*`) reads.\n *\n * EC-357 — `options.env`: the consumer's `import.meta.env`, captured HERE because\n * the entry module is code Vite always transforms live. The engine's own\n * `import.meta.env` reads are unreliable inside dep-optimized package code (Vite ≥8\n * omits custom `VITE_*` values there; Vite 7 froze them at optimize time), which\n * silently broke backend URL / mode / dev-login resolution in real consumers.\n * Always pass it: `createStudio(config, studioConfig, { env: import.meta.env })`.\n */\nexport interface StudioMountOptions {\n /** The consumer entry's `import.meta.env` (see `provideStudioEnv` for the contract). */\n env?: Record<string, unknown>\n}\n\nexport function createStudio(\n config: ElytraConfigInput,\n studio?: StudioConfig,\n options?: StudioMountOptions,\n): void {\n provideStudioEnv(options?.env)\n cached = resolveWorkspaceState({\n fileModule: { default: config },\n fileName: 'elytra.config.ts',\n })\n studioConfig = studio ?? null\n}\n\n/** Test seam: set/clear the studio customization config. Never used by app code. */\nexport function setStudioConfigForTests(next: StudioConfig | null): void {\n studioConfig = next\n}\n\n/**\n * The active project slug (EC-179, vision AD-8). One project per backend, so the\n * active project selects which Convex deployment the studio talks to. The root\n * layout sets this from the URL (`/<slug>`) — or, on the sign-in screen, from\n * the `redirect` target's slug — before the auth/persistence adapters are\n * selected, so the *initial* sign-in authenticates against the project the user\n * is heading for (Sanity-style: authenticate to one workspace, then the full\n * config-based switcher is available). `null` at the launcher → the default\n * (first) configured project's deployment.\n */\nlet activeProjectSlug: string | null = null\n\n/** Set the active project slug; returns whether the resolved deployment changed. */\nexport function setActiveProjectSlug(slug: string | null): boolean {\n const before = resolveDeploymentUrl('')\n activeProjectSlug = slug && slug.trim() !== '' ? slug : null\n return resolveDeploymentUrl('') !== before\n}\n\nexport function getActiveProjectSlug(): string | null {\n return activeProjectSlug\n}\n\n/** Test seam: reset the active project slug. Never used by app code. */\nexport function setActiveProjectSlugForTests(slug: string | null): void {\n activeProjectSlug = slug\n}\n\n/**\n * Reserved first-path segments that are NOT project slugs: the studio's own\n * root routes (EC-220). Project surfaces now live at `/<slug>/...` (no `/p/`\n * prefix), so the studio's root namespace is shared between project slugs and\n * these fixed routes — they must be excluded from slug extraction. Keep in\n * sync with the non-`$projectId` files in `src/routes/`; the launcher (`/`) is\n * the empty segment, excluded by the regex requiring one path char.\n */\nconst RESERVED_ROOT_SEGMENTS: ReadonlySet<string> = new Set(['sign-in', 'styleguide'])\n\n/** Extract a project slug from a studio path (`/<slug>/...`), else `null`. */\nexport function projectSlugFromPath(pathname: string): string | null {\n const match = /^\\/([^/?#]+)/.exec(pathname)\n if (!match) return null\n const segment = decodeURIComponent(match[1]!)\n return RESERVED_ROOT_SEGMENTS.has(segment) ? null : segment\n}\n\n/**\n * The project whose backend the studio should bind for a given location\n * (EC-179). A project path (`/<slug>`) binds that project; the sign-in screen\n * binds the project from its `redirect` target so the *initial* sign-in\n * authenticates against the backend the user is heading for (Sanity-style).\n * Anywhere else (the launcher) → `null`, i.e. the default/first project.\n */\nexport function activeSlugForLocation(pathname: string, redirect?: string): string | null {\n const direct = projectSlugFromPath(pathname)\n if (direct) return direct\n if (pathname === '/sign-in' && redirect) return projectSlugFromPath(redirect)\n return null\n}\n\n/** The active config project (by slug), or the first configured project. */\nfunction activeConfigProject() {\n const state = getWorkspaceState()\n if (state.kind !== 'config') return undefined\n if (activeProjectSlug) {\n const match = state.projects.find((p) => p.slug === activeProjectSlug)\n if (match) return match\n }\n return state.projects[0]\n}\n\n/**\n * The studio's resolved workspace state. Deterministic per boot: the config is\n * static input (bundled file or deployment env), so it is resolved once and\n * cached — every consumer (gate, switcher, launcher) sees the same state.\n */\nexport function getWorkspaceState(): WorkspaceState {\n // Seeded by `createStudio(config)` at the entry point. Until then — or when the\n // consumer wires no config — resolve to the empty `none` state (the single-project\n // golden path), rather than reaching for the filesystem.\n if (!cached) cached = resolveWorkspaceState({})\n return cached\n}\n\n/** Test seam: clear/replace the cached state. Never used by app code. */\nexport function setWorkspaceStateForTests(next: WorkspaceState | null): void {\n cached = next\n}\n\n/**\n * Resolve the Convex deployment URL the studio should connect to (vision AD-8:\n * the backend is wired in config). One project per backend, so the **active**\n * project (see {@link setActiveProjectSlug}) selects the deployment via its\n * `convex.deployment` (or its production environment's). At the launcher (no\n * active project) the first configured project is the default backend. Absent\n * config — or a project without a wired deployment — falls back to\n * `VITE_CONVEX_URL` (`fallbackUrl`) for local single-deployment development.\n */\nexport function resolveDeploymentUrl(fallbackUrl: string): string {\n return projectDeploymentUrl(activeConfigProject(), fallbackUrl)\n}\n\n/** The deployment URL for a specific project slug (per-backend auth checks). */\nexport function deploymentUrlForSlug(slug: string, fallbackUrl: string): string {\n const state = getWorkspaceState()\n const project = state.kind === 'config' ? state.projects.find((p) => p.slug === slug) : undefined\n return projectDeploymentUrl(project, fallbackUrl)\n}\n\n/** A project's wired deployment: per-environment ref → project ref → env fallback. */\nfunction projectDeploymentUrl(\n project: ResolvedWorkspaceProject | undefined,\n fallbackUrl: string,\n): string {\n if (!project) return fallbackUrl\n const envDeployment = project.environments.find((e) => e.convex?.deployment)?.convex?.deployment\n return envDeployment ?? project.convex?.deployment ?? fallbackUrl\n}\n\n/**\n * The slug of the project bound to the active deployment (EC-179). One project\n * per backend, so the active config project's slug identifies the deployment's\n * one project; the Convex adapter stamps it onto deployment-scoped records.\n * Absent config falls back to `fallbackId` (used only when wired by env alone).\n */\nexport function resolveDeploymentProjectId(fallbackId: string): string {\n return activeConfigProject()?.slug ?? fallbackId\n}\n\n/**\n * The CMS collections of the active config project (AD-11, EC-183). CMS\n * collection schemas are config-owned: the studio reads the model from\n * `elytra.config.ts` (served through the persistence port — the backend has no\n * `cmsSchemas` table). `[]` when the active project declares none, or when the\n * studio is wired by env alone with no config.\n */\nexport function resolveDeploymentCollections(): CollectionDef[] {\n return activeConfigProject()?.collections ?? []\n}\n\n/**\n * The canvas preview breakpoints of the active config project (EC-250).\n * Config-sourced (config-as-code, derived from the site's Tailwind `screens`);\n * `undefined` when the project declares none — the canvas then falls back to\n * Tailwind's default screens (see `resolveCanvasViewports`), never a baked-in triple.\n */\nexport function resolveDeploymentBreakpoints(): BreakpointConfig[] | undefined {\n return activeConfigProject()?.breakpoints\n}\n\n/**\n * The custom field renderers from the studio config (EC-238): control name →\n * component. Studio-only live code (from `defineStudio`, never the project schema\n * or the delivery bundle); `undefined` when none — the document editor then uses\n * its built-in control for every field. Studio-wide (not per-project): the rail and\n * renderers customize the studio, not one backend.\n */\nexport function resolveDeploymentFieldRenderers(): Record<string, CustomFieldRenderer> | undefined {\n return studioConfig?.fieldRenderers\n}\n\n/**\n * The studio-UI localization config from the studio config (EC-241): the default\n * locale + imported translation catalogs. Studio-only live code (from `defineStudio`,\n * never the project schema or the delivery bundle); `undefined` when none — the\n * studio then renders its built-in English chrome. Studio-wide, like the rail and\n * field renderers (localizes the studio, not one backend).\n */\nexport function resolveDeploymentI18n(): StudioConfig['i18n'] {\n return studioConfig?.i18n\n}\n\n/**\n * The AI platform config from the studio config (EC-331): provider, knowledge, and\n * limits. Studio-only live code (from `defineStudio`, never the delivery bundle);\n * `undefined` when no `ai` block is configured — the feature is then fully absent\n * (the AI subsystem never boots, so `@elytracms/studio/lib/ai`'s runtime — and the\n * `ai`/`@ai-sdk/*` SDK behind it — is never imported). Studio-wide, like the rail\n * and field renderers.\n */\nexport function resolveDeploymentAi(): StudioConfig['ai'] {\n return studioConfig?.ai\n}\n\n/**\n * The generated-preview lookup from the studio config (EC-439 AD-2, the seam\n * EC-440 fills): a pure `componentId + fixtureName → URL` function the blocks\n * panel threads into its tile model. Studio-only live code (from `defineStudio`,\n * never the delivery bundle); `undefined` when none — tiles then fall back from\n * a curated `manifest.previewImage` straight to the icon+name placeholder.\n * Studio-wide, like the rail and field renderers.\n */\nexport function resolveDeploymentGeneratedPreviews(): GeneratedPreviewLookup | undefined {\n return studioConfig?.previews?.generated\n}\n\n/**\n * The configurable section rail from the studio config (EC-237): an ordered list of\n * `nav.*` nodes (views / groups / separators). Studio-only live code (carries view\n * objects + icons); `undefined` when none — the studio then derives the default\n * rail from its built-in views. Invalid entries surface as visible issues at\n * resolution, never silent.\n */\nexport function resolveDeploymentSidebar(): SidebarNode[] | undefined {\n return studioConfig?.sidebar\n}\n\n/**\n * The canvas page-surface theme from the studio config (EC-389): body classes +\n * raw CSS for the preview iframe, so the canvas wears the SITE's chrome. Studio-only\n * (from `defineStudio`, never the delivery bundle); `undefined` when none — the\n * canvas keeps its built-in defaults (`bg-background text-foreground font-sans`\n * body). Studio-wide, like the rail and field renderers.\n */\nexport function resolveDeploymentCanvasTheme(): StudioConfig['canvas'] {\n return studioConfig?.canvas\n}\n\n/**\n * The reusable-elements declaration from the studio config (EC-467): WHICH\n * component is the project's reuse embed. Studio-only (from `defineStudio`, never\n * the delivery bundle); `undefined` when none — the studio then INFERS the embed\n * from the manifests + collections exactly as it always has (absence means infer,\n * not off). Studio-wide, like the rail and field renderers.\n */\nexport function resolveDeploymentReuse(): StudioConfig['reuse'] {\n return studioConfig?.reuse\n}\n\n/**\n * The resolved URL routes of the active config project (EC-207, \"routes as\n * code\"). Routes are config-sourced: the studio reads the materialized\n * `RouteRecord[]` from the active `elytra.config.ts` project — never from the\n * backend (the `routes` system collection is retired). The Routes surface and\n * the per-page URL preview both read through here. `[]` when the active project\n * declares none, or when the studio is wired by env alone with no config.\n */\nexport function resolveDeploymentRoutes(): RouteRecord[] {\n return activeConfigProject()?.routes ?? []\n}\n\n// Redirects are editorial DATA now (EC-303), managed in the studio Redirects rail\n// and served dynamically — no longer resolved from config. `resolveDeploymentRedirects`\n// is retired; static/infra redirects are the dev's job via native next.config/vercel.\n\n/**\n * The configured locales of the active config project (AD-8). The same locale\n * set the host stamps onto its per-locale cache tags — used by the revalidation\n * webhook layer (EC-192) so a publish always invalidates EVERY configured locale\n * (e.g. `/de`, `/fr`), never just the default. `[]` when wired by env alone.\n */\nexport function resolveDeploymentLocales(): string[] {\n return activeConfigProject()?.locales.locales ?? []\n}\n\n/**\n * The active config project's `LocaleConfig` (EC-357 finding #3). Locales are\n * structure and structure is code: when `elytra.config.ts` declares them, they\n * drive the studio's ambient locale switcher in EVERY mode — the CMS surface must\n * not fall back to the sample `en/de` fixture (playground, or a connected first\n * run with nothing persisted) when the config says otherwise. `undefined` when no\n * config project is active (env-only wiring keeps the existing kit/sample\n * fallbacks).\n *\n * EC-519: this used to REASSEMBLE the config's flat `locales` + `defaultLocale`\n * pair into a `LocaleConfig`, and had grown its own repair rule — an absent or\n * out-of-set default fell back to `locales[0]`. That is a fourth place a locale\n * could be invented, in the surface furthest from the author. The resolver now\n * hands over the same `LocaleConfig` the delivery host takes, so there is nothing\n * to reassemble and nothing to repair: a default outside the set is a config\n * ERROR (`default-locale-not-in-locales`) and the project never resolves.\n */\nexport function resolveDeploymentLocaleConfig(): LocaleConfig | undefined {\n return activeConfigProject()?.locales\n}\n\n/**\n * The active project's components reference (EC-194, AD-11): where the consuming\n * repo's OWN real components live (`{ package }` or `{ dir }`). When set, the\n * studio registry composes with the repo's components instead of the built-in\n * demo fixtures — so the page builder edits the repo's actual component library.\n * `undefined` → the project ships no code components; the studio falls back to the\n * golden-path fixtures.\n */\nexport function resolveDeploymentComponents() {\n return activeConfigProject()?.components\n}\n\n/**\n * The active project's public site URL (EC-215): the live site's base URL the\n * studio links to (a document's live URL + open-live button, EC-216). Resolved\n * in lockstep with {@link resolveDeploymentUrl} — the environment bound to the\n * active deployment (the first with a wired deployment) provides the URL, falling\n * back to the project-level `frontendUrl`. No active-environment selector exists\n * yet, so the bound environment is the active one. `undefined` when the active\n * project (or env-only wiring) configures no frontend URL.\n */\nexport function resolveDeploymentFrontendUrl(): string | undefined {\n const project = activeConfigProject()\n if (!project) return undefined\n const boundEnv = project.environments.find((e) => e.convex?.deployment)\n return frontendUrlForEnvironment(project, boundEnv?.key)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/workspace/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { frontendUrlForEnvironment } from '@elytracms/core/studio-core'\nimport type {\n BreakpointConfig,\n ElytraConfigInput,\n ResolvedWorkspaceProject,\n} from '@elytracms/core/studio-core'\nimport type { CollectionDef, LocaleConfig, RouteRecord } from '@elytracms/core/cms-core'\nimport type { SidebarNode } from '../../components/app-views/module'\nimport type { CustomFieldRenderer, StudioConfig } from '../../components/app-views/studio-config'\nimport type { GeneratedPreviewLookup } from '../../components/builder/blocks-model'\nimport { provideStudioEnv } from '../persistence/backend-mode'\nimport { resolveWorkspaceState } from './workspace-source'\nimport type { WorkspaceState } from './workspace-source'\n\nexport {\n PROJECT_CONFIG_FILE,\n PROJECT_CONFIG_ISSUE_KEYS,\n PROJECT_CONFIG_POINT,\n projectConfigIssues,\n} from './config-issues'\nexport { resolveWorkspaceState } from './workspace-source'\nexport type { WorkspaceSources, WorkspaceState } from './workspace-source'\nexport {\n isSingleProjectWorkspace,\n launcherAutoForwardTarget,\n persistedProjects,\n selectSwitcherProjects,\n soleWorkspaceProject,\n unprovisionedProjects,\n} from './switcher-source'\nexport type { SwitcherEntry, SwitcherSourceInput } from './switcher-source'\n\nlet cached: WorkspaceState | null = null\nlet studioConfig: StudioConfig | null = null\n\n/**\n * Mount the studio against a project config (EC-223 mount contract) and an\n * optional studio-only customization config (EC-236). The consumer's entry point\n * does `import config from './elytra.config'` + `import studio from\n * './studio.config'` and calls this — an explicit init, NOT an `import.meta.glob`\n * autoload that reaches into the consumer's filesystem (Oli, 2026-06-19: \"there's\n * always an entry point that can trigger a function call\").\n *\n * Two configs, deliberately: `config` (`elytra.config.ts`) is the project\n * definition, shared with the `frontend/` delivery bundle and projected to data;\n * `studio` (`studio.config.tsx` via `defineStudio`) is live builder UI code — the\n * section rail, custom field renderers, branding — that must NEVER reach delivery.\n * Keeping them separate is the customization boundary principle in the mount\n * signature. Seeds the workspace state + the studio-config holder every later\n * query (`getActiveProjectSlug`, `resolveDeployment*`) reads.\n *\n * EC-357 — `options.env`: the consumer's `import.meta.env`, captured HERE because\n * the entry module is code Vite always transforms live. The engine's own\n * `import.meta.env` reads are unreliable inside dep-optimized package code (Vite ≥8\n * omits custom `VITE_*` values there; Vite 7 froze them at optimize time), which\n * silently broke backend URL / mode / dev-login resolution in real consumers.\n * Always pass it: `createStudio(config, studioConfig, { env: import.meta.env })`.\n */\nexport interface StudioMountOptions {\n /** The consumer entry's `import.meta.env` (see `provideStudioEnv` for the contract). */\n env?: Record<string, unknown>\n}\n\nexport function createStudio(\n config: ElytraConfigInput,\n studio?: StudioConfig,\n options?: StudioMountOptions,\n): void {\n provideStudioEnv(options?.env)\n cached = resolveWorkspaceState({\n fileModule: { default: config },\n fileName: 'elytra.config.ts',\n })\n studioConfig = studio ?? null\n}\n\n/** Test seam: set/clear the studio customization config. Never used by app code. */\nexport function setStudioConfigForTests(next: StudioConfig | null): void {\n studioConfig = next\n}\n\n/**\n * The active project slug (EC-179, vision AD-8). One project per backend, so the\n * active project selects which Convex deployment the studio talks to. The root\n * layout sets this from the URL (`/<slug>`) — or, on the sign-in screen, from\n * the `redirect` target's slug — before the auth/persistence adapters are\n * selected, so the *initial* sign-in authenticates against the project the user\n * is heading for (Sanity-style: authenticate to one workspace, then the full\n * config-based switcher is available). `null` at the launcher → the default\n * (first) configured project's deployment.\n */\nlet activeProjectSlug: string | null = null\n\n/** Set the active project slug; returns whether the resolved deployment changed. */\nexport function setActiveProjectSlug(slug: string | null): boolean {\n const before = resolveDeploymentUrl('')\n activeProjectSlug = slug && slug.trim() !== '' ? slug : null\n return resolveDeploymentUrl('') !== before\n}\n\nexport function getActiveProjectSlug(): string | null {\n return activeProjectSlug\n}\n\n/** Test seam: reset the active project slug. Never used by app code. */\nexport function setActiveProjectSlugForTests(slug: string | null): void {\n activeProjectSlug = slug\n}\n\n/**\n * Reserved first-path segments that are NOT project slugs: the studio's own\n * root routes (EC-220). Project surfaces now live at `/<slug>/...` (no `/p/`\n * prefix), so the studio's root namespace is shared between project slugs and\n * these fixed routes — they must be excluded from slug extraction. Keep in\n * sync with the non-`$projectId` files in `src/routes/`; the launcher (`/`) is\n * the empty segment, excluded by the regex requiring one path char.\n */\nconst RESERVED_ROOT_SEGMENTS: ReadonlySet<string> = new Set(['sign-in', 'styleguide'])\n\n/** Extract a project slug from a studio path (`/<slug>/...`), else `null`. */\nexport function projectSlugFromPath(pathname: string): string | null {\n const match = /^\\/([^/?#]+)/.exec(pathname)\n if (!match) return null\n const segment = decodeURIComponent(match[1]!)\n return RESERVED_ROOT_SEGMENTS.has(segment) ? null : segment\n}\n\n/**\n * The project whose backend the studio should bind for a given location\n * (EC-179). A project path (`/<slug>`) binds that project; the sign-in screen\n * binds the project from its `redirect` target so the *initial* sign-in\n * authenticates against the backend the user is heading for (Sanity-style).\n * Anywhere else (the launcher) → `null`, i.e. the default/first project.\n */\nexport function activeSlugForLocation(pathname: string, redirect?: string): string | null {\n const direct = projectSlugFromPath(pathname)\n if (direct) return direct\n if (pathname === '/sign-in' && redirect) return projectSlugFromPath(redirect)\n return null\n}\n\n/** The active config project (by slug), or the first configured project. */\nfunction activeConfigProject() {\n const state = getWorkspaceState()\n if (state.kind !== 'config') return undefined\n if (activeProjectSlug) {\n const match = state.projects.find((p) => p.slug === activeProjectSlug)\n if (match) return match\n }\n return state.projects[0]\n}\n\n/**\n * The studio's resolved workspace state. Deterministic per boot: the config is\n * static input (bundled file or deployment env), so it is resolved once and\n * cached — every consumer (gate, switcher, launcher) sees the same state.\n */\nexport function getWorkspaceState(): WorkspaceState {\n // Seeded by `createStudio(config)` at the entry point. Until then — or when the\n // consumer wires no config — resolve to the empty `none` state (the single-project\n // golden path), rather than reaching for the filesystem.\n if (!cached) cached = resolveWorkspaceState({})\n return cached\n}\n\n/** Test seam: clear/replace the cached state. Never used by app code. */\nexport function setWorkspaceStateForTests(next: WorkspaceState | null): void {\n cached = next\n}\n\n/**\n * Resolve the Convex deployment URL the studio should connect to (vision AD-8:\n * the backend is wired in config). One project per backend, so the **active**\n * project (see {@link setActiveProjectSlug}) selects the deployment via its\n * `convex.deployment` (or its production environment's). At the launcher (no\n * active project) the first configured project is the default backend. Absent\n * config — or a project without a wired deployment — falls back to\n * `VITE_CONVEX_URL` (`fallbackUrl`) for local single-deployment development.\n */\nexport function resolveDeploymentUrl(fallbackUrl: string): string {\n return projectDeploymentUrl(activeConfigProject(), fallbackUrl)\n}\n\n/** The deployment URL for a specific project slug (per-backend auth checks). */\nexport function deploymentUrlForSlug(slug: string, fallbackUrl: string): string {\n const state = getWorkspaceState()\n const project = state.kind === 'config' ? state.projects.find((p) => p.slug === slug) : undefined\n return projectDeploymentUrl(project, fallbackUrl)\n}\n\n/** A project's wired deployment: per-environment ref → project ref → env fallback. */\nfunction projectDeploymentUrl(\n project: ResolvedWorkspaceProject | undefined,\n fallbackUrl: string,\n): string {\n if (!project) return fallbackUrl\n const envDeployment = project.environments.find((e) => e.convex?.deployment)?.convex?.deployment\n return envDeployment ?? project.convex?.deployment ?? fallbackUrl\n}\n\n/**\n * The slug of the project bound to the active deployment (EC-179). One project\n * per backend, so the active config project's slug identifies the deployment's\n * one project; the Convex adapter stamps it onto deployment-scoped records.\n * Absent config falls back to `fallbackId` (used only when wired by env alone).\n */\nexport function resolveDeploymentProjectId(fallbackId: string): string {\n return activeConfigProject()?.slug ?? fallbackId\n}\n\n/**\n * The CMS collections of the active config project (AD-11, EC-183). CMS\n * collection schemas are config-owned: the studio reads the model from\n * `elytra.config.ts` (served through the persistence port — the backend has no\n * `cmsSchemas` table). `[]` when the active project declares none, or when the\n * studio is wired by env alone with no config.\n */\nexport function resolveDeploymentCollections(): CollectionDef[] {\n return activeConfigProject()?.collections ?? []\n}\n\n/**\n * The canvas preview breakpoints of the active config project (EC-250).\n * Config-sourced (config-as-code, derived from the site's Tailwind `screens`);\n * `undefined` when the project declares none — the canvas then falls back to\n * Tailwind's default screens (see `resolveCanvasViewports`), never a baked-in triple.\n */\nexport function resolveDeploymentBreakpoints(): BreakpointConfig[] | undefined {\n return activeConfigProject()?.breakpoints\n}\n\n/**\n * The custom field renderers from the studio config (EC-238): control name →\n * component. Studio-only live code (from `defineStudio`, never the project schema\n * or the delivery bundle); `undefined` when none — the document editor then uses\n * its built-in control for every field. Studio-wide (not per-project): the rail and\n * renderers customize the studio, not one backend.\n */\nexport function resolveDeploymentFieldRenderers(): Record<string, CustomFieldRenderer> | undefined {\n return studioConfig?.fieldRenderers\n}\n\n/**\n * The studio-UI localization config from the studio config (EC-241): the default\n * locale + imported translation catalogs. Studio-only live code (from `defineStudio`,\n * never the project schema or the delivery bundle); `undefined` when none — the\n * studio then renders its built-in English chrome. Studio-wide, like the rail and\n * field renderers (localizes the studio, not one backend).\n */\nexport function resolveDeploymentI18n(): StudioConfig['i18n'] {\n return studioConfig?.i18n\n}\n\n/**\n * The AI platform config from the studio config (EC-331): provider, knowledge, and\n * limits. Studio-only live code (from `defineStudio`, never the delivery bundle);\n * `undefined` when no `ai` block is configured — the feature is then fully absent\n * (the AI subsystem never boots, so `@elytracms/studio/lib/ai`'s runtime — and the\n * `ai`/`@ai-sdk/*` SDK behind it — is never imported). Studio-wide, like the rail\n * and field renderers.\n */\nexport function resolveDeploymentAi(): StudioConfig['ai'] {\n return studioConfig?.ai\n}\n\n/**\n * The generated-preview lookup from the studio config (EC-439 AD-2, the seam\n * EC-440 fills): a pure `componentId + fixtureName → URL` function the blocks\n * panel threads into its tile model. Studio-only live code (from `defineStudio`,\n * never the delivery bundle); `undefined` when none — tiles then fall back from\n * a curated `manifest.previewImage` straight to the icon+name placeholder.\n * Studio-wide, like the rail and field renderers.\n */\nexport function resolveDeploymentGeneratedPreviews(): GeneratedPreviewLookup | undefined {\n return studioConfig?.previews?.generated\n}\n\n/**\n * The configurable section rail from the studio config (EC-237): an ordered list of\n * `nav.*` nodes (views / groups / separators). Studio-only live code (carries view\n * objects + icons); `undefined` when none — the studio then derives the default\n * rail from its built-in views. Invalid entries surface as visible issues at\n * resolution, never silent.\n */\nexport function resolveDeploymentSidebar(): SidebarNode[] | undefined {\n return studioConfig?.sidebar\n}\n\n/**\n * The canvas page-surface theme from the studio config (EC-389): body classes +\n * raw CSS for the preview iframe, so the canvas wears the SITE's chrome. Studio-only\n * (from `defineStudio`, never the delivery bundle); `undefined` when none — the\n * canvas keeps its built-in defaults (`bg-background text-foreground font-sans`\n * body). Studio-wide, like the rail and field renderers.\n */\nexport function resolveDeploymentCanvasTheme(): StudioConfig['canvas'] {\n return studioConfig?.canvas\n}\n\n/**\n * The reusable-elements declaration from the studio config (EC-467): WHICH\n * component is the project's reuse embed. Studio-only (from `defineStudio`, never\n * the delivery bundle); `undefined` when none — the studio then INFERS the embed\n * from the manifests + collections exactly as it always has (absence means infer,\n * not off). Studio-wide, like the rail and field renderers.\n */\nexport function resolveDeploymentReuse(): StudioConfig['reuse'] {\n return studioConfig?.reuse\n}\n\n/**\n * The resolved URL routes of the active config project (EC-207, \"routes as\n * code\"). Routes are config-sourced: the studio reads the materialized\n * `RouteRecord[]` from the active `elytra.config.ts` project — never from the\n * backend (the `routes` system collection is retired). The Routes surface and\n * the per-page URL preview both read through here. `[]` when the active project\n * declares none, or when the studio is wired by env alone with no config.\n */\nexport function resolveDeploymentRoutes(): RouteRecord[] {\n return activeConfigProject()?.routes ?? []\n}\n\n// Redirects are editorial DATA now (EC-303), managed in the studio Redirects rail\n// and served dynamically — no longer resolved from config. `resolveDeploymentRedirects`\n// is retired; static/infra redirects are the dev's job via native next.config/vercel.\n\n/**\n * The active config project's `LocaleConfig` (EC-357 finding #3). Locales are\n * structure and structure is code: when `elytra.config.ts` declares them, they\n * drive the studio's ambient locale switcher in EVERY mode — the CMS surface must\n * not fall back to the sample `en/de` fixture (playground, or a connected first\n * run with nothing persisted) when the config says otherwise. `undefined` when no\n * config project is active (env-only wiring keeps the existing kit/sample\n * fallbacks).\n *\n * EC-519: this used to REASSEMBLE the config's flat `locales` + `defaultLocale`\n * pair into a `LocaleConfig`, and had grown its own repair rule — an absent or\n * out-of-set default fell back to `locales[0]`. That is a fourth place a locale\n * could be invented, in the surface furthest from the author. The resolver now\n * hands over the same `LocaleConfig` the delivery host takes, so there is nothing\n * to reassemble and nothing to repair: a default outside the set is a config\n * ERROR (`default-locale-not-in-locales`) and the project never resolves.\n */\nexport function resolveDeploymentLocaleConfig(): LocaleConfig | undefined {\n return activeConfigProject()?.locales\n}\n\n/**\n * The active project's components reference (EC-194, AD-11): where the consuming\n * repo's OWN real components live (`{ package }` or `{ dir }`). When set, the\n * studio registry composes with the repo's components instead of the built-in\n * demo fixtures — so the page builder edits the repo's actual component library.\n * `undefined` → the project ships no code components; the studio falls back to the\n * golden-path fixtures.\n */\nexport function resolveDeploymentComponents() {\n return activeConfigProject()?.components\n}\n\n/**\n * The active project's public site URL (EC-215): the live site's base URL the\n * studio links to (a document's live URL + open-live button, EC-216). Resolved\n * in lockstep with {@link resolveDeploymentUrl} — the environment bound to the\n * active deployment (the first with a wired deployment) provides the URL, falling\n * back to the project-level `frontendUrl`. No active-environment selector exists\n * yet, so the bound environment is the active one. `undefined` when the active\n * project (or env-only wiring) configures no frontend URL.\n */\nexport function resolveDeploymentFrontendUrl(): string | undefined {\n const project = activeConfigProject()\n if (!project) return undefined\n const boundEnv = project.environments.find((e) => e.convex?.deployment)\n return frontendUrlForEnvironment(project, boundEnv?.key)\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ComponentFixture,
|
|
2
|
-
import type {
|
|
1
|
+
import type { ComponentFixture, ComponentManifest } from '@elytracms/core/component-registry';
|
|
2
|
+
import type { ComponentEntry } from '@elytracms/core/runtime-renderer';
|
|
3
3
|
import type { StudioRegistry } from '../lib/registry/index.js';
|
|
4
4
|
/**
|
|
5
5
|
* EC-440 — the fixture-preview enumeration behind `elytra previews`. ONE
|
|
@@ -13,9 +13,10 @@ import type { StudioRegistry } from '../lib/registry/index.js';
|
|
|
13
13
|
* manifests + implementations — code), never a persistence adapter, auth, or
|
|
14
14
|
* content data. The entry set mirrors the blocks panel exactly (EC-439): every
|
|
15
15
|
* canvas-eligible manifest, one entry per declared fixture, and a single
|
|
16
|
-
* implicit {@link DEFAULT_VARIANT} entry for fixture-less manifests.
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* implicit {@link DEFAULT_VARIANT} entry for fixture-less manifests. Exactly the
|
|
17
|
+
* config's components — the engine adds none (EC-591; the shipped components are
|
|
18
|
+
* there when the config lists `@elytracms/components`). Layout-helper presets are
|
|
19
|
+
* NOT manifests and keep their placeholder treatment.
|
|
19
20
|
*/
|
|
20
21
|
/** One renderable preview: a component fixture plus its staleness-hash inputs. */
|
|
21
22
|
export interface PreviewEntrySpec {
|
|
@@ -49,30 +50,25 @@ export interface PreviewEntrySpec {
|
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
52
|
/**
|
|
52
|
-
* The component
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* id
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* fallback, never `createElement`) and returned as an `invalid-implementation`
|
|
64
|
-
* issue. The first-wins dedupe runs BEFORE the check, so a withheld entry still
|
|
65
|
-
* claims its id.
|
|
53
|
+
* The component list a config carries, as the harness registry input: every
|
|
54
|
+
* resolved project's `components`, concatenated in project order. An entry two
|
|
55
|
+
* projects share (the same `{ manifest, implementation }` object — e.g. both list
|
|
56
|
+
* `...elytraComponents`) is the SAME component and is kept once; two DIFFERENT
|
|
57
|
+
* entries under one id are a real conflict — the generated-preview seam is keyed
|
|
58
|
+
* by component id alone, so one image would serve both — and go to the one
|
|
59
|
+
* registration rule like any duplicate: the first is kept whole, the second is a
|
|
60
|
+
* `duplicate-id` issue (EC-591; before, previews skipped it silently and showed
|
|
61
|
+
* the first project's render in the second project's tile — COMPOSITION #13).
|
|
62
|
+
* A config that fails shape-resolution contributes no components; the CLI
|
|
63
|
+
* surfaces config problems through the studio/doctor, not here.
|
|
66
64
|
*/
|
|
67
|
-
export declare function previewComponentsFromConfig(config: unknown):
|
|
68
|
-
manifests: ComponentManifest[];
|
|
69
|
-
implementations: ComponentImplementations;
|
|
70
|
-
issues: ComponentIssue[];
|
|
71
|
-
};
|
|
65
|
+
export declare function previewComponentsFromConfig(config: unknown): readonly ComponentEntry[];
|
|
72
66
|
/**
|
|
73
|
-
* The {@link StudioRegistry}-shaped registry the harness renders against
|
|
74
|
-
*
|
|
75
|
-
* studio registry
|
|
67
|
+
* The {@link StudioRegistry}-shaped registry the harness renders against: the
|
|
68
|
+
* config's list through core's ONE registration step (`registerComponents`, the
|
|
69
|
+
* same call the studio registry and the Next host make — EC-591), so a duplicate,
|
|
70
|
+
* a role claim and an `invalid-implementation` are the identical issue in
|
|
71
|
+
* `registry.issues` on every surface.
|
|
76
72
|
*/
|
|
77
73
|
export declare function previewRegistryFromConfig(config: unknown): StudioRegistry;
|
|
78
74
|
/**
|
|
@@ -1,63 +1,42 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { isCanvasEligible } from '@elytracms/core/component-registry';
|
|
2
|
+
import { registerComponents } from '@elytracms/core/runtime-renderer';
|
|
3
3
|
import { resolveWorkspaceConfig } from '@elytracms/core/studio-core';
|
|
4
4
|
import { DEFAULT_VARIANT } from '../components/builder/blocks-model.js';
|
|
5
5
|
/**
|
|
6
|
-
* The component
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* id
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* fallback, never `createElement`) and returned as an `invalid-implementation`
|
|
18
|
-
* issue. The first-wins dedupe runs BEFORE the check, so a withheld entry still
|
|
19
|
-
* claims its id.
|
|
6
|
+
* The component list a config carries, as the harness registry input: every
|
|
7
|
+
* resolved project's `components`, concatenated in project order. An entry two
|
|
8
|
+
* projects share (the same `{ manifest, implementation }` object — e.g. both list
|
|
9
|
+
* `...elytraComponents`) is the SAME component and is kept once; two DIFFERENT
|
|
10
|
+
* entries under one id are a real conflict — the generated-preview seam is keyed
|
|
11
|
+
* by component id alone, so one image would serve both — and go to the one
|
|
12
|
+
* registration rule like any duplicate: the first is kept whole, the second is a
|
|
13
|
+
* `duplicate-id` issue (EC-591; before, previews skipped it silently and showed
|
|
14
|
+
* the first project's render in the second project's tile — COMPOSITION #13).
|
|
15
|
+
* A config that fails shape-resolution contributes no components; the CLI
|
|
16
|
+
* surfaces config problems through the studio/doctor, not here.
|
|
20
17
|
*/
|
|
21
18
|
export function previewComponentsFromConfig(config) {
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
for (const manifest of base.manifests) {
|
|
26
|
-
const implementation = getImplementation(base.implementations, manifest.id);
|
|
27
|
-
if (implementation)
|
|
28
|
-
implementations[manifest.id] = implementation;
|
|
29
|
-
}
|
|
30
|
-
const seen = new Set(manifests.map((m) => m.id));
|
|
31
|
-
const kept = [];
|
|
32
|
-
const resolved = resolveWorkspaceConfig(config);
|
|
33
|
-
for (const project of resolved.projects) {
|
|
19
|
+
const seen = new Set();
|
|
20
|
+
const entries = [];
|
|
21
|
+
for (const project of resolveWorkspaceConfig(config).projects) {
|
|
34
22
|
for (const entry of project.components ?? []) {
|
|
35
|
-
if (seen.has(entry
|
|
23
|
+
if (seen.has(entry))
|
|
36
24
|
continue;
|
|
37
|
-
seen.add(entry
|
|
38
|
-
|
|
39
|
-
kept.push(entry);
|
|
25
|
+
seen.add(entry);
|
|
26
|
+
entries.push(entry);
|
|
40
27
|
}
|
|
41
28
|
}
|
|
42
|
-
|
|
43
|
-
// so the merge order below decides nothing.
|
|
44
|
-
const project = registerImplementations(kept);
|
|
45
|
-
return {
|
|
46
|
-
manifests,
|
|
47
|
-
implementations: { ...implementations, ...project.implementations },
|
|
48
|
-
issues: project.issues,
|
|
49
|
-
};
|
|
29
|
+
return entries;
|
|
50
30
|
}
|
|
51
31
|
/**
|
|
52
|
-
* The {@link StudioRegistry}-shaped registry the harness renders against
|
|
53
|
-
*
|
|
54
|
-
* studio registry
|
|
32
|
+
* The {@link StudioRegistry}-shaped registry the harness renders against: the
|
|
33
|
+
* config's list through core's ONE registration step (`registerComponents`, the
|
|
34
|
+
* same call the studio registry and the Next host make — EC-591), so a duplicate,
|
|
35
|
+
* a role claim and an `invalid-implementation` are the identical issue in
|
|
36
|
+
* `registry.issues` on every surface.
|
|
55
37
|
*/
|
|
56
38
|
export function previewRegistryFromConfig(config) {
|
|
57
|
-
|
|
58
|
-
const registry = new ComponentRegistry(manifests);
|
|
59
|
-
registry.issues.push(...issues);
|
|
60
|
-
return { registry, implementations };
|
|
39
|
+
return registerComponents(previewComponentsFromConfig(config));
|
|
61
40
|
}
|
|
62
41
|
/**
|
|
63
42
|
* Enumerate every generated-preview entry for a config: canvas-eligible
|
|
@@ -68,7 +47,7 @@ export function previewRegistryFromConfig(config) {
|
|
|
68
47
|
* deterministic regardless of registration order.
|
|
69
48
|
*/
|
|
70
49
|
export function previewEntriesForConfig(config) {
|
|
71
|
-
const
|
|
50
|
+
const manifests = previewRegistryFromConfig(config).registry.list();
|
|
72
51
|
const entries = [];
|
|
73
52
|
for (const manifest of [...manifests].sort((a, b) => a.id.localeCompare(b.id))) {
|
|
74
53
|
if (!isCanvasEligible(manifest))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enumerate.js","sourceRoot":"","sources":["../../src/previews/enumerate.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentType } from 'react'\nimport { ComponentRegistry, isCanvasEligible } from '@elytracms/core/component-registry'\nimport type {\n ComponentFixture,\n ComponentIssue,\n ComponentManifest,\n} from '@elytracms/core/component-registry'\nimport { basePrimitives, getImplementation, registerImplementations } from '@elytracms/core/runtime-renderer'\nimport type { ComponentImplementations } from '@elytracms/core/runtime-renderer'\nimport { resolveWorkspaceConfig } from '@elytracms/core/studio-core'\nimport type { HostComponentEntry } from '@elytracms/core/studio-core'\nimport { DEFAULT_VARIANT } from '../components/builder/blocks-model'\nimport type { StudioRegistry } from '../lib/registry'\n\n/**\n * EC-440 — the fixture-preview enumeration behind `elytra previews`. ONE\n * source of truth for \"which previews exist\" shared by the CLI's node-side\n * driver (hashing, staleness `--check`) and the browser render harness\n * ({@link import('./harness').mountPreviewHarness}) — both derive the entry\n * set from the consumer's `elytra.config.ts` default export through here, so\n * generation, staleness checking, and rendering can never disagree.\n *\n * DATA-FREE by construction: this module reads the consumer CONFIG (registry\n * manifests + implementations — code), never a persistence adapter, auth, or\n * content data. The entry set mirrors the blocks panel exactly (EC-439): every\n * canvas-eligible manifest, one entry per declared fixture, and a single\n * implicit {@link DEFAULT_VARIANT} entry for fixture-less manifests. Base\n * primitives are included — they are insertable tiles too. Layout-helper\n * presets are NOT manifests and keep their placeholder treatment.\n */\n\n/** One renderable preview: a component fixture plus its staleness-hash inputs. */\nexport interface PreviewEntrySpec {\n componentId: string\n /** The fixture name, or {@link DEFAULT_VARIANT} for a fixture-less manifest. */\n fixture: string\n /**\n * The DECLARED render inputs the CLI hashes per entry for staleness: the\n * manifest's prop + slot shape (defaults live in the prop field-defs, so\n * seeded renders are covered) and the declared fixture's own\n * props/slotChildren (`fixture: null` for the implicit Default variant). A\n * change here re-renders one entry.\n *\n * NOT in here: the component implementation and the theme — a config arrives\n * evaluated, so neither has a per-entry identity. The CLI covers them as\n * manifest-level render dependencies instead (EC-600: content hashes of the\n * project source files the config imports plus the theme `@import` chain),\n * and a change there re-renders every entry. Before EC-600 this comment said\n * regeneration caught such drift; it did not — regeneration only rendered what\n * this hash called stale.\n */\n hashInput: {\n manifest: { props: ComponentManifest['props']; slots: ComponentManifest['slots'] }\n fixture: {\n props: NonNullable<ComponentFixture['props']> | null\n slotChildren: NonNullable<ComponentFixture['slotChildren']> | null\n } | null\n }\n}\n\n/**\n * The component set a config carries, as the harness registry input: the base\n * primitives plus every resolved project's `components` entries, deduped by\n * component id (first declaration wins — the generated-preview seam is keyed\n * by component id alone, so one image serves every project that registers the\n * id). A config that fails shape-resolution contributes no project components\n * — the primitives still enumerate, and the CLI surfaces config problems\n * through the studio/doctor, not here.\n *\n * EC-555: the kept entries' implementations (`unknown` in the config) go through\n * core's one registration step, like the studio registry's and the Next host's —\n * a non-component is withheld (its preview renders the missing-component\n * fallback, never `createElement`) and returned as an `invalid-implementation`\n * issue. The first-wins dedupe runs BEFORE the check, so a withheld entry still\n * claims its id.\n */\nexport function previewComponentsFromConfig(config: unknown): {\n manifests: ComponentManifest[]\n implementations: ComponentImplementations\n issues: ComponentIssue[]\n} {\n const base = basePrimitives()\n const manifests: ComponentManifest[] = [...base.manifests]\n const implementations: Record<string, ComponentType<Record<string, unknown>>> = {}\n for (const manifest of base.manifests) {\n const implementation = getImplementation(base.implementations, manifest.id)\n if (implementation) implementations[manifest.id] = implementation\n }\n const seen = new Set(manifests.map((m) => m.id))\n\n const kept: HostComponentEntry[] = []\n const resolved = resolveWorkspaceConfig(config)\n for (const project of resolved.projects) {\n for (const entry of project.components ?? []) {\n if (seen.has(entry.manifest.id)) continue\n seen.add(entry.manifest.id)\n manifests.push(entry.manifest)\n kept.push(entry)\n }\n }\n // Ids are unique by construction here (deduped against the primitives too),\n // so the merge order below decides nothing.\n const project = registerImplementations(kept)\n return {\n manifests,\n implementations: { ...implementations, ...project.implementations },\n issues: project.issues,\n }\n}\n\n/**\n * The {@link StudioRegistry}-shaped registry the harness renders against. Its\n * `registry.issues` carries the implementation issues too — the same list the\n * studio registry records them into (EC-555).\n */\nexport function previewRegistryFromConfig(config: unknown): StudioRegistry {\n const { manifests, implementations, issues } = previewComponentsFromConfig(config)\n const registry = new ComponentRegistry(manifests)\n registry.issues.push(...issues)\n return { registry, implementations }\n}\n\n/**\n * Enumerate every generated-preview entry for a config: canvas-eligible\n * manifests only (server-only components never render on the client canvas),\n * one entry per declared fixture in declaration order, else one implicit\n * {@link DEFAULT_VARIANT} entry. Components are sorted by id so the output —\n * and everything the CLI derives from it (manifest JSON, file writes) — is\n * deterministic regardless of registration order.\n */\nexport function previewEntriesForConfig(config: unknown): PreviewEntrySpec[] {\n const { manifests } = previewComponentsFromConfig(config)\n const entries: PreviewEntrySpec[] = []\n for (const manifest of [...manifests].sort((a, b) => a.id.localeCompare(b.id))) {\n if (!isCanvasEligible(manifest)) continue\n const manifestShape = { props: manifest.props, slots: manifest.slots }\n if (manifest.fixtures && manifest.fixtures.length > 0) {\n for (const fixture of manifest.fixtures) {\n entries.push({\n componentId: manifest.id,\n fixture: fixture.name,\n hashInput: {\n manifest: manifestShape,\n fixture: { props: fixture.props ?? null, slotChildren: fixture.slotChildren ?? null },\n },\n })\n }\n } else {\n entries.push({\n componentId: manifest.id,\n fixture: DEFAULT_VARIANT,\n hashInput: { manifest: manifestShape, fixture: null },\n })\n }\n }\n return entries\n}\n"]}
|
|
1
|
+
{"version":3,"file":"enumerate.js","sourceRoot":"","sources":["../../src/previews/enumerate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAA;AAErE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AAErE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAA;AAkDpE;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,2BAA2B,CAAC,MAAe;IACzD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAA;IACtC,MAAM,OAAO,GAAqB,EAAE,CAAA;IACpC,KAAK,MAAM,OAAO,IAAI,sBAAsB,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC9D,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;YAC7C,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,SAAQ;YAC7B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;YACf,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,MAAe;IACvD,OAAO,kBAAkB,CAAC,2BAA2B,CAAC,MAAM,CAAC,CAAC,CAAA;AAChE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAe;IACrD,MAAM,SAAS,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAA;IACnE,MAAM,OAAO,GAAuB,EAAE,CAAA;IACtC,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;QAC/E,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC;YAAE,SAAQ;QACzC,MAAM,aAAa,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAA;QACtE,IAAI,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtD,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;gBACxC,OAAO,CAAC,IAAI,CAAC;oBACX,WAAW,EAAE,QAAQ,CAAC,EAAE;oBACxB,OAAO,EAAE,OAAO,CAAC,IAAI;oBACrB,SAAS,EAAE;wBACT,QAAQ,EAAE,aAAa;wBACvB,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,IAAI,EAAE;qBACtF;iBACF,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC;gBACX,WAAW,EAAE,QAAQ,CAAC,EAAE;gBACxB,OAAO,EAAE,eAAe;gBACxB,SAAS,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE;aACtD,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC","sourcesContent":["import { isCanvasEligible } from '@elytracms/core/component-registry'\nimport type { ComponentFixture, ComponentManifest } from '@elytracms/core/component-registry'\nimport { registerComponents } from '@elytracms/core/runtime-renderer'\nimport type { ComponentEntry } from '@elytracms/core/runtime-renderer'\nimport { resolveWorkspaceConfig } from '@elytracms/core/studio-core'\nimport { DEFAULT_VARIANT } from '../components/builder/blocks-model'\nimport type { StudioRegistry } from '../lib/registry'\n\n/**\n * EC-440 — the fixture-preview enumeration behind `elytra previews`. ONE\n * source of truth for \"which previews exist\" shared by the CLI's node-side\n * driver (hashing, staleness `--check`) and the browser render harness\n * ({@link import('./harness').mountPreviewHarness}) — both derive the entry\n * set from the consumer's `elytra.config.ts` default export through here, so\n * generation, staleness checking, and rendering can never disagree.\n *\n * DATA-FREE by construction: this module reads the consumer CONFIG (registry\n * manifests + implementations — code), never a persistence adapter, auth, or\n * content data. The entry set mirrors the blocks panel exactly (EC-439): every\n * canvas-eligible manifest, one entry per declared fixture, and a single\n * implicit {@link DEFAULT_VARIANT} entry for fixture-less manifests. Exactly the\n * config's components — the engine adds none (EC-591; the shipped components are\n * there when the config lists `@elytracms/components`). Layout-helper presets are\n * NOT manifests and keep their placeholder treatment.\n */\n\n/** One renderable preview: a component fixture plus its staleness-hash inputs. */\nexport interface PreviewEntrySpec {\n componentId: string\n /** The fixture name, or {@link DEFAULT_VARIANT} for a fixture-less manifest. */\n fixture: string\n /**\n * The DECLARED render inputs the CLI hashes per entry for staleness: the\n * manifest's prop + slot shape (defaults live in the prop field-defs, so\n * seeded renders are covered) and the declared fixture's own\n * props/slotChildren (`fixture: null` for the implicit Default variant). A\n * change here re-renders one entry.\n *\n * NOT in here: the component implementation and the theme — a config arrives\n * evaluated, so neither has a per-entry identity. The CLI covers them as\n * manifest-level render dependencies instead (EC-600: content hashes of the\n * project source files the config imports plus the theme `@import` chain),\n * and a change there re-renders every entry. Before EC-600 this comment said\n * regeneration caught such drift; it did not — regeneration only rendered what\n * this hash called stale.\n */\n hashInput: {\n manifest: { props: ComponentManifest['props']; slots: ComponentManifest['slots'] }\n fixture: {\n props: NonNullable<ComponentFixture['props']> | null\n slotChildren: NonNullable<ComponentFixture['slotChildren']> | null\n } | null\n }\n}\n\n/**\n * The component list a config carries, as the harness registry input: every\n * resolved project's `components`, concatenated in project order. An entry two\n * projects share (the same `{ manifest, implementation }` object — e.g. both list\n * `...elytraComponents`) is the SAME component and is kept once; two DIFFERENT\n * entries under one id are a real conflict — the generated-preview seam is keyed\n * by component id alone, so one image would serve both — and go to the one\n * registration rule like any duplicate: the first is kept whole, the second is a\n * `duplicate-id` issue (EC-591; before, previews skipped it silently and showed\n * the first project's render in the second project's tile — COMPOSITION #13).\n * A config that fails shape-resolution contributes no components; the CLI\n * surfaces config problems through the studio/doctor, not here.\n */\nexport function previewComponentsFromConfig(config: unknown): readonly ComponentEntry[] {\n const seen = new Set<ComponentEntry>()\n const entries: ComponentEntry[] = []\n for (const project of resolveWorkspaceConfig(config).projects) {\n for (const entry of project.components ?? []) {\n if (seen.has(entry)) continue\n seen.add(entry)\n entries.push(entry)\n }\n }\n return entries\n}\n\n/**\n * The {@link StudioRegistry}-shaped registry the harness renders against: the\n * config's list through core's ONE registration step (`registerComponents`, the\n * same call the studio registry and the Next host make — EC-591), so a duplicate,\n * a role claim and an `invalid-implementation` are the identical issue in\n * `registry.issues` on every surface.\n */\nexport function previewRegistryFromConfig(config: unknown): StudioRegistry {\n return registerComponents(previewComponentsFromConfig(config))\n}\n\n/**\n * Enumerate every generated-preview entry for a config: canvas-eligible\n * manifests only (server-only components never render on the client canvas),\n * one entry per declared fixture in declaration order, else one implicit\n * {@link DEFAULT_VARIANT} entry. Components are sorted by id so the output —\n * and everything the CLI derives from it (manifest JSON, file writes) — is\n * deterministic regardless of registration order.\n */\nexport function previewEntriesForConfig(config: unknown): PreviewEntrySpec[] {\n const manifests = previewRegistryFromConfig(config).registry.list()\n const entries: PreviewEntrySpec[] = []\n for (const manifest of [...manifests].sort((a, b) => a.id.localeCompare(b.id))) {\n if (!isCanvasEligible(manifest)) continue\n const manifestShape = { props: manifest.props, slots: manifest.slots }\n if (manifest.fixtures && manifest.fixtures.length > 0) {\n for (const fixture of manifest.fixtures) {\n entries.push({\n componentId: manifest.id,\n fixture: fixture.name,\n hashInput: {\n manifest: manifestShape,\n fixture: { props: fixture.props ?? null, slotChildren: fixture.slotChildren ?? null },\n },\n })\n }\n } else {\n entries.push({\n componentId: manifest.id,\n fixture: DEFAULT_VARIANT,\n hashInput: { manifest: manifestShape, fixture: null },\n })\n }\n }\n return entries\n}\n"]}
|
package/dist/previews/harness.js
CHANGED
|
@@ -71,7 +71,7 @@ function nodeForEntry(studio, componentId, fixture) {
|
|
|
71
71
|
if (declared)
|
|
72
72
|
return { node: fixtureSubtree(studio, manifest, declared, `preview-${componentId}`).node };
|
|
73
73
|
if (fixture === DEFAULT_VARIANT && (manifest.fixtures ?? []).length === 0) {
|
|
74
|
-
return { node: seededNodeFromManifest(manifest) };
|
|
74
|
+
return { node: seededNodeFromManifest(manifest, studio.registry) };
|
|
75
75
|
}
|
|
76
76
|
return { error: `Unknown fixture "${fixture}" for component "${componentId}".` };
|
|
77
77
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harness.js","sourceRoot":"","sources":["../../src/previews/harness.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Component } from 'react'\nimport type { ReactNode } from 'react'\nimport { createRoot, type Root } from 'react-dom/client'\nimport { flushSync } from 'react-dom'\nimport { renderNode } from '@elytracms/core/runtime-renderer'\nimport type { RenderContext } from '@elytracms/core/runtime-renderer'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { DEFAULT_VARIANT, fixtureSubtree } from '../components/builder/blocks-model'\nimport { seededNodeFromManifest } from '../components/builder/node-factory'\nimport { previewContext } from '../lib/registry'\nimport type { StudioRegistry } from '../lib/registry'\nimport { previewEntriesForConfig, previewRegistryFromConfig } from './enumerate'\n\n/**\n * EC-440 — the browser half of the `elytra previews` render harness. The CLI\n * boots the consumer studio app's OWN Vite (programmatically, with a minimal\n * inline config) and serves a page whose entry module does exactly three\n * things: import the consumer's `elytra.config.ts` (the component registry —\n * code), import the consumer THEME (the same standalone Tailwind entry the\n * canvas iframe and delivery load, EC-414 — so preview == canvas == delivery\n * by construction), and call {@link mountPreviewHarness}.\n *\n * DATA-FREE, deliberately (no new trust boundary): no persistence adapter, no\n * auth, no content data, no env secrets — `createStudio` is never called. The\n * registry is built directly from the config's `components` entries plus the\n * base primitives ({@link previewRegistryFromConfig}), and bindings resolve to\n * `undefined` (`previewContext`), so data-dependent components render their\n * honest empty states.\n *\n * The driving CLI talks to the page through `window.__ELYTRA_PREVIEWS__`:\n * `entries` (the same enumeration the CLI derives node-side — a handshake\n * guard) and `render(componentId, fixture)`, which mounts ONE fixture subtree\n * — the exact subtree the blocks panel inserts (`fixtureSubtree`, else the\n * seeded manifest default, EC-439) — into the stage and resolves once the\n * document has settled (fonts loaded, images complete, two animation frames).\n * The CLI then screenshots the viewport.\n */\n\n/** The id of the stage element fixtures render into (created when absent). */\nexport const PREVIEW_STAGE_ID = 'elytra-preview-stage'\n\nexport type PreviewRenderResult = { ok: true } | { ok: false; error: string }\n\nexport interface PreviewHarnessApi {\n ready: true\n entries: Array<{ componentId: string; fixture: string }>\n render: (componentId: string, fixture: string) => Promise<PreviewRenderResult>\n}\n\ndeclare global {\n interface Window {\n __ELYTRA_PREVIEWS__?: PreviewHarnessApi\n }\n}\n\n/** Catches a fixture whose render throws — the entry fails visibly, the page survives. */\nclass HarnessErrorBoundary extends Component<\n { children: ReactNode; onError: (error: Error) => void },\n { failed: boolean }\n> {\n override state = { failed: false }\n\n static getDerivedStateFromError(): { failed: boolean } {\n return { failed: true }\n }\n\n override componentDidCatch(error: Error): void {\n this.props.onError(error)\n }\n\n override render(): ReactNode {\n return this.state.failed ? null : this.props.children\n }\n}\n\n/** Resolve once the document is visually settled: fonts, images, two frames. */\nasync function settled(doc: Document): Promise<void> {\n try {\n await doc.fonts?.ready\n } catch {\n // Font-loading failures must not wedge the run — the render is still shot.\n }\n await Promise.all(\n Array.from(doc.images).map((img) =>\n img.complete\n ? Promise.resolve()\n : new Promise<void>((resolve) => {\n img.addEventListener('load', () => resolve(), { once: true })\n img.addEventListener('error', () => resolve(), { once: true })\n }),\n ),\n )\n await new Promise<void>((resolve) =>\n requestAnimationFrame(() => requestAnimationFrame(() => resolve())),\n )\n}\n\n/** Build the node a fixture renders — the exact subtree the blocks panel inserts. */\nfunction nodeForEntry(\n studio: StudioRegistry,\n componentId: string,\n fixture: string,\n): { node: ComponentNode } | { error: string } {\n const manifest = studio.registry.getManifest(componentId)\n if (!manifest) return { error: `Unknown component \"${componentId}\".` }\n const declared = manifest.fixtures?.find((f) => f.name === fixture)\n if (declared) return { node: fixtureSubtree(studio, manifest, declared, `preview-${componentId}`).node }\n if (fixture === DEFAULT_VARIANT && (manifest.fixtures ?? []).length === 0) {\n return { node: seededNodeFromManifest(manifest) }\n }\n return { error: `Unknown fixture \"${fixture}\" for component \"${componentId}\".` }\n}\n\n/**\n * Mount the harness: build the registry from the consumer config, expose the\n * driver API on `window.__ELYTRA_PREVIEWS__`. Idempotent per document.\n */\nexport function mountPreviewHarness(config: unknown): PreviewHarnessApi {\n const studio = previewRegistryFromConfig(config)\n const ctx: RenderContext = previewContext(studio)\n const entries = previewEntriesForConfig(config).map(({ componentId, fixture }) => ({\n componentId,\n fixture,\n }))\n\n let stage = document.getElementById(PREVIEW_STAGE_ID)\n if (!stage) {\n stage = document.createElement('div')\n stage.id = PREVIEW_STAGE_ID\n document.body.appendChild(stage)\n }\n\n let root: Root | null = null\n\n const render = async (componentId: string, fixture: string): Promise<PreviewRenderResult> => {\n const built = nodeForEntry(studio, componentId, fixture)\n if ('error' in built) return { ok: false, error: built.error }\n\n // A fresh root per fixture: no state carryover between shots.\n root?.unmount()\n root = createRoot(stage!)\n let renderError: Error | null = null\n try {\n flushSync(() => {\n root!.render(\n <HarnessErrorBoundary\n onError={(error) => {\n renderError = error\n }}\n >\n {renderNode(built.node, ctx)}\n </HarnessErrorBoundary>,\n )\n })\n } catch (error) {\n renderError = error instanceof Error ? error : new Error(String(error))\n }\n if (renderError !== null) return { ok: false, error: (renderError as Error).message }\n await settled(document)\n return { ok: true }\n }\n\n const api: PreviewHarnessApi = { ready: true, entries, render }\n window.__ELYTRA_PREVIEWS__ = api\n return api\n}\n"]}
|
|
1
|
+
{"version":3,"file":"harness.js","sourceRoot":"","sources":["../../src/previews/harness.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Component } from 'react'\nimport type { ReactNode } from 'react'\nimport { createRoot, type Root } from 'react-dom/client'\nimport { flushSync } from 'react-dom'\nimport { renderNode } from '@elytracms/core/runtime-renderer'\nimport type { RenderContext } from '@elytracms/core/runtime-renderer'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { DEFAULT_VARIANT, fixtureSubtree } from '../components/builder/blocks-model'\nimport { seededNodeFromManifest } from '../components/builder/node-factory'\nimport { previewContext } from '../lib/registry'\nimport type { StudioRegistry } from '../lib/registry'\nimport { previewEntriesForConfig, previewRegistryFromConfig } from './enumerate'\n\n/**\n * EC-440 — the browser half of the `elytra previews` render harness. The CLI\n * boots the consumer studio app's OWN Vite (programmatically, with a minimal\n * inline config) and serves a page whose entry module does exactly three\n * things: import the consumer's `elytra.config.ts` (the component registry —\n * code), import the consumer THEME (the same standalone Tailwind entry the\n * canvas iframe and delivery load, EC-414 — so preview == canvas == delivery\n * by construction), and call {@link mountPreviewHarness}.\n *\n * DATA-FREE, deliberately (no new trust boundary): no persistence adapter, no\n * auth, no content data, no env secrets — `createStudio` is never called. The\n * registry is built directly from the config's `components` entries plus the\n * base primitives ({@link previewRegistryFromConfig}), and bindings resolve to\n * `undefined` (`previewContext`), so data-dependent components render their\n * honest empty states.\n *\n * The driving CLI talks to the page through `window.__ELYTRA_PREVIEWS__`:\n * `entries` (the same enumeration the CLI derives node-side — a handshake\n * guard) and `render(componentId, fixture)`, which mounts ONE fixture subtree\n * — the exact subtree the blocks panel inserts (`fixtureSubtree`, else the\n * seeded manifest default, EC-439) — into the stage and resolves once the\n * document has settled (fonts loaded, images complete, two animation frames).\n * The CLI then screenshots the viewport.\n */\n\n/** The id of the stage element fixtures render into (created when absent). */\nexport const PREVIEW_STAGE_ID = 'elytra-preview-stage'\n\nexport type PreviewRenderResult = { ok: true } | { ok: false; error: string }\n\nexport interface PreviewHarnessApi {\n ready: true\n entries: Array<{ componentId: string; fixture: string }>\n render: (componentId: string, fixture: string) => Promise<PreviewRenderResult>\n}\n\ndeclare global {\n interface Window {\n __ELYTRA_PREVIEWS__?: PreviewHarnessApi\n }\n}\n\n/** Catches a fixture whose render throws — the entry fails visibly, the page survives. */\nclass HarnessErrorBoundary extends Component<\n { children: ReactNode; onError: (error: Error) => void },\n { failed: boolean }\n> {\n override state = { failed: false }\n\n static getDerivedStateFromError(): { failed: boolean } {\n return { failed: true }\n }\n\n override componentDidCatch(error: Error): void {\n this.props.onError(error)\n }\n\n override render(): ReactNode {\n return this.state.failed ? null : this.props.children\n }\n}\n\n/** Resolve once the document is visually settled: fonts, images, two frames. */\nasync function settled(doc: Document): Promise<void> {\n try {\n await doc.fonts?.ready\n } catch {\n // Font-loading failures must not wedge the run — the render is still shot.\n }\n await Promise.all(\n Array.from(doc.images).map((img) =>\n img.complete\n ? Promise.resolve()\n : new Promise<void>((resolve) => {\n img.addEventListener('load', () => resolve(), { once: true })\n img.addEventListener('error', () => resolve(), { once: true })\n }),\n ),\n )\n await new Promise<void>((resolve) =>\n requestAnimationFrame(() => requestAnimationFrame(() => resolve())),\n )\n}\n\n/** Build the node a fixture renders — the exact subtree the blocks panel inserts. */\nfunction nodeForEntry(\n studio: StudioRegistry,\n componentId: string,\n fixture: string,\n): { node: ComponentNode } | { error: string } {\n const manifest = studio.registry.getManifest(componentId)\n if (!manifest) return { error: `Unknown component \"${componentId}\".` }\n const declared = manifest.fixtures?.find((f) => f.name === fixture)\n if (declared) return { node: fixtureSubtree(studio, manifest, declared, `preview-${componentId}`).node }\n if (fixture === DEFAULT_VARIANT && (manifest.fixtures ?? []).length === 0) {\n return { node: seededNodeFromManifest(manifest, studio.registry) }\n }\n return { error: `Unknown fixture \"${fixture}\" for component \"${componentId}\".` }\n}\n\n/**\n * Mount the harness: build the registry from the consumer config, expose the\n * driver API on `window.__ELYTRA_PREVIEWS__`. Idempotent per document.\n */\nexport function mountPreviewHarness(config: unknown): PreviewHarnessApi {\n const studio = previewRegistryFromConfig(config)\n const ctx: RenderContext = previewContext(studio)\n const entries = previewEntriesForConfig(config).map(({ componentId, fixture }) => ({\n componentId,\n fixture,\n }))\n\n let stage = document.getElementById(PREVIEW_STAGE_ID)\n if (!stage) {\n stage = document.createElement('div')\n stage.id = PREVIEW_STAGE_ID\n document.body.appendChild(stage)\n }\n\n let root: Root | null = null\n\n const render = async (componentId: string, fixture: string): Promise<PreviewRenderResult> => {\n const built = nodeForEntry(studio, componentId, fixture)\n if ('error' in built) return { ok: false, error: built.error }\n\n // A fresh root per fixture: no state carryover between shots.\n root?.unmount()\n root = createRoot(stage!)\n let renderError: Error | null = null\n try {\n flushSync(() => {\n root!.render(\n <HarnessErrorBoundary\n onError={(error) => {\n renderError = error\n }}\n >\n {renderNode(built.node, ctx)}\n </HarnessErrorBoundary>,\n )\n })\n } catch (error) {\n renderError = error instanceof Error ? error : new Error(String(error))\n }\n if (renderError !== null) return { ok: false, error: (renderError as Error).message }\n await settled(document)\n return { ok: true }\n }\n\n const api: PreviewHarnessApi = { ready: true, entries, render }\n window.__ELYTRA_PREVIEWS__ = api\n return api\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elytracms/studio",
|
|
3
3
|
"author": "finaldream <hi@finaldream.de>",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.91",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22.18.0"
|
|
7
7
|
},
|
|
@@ -67,10 +67,6 @@
|
|
|
67
67
|
"types": "./dist/lib/registry/index.d.ts",
|
|
68
68
|
"default": "./dist/lib/registry/index.js"
|
|
69
69
|
},
|
|
70
|
-
"./lib/webhooks": {
|
|
71
|
-
"types": "./dist/lib/webhooks/index.d.ts",
|
|
72
|
-
"default": "./dist/lib/webhooks/index.js"
|
|
73
|
-
},
|
|
74
70
|
"./lib/persistence": {
|
|
75
71
|
"types": "./dist/lib/persistence/index.d.ts",
|
|
76
72
|
"default": "./dist/lib/persistence/index.js"
|
|
@@ -153,7 +149,7 @@
|
|
|
153
149
|
"lucide-react": "^1.23.0",
|
|
154
150
|
"tailwind-merge": "^3.6.0",
|
|
155
151
|
"zod": "^4.4.3",
|
|
156
|
-
"@elytracms/core": "^0.0.
|
|
152
|
+
"@elytracms/core": "^0.0.91"
|
|
157
153
|
},
|
|
158
154
|
"peerDependencies": {
|
|
159
155
|
"@tanstack/react-router": "^1.170.0",
|
|
@@ -176,7 +172,8 @@
|
|
|
176
172
|
"react": "^19.2.7",
|
|
177
173
|
"react-dom": "^19.2.7",
|
|
178
174
|
"typescript": "^6.0.3",
|
|
179
|
-
"vite": "^8.1.3"
|
|
175
|
+
"vite": "^8.1.3",
|
|
176
|
+
"@elytracms/components": "^0.0.91"
|
|
180
177
|
},
|
|
181
178
|
"scripts": {
|
|
182
179
|
"build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json && node ../../scripts/fix-esm-extensions.mjs dist && node ../../scripts/check-esm-extensions.mjs dist",
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { ComponentManifest } from '@elytracms/core/component-registry';
|
|
2
|
-
import type { ComponentType } from 'react';
|
|
3
|
-
/**
|
|
4
|
-
* Sample `project.*` components for the Studio Components surface (brief §4.2,
|
|
5
|
-
* §8.3). These stand in for the project-specific components that are the center
|
|
6
|
-
* of the product: each declares a full manifest (prop schema metadata, defaults,
|
|
7
|
-
* inspector controls, binding permissions, slot rules, design-token hints, and
|
|
8
|
-
* preview fixtures) and ships a self-contained implementation the fixture runner
|
|
9
|
-
* renders. Deterministic — no time/random.
|
|
10
|
-
*/
|
|
11
|
-
type Props = Record<string, unknown>;
|
|
12
|
-
/** Project component manifests (including the deliberately-unimplemented one). */
|
|
13
|
-
export declare const projectComponentManifests: ComponentManifest[];
|
|
14
|
-
/** Implementations for the project components — note `LegacyBanner` is absent. */
|
|
15
|
-
export declare const projectComponentImplementations: Record<string, ComponentType<Props>>;
|
|
16
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"project-components.js","sourceRoot":"","sources":["../../../src/lib/registry/project-components.tsx"],"names":[],"mappings":"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","sourcesContent":["import { defineComponent } from '@elytracms/core/component-registry'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport type { ComponentType, ReactNode } from 'react'\n\n/**\n * Sample `project.*` components for the Studio Components surface (brief §4.2,\n * §8.3). These stand in for the project-specific components that are the center\n * of the product: each declares a full manifest (prop schema metadata, defaults,\n * inspector controls, binding permissions, slot rules, design-token hints, and\n * preview fixtures) and ships a self-contained implementation the fixture runner\n * renders. Deterministic — no time/random.\n */\n\ntype Props = Record<string, unknown>\nfunction str(value: unknown, fallback = ''): string {\n return typeof value === 'string' && value.length > 0 ? value : fallback\n}\n\n// --- project.MarketingHero -------------------------------------------------\n\nfunction MarketingHero(props: Props): ReactNode {\n const align = props.align === 'center' ? 'items-center text-center' : 'items-start text-left'\n return (\n <div className={`flex flex-col gap-3 rounded-xl border border-border bg-card p-8 ${align}`}>\n {str(props.eyebrow) ? (\n <span className=\"text-[0.7rem] font-semibold uppercase tracking-[0.14em] text-primary\">\n {str(props.eyebrow)}\n </span>\n ) : null}\n <h2 className=\"text-2xl font-semibold tracking-tight text-foreground\">\n {str(props.heading, 'Headline goes here')}\n </h2>\n {str(props.subheading) ? (\n <p className=\"max-w-prose text-sm text-muted-foreground\">{str(props.subheading)}</p>\n ) : null}\n {str(props.ctaLabel) ? (\n <span className=\"mt-1 inline-flex w-fit items-center rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground\">\n {str(props.ctaLabel)}\n </span>\n ) : null}\n </div>\n )\n}\n\nconst MarketingHeroManifest = defineComponent({\n id: 'project.MarketingHero',\n namespace: 'project',\n title: 'Marketing Hero',\n description: 'A headline hero section with eyebrow, subheading, and a call to action.',\n category: 'marketing',\n props: {\n eyebrow: { type: 'text', default: 'New', form: { label: 'Eyebrow' } },\n heading: {\n type: 'text',\n default: 'Build code-first websites',\n validation: { min: 1 },\n form: { label: 'Heading' },\n },\n subheading: {\n type: 'text',\n default: 'One canonical graph powers editing, content, and export.',\n form: { label: 'Subheading', control: 'textarea' },\n },\n ctaLabel: { type: 'text', default: 'Get started', form: { label: 'CTA label' } },\n align: {\n type: 'select',\n default: 'left',\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n ],\n form: { label: 'Alignment' },\n },\n },\n slots: [],\n fixtures: [\n { name: 'Default' },\n {\n name: 'Centered',\n props: { align: 'center', eyebrow: 'Launch', heading: 'Ship faster', ctaLabel: 'Try it' },\n },\n { name: 'Minimal', props: { eyebrow: '', subheading: '', ctaLabel: '', heading: 'Just a headline' } },\n ],\n})\n\n// --- project.FeatureCard ---------------------------------------------------\n\nfunction FeatureCard(props: Props): ReactNode {\n const accent = props.tone === 'accent'\n return (\n <div\n className={`flex flex-col gap-2 rounded-lg border p-5 ${\n accent ? 'border-primary/30 bg-primary/5' : 'border-border bg-card'\n }`}\n >\n <div className={`grid size-8 place-items-center rounded-md ${accent ? 'bg-primary text-primary-foreground' : 'bg-muted text-foreground'}`}>\n <span className=\"text-sm font-bold\">{str(props.title, 'F').charAt(0)}</span>\n </div>\n <h3 className=\"text-sm font-semibold text-foreground\">{str(props.title, 'Feature title')}</h3>\n <p className=\"text-xs text-muted-foreground\">{str(props.body, 'A short description of the feature.')}</p>\n </div>\n )\n}\n\nconst FeatureCardManifest = defineComponent({\n id: 'project.FeatureCard',\n namespace: 'project',\n title: 'Feature Card',\n description: 'A compact card highlighting a single feature.',\n category: 'marketing',\n props: {\n title: {\n type: 'text',\n default: 'Validation first',\n validation: { min: 1 },\n form: { label: 'Title' },\n },\n body: {\n type: 'text',\n default: 'Broken bindings and missing components are explicit, never silent.',\n form: { label: 'Body', control: 'textarea' },\n },\n tone: {\n type: 'select',\n default: 'default',\n options: [\n { value: 'default', label: 'Default' },\n { value: 'accent', label: 'Accent' },\n ],\n form: { label: 'Tone' },\n },\n },\n slots: [],\n fixtures: [{ name: 'Default' }, { name: 'Accent', props: { tone: 'accent', title: 'AI inspectable' } }],\n})\n\n// --- project.PricingCallout ------------------------------------------------\n\nfunction PricingCallout(props: Props): ReactNode {\n const featured = props.featured === true\n return (\n <div\n className={`flex flex-col gap-1 rounded-xl border p-6 ${\n featured ? 'border-primary bg-primary/5 shadow-md' : 'border-border bg-card'\n }`}\n >\n {featured ? (\n <span className=\"mb-1 w-fit rounded-full bg-primary px-2 py-0.5 text-[0.65rem] font-semibold uppercase tracking-wide text-primary-foreground\">\n Popular\n </span>\n ) : null}\n <span className=\"text-sm font-medium text-muted-foreground\">{str(props.plan, 'Plan')}</span>\n <div className=\"flex items-baseline gap-1\">\n <span className=\"text-3xl font-semibold tracking-tight text-foreground\">{str(props.price, '$0')}</span>\n <span className=\"text-sm text-muted-foreground\">/{str(props.period, 'mo')}</span>\n </div>\n </div>\n )\n}\n\nconst PricingCalloutManifest = defineComponent({\n id: 'project.PricingCallout',\n namespace: 'project',\n title: 'Pricing Callout',\n description: 'A pricing plan callout with an optional featured emphasis.',\n category: 'commerce',\n props: {\n plan: {\n type: 'text',\n default: 'Pro',\n validation: { min: 1 },\n form: { label: 'Plan' },\n },\n price: { type: 'text', default: '$29', form: { label: 'Price' } },\n period: { type: 'text', default: 'mo', form: { label: 'Period' } },\n featured: { type: 'boolean', default: false, form: { label: 'Featured' } },\n },\n slots: [],\n fixtures: [{ name: 'Standard' }, { name: 'Featured', props: { featured: true, plan: 'Team', price: '$79' } }],\n})\n\n// --- project.LegacyBanner (intentionally has NO implementation) -------------\n//\n// Registered manifest with no implementation — exercises the \"incompatible /\n// missing implementation\" component status (EC-055) and the renderer's\n// missing-component fallback (brief §4.11).\n\nconst LegacyBannerManifest = defineComponent({\n id: 'project.LegacyBanner',\n namespace: 'project',\n title: 'Legacy Banner',\n description: 'A deprecated banner whose implementation has not been wired up.',\n category: 'marketing',\n props: {\n message: { type: 'text', default: 'Deprecated', form: { label: 'Message' } },\n },\n slots: [],\n fixtures: [{ name: 'Default' }],\n})\n\n/** Project component manifests (including the deliberately-unimplemented one). */\nexport const projectComponentManifests: ComponentManifest[] = [\n MarketingHeroManifest,\n FeatureCardManifest,\n PricingCalloutManifest,\n LegacyBannerManifest,\n]\n\n/** Implementations for the project components — note `LegacyBanner` is absent. */\nexport const projectComponentImplementations: Record<string, ComponentType<Props>> = {\n 'project.MarketingHero': MarketingHero,\n 'project.FeatureCard': FeatureCard,\n 'project.PricingCallout': PricingCallout,\n}\n"]}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { OperationEventBus } from '@elytracms/core/operations';
|
|
2
|
-
import type { WebhookDispatcherAttachment } from './dispatcher.js';
|
|
3
|
-
/**
|
|
4
|
-
* Attach the dispatcher from environment configuration. Returns `null` when
|
|
5
|
-
* no endpoint/secret is configured (the common local-dev case).
|
|
6
|
-
*/
|
|
7
|
-
export declare function attachConfiguredRevalidationWebhooks(events: OperationEventBus): WebhookDispatcherAttachment | null;
|
|
8
|
-
/**
|
|
9
|
-
* The locales the dispatcher emits per-collection tags for. The project's
|
|
10
|
-
* configured locales (the same set the host tags by) are always covered so no
|
|
11
|
-
* locale can be silently dropped (EC-192 — the `/de` freshness fix); the
|
|
12
|
-
* `VITE_REVALIDATE_LOCALES` env may only ADD to that set. Falls back to `en`
|
|
13
|
-
* only when neither config nor env supplies a locale (env-only wiring).
|
|
14
|
-
*/
|
|
15
|
-
export declare function revalidationLocales(): string[];
|