@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.
Files changed (246) hide show
  1. package/convex/asset-revalidate.test.ts +49 -43
  2. package/convex/cms.ts +3 -4
  3. package/convex/delivery.ts +8 -11
  4. package/convex/document-revalidate.test.ts +188 -0
  5. package/convex/redirect-revalidate.test.ts +50 -37
  6. package/convex/revalidate.ts +18 -30
  7. package/convex/triggers.ts +47 -38
  8. package/convex/webhooks.ts +77 -62
  9. package/dist/components/account/self-account-view-source.js +2 -1
  10. package/dist/components/account/self-account-view-source.js.map +1 -1
  11. package/dist/components/ai/ai-chat-panel.js +1 -1
  12. package/dist/components/ai/ai-chat-panel.js.map +1 -1
  13. package/dist/components/app-views/default-modules.js +3 -3
  14. package/dist/components/app-views/default-modules.js.map +1 -1
  15. package/dist/components/assets/asset-grid.js +1 -1
  16. package/dist/components/assets/asset-grid.js.map +1 -1
  17. package/dist/components/assets/asset-selection.d.ts +8 -9
  18. package/dist/components/assets/asset-selection.js +26 -16
  19. package/dist/components/assets/asset-selection.js.map +1 -1
  20. package/dist/components/assets/assets-collection-view-source.js +3 -2
  21. package/dist/components/assets/assets-collection-view-source.js.map +1 -1
  22. package/dist/components/builder/blocks-model.d.ts +7 -5
  23. package/dist/components/builder/blocks-model.js +11 -9
  24. package/dist/components/builder/blocks-model.js.map +1 -1
  25. package/dist/components/builder/blocks-panel.js +1 -1
  26. package/dist/components/builder/blocks-panel.js.map +1 -1
  27. package/dist/components/builder/builder-store.d.ts +11 -3
  28. package/dist/components/builder/builder-store.js +12 -5
  29. package/dist/components/builder/builder-store.js.map +1 -1
  30. package/dist/components/builder/canvas-dnd.d.ts +7 -1
  31. package/dist/components/builder/canvas-dnd.js +6 -3
  32. package/dist/components/builder/canvas-dnd.js.map +1 -1
  33. package/dist/components/builder/canvas-render-context.d.ts +13 -1
  34. package/dist/components/builder/canvas-render-context.js +28 -18
  35. package/dist/components/builder/canvas-render-context.js.map +1 -1
  36. package/dist/components/builder/canvas.js +1 -1
  37. package/dist/components/builder/canvas.js.map +1 -1
  38. package/dist/components/builder/clipboard-actions.d.ts +0 -6
  39. package/dist/components/builder/clipboard-actions.js +9 -4
  40. package/dist/components/builder/clipboard-actions.js.map +1 -1
  41. package/dist/components/builder/composition-field-editor.d.ts +22 -0
  42. package/dist/components/builder/composition-field-editor.js +47 -8
  43. package/dist/components/builder/composition-field-editor.js.map +1 -1
  44. package/dist/components/builder/inline-edit.d.ts +7 -4
  45. package/dist/components/builder/inline-edit.js +11 -8
  46. package/dist/components/builder/inline-edit.js.map +1 -1
  47. package/dist/components/builder/insert-context.js +9 -5
  48. package/dist/components/builder/insert-context.js.map +1 -1
  49. package/dist/components/builder/layout-inspector.d.ts +2 -2
  50. package/dist/components/builder/layout-inspector.js +8 -6
  51. package/dist/components/builder/layout-inspector.js.map +1 -1
  52. package/dist/components/builder/node-factory.d.ts +31 -14
  53. package/dist/components/builder/node-factory.js +73 -93
  54. package/dist/components/builder/node-factory.js.map +1 -1
  55. package/dist/components/builder/page-tree.d.ts +3 -1
  56. package/dist/components/builder/page-tree.js +6 -4
  57. package/dist/components/builder/page-tree.js.map +1 -1
  58. package/dist/components/builder/property-inspector.js +2 -1
  59. package/dist/components/builder/property-inspector.js.map +1 -1
  60. package/dist/components/builder/reuse-context.js +2 -2
  61. package/dist/components/builder/reuse-context.js.map +1 -1
  62. package/dist/components/builder/reuse.d.ts +7 -3
  63. package/dist/components/builder/reuse.js +19 -11
  64. package/dist/components/builder/reuse.js.map +1 -1
  65. package/dist/components/builder/selection-overlay.d.ts +2 -2
  66. package/dist/components/builder/selection-overlay.js +3 -3
  67. package/dist/components/builder/selection-overlay.js.map +1 -1
  68. package/dist/components/builder/spacing-handles.d.ts +2 -2
  69. package/dist/components/builder/spacing-handles.js +3 -3
  70. package/dist/components/builder/spacing-handles.js.map +1 -1
  71. package/dist/components/cms/asset-field.d.ts +1 -1
  72. package/dist/components/cms/asset-field.js +15 -13
  73. package/dist/components/cms/asset-field.js.map +1 -1
  74. package/dist/components/cms/asset-picker-core.js +3 -4
  75. package/dist/components/cms/asset-picker-core.js.map +1 -1
  76. package/dist/components/cms/cell-edit.d.ts +0 -6
  77. package/dist/components/cms/cell-edit.js +5 -23
  78. package/dist/components/cms/cell-edit.js.map +1 -1
  79. package/dist/components/cms/cms-store.d.ts +3 -2
  80. package/dist/components/cms/cms-store.js +23 -24
  81. package/dist/components/cms/cms-store.js.map +1 -1
  82. package/dist/components/cms/collection-table.js +14 -4
  83. package/dist/components/cms/collection-table.js.map +1 -1
  84. package/dist/components/cms/detail-editor.js +28 -24
  85. package/dist/components/cms/detail-editor.js.map +1 -1
  86. package/dist/components/cms/discard-draft.d.ts +1 -2
  87. package/dist/components/cms/discard-draft.js +1 -1
  88. package/dist/components/cms/discard-draft.js.map +1 -1
  89. package/dist/components/cms/duplicate-document.d.ts +3 -2
  90. package/dist/components/cms/duplicate-document.js +5 -5
  91. package/dist/components/cms/duplicate-document.js.map +1 -1
  92. package/dist/components/cms/editable-cell.js +11 -7
  93. package/dist/components/cms/editable-cell.js.map +1 -1
  94. package/dist/components/cms/field-control-lib.d.ts +10 -0
  95. package/dist/components/cms/field-control-lib.js +20 -0
  96. package/dist/components/cms/field-control-lib.js.map +1 -1
  97. package/dist/components/cms/field-control.js +10 -9
  98. package/dist/components/cms/field-control.js.map +1 -1
  99. package/dist/components/cms/object-array-field.js +3 -5
  100. package/dist/components/cms/object-array-field.js.map +1 -1
  101. package/dist/components/cms/prop-fields.d.ts +16 -3
  102. package/dist/components/cms/prop-fields.js +57 -12
  103. package/dist/components/cms/prop-fields.js.map +1 -1
  104. package/dist/components/cms/publish-clean.d.ts +13 -6
  105. package/dist/components/cms/publish-clean.js +14 -6
  106. package/dist/components/cms/publish-clean.js.map +1 -1
  107. package/dist/components/cms/rich-text/embed-props.d.ts +6 -1
  108. package/dist/components/cms/rich-text/embed-props.js +7 -12
  109. package/dist/components/cms/rich-text/embed-props.js.map +1 -1
  110. package/dist/components/cms/rich-text/insert.d.ts +13 -6
  111. package/dist/components/cms/rich-text/insert.js +16 -17
  112. package/dist/components/cms/rich-text/insert.js.map +1 -1
  113. package/dist/components/cms/use-cell-edit.js +7 -12
  114. package/dist/components/cms/use-cell-edit.js.map +1 -1
  115. package/dist/components/cms/use-discard-controller.d.ts +1 -2
  116. package/dist/components/cms/use-discard-controller.js +1 -2
  117. package/dist/components/cms/use-discard-controller.js.map +1 -1
  118. package/dist/components/cms/use-publish-controller.js +3 -2
  119. package/dist/components/cms/use-publish-controller.js.map +1 -1
  120. package/dist/components/cms/versions-pane.js +2 -2
  121. package/dist/components/cms/versions-pane.js.map +1 -1
  122. package/dist/components/components/component-detail.d.ts +1 -1
  123. package/dist/components/components/component-detail.js +2 -4
  124. package/dist/components/components/component-detail.js.map +1 -1
  125. package/dist/components/components/component-preview.js +9 -4
  126. package/dist/components/components/component-preview.js.map +1 -1
  127. package/dist/components/components/components-index.d.ts +2 -2
  128. package/dist/components/components/components-index.js +3 -6
  129. package/dist/components/components/components-index.js.map +1 -1
  130. package/dist/components/components/field-inspector.js +5 -2
  131. package/dist/components/components/field-inspector.js.map +1 -1
  132. package/dist/components/components/registry-browser.d.ts +6 -5
  133. package/dist/components/components/registry-browser.js +11 -37
  134. package/dist/components/components/registry-browser.js.map +1 -1
  135. package/dist/components/components/taxonomy.d.ts +14 -21
  136. package/dist/components/components/taxonomy.js +5 -29
  137. package/dist/components/components/taxonomy.js.map +1 -1
  138. package/dist/components/components/transfer-dialog.js +1 -0
  139. package/dist/components/components/transfer-dialog.js.map +1 -1
  140. package/dist/components/components/transfer.d.ts +20 -12
  141. package/dist/components/components/transfer.js +27 -21
  142. package/dist/components/components/transfer.js.map +1 -1
  143. package/dist/components/delivery-health-banner.d.ts +3 -2
  144. package/dist/components/delivery-health-banner.js +3 -2
  145. package/dist/components/delivery-health-banner.js.map +1 -1
  146. package/dist/components/redirects/redirects-collection-view-source.js +3 -2
  147. package/dist/components/redirects/redirects-collection-view-source.js.map +1 -1
  148. package/dist/components/users/users-collection-view-source.js +2 -1
  149. package/dist/components/users/users-collection-view-source.js.map +1 -1
  150. package/dist/lib/ai/apply-content.js +32 -28
  151. package/dist/lib/ai/apply-content.js.map +1 -1
  152. package/dist/lib/ai/constraints.d.ts +16 -14
  153. package/dist/lib/ai/constraints.js +64 -48
  154. package/dist/lib/ai/constraints.js.map +1 -1
  155. package/dist/lib/ai/translate-document.js +11 -7
  156. package/dist/lib/ai/translate-document.js.map +1 -1
  157. package/dist/lib/ai/types.d.ts +9 -5
  158. package/dist/lib/ai/types.js.map +1 -1
  159. package/dist/lib/ai/update-fields.js +15 -32
  160. package/dist/lib/ai/update-fields.js.map +1 -1
  161. package/dist/lib/ai/vite.d.ts +22 -2
  162. package/dist/lib/ai/vite.js +12 -3
  163. package/dist/lib/ai/vite.js.map +1 -1
  164. package/dist/lib/assets/asset-placements.d.ts +3 -10
  165. package/dist/lib/assets/asset-placements.js +17 -8
  166. package/dist/lib/assets/asset-placements.js.map +1 -1
  167. package/dist/lib/clipboard/paste-target.js +3 -3
  168. package/dist/lib/clipboard/paste-target.js.map +1 -1
  169. package/dist/lib/cms/published-documents.d.ts +14 -3
  170. package/dist/lib/cms/published-documents.js +12 -3
  171. package/dist/lib/cms/published-documents.js.map +1 -1
  172. package/dist/lib/cms-views/view-model.d.ts +4 -4
  173. package/dist/lib/cms-views/view-model.js +26 -20
  174. package/dist/lib/cms-views/view-model.js.map +1 -1
  175. package/dist/lib/data/collection-content.d.ts +10 -7
  176. package/dist/lib/data/collection-content.js +70 -29
  177. package/dist/lib/data/collection-content.js.map +1 -1
  178. package/dist/lib/delivery/instant-publish.d.ts +0 -3
  179. package/dist/lib/delivery/instant-publish.js +0 -1
  180. package/dist/lib/delivery/instant-publish.js.map +1 -1
  181. package/dist/lib/dnd/drop-rules.d.ts +22 -7
  182. package/dist/lib/dnd/drop-rules.js +21 -14
  183. package/dist/lib/dnd/drop-rules.js.map +1 -1
  184. package/dist/lib/engine-version.d.ts +1 -1
  185. package/dist/lib/engine-version.js +1 -1
  186. package/dist/lib/engine-version.js.map +1 -1
  187. package/dist/lib/i18n/messages.de.js +25 -9
  188. package/dist/lib/i18n/messages.de.js.map +1 -1
  189. package/dist/lib/i18n/messages.en.d.ts +21 -7
  190. package/dist/lib/i18n/messages.en.js +28 -9
  191. package/dist/lib/i18n/messages.en.js.map +1 -1
  192. package/dist/lib/locale/fallback.d.ts +5 -19
  193. package/dist/lib/locale/fallback.js +18 -39
  194. package/dist/lib/locale/fallback.js.map +1 -1
  195. package/dist/lib/locale/locale-store.js +2 -1
  196. package/dist/lib/locale/locale-store.js.map +1 -1
  197. package/dist/lib/operations/cms-persistence.js +7 -4
  198. package/dist/lib/operations/cms-persistence.js.map +1 -1
  199. package/dist/lib/operations/studio-operations.js +4 -6
  200. package/dist/lib/operations/studio-operations.js.map +1 -1
  201. package/dist/lib/persistence/backend-mode.d.ts +5 -25
  202. package/dist/lib/persistence/backend-mode.js +13 -30
  203. package/dist/lib/persistence/backend-mode.js.map +1 -1
  204. package/dist/lib/persona/labels.d.ts +4 -9
  205. package/dist/lib/persona/labels.js +5 -12
  206. package/dist/lib/persona/labels.js.map +1 -1
  207. package/dist/lib/references/reference-model.d.ts +17 -5
  208. package/dist/lib/references/reference-model.js +20 -28
  209. package/dist/lib/references/reference-model.js.map +1 -1
  210. package/dist/lib/references/references-store.js +7 -0
  211. package/dist/lib/references/references-store.js.map +1 -1
  212. package/dist/lib/registry/__fixtures__/demo-components.d.ts +32 -0
  213. package/dist/lib/registry/{project-components.js → __fixtures__/demo-components.js} +18 -5
  214. package/dist/lib/registry/__fixtures__/demo-components.js.map +1 -0
  215. package/dist/lib/registry/index.d.ts +9 -13
  216. package/dist/lib/registry/index.js +34 -61
  217. package/dist/lib/registry/index.js.map +1 -1
  218. package/dist/lib/registry/preview.d.ts +22 -7
  219. package/dist/lib/registry/preview.js +26 -89
  220. package/dist/lib/registry/preview.js.map +1 -1
  221. package/dist/lib/workspace/index.d.ts +0 -7
  222. package/dist/lib/workspace/index.js +0 -9
  223. package/dist/lib/workspace/index.js.map +1 -1
  224. package/dist/previews/enumerate.d.ts +23 -27
  225. package/dist/previews/enumerate.js +27 -48
  226. package/dist/previews/enumerate.js.map +1 -1
  227. package/dist/previews/harness.js +1 -1
  228. package/dist/previews/harness.js.map +1 -1
  229. package/package.json +4 -7
  230. package/dist/lib/registry/project-components.d.ts +0 -16
  231. package/dist/lib/registry/project-components.js.map +0 -1
  232. package/dist/lib/webhooks/config.d.ts +0 -15
  233. package/dist/lib/webhooks/config.js +0 -65
  234. package/dist/lib/webhooks/config.js.map +0 -1
  235. package/dist/lib/webhooks/dispatcher.d.ts +0 -70
  236. package/dist/lib/webhooks/dispatcher.js +0 -61
  237. package/dist/lib/webhooks/dispatcher.js.map +0 -1
  238. package/dist/lib/webhooks/index.d.ts +0 -11
  239. package/dist/lib/webhooks/index.js +0 -10
  240. package/dist/lib/webhooks/index.js.map +0 -1
  241. package/dist/lib/webhooks/signing.d.ts +0 -15
  242. package/dist/lib/webhooks/signing.js +0 -24
  243. package/dist/lib/webhooks/signing.js.map +0 -1
  244. package/dist/lib/webhooks/tags.d.ts +0 -25
  245. package/dist/lib/webhooks/tags.js +0 -289
  246. package/dist/lib/webhooks/tags.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"references-store.js","sourceRoot":"","sources":["../../../src/lib/references/references-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react'\nimport { documentKey } from '@elytracms/core/cms-core'\nimport { useOperationQuery } from '@elytracms/studio/lib/query'\nimport type { CmsDocument, DocumentRef } from '@elytracms/core/cms-core'\nimport type {\n ReferenceEntryRecord,\n ReferenceSource,\n ReferenceTarget,\n} from '@elytracms/core/persistence'\nimport { localizedLabel } from '../i18n'\nimport { getStudioOperations } from '../operations'\nimport type { OperationEvent, OperationsClient, StudioOperations } from '../operations'\nimport { getStudioRegistry } from '../registry'\nimport { buildReferenceEntryViews } from './reference-model'\nimport type { ReferenceEntryView, ReferenceLabelContext } from './reference-model'\n\n/**\n * Data layer for the EC-172 references panels. All reads go through\n * `@elytracms/core/operations` queries (`references.outbound` / `references.inbound`\n * plus the labeling queries) — UI code never touches persistence repositories\n * or index tables. The hook keeps panels live: any succeeded command in a\n * content-bearing domain re-queries after the EC-155 maintained-on-write\n * attachment has flushed.\n */\n\nexport interface ReferencePanelQuery {\n projectId: string\n /** Enables the outbound (\"references\") section. */\n source?: ReferenceSource | undefined\n /** Enables the inbound (\"used by\") section. */\n target?: ReferenceTarget | undefined\n}\n\nexport interface ReferencePanelData {\n outbound: ReferenceEntryView[]\n inbound: ReferenceEntryView[]\n /**\n * Per-direction read error (EC-205): a direction whose query returns a\n * structured non-ok degrades to an empty list plus this message, surfaced as a\n * visible per-section error — instead of collapsing the whole panel into its\n * hard-error state. `null` when the direction loaded (or was not requested).\n */\n outboundError: string | null\n inboundError: string | null\n}\n\n// --- Index bootstrap -----------------------------------------------------------\n\n/**\n * The EC-155 index is maintained on write, but seeded/imported projects start\n * with an empty index. {@link ensureReferenceIndex} seeds it once per project per\n * operations client; failures (e.g. a viewer's command denial) degrade to\n * whatever the maintained-on-write index already holds.\n */\nconst backfillsByClient = new WeakMap<StudioOperations, Map<string, Promise<void>>>()\n\n/**\n * Seed the index once if it has never been backfilled, then never again — the\n * EC-269 mutation-free read path. The index is maintained on write, so the\n * (clear + rebuild) `backfill` is only needed to seed a never-built index. A\n * persisted marker (`references.buildMarker`, a read) records completion; when\n * present this returns after a single query with ZERO mutations — so opening the\n * References panel no longer rewrites the whole index every session (the normal\n * Convex case: the EC-233 import already backfilled). Only a genuinely empty/\n * never-built project still pays the one-time backfill (and a viewer who cannot\n * run it degrades to the maintained-on-write index, as before).\n */\nasync function seedReferenceIndex(studio: StudioOperations, projectId: string): Promise<void> {\n try {\n const marker = await studio.operations.references.buildMarker({ projectId })\n if (marker.ok && marker.value !== null) return\n } catch {\n // A marker-read failure falls through to the backfill (best-effort seed).\n }\n await studio.operations.references.backfill({ projectId }).then(\n () => undefined,\n () => undefined,\n )\n}\n\nexport function ensureReferenceIndex(studio: StudioOperations, projectId: string): Promise<void> {\n let byProject = backfillsByClient.get(studio)\n if (!byProject) {\n byProject = new Map()\n backfillsByClient.set(studio, byProject)\n }\n let pending = byProject.get(projectId)\n if (!pending) {\n pending = seedReferenceIndex(studio, projectId)\n byProject.set(projectId, pending)\n }\n return pending\n}\n\n// --- Label context loading -------------------------------------------------------\n\nasync function loadLabelContext(\n operations: OperationsClient,\n projectId: string,\n): Promise<ReferenceLabelContext> {\n const [schemaResult, documentsResult, assetsResult] = await Promise.all([\n operations.schema.get({ projectId }),\n operations.documents.list({ projectId }),\n operations.assets.list({ projectId }),\n ])\n\n const collections = schemaResult.ok ? schemaResult.value.collections : []\n const documents = new Map<string, CmsDocument>()\n if (documentsResult.ok) {\n for (const record of documentsResult.value) {\n documents.set(\n documentKey({ collection: record.document.collection, id: record.document.id }),\n record.document,\n )\n }\n }\n // EC-287: the project graph is retired; reference source labels resolve from\n // CMS documents only. `page`/`layout` graph sources no longer exist.\n const assets = new Map<string, { filename: string; title?: string | undefined }>()\n if (assetsResult.ok) {\n for (const asset of assetsResult.value) {\n assets.set(asset.id, { filename: asset.filename, title: asset.title })\n }\n }\n const registry = getStudioRegistry().registry\n return {\n collections,\n documents,\n assets,\n // EC-407: `title` is a LocalizedString — resolve it to the active UI locale\n // (a plain string is unchanged), never leak a `{ en, de }` object to the label.\n componentTitle: (componentId) => {\n const title = registry.getManifest(componentId)?.title\n return title === undefined ? undefined : localizedLabel(title) || undefined\n },\n }\n}\n\n// --- Panel data loading -----------------------------------------------------------\n\n/** Load both directions + labels for one panel (operations queries only). */\nexport async function loadReferencePanelData(\n query: ReferencePanelQuery,\n): Promise<ReferencePanelData> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, query.projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n\n // EC-205: load each direction INDEPENDENTLY. A structured non-ok from one\n // direction is a degradable operation result (it carries an OperationError —\n // the documented contract), not an exception: degrade that direction to empty\n // and surface its error per-section (references-panel) while the other still\n // renders, instead of throwing the whole panel into its hard-error state. A\n // genuinely broken backend still throws from getStudioOperations/flush above.\n const [outbound, inbound] = await Promise.all([\n query.source\n ? operations.references.outbound({ projectId: query.projectId, source: query.source })\n : null,\n query.target\n ? operations.references.inbound({ projectId: query.projectId, target: query.target })\n : null,\n ])\n\n type DirectionResult = Awaited<ReturnType<typeof operations.references.outbound>>\n const settle = (\n result: DirectionResult | null,\n ): { records: ReferenceEntryRecord[]; error: string | null } =>\n result === null\n ? { records: [], error: null }\n : result.ok\n ? { records: result.value, error: null }\n : { records: [], error: result.error.message }\n const out = settle(outbound)\n const inb = settle(inbound)\n\n // EC-302: nothing referenced in either direction → no sources/targets to label,\n // so skip the label-context load entirely (schema + ALL documents + ALL assets).\n // Common for leaf content; mirrors the asset short-circuit in\n // `loadInboundAssetReferenceViews`.\n if (out.records.length === 0 && inb.records.length === 0) {\n return { outbound: [], inbound: [], outboundError: out.error, inboundError: inb.error }\n }\n\n const context = await loadLabelContext(operations, query.projectId)\n return {\n outbound: buildReferenceEntryViews(out.records, context),\n inbound: buildReferenceEntryViews(inb.records, context),\n outboundError: out.error,\n inboundError: inb.error,\n }\n}\n\n// --- Impact loading (EC-173 safe unpublish/delete) -------------------------------\n\n/**\n * Load the labelled inbound entry views for a set of target documents in ONE\n * pass (one index flush, one label-context load) — the pre-flight data of the\n * EC-173 confirmation dialogs, including the bulk flows. Returns entries in\n * `targets` order; pair with `buildReferenceImpact` for break/block semantics.\n *\n * `toTarget` maps each acted-on ref to its reference-index target shape\n * (EC-433): the default is the document shape every `cmsDocuments` collection\n * indexes under; a rail whose records live under a different target type (the\n * assets rail, `{ type: 'asset' }`) supplies its own via\n * `CollectionViewSource.referenceTarget` — otherwise its pre-flight would\n * query the wrong shape and report referenced records as \"not referenced\".\n */\nexport async function loadInboundReferenceViews(\n projectId: string,\n targets: readonly DocumentRef[],\n toTarget: (ref: DocumentRef) => ReferenceTarget = (ref) => ({\n type: 'document',\n id: documentKey(ref),\n }),\n): Promise<{ ref: DocumentRef; inbound: ReferenceEntryView[] }[]> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n\n // EC-308: one batched query for ALL targets (the operation loops the by_target\n // index server-side) instead of a per-target `inbound` fan-out. Returns one\n // record list per target, in `targets` order.\n const recordsPerTarget = await operations.references\n .inboundMany({\n projectId,\n targets: targets.map((ref) => toTarget(ref)),\n })\n .then((result) => {\n if (!result.ok) throw new Error(result.error.message)\n return result.value\n })\n // EC-302: no inbound refs for ANY target → no sources to label, so skip the\n // label-context load (schema + ALL documents + ALL assets). The common case when\n // deleting/unpublishing unreferenced documents (incl. bulk); mirrors the EC-301\n // short-circuit already in `loadInboundAssetReferenceViews`.\n if (recordsPerTarget.every((records) => records.length === 0)) {\n return targets.map((ref) => ({ ref, inbound: [] }))\n }\n const context = await loadLabelContext(operations, projectId)\n return targets.map((ref, index) => ({\n ref,\n inbound: buildReferenceEntryViews(recordsPerTarget[index] ?? [], context),\n }))\n}\n\n/**\n * Inbound reference views for ONE asset (EC-202): the \"what uses this asset\"\n * pre-flight of the asset-delete dialog, index-accurate (the EC-155 reference\n * index, not the retired `find-asset-usages` string scan). Mirrors\n * {@link loadInboundReferenceViews} for a single `{ type: 'asset' }` target —\n * the source labels resolve the referencing documents/graph-nodes identically.\n */\nexport async function loadInboundAssetReferenceViews(\n projectId: string,\n assetId: string,\n): Promise<ReferenceEntryView[]> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n const result = await operations.references.inbound({\n projectId,\n target: { type: 'asset', id: assetId },\n })\n if (!result.ok) throw new Error(result.error.message)\n // No inbound references → no sources to label, so skip the label-context load\n // entirely (schema + ALL documents + ALL assets). This is the common case for a\n // deletable asset, making the delete dialog's pre-flight a single indexed query\n // instead of three full-table lists (EC-301 perf).\n if (result.value.length === 0) return []\n const context = await loadLabelContext(operations, projectId)\n return buildReferenceEntryViews(result.value, context)\n}\n\n// --- React hook ----------------------------------------------------------------\n\n/**\n * Domains whose successful commands can change the reference index or labels and\n * should refresh an open panel. `references` is deliberately ABSENT (EC-269):\n * reacting to reference-index command events (the `backfill`) would make the\n * panel re-trigger on its OWN index maintenance — a self-cascade that, compounded\n * with the per-keystroke `documents` events, produced hundreds of redundant\n * reloads per document. The index is maintained on write; the panel reacts to the\n * CONTENT domains that feed it, never to its own index writes.\n */\nconst REFERENCE_REFRESH_TRIGGERS = [\n 'documents',\n 'graph',\n 'publishing',\n 'assets',\n 'schema',\n] as const\n\nconst REFRESH_DOMAINS: ReadonlySet<string> = new Set(REFERENCE_REFRESH_TRIGGERS)\n\n/**\n * Whether one operation event should refresh an open reference panel. Exported\n * for tests — pins the EC-269 contract that reference-index commands (and any\n * non-succeeded / non-command event) never trigger a reload.\n */\nexport function isReferenceRefreshEvent(event: OperationEvent): boolean {\n return (\n event.kind === 'command' && event.status === 'succeeded' && REFRESH_DOMAINS.has(event.domain)\n )\n}\n\nexport type ReferencePanelStatus = 'loading' | 'ready' | 'error'\n\nexport interface ReferencePanelState {\n status: ReferencePanelStatus\n data: ReferencePanelData | null\n errorMessage: string | null\n refresh: () => void\n}\n\n/**\n * Live panel state for one entity (EC-308/AD-17): loads + caches through the one\n * `useOperationQuery` seam, and re-queries whenever a content-bearing command\n * succeeds anywhere in the studio (the index is maintained on write). Per-direction\n * read errors degrade INSIDE `loadReferencePanelData` (carried on the data); a hard\n * failure (broken backend) surfaces as the query's error → `status: 'error'`.\n */\nexport function useReferencePanel(query: ReferencePanelQuery): ReferencePanelState {\n const { projectId } = query\n const sourceType = query.source?.type\n const sourceId = query.source?.id\n const targetType = query.target?.type\n const targetId = query.target?.id\n\n const stableQuery = useMemo<ReferencePanelQuery>(\n () => ({\n projectId,\n ...(sourceType && sourceId ? { source: { type: sourceType, id: sourceId } } : {}),\n ...(targetType && targetId ? { target: { type: targetType, id: targetId } } : {}),\n }),\n [projectId, sourceType, sourceId, targetType, targetId],\n )\n\n const result = useOperationQuery<ReferencePanelData>({\n queryKey: [\n 'referencePanel',\n projectId,\n sourceType ?? '',\n sourceId ?? '',\n targetType ?? '',\n targetId ?? '',\n ],\n loader: () => loadReferencePanelData(stableQuery),\n invalidateOn: REFERENCE_REFRESH_TRIGGERS,\n })\n\n const status: ReferencePanelStatus = result.isPending\n ? 'loading'\n : result.isError\n ? 'error'\n : 'ready'\n return {\n status,\n data: result.data ?? null,\n errorMessage: result.error instanceof Error ? result.error.message : null,\n refresh: () => void result.refetch(),\n }\n}\n"]}
1
+ {"version":3,"file":"references-store.js","sourceRoot":"","sources":["../../../src/lib/references/references-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react'\nimport { documentKey } from '@elytracms/core/cms-core'\nimport { useOperationQuery } from '@elytracms/studio/lib/query'\nimport type { CmsDocument, DocumentRef } from '@elytracms/core/cms-core'\nimport type {\n ReferenceEntryRecord,\n ReferenceSource,\n ReferenceTarget,\n} from '@elytracms/core/persistence'\nimport { localizedLabel } from '../i18n'\nimport { getStudioLocaleConfig } from '../locale/locale-store'\nimport { resolveDeploymentLocaleConfig } from '../workspace'\nimport { getStudioOperations } from '../operations'\nimport type { OperationEvent, OperationsClient, StudioOperations } from '../operations'\nimport { getStudioRegistry } from '../registry'\nimport { buildReferenceEntryViews } from './reference-model'\nimport type { ReferenceEntryView, ReferenceLabelContext } from './reference-model'\n\n/**\n * Data layer for the EC-172 references panels. All reads go through\n * `@elytracms/core/operations` queries (`references.outbound` / `references.inbound`\n * plus the labeling queries) — UI code never touches persistence repositories\n * or index tables. The hook keeps panels live: any succeeded command in a\n * content-bearing domain re-queries after the EC-155 maintained-on-write\n * attachment has flushed.\n */\n\nexport interface ReferencePanelQuery {\n projectId: string\n /** Enables the outbound (\"references\") section. */\n source?: ReferenceSource | undefined\n /** Enables the inbound (\"used by\") section. */\n target?: ReferenceTarget | undefined\n}\n\nexport interface ReferencePanelData {\n outbound: ReferenceEntryView[]\n inbound: ReferenceEntryView[]\n /**\n * Per-direction read error (EC-205): a direction whose query returns a\n * structured non-ok degrades to an empty list plus this message, surfaced as a\n * visible per-section error — instead of collapsing the whole panel into its\n * hard-error state. `null` when the direction loaded (or was not requested).\n */\n outboundError: string | null\n inboundError: string | null\n}\n\n// --- Index bootstrap -----------------------------------------------------------\n\n/**\n * The EC-155 index is maintained on write, but seeded/imported projects start\n * with an empty index. {@link ensureReferenceIndex} seeds it once per project per\n * operations client; failures (e.g. a viewer's command denial) degrade to\n * whatever the maintained-on-write index already holds.\n */\nconst backfillsByClient = new WeakMap<StudioOperations, Map<string, Promise<void>>>()\n\n/**\n * Seed the index once if it has never been backfilled, then never again — the\n * EC-269 mutation-free read path. The index is maintained on write, so the\n * (clear + rebuild) `backfill` is only needed to seed a never-built index. A\n * persisted marker (`references.buildMarker`, a read) records completion; when\n * present this returns after a single query with ZERO mutations — so opening the\n * References panel no longer rewrites the whole index every session (the normal\n * Convex case: the EC-233 import already backfilled). Only a genuinely empty/\n * never-built project still pays the one-time backfill (and a viewer who cannot\n * run it degrades to the maintained-on-write index, as before).\n */\nasync function seedReferenceIndex(studio: StudioOperations, projectId: string): Promise<void> {\n try {\n const marker = await studio.operations.references.buildMarker({ projectId })\n if (marker.ok && marker.value !== null) return\n } catch {\n // A marker-read failure falls through to the backfill (best-effort seed).\n }\n await studio.operations.references.backfill({ projectId }).then(\n () => undefined,\n () => undefined,\n )\n}\n\nexport function ensureReferenceIndex(studio: StudioOperations, projectId: string): Promise<void> {\n let byProject = backfillsByClient.get(studio)\n if (!byProject) {\n byProject = new Map()\n backfillsByClient.set(studio, byProject)\n }\n let pending = byProject.get(projectId)\n if (!pending) {\n pending = seedReferenceIndex(studio, projectId)\n byProject.set(projectId, pending)\n }\n return pending\n}\n\n// --- Label context loading -------------------------------------------------------\n\nasync function loadLabelContext(\n operations: OperationsClient,\n projectId: string,\n): Promise<ReferenceLabelContext> {\n const [schemaResult, documentsResult, assetsResult] = await Promise.all([\n operations.schema.get({ projectId }),\n operations.documents.list({ projectId }),\n operations.assets.list({ projectId }),\n ])\n\n const collections = schemaResult.ok ? schemaResult.value.collections : []\n const documents = new Map<string, CmsDocument>()\n if (documentsResult.ok) {\n for (const record of documentsResult.value) {\n documents.set(\n documentKey({ collection: record.document.collection, id: record.document.id }),\n record.document,\n )\n }\n }\n // EC-287: the project graph is retired; reference source labels resolve from\n // CMS documents only. `page`/`layout` graph sources no longer exist.\n const assets = new Map<string, { filename: string; title?: string | undefined }>()\n if (assetsResult.ok) {\n for (const asset of assetsResult.value) {\n assets.set(asset.id, { filename: asset.filename, title: asset.title })\n }\n }\n const registry = getStudioRegistry().registry\n return {\n collections,\n documents,\n assets,\n // EC-580: titles read the default-locale view of the project's ONE locale\n // config — the one the CMS session installed (`configureStudioLocales`), else\n // (a rail opened before any CMS session, e.g. Assets) the active config\n // project's, which is where the session's value comes from in the first place.\n locales: getStudioLocaleConfig() ?? resolveDeploymentLocaleConfig() ?? null,\n // EC-407: `title` is a LocalizedString — resolve it to the active UI locale\n // (a plain string is unchanged), never leak a `{ en, de }` object to the label.\n componentTitle: (componentId) => {\n const title = registry.getManifest(componentId)?.title\n return title === undefined ? undefined : localizedLabel(title) || undefined\n },\n }\n}\n\n// --- Panel data loading -----------------------------------------------------------\n\n/** Load both directions + labels for one panel (operations queries only). */\nexport async function loadReferencePanelData(\n query: ReferencePanelQuery,\n): Promise<ReferencePanelData> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, query.projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n\n // EC-205: load each direction INDEPENDENTLY. A structured non-ok from one\n // direction is a degradable operation result (it carries an OperationError —\n // the documented contract), not an exception: degrade that direction to empty\n // and surface its error per-section (references-panel) while the other still\n // renders, instead of throwing the whole panel into its hard-error state. A\n // genuinely broken backend still throws from getStudioOperations/flush above.\n const [outbound, inbound] = await Promise.all([\n query.source\n ? operations.references.outbound({ projectId: query.projectId, source: query.source })\n : null,\n query.target\n ? operations.references.inbound({ projectId: query.projectId, target: query.target })\n : null,\n ])\n\n type DirectionResult = Awaited<ReturnType<typeof operations.references.outbound>>\n const settle = (\n result: DirectionResult | null,\n ): { records: ReferenceEntryRecord[]; error: string | null } =>\n result === null\n ? { records: [], error: null }\n : result.ok\n ? { records: result.value, error: null }\n : { records: [], error: result.error.message }\n const out = settle(outbound)\n const inb = settle(inbound)\n\n // EC-302: nothing referenced in either direction → no sources/targets to label,\n // so skip the label-context load entirely (schema + ALL documents + ALL assets).\n // Common for leaf content; mirrors the asset short-circuit in\n // `loadInboundAssetReferenceViews`.\n if (out.records.length === 0 && inb.records.length === 0) {\n return { outbound: [], inbound: [], outboundError: out.error, inboundError: inb.error }\n }\n\n const context = await loadLabelContext(operations, query.projectId)\n return {\n outbound: buildReferenceEntryViews(out.records, context),\n inbound: buildReferenceEntryViews(inb.records, context),\n outboundError: out.error,\n inboundError: inb.error,\n }\n}\n\n// --- Impact loading (EC-173 safe unpublish/delete) -------------------------------\n\n/**\n * Load the labelled inbound entry views for a set of target documents in ONE\n * pass (one index flush, one label-context load) — the pre-flight data of the\n * EC-173 confirmation dialogs, including the bulk flows. Returns entries in\n * `targets` order; pair with `buildReferenceImpact` for break/block semantics.\n *\n * `toTarget` maps each acted-on ref to its reference-index target shape\n * (EC-433): the default is the document shape every `cmsDocuments` collection\n * indexes under; a rail whose records live under a different target type (the\n * assets rail, `{ type: 'asset' }`) supplies its own via\n * `CollectionViewSource.referenceTarget` — otherwise its pre-flight would\n * query the wrong shape and report referenced records as \"not referenced\".\n */\nexport async function loadInboundReferenceViews(\n projectId: string,\n targets: readonly DocumentRef[],\n toTarget: (ref: DocumentRef) => ReferenceTarget = (ref) => ({\n type: 'document',\n id: documentKey(ref),\n }),\n): Promise<{ ref: DocumentRef; inbound: ReferenceEntryView[] }[]> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n\n // EC-308: one batched query for ALL targets (the operation loops the by_target\n // index server-side) instead of a per-target `inbound` fan-out. Returns one\n // record list per target, in `targets` order.\n const recordsPerTarget = await operations.references\n .inboundMany({\n projectId,\n targets: targets.map((ref) => toTarget(ref)),\n })\n .then((result) => {\n if (!result.ok) throw new Error(result.error.message)\n return result.value\n })\n // EC-302: no inbound refs for ANY target → no sources to label, so skip the\n // label-context load (schema + ALL documents + ALL assets). The common case when\n // deleting/unpublishing unreferenced documents (incl. bulk); mirrors the EC-301\n // short-circuit already in `loadInboundAssetReferenceViews`.\n if (recordsPerTarget.every((records) => records.length === 0)) {\n return targets.map((ref) => ({ ref, inbound: [] }))\n }\n const context = await loadLabelContext(operations, projectId)\n return targets.map((ref, index) => ({\n ref,\n inbound: buildReferenceEntryViews(recordsPerTarget[index] ?? [], context),\n }))\n}\n\n/**\n * Inbound reference views for ONE asset (EC-202): the \"what uses this asset\"\n * pre-flight of the asset-delete dialog, index-accurate (the EC-155 reference\n * index, not the retired `find-asset-usages` string scan). Mirrors\n * {@link loadInboundReferenceViews} for a single `{ type: 'asset' }` target —\n * the source labels resolve the referencing documents/graph-nodes identically.\n */\nexport async function loadInboundAssetReferenceViews(\n projectId: string,\n assetId: string,\n): Promise<ReferenceEntryView[]> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n const result = await operations.references.inbound({\n projectId,\n target: { type: 'asset', id: assetId },\n })\n if (!result.ok) throw new Error(result.error.message)\n // No inbound references → no sources to label, so skip the label-context load\n // entirely (schema + ALL documents + ALL assets). This is the common case for a\n // deletable asset, making the delete dialog's pre-flight a single indexed query\n // instead of three full-table lists (EC-301 perf).\n if (result.value.length === 0) return []\n const context = await loadLabelContext(operations, projectId)\n return buildReferenceEntryViews(result.value, context)\n}\n\n// --- React hook ----------------------------------------------------------------\n\n/**\n * Domains whose successful commands can change the reference index or labels and\n * should refresh an open panel. `references` is deliberately ABSENT (EC-269):\n * reacting to reference-index command events (the `backfill`) would make the\n * panel re-trigger on its OWN index maintenance — a self-cascade that, compounded\n * with the per-keystroke `documents` events, produced hundreds of redundant\n * reloads per document. The index is maintained on write; the panel reacts to the\n * CONTENT domains that feed it, never to its own index writes.\n */\nconst REFERENCE_REFRESH_TRIGGERS = [\n 'documents',\n 'graph',\n 'publishing',\n 'assets',\n 'schema',\n] as const\n\nconst REFRESH_DOMAINS: ReadonlySet<string> = new Set(REFERENCE_REFRESH_TRIGGERS)\n\n/**\n * Whether one operation event should refresh an open reference panel. Exported\n * for tests — pins the EC-269 contract that reference-index commands (and any\n * non-succeeded / non-command event) never trigger a reload.\n */\nexport function isReferenceRefreshEvent(event: OperationEvent): boolean {\n return (\n event.kind === 'command' && event.status === 'succeeded' && REFRESH_DOMAINS.has(event.domain)\n )\n}\n\nexport type ReferencePanelStatus = 'loading' | 'ready' | 'error'\n\nexport interface ReferencePanelState {\n status: ReferencePanelStatus\n data: ReferencePanelData | null\n errorMessage: string | null\n refresh: () => void\n}\n\n/**\n * Live panel state for one entity (EC-308/AD-17): loads + caches through the one\n * `useOperationQuery` seam, and re-queries whenever a content-bearing command\n * succeeds anywhere in the studio (the index is maintained on write). Per-direction\n * read errors degrade INSIDE `loadReferencePanelData` (carried on the data); a hard\n * failure (broken backend) surfaces as the query's error → `status: 'error'`.\n */\nexport function useReferencePanel(query: ReferencePanelQuery): ReferencePanelState {\n const { projectId } = query\n const sourceType = query.source?.type\n const sourceId = query.source?.id\n const targetType = query.target?.type\n const targetId = query.target?.id\n\n const stableQuery = useMemo<ReferencePanelQuery>(\n () => ({\n projectId,\n ...(sourceType && sourceId ? { source: { type: sourceType, id: sourceId } } : {}),\n ...(targetType && targetId ? { target: { type: targetType, id: targetId } } : {}),\n }),\n [projectId, sourceType, sourceId, targetType, targetId],\n )\n\n const result = useOperationQuery<ReferencePanelData>({\n queryKey: [\n 'referencePanel',\n projectId,\n sourceType ?? '',\n sourceId ?? '',\n targetType ?? '',\n targetId ?? '',\n ],\n loader: () => loadReferencePanelData(stableQuery),\n invalidateOn: REFERENCE_REFRESH_TRIGGERS,\n })\n\n const status: ReferencePanelStatus = result.isPending\n ? 'loading'\n : result.isError\n ? 'error'\n : 'ready'\n return {\n status,\n data: result.data ?? null,\n errorMessage: result.error instanceof Error ? result.error.message : null,\n refresh: () => void result.refetch(),\n }\n}\n"]}
@@ -0,0 +1,32 @@
1
+ import type { ComponentManifest } from '@elytracms/core/component-registry';
2
+ import type { ComponentEntry } from '@elytracms/core/runtime-renderer';
3
+ import type { ComponentType } from 'react';
4
+ import type { StudioRegistry } from '../index.js';
5
+ /**
6
+ * TEST FIXTURES (EC-591). Sample `project.*` components that stand in for the
7
+ * project-specific components at the center of the product: each declares a full
8
+ * manifest (prop schema metadata, defaults, inspector controls, slot rules, and
9
+ * preview fixtures) and ships a self-contained implementation the fixture runner
10
+ * renders. Deterministic — no time/random.
11
+ *
12
+ * Until EC-591 these were the studio's DEMO FALLBACK: a project whose config
13
+ * listed no components got them in its palette — insertable, publish-valid, and
14
+ * missing on delivery (the worst row). The fallback is deleted; they live on only
15
+ * as the deterministic component set studio tests pin to.
16
+ */
17
+ type Props = Record<string, unknown>;
18
+ /** Project component manifests (including the deliberately-unimplemented one). */
19
+ export declare const projectComponentManifests: ComponentManifest[];
20
+ /** Implementations for the project components — note `LegacyBanner` is absent. */
21
+ export declare const projectComponentImplementations: Record<string, ComponentType<Props>>;
22
+ /** The demo components as config entries (`LegacyBanner` with no implementation). */
23
+ export declare const demoComponentEntries: readonly ComponentEntry[];
24
+ /**
25
+ * The deterministic registry studio tests pin to: the shipped components plus the
26
+ * demo components — what a config listing `[...elytraComponents, ...demo]` gets,
27
+ * through the one registration step.
28
+ */
29
+ export declare function fixtureStudioRegistry(): StudioRegistry;
30
+ /** The same list, for `setStudioComponentsForTests`. */
31
+ export declare const fixtureComponentEntries: readonly ComponentEntry[];
32
+ export {};
@@ -1,5 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { defineComponent } from '@elytracms/core/component-registry';
3
+ import { registerComponents } from '@elytracms/core/runtime-renderer';
4
+ import { elytraComponents } from '@elytracms/components';
3
5
  function str(value, fallback = '') {
4
6
  return typeof value === 'string' && value.length > 0 ? value : fallback;
5
7
  }
@@ -10,7 +12,6 @@ function MarketingHero(props) {
10
12
  }
11
13
  const MarketingHeroManifest = defineComponent({
12
14
  id: 'project.MarketingHero',
13
- namespace: 'project',
14
15
  title: 'Marketing Hero',
15
16
  description: 'A headline hero section with eyebrow, subheading, and a call to action.',
16
17
  category: 'marketing',
@@ -55,7 +56,6 @@ function FeatureCard(props) {
55
56
  }
56
57
  const FeatureCardManifest = defineComponent({
57
58
  id: 'project.FeatureCard',
58
- namespace: 'project',
59
59
  title: 'Feature Card',
60
60
  description: 'A compact card highlighting a single feature.',
61
61
  category: 'marketing',
@@ -91,7 +91,6 @@ function PricingCallout(props) {
91
91
  }
92
92
  const PricingCalloutManifest = defineComponent({
93
93
  id: 'project.PricingCallout',
94
- namespace: 'project',
95
94
  title: 'Pricing Callout',
96
95
  description: 'A pricing plan callout with an optional featured emphasis.',
97
96
  category: 'commerce',
@@ -116,7 +115,6 @@ const PricingCalloutManifest = defineComponent({
116
115
  // missing-component fallback (brief §4.11).
117
116
  const LegacyBannerManifest = defineComponent({
118
117
  id: 'project.LegacyBanner',
119
- namespace: 'project',
120
118
  title: 'Legacy Banner',
121
119
  description: 'A deprecated banner whose implementation has not been wired up.',
122
120
  category: 'marketing',
@@ -139,4 +137,19 @@ export const projectComponentImplementations = {
139
137
  'project.FeatureCard': FeatureCard,
140
138
  'project.PricingCallout': PricingCallout,
141
139
  };
142
- //# sourceMappingURL=project-components.js.map
140
+ /** The demo components as config entries (`LegacyBanner` with no implementation). */
141
+ export const demoComponentEntries = projectComponentManifests.map((manifest) => ({
142
+ manifest,
143
+ implementation: projectComponentImplementations[manifest.id],
144
+ }));
145
+ /**
146
+ * The deterministic registry studio tests pin to: the shipped components plus the
147
+ * demo components — what a config listing `[...elytraComponents, ...demo]` gets,
148
+ * through the one registration step.
149
+ */
150
+ export function fixtureStudioRegistry() {
151
+ return registerComponents([...elytraComponents, ...demoComponentEntries]);
152
+ }
153
+ /** The same list, for `setStudioComponentsForTests`. */
154
+ export const fixtureComponentEntries = [...elytraComponents, ...demoComponentEntries];
155
+ //# sourceMappingURL=demo-components.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demo-components.js","sourceRoot":"","sources":["../../../../src/lib/registry/__fixtures__/demo-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 { registerComponents } from '@elytracms/core/runtime-renderer'\nimport type { ComponentEntry } from '@elytracms/core/runtime-renderer'\nimport { elytraComponents } from '@elytracms/components'\nimport type { ComponentType, ReactNode } from 'react'\nimport type { StudioRegistry } from '../index'\n\n/**\n * TEST FIXTURES (EC-591). Sample `project.*` components that stand in for the\n * project-specific components at the center of the product: each declares a full\n * manifest (prop schema metadata, defaults, inspector controls, slot rules, and\n * preview fixtures) and ships a self-contained implementation the fixture runner\n * renders. Deterministic — no time/random.\n *\n * Until EC-591 these were the studio's DEMO FALLBACK: a project whose config\n * listed no components got them in its palette — insertable, publish-valid, and\n * missing on delivery (the worst row). The fallback is deleted; they live on only\n * as the deterministic component set studio tests pin to.\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 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 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 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 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\n/** The demo components as config entries (`LegacyBanner` with no implementation). */\nexport const demoComponentEntries: readonly ComponentEntry[] = projectComponentManifests.map((manifest) => ({\n manifest,\n implementation: projectComponentImplementations[manifest.id],\n}))\n\n/**\n * The deterministic registry studio tests pin to: the shipped components plus the\n * demo components — what a config listing `[...elytraComponents, ...demo]` gets,\n * through the one registration step.\n */\nexport function fixtureStudioRegistry(): StudioRegistry {\n return registerComponents([...elytraComponents, ...demoComponentEntries])\n}\n\n/** The same list, for `setStudioComponentsForTests`. */\nexport const fixtureComponentEntries: readonly ComponentEntry[] = [...elytraComponents, ...demoComponentEntries]\n"]}
@@ -1,11 +1,10 @@
1
- import { ComponentRegistry } from '@elytracms/core/component-registry';
2
- import type { ComponentManifest } from '@elytracms/core/component-registry';
1
+ import type { ComponentManifest, ComponentRegistry } from '@elytracms/core/component-registry';
3
2
  import type { PropFieldLookup } from '@elytracms/core/cms-core';
4
- import type { ComponentImplementations, RenderContext } from '@elytracms/core/runtime-renderer';
3
+ import type { ComponentEntry, ComponentImplementations, RenderContext } from '@elytracms/core/runtime-renderer';
5
4
  /**
6
5
  * The Studio's view of a project's component registry (brief §7.2, §8.3): the
7
- * platform primitives plus the project's own components, with their
8
- * implementations for live previews. Deterministic singleton, built in-process
6
+ * project's component LIST — exactly what its config's `components` names — with
7
+ * the implementations for live previews. Deterministic singleton, built in-process
9
8
  * from code-owned manifests — component manifests live in TS next to the
10
9
  * components, so local/CI/browser runs need no backend.
11
10
  */
@@ -13,16 +12,13 @@ export interface StudioRegistry {
13
12
  registry: ComponentRegistry;
14
13
  implementations: ComponentImplementations;
15
14
  }
16
- export declare function getStudioRegistry(): StudioRegistry;
17
15
  /**
18
- * The built-in demo fixtures as a StudioRegistry, independent of the active
19
- * project's `components` config ref. This is the golden-path component set
20
- * (`project.MarketingHero`, `FeatureCard`, `PricingCallout`, `LegacyBanner`) used
21
- * when a project declares no ref — and the deterministic registry that blocks /
22
- * clipboard / thumbnail tests pin to, so they assert against a known component set
23
- * regardless of which library `elytra.config.ts` happens to select (EC-194).
16
+ * Test seam: register this list instead of the active project's config list
17
+ * (`null` restores the config). Never used by app code — the studio's components
18
+ * come from `elytra.config.ts`.
24
19
  */
25
- export declare function fixtureStudioRegistry(): StudioRegistry;
20
+ export declare function setStudioComponentsForTests(entries: readonly ComponentEntry[] | null): void;
21
+ export declare function getStudioRegistry(): StudioRegistry;
26
22
  /**
27
23
  * Adapt the live registry to the cms-core {@link PropFieldLookup} contract (EC-277):
28
24
  * a component id → its prop field-defs, name-injected. Threaded into the studio's
@@ -1,72 +1,45 @@
1
- import { ComponentRegistry, manifestPropFields } from '@elytracms/core/component-registry';
2
- import { basePrimitives, getImplementation, registerImplementations } from '@elytracms/core/runtime-renderer';
3
- import { getActiveProjectSlug, resolveDeploymentComponents } from '../workspace/index.js';
4
- import { projectComponentImplementations, projectComponentManifests } from './project-components.js';
1
+ import { manifestPropFields } from '@elytracms/core/component-registry';
2
+ import { getImplementation, registerComponents } from '@elytracms/core/runtime-renderer';
3
+ import { resolveDeploymentComponents } from '../workspace/index.js';
5
4
  /**
6
- * EC-555: the config's implementations are `unknown` (`HostComponentEntry`), so
7
- * they go through core's one registration step — the same one the Next host
8
- * uses (EC-541) — instead of a cast: a non-component is withheld (its nodes
9
- * take the renderer's missing-component path, never `createElement`) and
10
- * recorded as an `invalid-implementation` issue. The fixture set below is
11
- * engine-owned and typed as components, so it needs no runtime check.
5
+ * EC-194/EC-208/EC-223, reshaped by EC-591: the consuming repo's component list,
6
+ * INJECTED through config-as-code (the mount contract) — the active project's
7
+ * `components`, the live (manifest + implementation) pairs its `elytra.config.ts`
8
+ * hands in (the components Elytra ships are an opt-in package listed there:
9
+ * `[...elytraComponents, ...siteComponents]`). The studio registers NOTHING of its
10
+ * own: no base primitives and no demo fallback. A project that lists no components
11
+ * gets an empty palette — the canvas says why (its role notices) — never a set of
12
+ * demo components the delivery host would render as missing-component fallbacks
13
+ * (the EC-591 worst row).
14
+ *
15
+ * The list goes through core's ONE registration step `registerComponents` — the
16
+ * same call the Next host and the preview harness make — so a duplicate id, a role
17
+ * claim or an `invalid-implementation` is the identical issue on every surface, in
18
+ * `registry.issues` (the one record the status readers below consult).
12
19
  */
13
- function projectComponents() {
14
- const components = resolveDeploymentComponents();
15
- if (components && components.length > 0) {
16
- const { implementations, issues } = registerImplementations(components);
17
- return { manifests: components.map((c) => c.manifest), implementations, issues };
18
- }
19
- return {
20
- manifests: projectComponentManifests,
21
- implementations: projectComponentImplementations,
22
- issues: [],
23
- };
24
- }
25
- /** Compose a StudioRegistry from the base primitives plus a project component set. */
26
- function buildStudioRegistry(project) {
27
- const base = basePrimitives();
28
- // Inline-editable opt-ins (EC-158) live directly on the base-primitive
29
- // manifests (`Heading.text`, `Text.value`) in `@elytracms/core/runtime-renderer`.
30
- const registry = new ComponentRegistry([...base.manifests, ...project.manifests]);
31
- // EC-555: implementation issues join the registry's OWN list, because that is
32
- // the one list every studio status reader consults — `componentStatus` below
33
- // (→ 'invalid'), the Components surface, and the blocks panel tile, which
34
- // prints each issue's message. A second list would be a second reader.
35
- registry.issues.push(...project.issues);
36
- return {
37
- registry,
38
- // Project wins over base by spread (a project entry under a `base.*` id
39
- // replaces the primitive; a WITHHELD one leaves the primitive in place).
40
- implementations: { ...base.implementations, ...project.implementations },
41
- };
20
+ let componentsForTests = null;
21
+ /**
22
+ * Test seam: register this list instead of the active project's config list
23
+ * (`null` restores the config). Never used by app code — the studio's components
24
+ * come from `elytra.config.ts`.
25
+ */
26
+ export function setStudioComponentsForTests(entries) {
27
+ componentsForTests = entries;
28
+ cached = null;
42
29
  }
30
+ const NO_COMPONENTS = [];
43
31
  let cached = null;
44
- let cachedSlug = null;
32
+ let cachedList = null;
45
33
  export function getStudioRegistry() {
46
- // Re-derive when the active project changes — different projects may register
47
- // different component libraries (per-project registry, AD-8).
48
- const slug = getActiveProjectSlug();
49
- if (cached && cachedSlug === slug)
34
+ // Re-derive when the list changes — a different active project (per-project
35
+ // registry, AD-8) or a re-mounted config hands over a different list.
36
+ const list = componentsForTests ?? resolveDeploymentComponents() ?? NO_COMPONENTS;
37
+ if (cached && cachedList === list)
50
38
  return cached;
51
- cached = buildStudioRegistry(projectComponents());
52
- cachedSlug = slug;
39
+ cached = registerComponents(list);
40
+ cachedList = list;
53
41
  return cached;
54
42
  }
55
- /**
56
- * The built-in demo fixtures as a StudioRegistry, independent of the active
57
- * project's `components` config ref. This is the golden-path component set
58
- * (`project.MarketingHero`, `FeatureCard`, `PricingCallout`, `LegacyBanner`) used
59
- * when a project declares no ref — and the deterministic registry that blocks /
60
- * clipboard / thumbnail tests pin to, so they assert against a known component set
61
- * regardless of which library `elytra.config.ts` happens to select (EC-194).
62
- */
63
- export function fixtureStudioRegistry() {
64
- return buildStudioRegistry({
65
- manifests: projectComponentManifests,
66
- implementations: projectComponentImplementations,
67
- issues: [],
68
- });
69
- }
70
43
  /**
71
44
  * Adapt the live registry to the cms-core {@link PropFieldLookup} contract (EC-277):
72
45
  * a component id → its prop field-defs, name-injected. Threaded into the studio's
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/registry/index.tsx"],"names":[],"mappings":"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","sourcesContent":["import { ComponentRegistry, manifestPropFields } from '@elytracms/core/component-registry'\nimport type { ComponentIssue, ComponentManifest } from '@elytracms/core/component-registry'\nimport type { PropFieldLookup } from '@elytracms/core/cms-core'\nimport { basePrimitives, getImplementation, registerImplementations } from '@elytracms/core/runtime-renderer'\nimport type { ComponentImplementations, RenderContext } from '@elytracms/core/runtime-renderer'\nimport { getActiveProjectSlug, resolveDeploymentComponents } from '../workspace'\nimport { projectComponentImplementations, projectComponentManifests } from './project-components'\n\n/**\n * The Studio's view of a project's component registry (brief §7.2, §8.3): the\n * platform primitives plus the project's own components, with their\n * implementations for live previews. Deterministic singleton, built in-process\n * from code-owned manifests — component manifests live in TS next to the\n * components, so local/CI/browser runs need no backend.\n */\nexport interface StudioRegistry {\n registry: ComponentRegistry\n implementations: ComponentImplementations\n}\n\n/**\n * EC-194/EC-208/EC-223: the consuming repo's OWN real components, INJECTED through\n * config-as-code (the mount contract). The active project's `components` field holds\n * the live (manifest + implementation) pairs the consumer's `elytra.config.ts` handed\n * in by dependency injection — for Aurora these live in\n * `apps/example-site/components/marketing` (Next-free, so they render identically in\n * the studio canvas and the host). No `@site` alias and no static codegen seam: the\n * registry reads the live set off the resolved config. Absent any, the studio falls\n * back to the built-in demo fixtures (golden path).\n */\n/** A project component set, ready to layer over the base primitives. */\ninterface ProjectComponentSet {\n manifests: ComponentManifest[]\n implementations: ComponentImplementations\n /** Registration issues the registry itself cannot see (`invalid-implementation`, EC-555). */\n issues: readonly ComponentIssue[]\n}\n\n/**\n * EC-555: the config's implementations are `unknown` (`HostComponentEntry`), so\n * they go through core's one registration step — the same one the Next host\n * uses (EC-541) — instead of a cast: a non-component is withheld (its nodes\n * take the renderer's missing-component path, never `createElement`) and\n * recorded as an `invalid-implementation` issue. The fixture set below is\n * engine-owned and typed as components, so it needs no runtime check.\n */\nfunction projectComponents(): ProjectComponentSet {\n const components = resolveDeploymentComponents()\n if (components && components.length > 0) {\n const { implementations, issues } = registerImplementations(components)\n return { manifests: components.map((c) => c.manifest), implementations, issues }\n }\n return {\n manifests: projectComponentManifests,\n implementations: projectComponentImplementations,\n issues: [],\n }\n}\n\n/** Compose a StudioRegistry from the base primitives plus a project component set. */\nfunction buildStudioRegistry(project: ProjectComponentSet): StudioRegistry {\n const base = basePrimitives()\n // Inline-editable opt-ins (EC-158) live directly on the base-primitive\n // manifests (`Heading.text`, `Text.value`) in `@elytracms/core/runtime-renderer`.\n const registry = new ComponentRegistry([...base.manifests, ...project.manifests])\n // EC-555: implementation issues join the registry's OWN list, because that is\n // the one list every studio status reader consults — `componentStatus` below\n // (→ 'invalid'), the Components surface, and the blocks panel tile, which\n // prints each issue's message. A second list would be a second reader.\n registry.issues.push(...project.issues)\n return {\n registry,\n // Project wins over base by spread (a project entry under a `base.*` id\n // replaces the primitive; a WITHHELD one leaves the primitive in place).\n implementations: { ...base.implementations, ...project.implementations },\n }\n}\n\nlet cached: StudioRegistry | null = null\nlet cachedSlug: string | null = null\n\nexport function getStudioRegistry(): StudioRegistry {\n // Re-derive when the active project changes — different projects may register\n // different component libraries (per-project registry, AD-8).\n const slug = getActiveProjectSlug()\n if (cached && cachedSlug === slug) return cached\n cached = buildStudioRegistry(projectComponents())\n cachedSlug = slug\n return cached\n}\n\n/**\n * The built-in demo fixtures as a StudioRegistry, independent of the active\n * project's `components` config ref. This is the golden-path component set\n * (`project.MarketingHero`, `FeatureCard`, `PricingCallout`, `LegacyBanner`) used\n * when a project declares no ref — and the deterministic registry that blocks /\n * clipboard / thumbnail tests pin to, so they assert against a known component set\n * regardless of which library `elytra.config.ts` happens to select (EC-194).\n */\nexport function fixtureStudioRegistry(): StudioRegistry {\n return buildStudioRegistry({\n manifests: projectComponentManifests,\n implementations: projectComponentImplementations,\n issues: [],\n })\n}\n\n/**\n * Adapt the live registry to the cms-core {@link PropFieldLookup} contract (EC-277):\n * a component id → its prop field-defs, name-injected. Threaded into the studio's\n * write-time self-typing lens so relation/asset props inside compositions are\n * self-typed on write (\"schema leads, component adopts\"). Manifests are code (AD-11);\n * persistence/cms-core never depend on the registry — the studio supplies this shape.\n */\nexport function studioManifestLookup(): PropFieldLookup {\n const { registry } = getStudioRegistry()\n return (componentId) => {\n const manifest = registry.getManifest(componentId)\n if (!manifest) return undefined\n return Object.fromEntries(manifestPropFields(manifest).map((field) => [field.name, field]))\n }\n}\n\n/** A RenderContext for fixture previews (static props only; no asset/relation resolvers). */\nexport function previewContext(studio: StudioRegistry): RenderContext {\n return { registry: studio.registry, implementations: studio.implementations }\n}\n\n/**\n * Per-component status (brief §4.11, §8.3, EC-055):\n * - `invalid`: the registry reported a structured issue for this id — including\n * an `invalid-implementation` (EC-555: the entry HAS an implementation, it is\n * just not a component, so it is not merely missing).\n * - `incompatible`: a manifest exists but no implementation is wired up.\n * - `valid`: healthy.\n */\nexport type ComponentStatus = 'valid' | 'incompatible' | 'invalid'\n\nexport function componentStatus(\n studio: StudioRegistry,\n manifest: ComponentManifest,\n): ComponentStatus {\n if (studio.registry.issues.some((i) => i.componentId === manifest.id)) return 'invalid'\n if (!getImplementation(studio.implementations, manifest.id)) return 'incompatible'\n return 'valid'\n}\n\nexport interface RegistryHealth {\n total: number\n valid: number\n incompatible: number\n invalid: number\n}\n\nexport function registryHealth(studio: StudioRegistry): RegistryHealth {\n const all = studio.registry.list()\n const health: RegistryHealth = { total: all.length, valid: 0, incompatible: 0, invalid: 0 }\n for (const m of all) health[componentStatus(studio, m)] += 1\n return health\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/registry/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAA;AAGvE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AAExF,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAA;AAc1D;;;;;;;;;;;;;;;GAeG;AACH,IAAI,kBAAkB,GAAqC,IAAI,CAAA;AAE/D;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CAAC,OAAyC;IACnF,kBAAkB,GAAG,OAAO,CAAA;IAC5B,MAAM,GAAG,IAAI,CAAA;AACf,CAAC;AAED,MAAM,aAAa,GAA8B,EAAE,CAAA;AACnD,IAAI,MAAM,GAA0B,IAAI,CAAA;AACxC,IAAI,UAAU,GAAqC,IAAI,CAAA;AAEvD,MAAM,UAAU,iBAAiB;IAC/B,4EAA4E;IAC5E,sEAAsE;IACtE,MAAM,IAAI,GAAG,kBAAkB,IAAI,2BAA2B,EAAE,IAAI,aAAa,CAAA;IACjF,IAAI,MAAM,IAAI,UAAU,KAAK,IAAI;QAAE,OAAO,MAAM,CAAA;IAChD,MAAM,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAA;IACjC,UAAU,GAAG,IAAI,CAAA;IACjB,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,EAAE,QAAQ,EAAE,GAAG,iBAAiB,EAAE,CAAA;IACxC,OAAO,CAAC,WAAW,EAAE,EAAE;QACrB,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,WAAW,CAAC,CAAA;QAClD,IAAI,CAAC,QAAQ;YAAE,OAAO,SAAS,CAAA;QAC/B,OAAO,MAAM,CAAC,WAAW,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;IAC7F,CAAC,CAAA;AACH,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,eAAe,EAAE,MAAM,CAAC,eAAe,EAAE,CAAA;AAC/E,CAAC;AAYD,MAAM,UAAU,eAAe,CAC7B,MAAsB,EACtB,QAA2B;IAE3B,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO,SAAS,CAAA;IACvF,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,eAAe,EAAE,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO,cAAc,CAAA;IAClF,OAAO,OAAO,CAAA;AAChB,CAAC;AASD,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAA;IAClC,MAAM,MAAM,GAAmB,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAA;IAC3F,KAAK,MAAM,CAAC,IAAI,GAAG;QAAE,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IAC5D,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["import { manifestPropFields } from '@elytracms/core/component-registry'\nimport type { ComponentManifest, ComponentRegistry } from '@elytracms/core/component-registry'\nimport type { PropFieldLookup } from '@elytracms/core/cms-core'\nimport { getImplementation, registerComponents } from '@elytracms/core/runtime-renderer'\nimport type { ComponentEntry, ComponentImplementations, RenderContext } from '@elytracms/core/runtime-renderer'\nimport { resolveDeploymentComponents } from '../workspace'\n\n/**\n * The Studio's view of a project's component registry (brief §7.2, §8.3): the\n * project's component LIST — exactly what its config's `components` names — with\n * the implementations for live previews. Deterministic singleton, built in-process\n * from code-owned manifests — component manifests live in TS next to the\n * components, so local/CI/browser runs need no backend.\n */\nexport interface StudioRegistry {\n registry: ComponentRegistry\n implementations: ComponentImplementations\n}\n\n/**\n * EC-194/EC-208/EC-223, reshaped by EC-591: the consuming repo's component list,\n * INJECTED through config-as-code (the mount contract) — the active project's\n * `components`, the live (manifest + implementation) pairs its `elytra.config.ts`\n * hands in (the components Elytra ships are an opt-in package listed there:\n * `[...elytraComponents, ...siteComponents]`). The studio registers NOTHING of its\n * own: no base primitives and no demo fallback. A project that lists no components\n * gets an empty palette — the canvas says why (its role notices) — never a set of\n * demo components the delivery host would render as missing-component fallbacks\n * (the EC-591 worst row).\n *\n * The list goes through core's ONE registration step `registerComponents` — the\n * same call the Next host and the preview harness make — so a duplicate id, a role\n * claim or an `invalid-implementation` is the identical issue on every surface, in\n * `registry.issues` (the one record the status readers below consult).\n */\nlet componentsForTests: readonly ComponentEntry[] | null = null\n\n/**\n * Test seam: register this list instead of the active project's config list\n * (`null` restores the config). Never used by app code — the studio's components\n * come from `elytra.config.ts`.\n */\nexport function setStudioComponentsForTests(entries: readonly ComponentEntry[] | null): void {\n componentsForTests = entries\n cached = null\n}\n\nconst NO_COMPONENTS: readonly ComponentEntry[] = []\nlet cached: StudioRegistry | null = null\nlet cachedList: readonly ComponentEntry[] | null = null\n\nexport function getStudioRegistry(): StudioRegistry {\n // Re-derive when the list changes — a different active project (per-project\n // registry, AD-8) or a re-mounted config hands over a different list.\n const list = componentsForTests ?? resolveDeploymentComponents() ?? NO_COMPONENTS\n if (cached && cachedList === list) return cached\n cached = registerComponents(list)\n cachedList = list\n return cached\n}\n\n/**\n * Adapt the live registry to the cms-core {@link PropFieldLookup} contract (EC-277):\n * a component id → its prop field-defs, name-injected. Threaded into the studio's\n * write-time self-typing lens so relation/asset props inside compositions are\n * self-typed on write (\"schema leads, component adopts\"). Manifests are code (AD-11);\n * persistence/cms-core never depend on the registry — the studio supplies this shape.\n */\nexport function studioManifestLookup(): PropFieldLookup {\n const { registry } = getStudioRegistry()\n return (componentId) => {\n const manifest = registry.getManifest(componentId)\n if (!manifest) return undefined\n return Object.fromEntries(manifestPropFields(manifest).map((field) => [field.name, field]))\n }\n}\n\n/** A RenderContext for fixture previews (static props only; no asset/relation resolvers). */\nexport function previewContext(studio: StudioRegistry): RenderContext {\n return { registry: studio.registry, implementations: studio.implementations }\n}\n\n/**\n * Per-component status (brief §4.11, §8.3, EC-055):\n * - `invalid`: the registry reported a structured issue for this id — including\n * an `invalid-implementation` (EC-555: the entry HAS an implementation, it is\n * just not a component, so it is not merely missing).\n * - `incompatible`: a manifest exists but no implementation is wired up.\n * - `valid`: healthy.\n */\nexport type ComponentStatus = 'valid' | 'incompatible' | 'invalid'\n\nexport function componentStatus(\n studio: StudioRegistry,\n manifest: ComponentManifest,\n): ComponentStatus {\n if (studio.registry.issues.some((i) => i.componentId === manifest.id)) return 'invalid'\n if (!getImplementation(studio.implementations, manifest.id)) return 'incompatible'\n return 'valid'\n}\n\nexport interface RegistryHealth {\n total: number\n valid: number\n incompatible: number\n invalid: number\n}\n\nexport function registryHealth(studio: StudioRegistry): RegistryHealth {\n const all = studio.registry.list()\n const health: RegistryHealth = { total: all.length, valid: 0, incompatible: 0, invalid: 0 }\n for (const m of all) health[componentStatus(studio, m)] += 1\n return health\n}\n"]}
@@ -1,15 +1,30 @@
1
1
  import type { ComponentNode, JsonValue } from '@elytracms/core/project-graph';
2
- import type { ComponentManifest } from '@elytracms/core/component-registry';
2
+ import type { ComponentManifest, ComponentRegistry } from '@elytracms/core/component-registry';
3
3
  /** A named, renderable preview state for a component. */
4
4
  export interface PreviewSpec {
5
5
  name: string;
6
6
  node: ComponentNode;
7
7
  }
8
- /** Build a node from a manifest's defaults, overlaying any provided prop values. */
9
- export declare function nodeFromProps(manifest: ComponentManifest, overrides: Record<string, JsonValue>, id: string): ComponentNode;
10
8
  /**
11
- * Resolve the preview states for a component (brief §7.2, §8.3, EC-054):
12
- * declared manifest fixtures first; otherwise authored primitive previews;
13
- * otherwise a single default-props preview. Deterministic ids throughout.
9
+ * A node carrying exactly the given prop values (EC-591 / ratified 2f): a prop
10
+ * the caller does not set stays ABSENT — the implementation's own default applies
11
+ * — and the manifest `default` is never copied in (it is an authoring hint). Until
12
+ * EC-591 this overlaid the overrides on every manifest default, so a fixture
13
+ * insert stored — and rendered — something other than the same fixture pasted
14
+ * from "copy exact configuration" (COMPOSITION #19).
15
+ *
16
+ * Node ids are derived deterministically from a stable base (manifest id +
17
+ * fixture index) so previews are byte-identical across SSR and client hydration
18
+ * and reproducible across runs — no module-level counter, no time/random.
14
19
  */
15
- export declare function previewSpecs(manifest: ComponentManifest): PreviewSpec[];
20
+ export declare function nodeFromProps(manifest: ComponentManifest, values: Record<string, JsonValue>, id: string): ComponentNode;
21
+ /**
22
+ * Resolve the preview states for a component (brief §7.2, §8.3, EC-054): the
23
+ * declared manifest fixtures; otherwise the single `Default` — the SAME seeded
24
+ * node the blocks panel inserts and `elytra previews` renders for a fixture-less
25
+ * component (a layout container comes pre-filled through its role, EC-383).
26
+ * EC-591: there are no authored previews keyed by engine ids any more — a
27
+ * component that wants a richer preview declares `fixtures`, like any other.
28
+ * Deterministic ids throughout.
29
+ */
30
+ export declare function previewSpecs(manifest: ComponentManifest, registry: ComponentRegistry): PreviewSpec[];
@@ -1,110 +1,47 @@
1
- import { staticProp } from '@elytracms/core/project-graph';
1
+ import { cloneWithNewIds, staticProp } from '@elytracms/core/project-graph';
2
+ import { seededNodeFromManifest } from '../../components/builder/node-factory.js';
2
3
  /**
4
+ * A node carrying exactly the given prop values (EC-591 / ratified 2f): a prop
5
+ * the caller does not set stays ABSENT — the implementation's own default applies
6
+ * — and the manifest `default` is never copied in (it is an authoring hint). Until
7
+ * EC-591 this overlaid the overrides on every manifest default, so a fixture
8
+ * insert stored — and rendered — something other than the same fixture pasted
9
+ * from "copy exact configuration" (COMPOSITION #19).
10
+ *
3
11
  * Node ids are derived deterministically from a stable base (manifest id +
4
12
  * fixture index) so previews are byte-identical across SSR and client hydration
5
13
  * and reproducible across runs — no module-level counter, no time/random.
6
14
  */
7
- function text(value, id) {
8
- return { id, componentId: 'base.Text', props: { value: staticProp(value) } };
9
- }
10
- /** Build a node from a manifest's defaults, overlaying any provided prop values. */
11
- export function nodeFromProps(manifest, overrides, id) {
15
+ export function nodeFromProps(manifest, values, id) {
12
16
  const props = {};
13
- for (const [name, spec] of Object.entries(manifest.props)) {
14
- const value = name in overrides ? overrides[name] : spec.default;
17
+ for (const name of Object.keys(manifest.props)) {
18
+ const value = values[name];
15
19
  if (value !== undefined)
16
20
  props[name] = staticProp(value);
17
21
  }
18
22
  return { id, componentId: manifest.id, props };
19
23
  }
20
- function nodeFromPropsRaw(componentId, props, id) {
21
- const out = {};
22
- for (const [k, v] of Object.entries(props))
23
- out[k] = staticProp(v);
24
- return { id, componentId, props: out };
25
- }
26
- /**
27
- * Authored preview nodes for the base primitives (which ship no fixtures of their
28
- * own). Slot-bearing primitives get sample children so previews aren't blank.
29
- * Each builder receives a deterministic `base` id prefix.
30
- */
31
- const PRIMITIVE_PREVIEWS = {
32
- 'base.Container': (base) => [
33
- {
34
- name: 'With content',
35
- node: {
36
- id: base,
37
- componentId: 'base.Container',
38
- slots: { children: [text('Container content', `${base}-c0`)] },
39
- },
40
- },
41
- ],
42
- 'base.Stack': (base) => [
43
- {
44
- name: 'Gap 4',
45
- node: {
46
- id: base,
47
- componentId: 'base.Stack',
48
- props: { gap: staticProp(4) },
49
- slots: {
50
- children: [
51
- text('Item one', `${base}-c0`),
52
- text('Item two', `${base}-c1`),
53
- text('Item three', `${base}-c2`),
54
- ],
55
- },
56
- },
57
- },
58
- ],
59
- 'base.Grid': (base) => [
60
- {
61
- name: '2 columns',
62
- node: {
63
- id: base,
64
- componentId: 'base.Grid',
65
- props: { columns: staticProp(2), gap: staticProp(3) },
66
- slots: {
67
- children: [
68
- text('Cell A', `${base}-c0`),
69
- text('Cell B', `${base}-c1`),
70
- text('Cell C', `${base}-c2`),
71
- text('Cell D', `${base}-c3`),
72
- ],
73
- },
74
- },
75
- },
76
- ],
77
- 'base.Text': (base) => [
78
- { name: 'Sample', node: nodeFromPropsRaw('base.Text', { value: 'The quick brown fox.' }, base) },
79
- ],
80
- 'base.Heading': (base) => [
81
- { name: 'Sample', node: nodeFromPropsRaw('base.Heading', { value: 'A section heading' }, base) },
82
- ],
83
- 'base.Image': (base) => [
84
- {
85
- name: 'Placeholder',
86
- node: nodeFromPropsRaw('base.Image', { src: 'https://placehold.co/320x180', alt: 'Placeholder image' }, base),
87
- },
88
- ],
89
- 'base.Button': (base) => [
90
- { name: 'Sample', node: nodeFromPropsRaw('base.Button', { label: 'Click me' }, base) },
91
- ],
92
- };
93
24
  /**
94
- * Resolve the preview states for a component (brief §7.2, §8.3, EC-054):
95
- * declared manifest fixtures first; otherwise authored primitive previews;
96
- * otherwise a single default-props preview. Deterministic ids throughout.
25
+ * Resolve the preview states for a component (brief §7.2, §8.3, EC-054): the
26
+ * declared manifest fixtures; otherwise the single `Default` — the SAME seeded
27
+ * node the blocks panel inserts and `elytra previews` renders for a fixture-less
28
+ * component (a layout container comes pre-filled through its role, EC-383).
29
+ * EC-591: there are no authored previews keyed by engine ids any more — a
30
+ * component that wants a richer preview declares `fixtures`, like any other.
31
+ * Deterministic ids throughout.
97
32
  */
98
- export function previewSpecs(manifest) {
33
+ export function previewSpecs(manifest, registry) {
99
34
  if (manifest.fixtures && manifest.fixtures.length > 0) {
100
35
  return manifest.fixtures.map((fixture, i) => ({
101
36
  name: fixture.name,
102
37
  node: nodeFromProps(manifest, fixture.props ?? {}, `preview-${manifest.id}-${i}`),
103
38
  }));
104
39
  }
105
- const authored = PRIMITIVE_PREVIEWS[manifest.id];
106
- if (authored)
107
- return authored(`preview-${manifest.id}-0`);
108
- return [{ name: 'Default', node: nodeFromProps(manifest, {}, `preview-${manifest.id}-0`) }];
40
+ // The seed's placeholder ids are re-minted deterministically (root `…-0`, then
41
+ // `…-0-1`, `…-0-2` …) so every node in the preview tree has a distinct, stable id.
42
+ const base = `preview-${manifest.id}-0`;
43
+ let n = 0;
44
+ const node = cloneWithNewIds(seededNodeFromManifest(manifest, registry), () => (n++ === 0 ? base : `${base}-${n - 1}`));
45
+ return [{ name: 'Default', node }];
109
46
  }
110
47
  //# sourceMappingURL=preview.js.map