@elytracms/studio 0.0.93 → 0.0.95
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/analytics-cost.test.ts +817 -0
- package/convex/asset-alt-migration.test.ts +2 -2
- package/convex/asset-meta-patch.test.ts +218 -0
- package/convex/asset-replace-delete-race.test.ts +127 -0
- package/convex/assets.ts +27 -2
- package/convex/auth.ts +5 -2
- package/convex/authority.ts +35 -11
- package/convex/contract-parity.test.ts +7 -0
- package/convex/delivery-redirects.test.ts +167 -0
- package/convex/delivery.ts +23 -10
- package/convex/guard.ts +9 -7
- package/convex/http-routes.test.ts +18 -8
- package/convex/http.ts +23 -17
- package/convex/ingest.test.ts +326 -0
- package/convex/ingest.ts +281 -0
- package/convex/invites.ts +13 -5
- package/convex/members.ts +10 -4
- package/convex/rail-write-parity.test.ts +6 -6
- package/convex/record-functions.ts +325 -0
- package/convex/records.test.ts +156 -0
- package/convex/redirect-migration.test.ts +205 -0
- package/convex/redirect-revalidate.test.ts +5 -1
- package/convex/redirects.ts +120 -79
- package/convex/role-policy.test.ts +315 -0
- package/convex/roles.ts +83 -0
- package/convex/schema.ts +36 -0
- package/convex/triggers.ts +21 -1
- package/dist/addon.d.ts +71 -0
- package/dist/addon.js +67 -0
- package/dist/addon.js.map +1 -0
- package/dist/components/account/account-settings.js +2 -2
- package/dist/components/account/account-settings.js.map +1 -1
- package/dist/components/account/credential-panel.js +2 -2
- package/dist/components/account/credential-panel.js.map +1 -1
- package/dist/components/account/self-account-schema.d.ts +6 -15
- package/dist/components/account/self-account-schema.js +52 -34
- package/dist/components/account/self-account-schema.js.map +1 -1
- package/dist/components/account/self-account-view-source.d.ts +1 -1
- package/dist/components/account/self-account-view-source.js +6 -4
- package/dist/components/account/self-account-view-source.js.map +1 -1
- package/dist/components/ai/ai-chat-panel-lazy.js +1 -1
- package/dist/components/ai/ai-chat-panel-lazy.js.map +1 -1
- package/dist/components/ai/ai-chat-panel.js +6 -6
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/ai/ai-cms-panel.js +2 -2
- package/dist/components/ai/ai-cms-panel.js.map +1 -1
- package/dist/components/ai/ai-panel-toggle.js +4 -4
- package/dist/components/ai/ai-panel-toggle.js.map +1 -1
- package/dist/components/ai/chat-ui.js +2 -2
- package/dist/components/ai/chat-ui.js.map +1 -1
- package/dist/components/ai/proposal-card.d.ts +2 -2
- package/dist/components/ai/proposal-card.js +2 -2
- package/dist/components/ai/proposal-card.js.map +1 -1
- package/dist/components/ai/proposal-issues.js +2 -2
- package/dist/components/ai/proposal-issues.js.map +1 -1
- package/dist/components/ai/translate-document-action.js +2 -2
- package/dist/components/ai/translate-document-action.js.map +1 -1
- package/dist/components/ai/translate-document-dialog.js +6 -6
- package/dist/components/ai/translate-document-dialog.js.map +1 -1
- package/dist/components/ai/translate-skipped.d.ts +1 -1
- package/dist/components/ai/translate-skipped.js +2 -2
- package/dist/components/ai/translate-skipped.js.map +1 -1
- package/dist/components/app-views/default-modules.d.ts +50 -2
- package/dist/components/app-views/default-modules.js +65 -20
- package/dist/components/app-views/default-modules.js.map +1 -1
- package/dist/components/app-views/module.d.ts +53 -3
- package/dist/components/app-views/module.js +14 -7
- package/dist/components/app-views/module.js.map +1 -1
- package/dist/components/app-views/sidebar.js +1 -1
- package/dist/components/app-views/sidebar.js.map +1 -1
- package/dist/components/app-views/studio-config.d.ts +11 -2
- package/dist/components/app-views/studio-config.js.map +1 -1
- package/dist/components/assets/asset-detail.js +28 -13
- package/dist/components/assets/asset-detail.js.map +1 -1
- package/dist/components/assets/asset-grid.js +2 -2
- package/dist/components/assets/asset-grid.js.map +1 -1
- package/dist/components/assets/asset-locale.d.ts +9 -0
- package/dist/components/assets/asset-locale.js +16 -0
- package/dist/components/assets/asset-locale.js.map +1 -0
- package/dist/components/assets/asset-meta-label.d.ts +12 -0
- package/dist/components/assets/asset-meta-label.js +15 -0
- package/dist/components/assets/asset-meta-label.js.map +1 -0
- package/dist/components/assets/asset-metadata.d.ts +69 -15
- package/dist/components/assets/asset-metadata.js +99 -11
- package/dist/components/assets/asset-metadata.js.map +1 -1
- package/dist/components/assets/asset-search.js +8 -2
- package/dist/components/assets/asset-search.js.map +1 -1
- package/dist/components/assets/asset-thumb.js +9 -5
- package/dist/components/assets/asset-thumb.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +25 -11
- package/dist/components/assets/assets-collection-view-source.js +78 -26
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/assets/assets-module.js +1 -1
- package/dist/components/assets/assets-module.js.map +1 -1
- package/dist/components/assets/assets-store.d.ts +2 -2
- package/dist/components/assets/assets-store.js +22 -9
- package/dist/components/assets/assets-store.js.map +1 -1
- package/dist/components/assets/assets-view-preference.js +1 -1
- package/dist/components/assets/assets-view-preference.js.map +1 -1
- package/dist/components/assets/replace-file.js +1 -1
- package/dist/components/assets/replace-file.js.map +1 -1
- package/dist/components/auth-context.d.ts +1 -1
- package/dist/components/auth-context.js +6 -6
- package/dist/components/auth-context.js.map +1 -1
- package/dist/components/builder/blocks-model.d.ts +1 -1
- package/dist/components/builder/blocks-model.js +3 -3
- package/dist/components/builder/blocks-model.js.map +1 -1
- package/dist/components/builder/blocks-panel.d.ts +1 -1
- package/dist/components/builder/blocks-panel.js +4 -4
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/builder-store.d.ts +21 -0
- package/dist/components/builder/builder-store.js +24 -2
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-breakpoints.js +1 -1
- package/dist/components/builder/canvas-breakpoints.js.map +1 -1
- package/dist/components/builder/canvas-commit-boundary.d.ts +22 -0
- package/dist/components/builder/canvas-commit-boundary.js +27 -0
- package/dist/components/builder/canvas-commit-boundary.js.map +1 -0
- package/dist/components/builder/canvas-dnd.d.ts +2 -2
- package/dist/components/builder/canvas-dnd.js +1 -1
- package/dist/components/builder/canvas-dnd.js.map +1 -1
- package/dist/components/builder/canvas-inline-editing.d.ts +1 -1
- package/dist/components/builder/canvas-inline-editing.js.map +1 -1
- package/dist/components/builder/canvas-overlay-geometry.d.ts +1 -1
- package/dist/components/builder/canvas-overlay-geometry.js.map +1 -1
- package/dist/components/builder/canvas-rebase.d.ts +77 -0
- package/dist/components/builder/canvas-rebase.js +169 -0
- package/dist/components/builder/canvas-rebase.js.map +1 -0
- package/dist/components/builder/canvas-render-context.d.ts +7 -4
- package/dist/components/builder/canvas-render-context.js +21 -13
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/canvas-theme-notice.js +2 -2
- package/dist/components/builder/canvas-theme-notice.js.map +1 -1
- package/dist/components/builder/canvas.js +3 -3
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/clipboard-actions.d.ts +2 -2
- package/dist/components/builder/clipboard-actions.js +4 -4
- package/dist/components/builder/clipboard-actions.js.map +1 -1
- package/dist/components/builder/composition-field-editor-lazy.js +1 -1
- package/dist/components/builder/composition-field-editor-lazy.js.map +1 -1
- package/dist/components/builder/composition-field-editor.d.ts +21 -3
- package/dist/components/builder/composition-field-editor.js +48 -30
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/drag-session.d.ts +1 -1
- package/dist/components/builder/drag-session.js.map +1 -1
- package/dist/components/builder/fixture-thumbnail.js +1 -1
- package/dist/components/builder/fixture-thumbnail.js.map +1 -1
- package/dist/components/builder/insert-context.d.ts +1 -1
- package/dist/components/builder/insert-context.js +3 -3
- package/dist/components/builder/insert-context.js.map +1 -1
- package/dist/components/builder/layout-inspector.js +1 -1
- package/dist/components/builder/layout-inspector.js.map +1 -1
- package/dist/components/builder/node-factory.js +15 -3
- package/dist/components/builder/node-factory.js.map +1 -1
- package/dist/components/builder/node-title.js +1 -1
- package/dist/components/builder/node-title.js.map +1 -1
- package/dist/components/builder/page-tree.d.ts +1 -1
- package/dist/components/builder/page-tree.js +5 -5
- package/dist/components/builder/page-tree.js.map +1 -1
- package/dist/components/builder/placement-refusal.js +3 -3
- package/dist/components/builder/placement-refusal.js.map +1 -1
- package/dist/components/builder/property-inspector.js +7 -5
- package/dist/components/builder/property-inspector.js.map +1 -1
- package/dist/components/builder/reuse-context.js +4 -4
- package/dist/components/builder/reuse-context.js.map +1 -1
- package/dist/components/builder/reuse.js +1 -1
- package/dist/components/builder/reuse.js.map +1 -1
- package/dist/components/builder/rich-text-toolbar.js +1 -1
- package/dist/components/builder/rich-text-toolbar.js.map +1 -1
- package/dist/components/builder/run-inspector.js +11 -7
- package/dist/components/builder/run-inspector.js.map +1 -1
- 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 +5 -2
- package/dist/components/cms/asset-field.js +36 -19
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/asset-picker.js +5 -3
- package/dist/components/cms/asset-picker.js.map +1 -1
- package/dist/components/cms/bulk-actions.d.ts +1 -1
- package/dist/components/cms/bulk-actions.js +2 -2
- package/dist/components/cms/bulk-actions.js.map +1 -1
- package/dist/components/cms/bulk-bar.d.ts +2 -2
- package/dist/components/cms/bulk-bar.js +2 -2
- package/dist/components/cms/bulk-bar.js.map +1 -1
- package/dist/components/cms/canvas-commit.d.ts +56 -0
- package/dist/components/cms/canvas-commit.js +35 -0
- package/dist/components/cms/canvas-commit.js.map +1 -0
- package/dist/components/cms/cell-edit.d.ts +1 -1
- package/dist/components/cms/cell-edit.js +1 -1
- package/dist/components/cms/cell-edit.js.map +1 -1
- package/dist/components/cms/cell.d.ts +1 -1
- package/dist/components/cms/cell.js +3 -3
- package/dist/components/cms/cell.js.map +1 -1
- package/dist/components/cms/cms-index.js +2 -2
- package/dist/components/cms/cms-index.js.map +1 -1
- package/dist/components/cms/cms-labels.js +2 -2
- package/dist/components/cms/cms-labels.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +2 -2
- package/dist/components/cms/cms-store.js +10 -10
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +30 -14
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/commit-policy.d.ts +6 -2
- package/dist/components/cms/commit-policy.js +1 -1
- package/dist/components/cms/commit-policy.js.map +1 -1
- package/dist/components/cms/detail-editor.d.ts +5 -2
- package/dist/components/cms/detail-editor.js +31 -15
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/details-pane.js +1 -1
- package/dist/components/cms/details-pane.js.map +1 -1
- package/dist/components/cms/doc-state.js +1 -1
- package/dist/components/cms/doc-state.js.map +1 -1
- package/dist/components/cms/document-diff-view.js +5 -5
- package/dist/components/cms/document-diff-view.js.map +1 -1
- package/dist/components/cms/document-editor-context.js +1 -1
- package/dist/components/cms/document-editor-context.js.map +1 -1
- package/dist/components/cms/document-staging.js +1 -1
- package/dist/components/cms/document-staging.js.map +1 -1
- package/dist/components/cms/document-url-path.js +2 -2
- package/dist/components/cms/document-url-path.js.map +1 -1
- package/dist/components/cms/duplicate-document.d.ts +1 -1
- package/dist/components/cms/duplicate-document.js.map +1 -1
- package/dist/components/cms/editable-cell.js +3 -3
- package/dist/components/cms/editable-cell.js.map +1 -1
- package/dist/components/cms/field-collaboration.d.ts +1 -1
- package/dist/components/cms/field-collaboration.js +1 -1
- package/dist/components/cms/field-collaboration.js.map +1 -1
- package/dist/components/cms/field-connection.d.ts +1 -1
- package/dist/components/cms/field-connection.js +2 -2
- package/dist/components/cms/field-connection.js.map +1 -1
- package/dist/components/cms/field-control-lib.js +1 -1
- package/dist/components/cms/field-control-lib.js.map +1 -1
- package/dist/components/cms/field-control.js +4 -4
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/field-renderer.d.ts +1 -1
- package/dist/components/cms/field-renderer.js +2 -2
- package/dist/components/cms/field-renderer.js.map +1 -1
- package/dist/components/cms/filter-builder.d.ts +1 -1
- package/dist/components/cms/filter-builder.js +3 -3
- package/dist/components/cms/filter-builder.js.map +1 -1
- package/dist/components/cms/gate-validation.js +1 -1
- package/dist/components/cms/gate-validation.js.map +1 -1
- package/dist/components/cms/import-export.js +2 -2
- package/dist/components/cms/import-export.js.map +1 -1
- package/dist/components/cms/issue-copy.js +1 -1
- package/dist/components/cms/issue-copy.js.map +1 -1
- package/dist/components/cms/issues-pane.js +3 -3
- package/dist/components/cms/issues-pane.js.map +1 -1
- package/dist/components/cms/object-array-field.js +2 -2
- package/dist/components/cms/object-array-field.js.map +1 -1
- package/dist/components/cms/prop-fields.js +2 -2
- package/dist/components/cms/prop-fields.js.map +1 -1
- package/dist/components/cms/reference-confirm.d.ts +1 -1
- package/dist/components/cms/reference-confirm.js +2 -2
- package/dist/components/cms/reference-confirm.js.map +1 -1
- package/dist/components/cms/relation-picker.js +2 -2
- package/dist/components/cms/relation-picker.js.map +1 -1
- package/dist/components/cms/rich-text/block-style.js +1 -1
- package/dist/components/cms/rich-text/block-style.js.map +1 -1
- package/dist/components/cms/rich-text/component-embed.js +4 -4
- package/dist/components/cms/rich-text/component-embed.js.map +1 -1
- package/dist/components/cms/rich-text/editor.js +3 -3
- package/dist/components/cms/rich-text/editor.js.map +1 -1
- package/dist/components/cms/rich-text/embed-picker.js +3 -3
- package/dist/components/cms/rich-text/embed-picker.js.map +1 -1
- package/dist/components/cms/rich-text/embed-props.js +3 -3
- package/dist/components/cms/rich-text/embed-props.js.map +1 -1
- package/dist/components/cms/rich-text/extensions.js +1 -1
- package/dist/components/cms/rich-text/extensions.js.map +1 -1
- package/dist/components/cms/rich-text/font-size-control.d.ts +1 -1
- package/dist/components/cms/rich-text/font-size-control.js +2 -2
- package/dist/components/cms/rich-text/font-size-control.js.map +1 -1
- package/dist/components/cms/rich-text/lazy.js +1 -1
- package/dist/components/cms/rich-text/lazy.js.map +1 -1
- package/dist/components/cms/rich-text/link-control.js +2 -2
- package/dist/components/cms/rich-text/link-control.js.map +1 -1
- package/dist/components/cms/rich-text/slash-command.js +3 -3
- package/dist/components/cms/rich-text/slash-command.js.map +1 -1
- package/dist/components/cms/rich-text/toolbar.js +1 -1
- package/dist/components/cms/rich-text/toolbar.js.map +1 -1
- package/dist/components/cms/rich-text/unknown-node.js +2 -2
- package/dist/components/cms/rich-text/unknown-node.js.map +1 -1
- package/dist/components/cms/rich-text-preview.js +2 -2
- package/dist/components/cms/rich-text-preview.js.map +1 -1
- package/dist/components/cms/row-actions.d.ts +1 -1
- package/dist/components/cms/row-actions.js.map +1 -1
- package/dist/components/cms/scalar-value-editor.d.ts +1 -1
- package/dist/components/cms/scalar-value-editor.js +2 -2
- package/dist/components/cms/scalar-value-editor.js.map +1 -1
- package/dist/components/cms/use-discard-controller.d.ts +1 -1
- package/dist/components/cms/use-discard-controller.js +1 -1
- package/dist/components/cms/use-discard-controller.js.map +1 -1
- package/dist/components/cms/use-document-draft.d.ts +69 -5
- package/dist/components/cms/use-document-draft.js +118 -34
- package/dist/components/cms/use-document-draft.js.map +1 -1
- package/dist/components/cms/use-document-versions.js +1 -1
- package/dist/components/cms/use-document-versions.js.map +1 -1
- package/dist/components/cms/use-field-focus.d.ts +1 -1
- package/dist/components/cms/use-field-focus.js +2 -2
- package/dist/components/cms/use-field-focus.js.map +1 -1
- package/dist/components/cms/use-live-url.d.ts +7 -2
- package/dist/components/cms/use-live-url.js +15 -12
- package/dist/components/cms/use-live-url.js.map +1 -1
- package/dist/components/cms/use-publish-controller.d.ts +1 -1
- package/dist/components/cms/use-publish-controller.js +1 -1
- package/dist/components/cms/use-publish-controller.js.map +1 -1
- package/dist/components/cms/use-published-perspective.d.ts +1 -1
- package/dist/components/cms/use-published-perspective.js.map +1 -1
- package/dist/components/cms/use-restore-version.d.ts +1 -1
- package/dist/components/cms/use-restore-version.js +1 -1
- package/dist/components/cms/use-restore-version.js.map +1 -1
- package/dist/components/cms/version-compare-lazy.js +1 -1
- package/dist/components/cms/version-compare-lazy.js.map +1 -1
- package/dist/components/cms/version-compare.js +1 -1
- package/dist/components/cms/version-compare.js.map +1 -1
- package/dist/components/cms/versions-pane.js +3 -3
- package/dist/components/cms/versions-pane.js.map +1 -1
- package/dist/components/cms/view-bar.js +3 -3
- package/dist/components/cms/view-bar.js.map +1 -1
- package/dist/components/collection-view/cms-collection-view-source.d.ts +1 -1
- package/dist/components/collection-view/cms-collection-view-source.js +11 -7
- package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
- package/dist/components/collection-view/collection-view-context.d.ts +1 -1
- package/dist/components/collection-view/collection-view-context.js +1 -1
- package/dist/components/collection-view/collection-view-context.js.map +1 -1
- package/dist/components/collection-view/collection-view-surface.d.ts +13 -0
- package/dist/components/collection-view/collection-view-surface.js +18 -0
- package/dist/components/collection-view/collection-view-surface.js.map +1 -0
- package/dist/components/components/component-detail.js +10 -10
- package/dist/components/components/component-detail.js.map +1 -1
- package/dist/components/components/component-preview.d.ts +2 -2
- package/dist/components/components/component-preview.js +5 -5
- package/dist/components/components/component-preview.js.map +1 -1
- package/dist/components/components/component-status.d.ts +1 -1
- package/dist/components/components/component-status.js +1 -1
- package/dist/components/components/component-status.js.map +1 -1
- package/dist/components/components/components-index.js +2 -2
- package/dist/components/components/components-index.js.map +1 -1
- package/dist/components/components/field-inspector.js +1 -1
- package/dist/components/components/field-inspector.js.map +1 -1
- package/dist/components/components/registry-browser.js +3 -3
- package/dist/components/components/registry-browser.js.map +1 -1
- package/dist/components/components/taxonomy.d.ts +1 -1
- package/dist/components/components/taxonomy.js.map +1 -1
- package/dist/components/components/transfer-dialog.js +3 -3
- package/dist/components/components/transfer-dialog.js.map +1 -1
- package/dist/components/current-user-menu.js +1 -1
- package/dist/components/current-user-menu.js.map +1 -1
- package/dist/components/delivery-health-banner.js +2 -2
- package/dist/components/delivery-health-banner.js.map +1 -1
- package/dist/components/deployment-setup-gate.js +13 -4
- package/dist/components/deployment-setup-gate.js.map +1 -1
- package/dist/components/dev-login-badge.js +2 -2
- package/dist/components/dev-login-badge.js.map +1 -1
- package/dist/components/guards.js +1 -1
- package/dist/components/guards.js.map +1 -1
- package/dist/components/inspector/document-panes.d.ts +54 -0
- package/dist/components/inspector/document-panes.js +41 -0
- package/dist/components/inspector/document-panes.js.map +1 -0
- package/dist/components/inspector/inspector-sidebar.js +1 -1
- package/dist/components/inspector/inspector-sidebar.js.map +1 -1
- package/dist/components/nav-issues-registry.d.ts +53 -0
- package/dist/components/nav-issues-registry.js +69 -0
- package/dist/components/nav-issues-registry.js.map +1 -0
- package/dist/components/nav-issues.js +4 -4
- package/dist/components/nav-issues.js.map +1 -1
- package/dist/components/operation-error.d.ts +1 -1
- package/dist/components/operation-error.js +4 -4
- package/dist/components/operation-error.js.map +1 -1
- package/dist/components/playground-badge.js +2 -2
- package/dist/components/playground-badge.js.map +1 -1
- package/dist/components/project-list.js +3 -3
- package/dist/components/project-list.js.map +1 -1
- package/dist/components/project-session.d.ts +2 -2
- package/dist/components/project-session.js +5 -5
- package/dist/components/project-session.js.map +1 -1
- package/dist/components/project-switcher.js +2 -2
- package/dist/components/project-switcher.js.map +1 -1
- package/dist/components/rail-store-status.js +1 -1
- package/dist/components/rail-store-status.js.map +1 -1
- package/dist/components/rail-view-source.d.ts +6 -5
- package/dist/components/rail-view-source.js +4 -3
- package/dist/components/rail-view-source.js.map +1 -1
- package/dist/components/records/record-lens.d.ts +51 -0
- package/dist/components/records/record-lens.js +151 -0
- package/dist/components/records/record-lens.js.map +1 -0
- package/dist/components/records/record-rail.d.ts +86 -0
- package/dist/components/records/record-rail.js +241 -0
- package/dist/components/records/record-rail.js.map +1 -0
- package/dist/components/redirects/redirects-schema.js +1 -1
- package/dist/components/redirects/redirects-schema.js.map +1 -1
- package/dist/components/references/references-panel.js +2 -2
- package/dist/components/references/references-panel.js.map +1 -1
- package/dist/components/section-nav.js +27 -11
- package/dist/components/section-nav.js.map +1 -1
- package/dist/components/section-state.js +2 -2
- package/dist/components/section-state.js.map +1 -1
- package/dist/components/section-surface.js +2 -2
- package/dist/components/section-surface.js.map +1 -1
- package/dist/components/session-expired-overlay.js +3 -3
- package/dist/components/session-expired-overlay.js.map +1 -1
- package/dist/components/settings-form.js +1 -1
- package/dist/components/settings-form.js.map +1 -1
- package/dist/components/sign-in-needed-badge.js +1 -1
- package/dist/components/sign-in-needed-badge.js.map +1 -1
- package/dist/components/sign-in-view.d.ts +2 -2
- package/dist/components/sign-in-view.js +5 -5
- package/dist/components/sign-in-view.js.map +1 -1
- package/dist/components/studio-context.js +3 -3
- package/dist/components/studio-context.js.map +1 -1
- package/dist/components/studio-project-screen.js +30 -3
- package/dist/components/studio-project-screen.js.map +1 -1
- package/dist/components/studio-shell.js +6 -5
- package/dist/components/studio-shell.js.map +1 -1
- package/dist/components/theme-context.d.ts +1 -1
- package/dist/components/theme-context.js +2 -2
- package/dist/components/theme-context.js.map +1 -1
- package/dist/components/use-hydration-phase.d.ts +1 -1
- package/dist/components/use-hydration-phase.js +1 -1
- package/dist/components/use-hydration-phase.js.map +1 -1
- package/dist/components/users/invite-user-bar.d.ts +27 -0
- package/dist/components/users/invite-user-bar.js +25 -9
- package/dist/components/users/invite-user-bar.js.map +1 -1
- package/dist/components/users/role-policy-drift-issues.d.ts +38 -0
- package/dist/components/users/role-policy-drift-issues.js +71 -0
- package/dist/components/users/role-policy-drift-issues.js.map +1 -0
- package/dist/components/users/users-collection-view-source.d.ts +1 -1
- package/dist/components/users/users-collection-view-source.js +4 -2
- package/dist/components/users/users-collection-view-source.js.map +1 -1
- package/dist/components/users/users-module.js +1 -1
- package/dist/components/users/users-module.js.map +1 -1
- package/dist/components/users/users-schema.d.ts +10 -1
- package/dist/components/users/users-schema.js +45 -25
- package/dist/components/users/users-schema.js.map +1 -1
- package/dist/components/users/users-store.js +3 -3
- package/dist/components/users/users-store.js.map +1 -1
- package/dist/components/workspace-projects.d.ts +1 -1
- package/dist/components/workspace-projects.js +1 -1
- package/dist/components/workspace-projects.js.map +1 -1
- package/dist/lib/ai/index.d.ts +1 -1
- package/dist/lib/ai/index.js +1 -1
- package/dist/lib/ai/index.js.map +1 -1
- package/dist/lib/ai/node-handler.d.ts +1 -1
- package/dist/lib/ai/node-handler.js.map +1 -1
- package/dist/lib/ai/translate-document.js +1 -1
- package/dist/lib/ai/translate-document.js.map +1 -1
- package/dist/lib/assets/upload.d.ts +25 -7
- package/dist/lib/assets/upload.js +30 -21
- package/dist/lib/assets/upload.js.map +1 -1
- package/dist/lib/clipboard/paste-issues.js +1 -1
- package/dist/lib/clipboard/paste-issues.js.map +1 -1
- package/dist/lib/clipboard/paste-target.js +2 -2
- package/dist/lib/clipboard/paste-target.js.map +1 -1
- package/dist/lib/cms/published-documents.d.ts +24 -6
- package/dist/lib/cms/published-documents.js +39 -6
- package/dist/lib/cms/published-documents.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +12 -99
- package/dist/lib/cms-views/view-model.js +22 -103
- package/dist/lib/cms-views/view-model.js.map +1 -1
- package/dist/lib/collection-view/collection-view-source.d.ts +13 -3
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/data/content-session-refresh.d.ts +1 -1
- package/dist/lib/data/content-session-refresh.js.map +1 -1
- package/dist/lib/data/playground-unload-guard.d.ts +1 -1
- package/dist/lib/data/playground-unload-guard.js +1 -1
- package/dist/lib/data/playground-unload-guard.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/i18n.js +1 -1
- package/dist/lib/i18n/i18n.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +11 -3
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +9 -1
- package/dist/lib/i18n/messages.en.js +16 -4
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/config-manifest-lookup.d.ts +1 -1
- package/dist/lib/operations/config-manifest-lookup.js +3 -3
- package/dist/lib/operations/config-manifest-lookup.js.map +1 -1
- package/dist/lib/operations/project-runtime-config.d.ts +9 -0
- package/dist/lib/operations/project-runtime-config.js +13 -0
- package/dist/lib/operations/project-runtime-config.js.map +1 -1
- package/dist/lib/operations/studio-operations.d.ts +9 -1
- package/dist/lib/operations/studio-operations.js +6 -1
- package/dist/lib/operations/studio-operations.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.d.ts +16 -3
- package/dist/lib/persistence/convex-adapter.js +78 -47
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persistence/deployment-readiness.js +14 -11
- package/dist/lib/persistence/deployment-readiness.js.map +1 -1
- package/dist/lib/persistence/index.js +14 -1
- package/dist/lib/persistence/index.js.map +1 -1
- package/dist/lib/persona/labels.js +2 -1
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/query/index.d.ts +2 -2
- package/dist/lib/query/index.js +1 -1
- package/dist/lib/query/index.js.map +1 -1
- package/dist/lib/query/operations-query.d.ts +27 -1
- package/dist/lib/query/operations-query.js +13 -1
- package/dist/lib/query/operations-query.js.map +1 -1
- package/dist/lib/references/reference-model.js +1 -1
- package/dist/lib/references/reference-model.js.map +1 -1
- package/dist/lib/references/references-store.js +1 -1
- package/dist/lib/references/references-store.js.map +1 -1
- package/dist/lib/registry/index.js +2 -2
- package/dist/lib/registry/index.js.map +1 -1
- package/dist/lib/registry/preview.d.ts +10 -0
- package/dist/lib/registry/preview.js +13 -4
- package/dist/lib/registry/preview.js.map +1 -1
- package/dist/lib/routes/route-issues.js +6 -3
- package/dist/lib/routes/route-issues.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +6 -0
- package/dist/lib/workspace/index.js +8 -0
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/lib/workspace/workspace-source.js +6 -7
- package/dist/lib/workspace/workspace-source.js.map +1 -1
- package/dist/mount.d.ts +28 -0
- package/dist/mount.js +27 -0
- package/dist/mount.js.map +1 -0
- package/dist/previews/entry-node.d.ts +14 -0
- package/dist/previews/entry-node.js +22 -0
- package/dist/previews/entry-node.js.map +1 -0
- package/dist/previews/harness.js +2 -16
- package/dist/previews/harness.js.map +1 -1
- package/dist/router/root.js +10 -10
- package/dist/router/root.js.map +1 -1
- package/dist/ui/badge.d.ts +1 -1
- package/dist/ui/button.d.ts +2 -2
- package/package.json +15 -11
- package/dist/components/redirects/redirects-collection-view-source.d.ts +0 -53
- package/dist/components/redirects/redirects-collection-view-source.js +0 -146
- package/dist/components/redirects/redirects-collection-view-source.js.map +0 -1
- package/dist/components/redirects/redirects-module.d.ts +0 -21
- package/dist/components/redirects/redirects-module.js +0 -40
- package/dist/components/redirects/redirects-module.js.map +0 -1
- package/dist/components/redirects/redirects-store.d.ts +0 -68
- package/dist/components/redirects/redirects-store.js +0 -132
- package/dist/components/redirects/redirects-store.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labels.js","sourceRoot":"","sources":["../../../src/lib/persona/labels.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CmsIssueCode, CmsIssueSeverity, CmsValidationIssue } from '@elytracms/core/cms-core'\nimport { resolveLabel } from '@elytracms/core/cms-core'\nimport type { OperationError, OperationErrorCode, OperationIssue } from '@elytracms/core/operations'\nimport type { BuildFragmentError, FragmentParseError } from '@elytracms/core/clipboard'\nimport type { ContentQueryError } from '@elytracms/core/content'\nimport type { CustomizationIssue } from '@elytracms/core/studio-core'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { humanizeName } from '../utils'\n\n/**\n * Persona-aware labels (EC-192 item 2, Milestone B promise).\n *\n * A non-technical editor must never see a manifest/binding/path-token/validation\n * identifier on — or one click off — the editor path. The engine speaks in stable\n * machine codes (`cmsIssueCodeSchema`, `operationErrorCodeSchema`) and machine ids\n * (`Heading`, `r-blog-slug-en`); this module is the single translation layer the\n * editor-facing surfaces route through.\n *\n * The maps are exhaustive `Record<…Code, string>` over the Zod enums, so adding a\n * code in `@elytracms/core/cms-core` or `@elytracms/core/operations`\n * fails typecheck here until it is given an editor-facing label — the persona\n * boundary cannot silently regress. A table is only worth that guard while something\n * renders it: EC-509 deleted two that nothing did (see below). Engineers keep the raw\n * code: every surface that\n * shows a label also carries the code in a `title=`/`data-*` attribute (inspectable\n * via hover/devtools, never rendered as visible copy).\n */\n\n/**\n * EC-509 — two label tables were DELETED here rather than localized, because measuring\n * first showed nothing rendered them.\n *\n * `GRAPH_ISSUE_LABELS`/`graphIssueLabel` (16 entries, project-graph `IssueCode`) and\n * `CONDITION_OPERATOR_LABELS`/`conditionOperatorLabel` (8, `ConditionOperator`) had\n * **zero call sites** outside this file and its own test — measured by grep over\n * `packages/` + `apps/`, with `cmsIssueLabel` (4 sites) and `operationErrorLabel` (1)\n * as the known-positive control that the search finds a render site when one exists.\n *\n * The graph table was unreachable structurally, not just unused: the ONE live path from\n * graph validation into the studio was `cms-composition-validators.ts` (since EC-582\n * `@elytracms/core/validation`'s `compositionValidators`), which maps a\n * graph issue to `CompositionIssue` — a type with `message`/`severity`/`nodePath` and\n * **no `code` field at all** (`cms-core/validate-document.ts:809`). The code an\n * exhaustiveness guard exists to force a label for cannot reach an editor surface.\n * `ConditionOperator` is live in the runtime renderer's condition gate\n * (`runtime-renderer/render.tsx:48`) but has no studio surface: v1 canvas is static\n * props, editor bindings parked, so no inspector ever shows an operator to name.\n *\n * **This overturns an EC-210 decision** (2026-06-17), which kept `graphIssueLabel` when\n * the validation panel was removed, on the rationale that it was \"the EC-192 boundary's\n * exhaustiveness guard, not panel code\". That was right then and its premise is false\n * now — the guard guards a boundary with no traffic, so it only forced editor copy for\n * strings no editor can see. Localizing them would have added 24 keys to the same\n * effect. If a surface ever renders these codes it needs labels written for it, and\n * will write them localized from the start.\n *\n * Neither symbol is referenced by either consumer repo (measured read-only) and neither\n * appears in docs.\n */\n\n/**\n * EC-443: the CMS issue titles live in the EC-241 message catalog\n * (`cms.issue.<code>`, EN source + shipped de), so the Issues pane / for-review\n * lane localize with the rest of the chrome. This map stays the exhaustive\n * `Record` over the Zod enum — a new code still fails typecheck here until it\n * gets an editor-facing catalog key (the persona boundary cannot silently\n * regress); {@link cmsIssueLabel} resolves it through `translate`.\n */\nconst CMS_ISSUE_LABEL_KEYS: Record<CmsIssueCode, string> = {\n 'duplicate-collection': 'cms.issue.duplicate-collection',\n 'duplicate-field': 'cms.issue.duplicate-field',\n 'unknown-collection': 'cms.issue.unknown-collection',\n 'invalid-collection-config': 'cms.issue.invalid-collection-config',\n 'invalid-field-config': 'cms.issue.invalid-field-config',\n 'reserved-field-name': 'cms.issue.reserved-field-name',\n 'missing-required-field': 'cms.issue.missing-required-field',\n 'invalid-field-value': 'cms.issue.invalid-field-value',\n 'unknown-relation-target': 'cms.issue.unknown-relation-target',\n 'unpublished-relation-target': 'cms.issue.unpublished-relation-target',\n 'cardinality-violation': 'cms.issue.cardinality-violation',\n 'unknown-asset': 'cms.issue.unknown-asset',\n 'duplicate-document': 'cms.issue.duplicate-document',\n 'unknown-locale': 'cms.issue.unknown-locale',\n 'missing-localized-value': 'cms.issue.missing-localized-value',\n 'route-conflict': 'cms.issue.route-conflict',\n 'redirect-loop': 'cms.issue.redirect-loop',\n 'invalid-redirect': 'cms.issue.invalid-redirect',\n 'unknown-route-target': 'cms.issue.unknown-route-target',\n 'hierarchy-cycle': 'cms.issue.hierarchy-cycle',\n 'document-nesting-depth': 'cms.issue.document-nesting-depth',\n 'singleton-document-drift': 'cms.issue.singleton-document-drift',\n 'duplicate-alias': 'cms.issue.duplicate-alias',\n 'unknown-alias': 'cms.issue.unknown-alias',\n 'unfilled-slot': 'cms.issue.unfilled-slot',\n 'custom-validation': 'cms.issue.custom-validation',\n 'hidden-predicate-error': 'cms.issue.hidden-predicate-error',\n 'unaddressable-url': 'cms.issue.unaddressable-url',\n 'unknown-version': 'cms.issue.unknown-version',\n 'invalid-stored-document': 'cms.issue.invalid-stored-document',\n}\n\n/**\n * EC-507 rider — the few codes whose TITLE overstates at `warning` severity.\n *\n * `invalid-field-value` reads \"Invalid value\", which is right when the value is\n * rejected (wrong type, not among its options) and wrong when it is not: EC-472\n * deliberately STORES `0202-09-18` as a legal ISO date and merely flags the year as\n * probably unintended. A title calling that invalid contradicts the value the editor\n * can plainly see was accepted, on all three surfaces at once.\n *\n * Keyed on SEVERITY rather than on the implausible-year case specifically, because\n * the overstatement belongs to the severity: any future warning under this code has\n * the same property (the value was kept), and the fix then needs no new entry. A\n * sparse `Partial` on purpose — a code with no entry keeps one title for both\n * severities, which is the correct default and keeps\n * {@link CMS_ISSUE_LABEL_KEYS} the single exhaustive map.\n */\nconst CMS_ISSUE_WARNING_LABEL_KEYS: Partial<Record<CmsIssueCode, string>> = {\n 'invalid-field-value': 'cms.issue.invalid-field-value.warning',\n}\n\n/**\n * EC-494: the badge titles live in the EC-241 catalog (`operations.errorLabel.<code>`),\n * exactly as EC-443 moved the CMS issue titles. **Measured before changing it:** this\n * map was a hard-coded English `Record`, so once the EC-494 bodies localized the banner\n * rendered a German sentence under an English badge — the inverse of the bug this\n * ticket exists for, and not what the ticket said was there. Still the exhaustive\n * `Record` over the Zod enum, so a new code fails typecheck until it has a key.\n */\nconst OPERATION_ERROR_LABEL_KEYS: Record<OperationErrorCode, string> = {\n validation: 'operations.errorLabel.validation',\n 'not-found': 'operations.errorLabel.not-found',\n conflict: 'operations.errorLabel.conflict',\n 'backend-unavailable': 'operations.errorLabel.backend-unavailable',\n unauthorized: 'operations.errorLabel.unauthorized',\n unknown: 'operations.errorLabel.unknown',\n}\n\n/**\n * Plain-language title for a CMS validation issue code — localized against the\n * active studio-UI locale (EC-443). A runtime-unknown code (impossible typewise;\n * a cast could smuggle one) degrades to its humanized form, never the raw enum.\n *\n * `severity` is optional and additive (EC-507): pass the issue's own severity and a\n * code with a softer warning title (see {@link CMS_ISSUE_WARNING_LABEL_KEYS}) uses\n * it; omit it, or pass `'error'`, and nothing changes. Every render site passes it —\n * the parameter is optional so that a pure/unit caller naming only a code still\n * compiles, not because a surface may skip it.\n */\nexport function cmsIssueLabel(code: CmsIssueCode, severity?: CmsIssueSeverity): string {\n const key =\n (severity === 'warning' ? CMS_ISSUE_WARNING_LABEL_KEYS[code] : undefined) ??\n (CMS_ISSUE_LABEL_KEYS[code] as string | undefined)\n return key !== undefined ? translate(key) : humanizeName(code)\n}\n\n/**\n * The localized text of a {@link CustomizationIssue} (EC-478) — the ONE place the\n * studio turns a config-level finding into rendered prose.\n *\n * The producing layer (`studio-core/customization.ts`, `app-views/default-modules.tsx`,\n * `builder/reuse.ts`) cannot own the wording: the EC-236 seam lives in `packages/core`,\n * a leaf of the DAG that may not import this catalog. So it emits a\n * `CustomizationMessageRef` — a stable key plus its interpolation values, all of\n * which are IDENTIFIERS (entry ids, extension points, component ids) and therefore the\n * same in every language — and this resolves it against the active UI locale. Same\n * shape as {@link cmsIssueLabel}, one layer down: the engine speaks codes and keys, the\n * studio speaks the editor's language.\n *\n * `messageRef.reason` fills the `{reason}` slot of `invalid-override`, whose detail is\n * itself prose (a `validate` rejection) and so carries its own key. A ref-less issue —\n * a third-party producer of this published type — degrades to its English\n * `issue.message`, which is honest: a visible English sentence beats a missing key.\n */\nexport function customizationIssueMessage(issue: CustomizationIssue): string {\n const ref = issue.messageRef\n if (!ref) return issue.message\n const vars = { ...ref.vars }\n if (ref.reason) vars.reason = translate(ref.reason.key, ref.reason.vars)\n return translate(ref.key, vars)\n}\n\n/**\n * The localized BODY of a {@link CmsValidationIssue} (EC-487) — the ONE place the\n * studio turns a CMS validation finding into rendered prose, exactly as\n * {@link customizationIssueMessage} does one layer up.\n *\n * EC-443 localized the issue TITLES ({@link cmsIssueLabel}); the bodies stayed English\n * because their producers live in `packages/core`, below this catalog in the DAG. So\n * the producer emits a `CmsMessageRef` — a stable key plus its interpolation values,\n * all of which are IDENTIFIERS (field/collection/route ids, locales, patterns, counts)\n * and therefore the same in every language — and this resolves it against the active\n * UI locale.\n *\n * `messageRef.details` fill placeholders whose text is itself prose, keyed by\n * placeholder name. A placeholder with no entry keeps whatever the producer put in\n * `vars` — the honest outcome for prose the engine did not author (a config-as-code\n * `validate` message, a composition finding), which then stays in its original\n * language rather than vanishing. A ref-less issue — a third-party producer of this\n * published type — degrades to its English `issue.message`: a visible English sentence\n * beats a missing key.\n */\nexport function cmsValidationIssueMessage(issue: CmsValidationIssue): string {\n return resolveMessageRef(issue.messageRef, issue.message)\n}\n\n/**\n * EC-494 — the ONE resolver every carried message ref goes through.\n *\n * Four published types now carry the same `{ key, vars?, details? }` reference —\n * `CmsValidationIssue` (EC-487), `CustomizationIssue` (EC-478, via its own `reason`\n * slot), and now `OperationIssue`/`OperationError` and the clipboard + content-query\n * errors. They are separate declarations only because the EC-265 DAG forbids the\n * lower subpaths from importing `cms-core`; the RESOLUTION is one rule, so it lives\n * in one function.\n *\n * `fallback` is the producer's prebuilt English, rendered whenever there is no ref —\n * a third-party producer, a Zod diagnostic, a persistence-layer message, a\n * config-authored `validate` string. A visible English sentence beats a missing key\n * (EC-443).\n */\nfunction resolveMessageRef(\n ref:\n | {\n readonly key: string\n readonly vars?: Readonly<Record<string, string | number>>\n readonly details?: Readonly<\n Record<string, { readonly key: string; readonly vars?: Readonly<Record<string, string | number>> }>\n >\n }\n | undefined,\n fallback: string,\n): string {\n if (!ref) return fallback\n const vars: Record<string, string | number> = { ...ref.vars }\n for (const [placeholder, nested] of Object.entries(ref.details ?? {})) {\n vars[placeholder] = translate(nested.key, nested.vars)\n }\n return translate(ref.key, vars)\n}\n\n/**\n * The localized text of ONE {@link OperationIssue} line in the error banner (EC-494).\n *\n * These issues cross a process boundary: `operations/documents.ts` rebuilds a\n * `CmsValidationIssue` as an `OperationIssue` **server-side, with no locale**, so the\n * producer cannot translate and the catalog key rides on the wire instead. A ref-less\n * issue — a Zod input diagnostic, a persistence-layer message, a third-party producer\n * of this published type — keeps its English.\n */\nexport function operationIssueMessage(issue: OperationIssue): string {\n return resolveMessageRef(issue.messageRef, issue.message)\n}\n\n/**\n * The localized ENVELOPE sentence of an {@link OperationError} (EC-494) — the line\n * beside the localized badge in `OperationErrorBanner`. Localizing only the issue\n * lines would have left exactly one English sentence under a German badge.\n */\nexport function operationErrorMessage(error: OperationError): string {\n return resolveMessageRef(error.messageRef, error.message)\n}\n\n/**\n * The localized text of a clipboard fragment failure (EC-494) — parse (`corrupt-json`,\n * `foreign-format`, `unsupported-version`, `invalid-payload`) or build\n * (`node-not-found`, `fixture-not-found`), rendered by the builder's paste notice.\n */\nexport function clipboardErrorMessage(error: FragmentParseError | BuildFragmentError): string {\n return resolveMessageRef(error.messageRef, error.message)\n}\n\n/**\n * The localized text of a {@link ContentQueryError} (EC-494) — the filter builder's\n * per-clause line and the canvas listing notice, both of which render errors built by\n * `core/content`'s `validateWhere`/`validateSort`/`validateLimit`.\n */\nexport function contentQueryErrorMessage(error: ContentQueryError): string {\n return resolveMessageRef(error.messageRef, error.message)\n}\n\n/**\n * Plain-language title for an operation failure code — localized against the active\n * studio-UI locale. A runtime-unknown code (impossible typewise; a cast could smuggle\n * one) degrades to the generic title, never the raw enum.\n */\nexport function operationErrorLabel(code: OperationErrorCode): string {\n const key = OPERATION_ERROR_LABEL_KEYS[code] as string | undefined\n return translate(key ?? OPERATION_ERROR_LABEL_KEYS.unknown)\n}\n\n/** The last dot-segment of a component id (`site.Hero` → `Hero`; `Heading` → `Heading`). */\nexport function componentLeafId(componentId: string): string {\n const dot = componentId.lastIndexOf('.')\n return dot === -1 ? componentId : componentId.slice(dot + 1)\n}\n\n/**\n * The human display name of a component TYPE for editor-facing surfaces: the\n * manifest's authored `title` (EC-407: resolved through {@link resolveLabel}, so a\n * `{ en, de }` localized title renders in the active studio-UI locale) when present,\n * else the humanized leaf of its id (`Heading` → `Heading`,\n * `site.MarketingHero` → `Marketing hero`). EC-591: the engine strips no id\n * prefix anywhere else — a label reads the manifest title.\n *\n * `locale` is optional: omit it (the render-site default) to resolve against the\n * active studio-UI locale non-reactively; pass it explicitly for pure/unit callers.\n */\nexport function componentDisplayName(\n componentId: string,\n manifest?: ComponentManifest | undefined,\n locale?: string,\n): string {\n const resolved =\n locale === undefined ? localizedLabel(manifest?.title) : (resolveLabel(manifest?.title, locale) ?? '')\n return resolved || humanizeName(componentLeafId(componentId))\n}\n"]}
|
|
1
|
+
{"version":3,"file":"labels.js","sourceRoot":"","sources":["../../../src/lib/persona/labels.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CmsIssueCode, CmsIssueSeverity, CmsValidationIssue } from '@elytracms/core/cms-core'\nimport { resolveLabel } from '@elytracms/core/cms-core'\nimport type { OperationError, OperationErrorCode, OperationIssue } from '@elytracms/core/operations'\nimport type { BuildFragmentError, FragmentParseError } from '@elytracms/core/clipboard'\nimport type { ContentQueryError } from '@elytracms/core/content'\nimport type { CustomizationIssue } from '@elytracms/core/studio-core'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { localizedLabel, translate } from '../i18n'\nimport { humanizeName } from '../utils'\n\n/**\n * Persona-aware labels (EC-192 item 2, Milestone B promise).\n *\n * A non-technical editor must never see a manifest/binding/path-token/validation\n * identifier on — or one click off — the editor path. The engine speaks in stable\n * machine codes (`cmsIssueCodeSchema`, `operationErrorCodeSchema`) and machine ids\n * (`Heading`, `r-blog-slug-en`); this module is the single translation layer the\n * editor-facing surfaces route through.\n *\n * The maps are exhaustive `Record<…Code, string>` over the Zod enums, so adding a\n * code in `@elytracms/core/cms-core` or `@elytracms/core/operations`\n * fails typecheck here until it is given an editor-facing label — the persona\n * boundary cannot silently regress. A table is only worth that guard while something\n * renders it: EC-509 deleted two that nothing did (see below). Engineers keep the raw\n * code: every surface that\n * shows a label also carries the code in a `title=`/`data-*` attribute (inspectable\n * via hover/devtools, never rendered as visible copy).\n */\n\n/**\n * EC-509 — two label tables were DELETED here rather than localized, because measuring\n * first showed nothing rendered them.\n *\n * `GRAPH_ISSUE_LABELS`/`graphIssueLabel` (16 entries, project-graph `IssueCode`) and\n * `CONDITION_OPERATOR_LABELS`/`conditionOperatorLabel` (8, `ConditionOperator`) had\n * **zero call sites** outside this file and its own test — measured by grep over\n * `packages/` + `apps/`, with `cmsIssueLabel` (4 sites) and `operationErrorLabel` (1)\n * as the known-positive control that the search finds a render site when one exists.\n *\n * The graph table was unreachable structurally, not just unused: the ONE live path from\n * graph validation into the studio was `cms-composition-validators.ts` (since EC-582\n * `@elytracms/core/validation`'s `compositionValidators`), which maps a\n * graph issue to `CompositionIssue` — a type with `message`/`severity`/`nodePath` and\n * **no `code` field at all** (`cms-core/validate-document.ts:809`). The code an\n * exhaustiveness guard exists to force a label for cannot reach an editor surface.\n * `ConditionOperator` is live in the runtime renderer's condition gate\n * (`runtime-renderer/render.tsx:48`) but has no studio surface: v1 canvas is static\n * props, editor bindings parked, so no inspector ever shows an operator to name.\n *\n * **This overturns an EC-210 decision** (2026-06-17), which kept `graphIssueLabel` when\n * the validation panel was removed, on the rationale that it was \"the EC-192 boundary's\n * exhaustiveness guard, not panel code\". That was right then and its premise is false\n * now — the guard guards a boundary with no traffic, so it only forced editor copy for\n * strings no editor can see. Localizing them would have added 24 keys to the same\n * effect. If a surface ever renders these codes it needs labels written for it, and\n * will write them localized from the start.\n *\n * Neither symbol is referenced by either consumer repo (measured read-only) and neither\n * appears in docs.\n */\n\n/**\n * EC-443: the CMS issue titles live in the EC-241 message catalog\n * (`cms.issue.<code>`, EN source + shipped de), so the Issues pane / for-review\n * lane localize with the rest of the chrome. This map stays the exhaustive\n * `Record` over the Zod enum — a new code still fails typecheck here until it\n * gets an editor-facing catalog key (the persona boundary cannot silently\n * regress); {@link cmsIssueLabel} resolves it through `translate`.\n */\nconst CMS_ISSUE_LABEL_KEYS: Record<CmsIssueCode, string> = {\n 'duplicate-collection': 'cms.issue.duplicate-collection',\n 'duplicate-field': 'cms.issue.duplicate-field',\n 'unknown-collection': 'cms.issue.unknown-collection',\n 'invalid-collection-config': 'cms.issue.invalid-collection-config',\n 'invalid-field-config': 'cms.issue.invalid-field-config',\n 'reserved-field-name': 'cms.issue.reserved-field-name',\n 'missing-required-field': 'cms.issue.missing-required-field',\n 'invalid-field-value': 'cms.issue.invalid-field-value',\n 'unknown-relation-target': 'cms.issue.unknown-relation-target',\n 'unpublished-relation-target': 'cms.issue.unpublished-relation-target',\n 'cardinality-violation': 'cms.issue.cardinality-violation',\n 'unknown-asset': 'cms.issue.unknown-asset',\n 'asset-without-url': 'cms.issue.asset-without-url',\n 'duplicate-document': 'cms.issue.duplicate-document',\n 'unknown-locale': 'cms.issue.unknown-locale',\n 'missing-localized-value': 'cms.issue.missing-localized-value',\n 'route-conflict': 'cms.issue.route-conflict',\n 'redirect-loop': 'cms.issue.redirect-loop',\n 'invalid-redirect': 'cms.issue.invalid-redirect',\n 'unknown-route-target': 'cms.issue.unknown-route-target',\n 'hierarchy-cycle': 'cms.issue.hierarchy-cycle',\n 'document-nesting-depth': 'cms.issue.document-nesting-depth',\n 'singleton-document-drift': 'cms.issue.singleton-document-drift',\n 'duplicate-alias': 'cms.issue.duplicate-alias',\n 'unknown-alias': 'cms.issue.unknown-alias',\n 'unfilled-slot': 'cms.issue.unfilled-slot',\n 'custom-validation': 'cms.issue.custom-validation',\n 'hidden-predicate-error': 'cms.issue.hidden-predicate-error',\n 'unaddressable-url': 'cms.issue.unaddressable-url',\n 'unknown-version': 'cms.issue.unknown-version',\n 'invalid-stored-document': 'cms.issue.invalid-stored-document',\n}\n\n/**\n * EC-507 rider — the few codes whose TITLE overstates at `warning` severity.\n *\n * `invalid-field-value` reads \"Invalid value\", which is right when the value is\n * rejected (wrong type, not among its options) and wrong when it is not: EC-472\n * deliberately STORES `0202-09-18` as a legal ISO date and merely flags the year as\n * probably unintended. A title calling that invalid contradicts the value the editor\n * can plainly see was accepted, on all three surfaces at once.\n *\n * Keyed on SEVERITY rather than on the implausible-year case specifically, because\n * the overstatement belongs to the severity: any future warning under this code has\n * the same property (the value was kept), and the fix then needs no new entry. A\n * sparse `Partial` on purpose — a code with no entry keeps one title for both\n * severities, which is the correct default and keeps\n * {@link CMS_ISSUE_LABEL_KEYS} the single exhaustive map.\n */\nconst CMS_ISSUE_WARNING_LABEL_KEYS: Partial<Record<CmsIssueCode, string>> = {\n 'invalid-field-value': 'cms.issue.invalid-field-value.warning',\n}\n\n/**\n * EC-494: the badge titles live in the EC-241 catalog (`operations.errorLabel.<code>`),\n * exactly as EC-443 moved the CMS issue titles. **Measured before changing it:** this\n * map was a hard-coded English `Record`, so once the EC-494 bodies localized the banner\n * rendered a German sentence under an English badge — the inverse of the bug this\n * ticket exists for, and not what the ticket said was there. Still the exhaustive\n * `Record` over the Zod enum, so a new code fails typecheck until it has a key.\n */\nconst OPERATION_ERROR_LABEL_KEYS: Record<OperationErrorCode, string> = {\n validation: 'operations.errorLabel.validation',\n 'not-found': 'operations.errorLabel.not-found',\n conflict: 'operations.errorLabel.conflict',\n 'backend-unavailable': 'operations.errorLabel.backend-unavailable',\n unauthorized: 'operations.errorLabel.unauthorized',\n unknown: 'operations.errorLabel.unknown',\n}\n\n/**\n * Plain-language title for a CMS validation issue code — localized against the\n * active studio-UI locale (EC-443). A runtime-unknown code (impossible typewise;\n * a cast could smuggle one) degrades to its humanized form, never the raw enum.\n *\n * `severity` is optional and additive (EC-507): pass the issue's own severity and a\n * code with a softer warning title (see {@link CMS_ISSUE_WARNING_LABEL_KEYS}) uses\n * it; omit it, or pass `'error'`, and nothing changes. Every render site passes it —\n * the parameter is optional so that a pure/unit caller naming only a code still\n * compiles, not because a surface may skip it.\n */\nexport function cmsIssueLabel(code: CmsIssueCode, severity?: CmsIssueSeverity): string {\n const key =\n (severity === 'warning' ? CMS_ISSUE_WARNING_LABEL_KEYS[code] : undefined) ??\n (CMS_ISSUE_LABEL_KEYS[code] as string | undefined)\n return key !== undefined ? translate(key) : humanizeName(code)\n}\n\n/**\n * The localized text of a {@link CustomizationIssue} (EC-478) — the ONE place the\n * studio turns a config-level finding into rendered prose.\n *\n * The producing layer (`studio-core/customization.ts`, `app-views/default-modules.tsx`,\n * `builder/reuse.ts`) cannot own the wording: the EC-236 seam lives in `packages/core`,\n * a leaf of the DAG that may not import this catalog. So it emits a\n * `CustomizationMessageRef` — a stable key plus its interpolation values, all of\n * which are IDENTIFIERS (entry ids, extension points, component ids) and therefore the\n * same in every language — and this resolves it against the active UI locale. Same\n * shape as {@link cmsIssueLabel}, one layer down: the engine speaks codes and keys, the\n * studio speaks the editor's language.\n *\n * `messageRef.reason` fills the `{reason}` slot of `invalid-override`, whose detail is\n * itself prose (a `validate` rejection) and so carries its own key. A ref-less issue —\n * a third-party producer of this published type — degrades to its English\n * `issue.message`, which is honest: a visible English sentence beats a missing key.\n */\nexport function customizationIssueMessage(issue: CustomizationIssue): string {\n const ref = issue.messageRef\n if (!ref) return issue.message\n const vars = { ...ref.vars }\n if (ref.reason) vars.reason = translate(ref.reason.key, ref.reason.vars)\n return translate(ref.key, vars)\n}\n\n/**\n * The localized BODY of a {@link CmsValidationIssue} (EC-487) — the ONE place the\n * studio turns a CMS validation finding into rendered prose, exactly as\n * {@link customizationIssueMessage} does one layer up.\n *\n * EC-443 localized the issue TITLES ({@link cmsIssueLabel}); the bodies stayed English\n * because their producers live in `packages/core`, below this catalog in the DAG. So\n * the producer emits a `CmsMessageRef` — a stable key plus its interpolation values,\n * all of which are IDENTIFIERS (field/collection/route ids, locales, patterns, counts)\n * and therefore the same in every language — and this resolves it against the active\n * UI locale.\n *\n * `messageRef.details` fill placeholders whose text is itself prose, keyed by\n * placeholder name. A placeholder with no entry keeps whatever the producer put in\n * `vars` — the honest outcome for prose the engine did not author (a config-as-code\n * `validate` message, a composition finding), which then stays in its original\n * language rather than vanishing. A ref-less issue — a third-party producer of this\n * published type — degrades to its English `issue.message`: a visible English sentence\n * beats a missing key.\n */\nexport function cmsValidationIssueMessage(issue: CmsValidationIssue): string {\n return resolveMessageRef(issue.messageRef, issue.message)\n}\n\n/**\n * EC-494 — the ONE resolver every carried message ref goes through.\n *\n * Four published types now carry the same `{ key, vars?, details? }` reference —\n * `CmsValidationIssue` (EC-487), `CustomizationIssue` (EC-478, via its own `reason`\n * slot), and now `OperationIssue`/`OperationError` and the clipboard + content-query\n * errors. They are separate declarations only because the EC-265 DAG forbids the\n * lower subpaths from importing `cms-core`; the RESOLUTION is one rule, so it lives\n * in one function.\n *\n * `fallback` is the producer's prebuilt English, rendered whenever there is no ref —\n * a third-party producer, a Zod diagnostic, a persistence-layer message, a\n * config-authored `validate` string. A visible English sentence beats a missing key\n * (EC-443).\n */\nfunction resolveMessageRef(\n ref:\n | {\n readonly key: string\n readonly vars?: Readonly<Record<string, string | number>>\n readonly details?: Readonly<\n Record<string, { readonly key: string; readonly vars?: Readonly<Record<string, string | number>> }>\n >\n }\n | undefined,\n fallback: string,\n): string {\n if (!ref) return fallback\n const vars: Record<string, string | number> = { ...ref.vars }\n for (const [placeholder, nested] of Object.entries(ref.details ?? {})) {\n vars[placeholder] = translate(nested.key, nested.vars)\n }\n return translate(ref.key, vars)\n}\n\n/**\n * The localized text of ONE {@link OperationIssue} line in the error banner (EC-494).\n *\n * These issues cross a process boundary: `operations/documents.ts` rebuilds a\n * `CmsValidationIssue` as an `OperationIssue` **server-side, with no locale**, so the\n * producer cannot translate and the catalog key rides on the wire instead. A ref-less\n * issue — a Zod input diagnostic, a persistence-layer message, a third-party producer\n * of this published type — keeps its English.\n */\nexport function operationIssueMessage(issue: OperationIssue): string {\n return resolveMessageRef(issue.messageRef, issue.message)\n}\n\n/**\n * The localized ENVELOPE sentence of an {@link OperationError} (EC-494) — the line\n * beside the localized badge in `OperationErrorBanner`. Localizing only the issue\n * lines would have left exactly one English sentence under a German badge.\n */\nexport function operationErrorMessage(error: OperationError): string {\n return resolveMessageRef(error.messageRef, error.message)\n}\n\n/**\n * The localized text of a clipboard fragment failure (EC-494) — parse (`corrupt-json`,\n * `foreign-format`, `unsupported-version`, `invalid-payload`) or build\n * (`node-not-found`, `fixture-not-found`), rendered by the builder's paste notice.\n */\nexport function clipboardErrorMessage(error: FragmentParseError | BuildFragmentError): string {\n return resolveMessageRef(error.messageRef, error.message)\n}\n\n/**\n * The localized text of a {@link ContentQueryError} (EC-494) — the filter builder's\n * per-clause line and the canvas listing notice, both of which render errors built by\n * `core/content`'s `validateWhere`/`validateSort`/`validateLimit`.\n */\nexport function contentQueryErrorMessage(error: ContentQueryError): string {\n return resolveMessageRef(error.messageRef, error.message)\n}\n\n/**\n * Plain-language title for an operation failure code — localized against the active\n * studio-UI locale. A runtime-unknown code (impossible typewise; a cast could smuggle\n * one) degrades to the generic title, never the raw enum.\n */\nexport function operationErrorLabel(code: OperationErrorCode): string {\n const key = OPERATION_ERROR_LABEL_KEYS[code] as string | undefined\n return translate(key ?? OPERATION_ERROR_LABEL_KEYS.unknown)\n}\n\n/** The last dot-segment of a component id (`site.Hero` → `Hero`; `Heading` → `Heading`). */\nexport function componentLeafId(componentId: string): string {\n const dot = componentId.lastIndexOf('.')\n return dot === -1 ? componentId : componentId.slice(dot + 1)\n}\n\n/**\n * The human display name of a component TYPE for editor-facing surfaces: the\n * manifest's authored `title` (EC-407: resolved through {@link resolveLabel}, so a\n * `{ en, de }` localized title renders in the active studio-UI locale) when present,\n * else the humanized leaf of its id (`Heading` → `Heading`,\n * `site.MarketingHero` → `Marketing hero`). EC-591: the engine strips no id\n * prefix anywhere else — a label reads the manifest title.\n *\n * `locale` is optional: omit it (the render-site default) to resolve against the\n * active studio-UI locale non-reactively; pass it explicitly for pure/unit callers.\n */\nexport function componentDisplayName(\n componentId: string,\n manifest?: ComponentManifest | undefined,\n locale?: string,\n): string {\n const resolved =\n locale === undefined ? localizedLabel(manifest?.title) : (resolveLabel(manifest?.title, locale) ?? '')\n return resolved || humanizeName(componentLeafId(componentId))\n}\n"]}
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* The studio client read seam (EC-308 / AD-17): one operations-backed query cache.
|
|
3
3
|
* See `./operations-query`.
|
|
4
4
|
*/
|
|
5
|
-
export { StudioQueryProvider, useOperationQuery, useOperationCache, listStoreState, OPERATION_INVALIDATE_DEBOUNCE_MS, } from './operations-query.js';
|
|
6
|
-
export type { OperationCache, UseOperationQueryOptions } from './operations-query.js';
|
|
5
|
+
export { StudioQueryProvider, useOperationQuery, useOperationCache, listStoreState, readStateOf, OPERATION_INVALIDATE_DEBOUNCE_MS, } from './operations-query.js';
|
|
6
|
+
export type { OperationCache, ReadState, UseOperationQueryOptions } from './operations-query.js';
|
package/dist/lib/query/index.js
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* The studio client read seam (EC-308 / AD-17): one operations-backed query cache.
|
|
3
3
|
* See `./operations-query`.
|
|
4
4
|
*/
|
|
5
|
-
export { StudioQueryProvider, useOperationQuery, useOperationCache, listStoreState, OPERATION_INVALIDATE_DEBOUNCE_MS, } from './operations-query.js';
|
|
5
|
+
export { StudioQueryProvider, useOperationQuery, useOperationCache, listStoreState, readStateOf, OPERATION_INVALIDATE_DEBOUNCE_MS, } from './operations-query.js';
|
|
6
6
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/query/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,gCAAgC,GACjC,MAAM,oBAAoB,CAAA","sourcesContent":["/**\n * The studio client read seam (EC-308 / AD-17): one operations-backed query cache.\n * See `./operations-query`.\n */\nexport {\n StudioQueryProvider,\n useOperationQuery,\n useOperationCache,\n listStoreState,\n OPERATION_INVALIDATE_DEBOUNCE_MS,\n} from './operations-query'\nexport type { OperationCache, UseOperationQueryOptions } from './operations-query'\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/query/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,WAAW,EACX,gCAAgC,GACjC,MAAM,oBAAoB,CAAA","sourcesContent":["/**\n * The studio client read seam (EC-308 / AD-17): one operations-backed query cache.\n * See `./operations-query`.\n */\nexport {\n StudioQueryProvider,\n useOperationQuery,\n useOperationCache,\n listStoreState,\n readStateOf,\n OPERATION_INVALIDATE_DEBOUNCE_MS,\n} from './operations-query'\nexport type { OperationCache, ReadState, UseOperationQueryOptions } from './operations-query'\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import type { QueryKey, UseQueryResult } from '@tanstack/react-query';
|
|
3
|
-
import type { OperationsClient } from '
|
|
3
|
+
import type { OperationsClient } from '../operations/index.js';
|
|
4
4
|
/**
|
|
5
5
|
* EC-308 / AD-17 — the ONE studio client read seam.
|
|
6
6
|
*
|
|
@@ -81,6 +81,32 @@ export interface OperationCache {
|
|
|
81
81
|
invalidate(queryKey: QueryKey): Promise<void>;
|
|
82
82
|
}
|
|
83
83
|
export declare function useOperationCache(): OperationCache;
|
|
84
|
+
/**
|
|
85
|
+
* EC-628 — a server read as a surface renders it: still loading, the value, or a failure.
|
|
86
|
+
* A union so a failed read cannot be represented as an empty value: a surface reads
|
|
87
|
+
* `value` only on `ready`, and has to say something on `error` (the published pool used to
|
|
88
|
+
* turn a failed `documents.listPublished` into an empty map, and every live URL then read
|
|
89
|
+
* "not published" — silently).
|
|
90
|
+
*/
|
|
91
|
+
export type ReadState<T> = {
|
|
92
|
+
readonly status: 'loading';
|
|
93
|
+
} | {
|
|
94
|
+
readonly status: 'ready';
|
|
95
|
+
readonly value: T;
|
|
96
|
+
} | {
|
|
97
|
+
readonly status: 'error';
|
|
98
|
+
readonly message: string;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* The {@link ReadState} of an operation query. A failure wins over a value: TanStack keeps
|
|
102
|
+
* the last good `data` when a refetch fails, and serving it would present a stale answer
|
|
103
|
+
* as the current one. `message` renders the failure (localized by the caller's catalog).
|
|
104
|
+
*/
|
|
105
|
+
export declare function readStateOf<T>(query: {
|
|
106
|
+
readonly isError: boolean;
|
|
107
|
+
readonly error: unknown;
|
|
108
|
+
readonly data: T | undefined;
|
|
109
|
+
}, message: (error: unknown) => string): ReadState<T>;
|
|
84
110
|
/**
|
|
85
111
|
* Derive a list store's public `{ status, errorMessage }` from its operation query —
|
|
86
112
|
* the identical mapping the assets/users/redirects rail stores (EC-319) each
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import { QueryClient, QueryClientProvider, useQuery, useQueryClient, } from '@tanstack/react-query';
|
|
4
|
-
import { getStudioOperations } from '
|
|
4
|
+
import { getStudioOperations } from '../operations/index.js';
|
|
5
5
|
/**
|
|
6
6
|
* EC-308 / AD-17 — the ONE studio client read seam.
|
|
7
7
|
*
|
|
@@ -114,6 +114,18 @@ export function useOperationCache() {
|
|
|
114
114
|
const invalidate = useCallback((queryKey) => queryClient.invalidateQueries({ queryKey }), [queryClient]);
|
|
115
115
|
return useMemo(() => ({ patch, invalidate }), [patch, invalidate]);
|
|
116
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* The {@link ReadState} of an operation query. A failure wins over a value: TanStack keeps
|
|
119
|
+
* the last good `data` when a refetch fails, and serving it would present a stale answer
|
|
120
|
+
* as the current one. `message` renders the failure (localized by the caller's catalog).
|
|
121
|
+
*/
|
|
122
|
+
export function readStateOf(query, message) {
|
|
123
|
+
if (query.isError)
|
|
124
|
+
return { status: 'error', message: message(query.error) };
|
|
125
|
+
if (query.data === undefined)
|
|
126
|
+
return { status: 'loading' };
|
|
127
|
+
return { status: 'ready', value: query.data };
|
|
128
|
+
}
|
|
117
129
|
/**
|
|
118
130
|
* Derive a list store's public `{ status, errorMessage }` from its operation query —
|
|
119
131
|
* the identical mapping the assets/users/redirects rail stores (EC-319) each
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operations-query.js","sourceRoot":"","sources":["../../../src/lib/query/operations-query.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport {\n QueryClient,\n QueryClientProvider,\n useQuery,\n useQueryClient,\n} from '@tanstack/react-query'\nimport type { QueryKey, UseQueryResult } from '@tanstack/react-query'\nimport { getStudioOperations } from '@elytracms/studio/lib/operations'\nimport type { OperationEvent, OperationsClient } from '@elytracms/studio/lib/operations'\n\n/**\n * EC-308 / AD-17 — the ONE studio client read seam.\n *\n * Every studio **server read** goes through {@link useOperationQuery} over the\n * shared operations client, cached by TanStack Query; invalidation rides the\n * operations event bus into the same cache. This replaces the bespoke per-store\n * `useState` + epoch-guard + debounce + reload machinery that each store used to\n * hand-roll (the patchwork AD-17 calls out). See\n * [`docs/client-data-flow.md`](../../../../../docs/client-data-flow.md) and\n * [`docs/principles.md`](../../../../../docs/principles.md) §12.\n *\n * It is also the **Phase-2 prep** (EC-309): reactivity/multiplayer is added by\n * swapping pull → push *behind this hook* (an adapter `subscribe` capability\n * driving `queryClient.setQueryData`), never at the call sites — so call sites\n * never change when push lands.\n *\n * Deliberately NOT here: the optimistic editing session (the draft being typed,\n * live validation, the table working view-state in `cms-store`). That is client\n * state, not server state, and stays local (principle 12).\n */\n\n/**\n * Trailing-debounce window (ms) for event-driven invalidation: a burst of\n * commands — e.g. a run of `documents.patch` commits from one editor, or one\n * event per row in a bulk action — coalesces into a single refetch once the\n * writes settle. (The shared replacement for the per-store `*_RELOAD_DEBOUNCE_MS`.)\n */\nexport const OPERATION_INVALIDATE_DEBOUNCE_MS = 300\n\n/** Studio-tuned cache defaults: invalidation (not polling/focus) drives freshness. */\nfunction createStudioQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 5_000,\n refetchOnWindowFocus: false,\n retry: false,\n },\n },\n })\n}\n\n/**\n * Mounts the single studio `QueryClient` (one per studio lifetime, surviving\n * project switches). Mounted package-owned in `StudioProvider`, above every\n * per-project provider, so every studio hook can reach the cache.\n */\nexport function StudioQueryProvider({ children }: { children: ReactNode }): ReactNode {\n const [client] = useState(createStudioQueryClient)\n return <QueryClientProvider client={client}>{children}</QueryClientProvider>\n}\n\nexport interface UseOperationQueryOptions<T> {\n /** Stable query key; the first element is the domain tag used for grouping. */\n queryKey: QueryKey\n /** Loads the data from the shared operations client. */\n loader: (operations: OperationsClient) => Promise<T>\n /**\n * Which succeeded commands invalidate this query — a list of operation names\n * (`'documents.publish'`) and/or domains (`'documents'`). A succeeded command\n * matches when its `operation` OR `domain` is in the list. Keep it a stable\n * (module-level) array. Omit for a query that only loads once / refetches\n * manually.\n */\n invalidateOn?: readonly string[]\n /** Skip the query (e.g. fixture/playground sessions with no backend). */\n enabled?: boolean\n}\n\n/**\n * One operations-backed, cached, self-invalidating query. Thin wrapper over\n * `useQuery` whose `queryFn` resolves the shared operations client and runs the\n * loader, plus one shared invalidation subscriber (the only place that touches\n * the operations event bus for reads).\n */\nexport function useOperationQuery<T>(options: UseOperationQueryOptions<T>): UseQueryResult<T> {\n const { queryKey, loader, invalidateOn, enabled = true } = options\n const queryClient = useQueryClient()\n\n const result = useQuery<T>({\n queryKey,\n queryFn: async () => {\n const { operations } = await getStudioOperations()\n return loader(operations)\n },\n enabled,\n })\n\n // Stable serializations so an inline `queryKey`/`invalidateOn` doesn't churn the\n // subscription every render (re-subscribe only when their CONTENTS change).\n const keyJson = JSON.stringify(queryKey)\n const invalidateJson = JSON.stringify(invalidateOn ?? [])\n\n useEffect(() => {\n const triggers = new Set<string>(JSON.parse(invalidateJson) as string[])\n if (triggers.size === 0) return\n const key = JSON.parse(keyJson) as QueryKey\n let timer: ReturnType<typeof setTimeout> | undefined\n let unsubscribe: (() => void) | undefined\n let disposed = false\n const matches = (event: OperationEvent): boolean =>\n event.kind === 'command' &&\n event.status === 'succeeded' &&\n (triggers.has(event.operation) || triggers.has(event.domain))\n void getStudioOperations().then((studio) => {\n if (disposed) return\n unsubscribe = studio.operations.events.subscribe((event) => {\n if (!matches(event)) return\n // Trailing-debounce: a burst (per-keystroke autosave, bulk actions)\n // collapses into one invalidation; the active query then refetches once.\n if (timer) clearTimeout(timer)\n timer = setTimeout(() => {\n void queryClient.invalidateQueries({ queryKey: key })\n }, OPERATION_INVALIDATE_DEBOUNCE_MS)\n })\n })\n return () => {\n disposed = true\n if (timer) clearTimeout(timer)\n unsubscribe?.()\n }\n }, [keyJson, invalidateJson, queryClient])\n\n return result\n}\n\n/**\n * The write-side companion to {@link useOperationQuery}: an imperative handle on\n * the studio cache for a store that OWNS its writes. Lets a store apply an\n * EC-301-style optimistic patch (drop/replace one record) instead of re-listing\n * the whole collection on every edit — keeping `@tanstack/react-query` out of the\n * call sites so the seam stays the only place that knows the cache exists.\n */\nexport interface OperationCache {\n /**\n * Patch a cached query in place — no refetch. The EC-301 pattern: a focal-point\n * or alt edit patches the one changed record; it does NOT trigger a full\n * `*.list` round-trip. No-op if nothing is cached under `queryKey` yet.\n */\n patch<T>(queryKey: QueryKey, updater: (prev: T | undefined) => T): void\n /**\n * Invalidate a cached query, refetching it if it is currently active. Returns a\n * promise that settles when the refetch does, so a caller that must sequence a\n * read after a local write (e.g. flush-then-reload) can await it.\n */\n invalidate(queryKey: QueryKey): Promise<void>\n}\n\nexport function useOperationCache(): OperationCache {\n const queryClient = useQueryClient()\n const patch = useCallback(\n <T,>(queryKey: QueryKey, updater: (prev: T | undefined) => T): void => {\n queryClient.setQueryData<T>(queryKey, updater)\n },\n [queryClient],\n )\n const invalidate = useCallback(\n (queryKey: QueryKey): Promise<void> => queryClient.invalidateQueries({ queryKey }),\n [queryClient],\n )\n return useMemo(() => ({ patch, invalidate }), [patch, invalidate])\n}\n\n/**\n * Derive a list store's public `{ status, errorMessage }` from its operation query —\n * the identical mapping the assets/users/redirects rail stores (EC-319) each\n * hand-rolled. `errorMessage` is set only on an error; a store spreads it\n * conditionally onto its public shape.\n */\nexport function listStoreState(query: { isPending: boolean; isError: boolean; error: unknown }): {\n status: 'loading' | 'ready' | 'error'\n errorMessage: string | undefined\n} {\n return {\n status: query.isPending ? 'loading' : query.isError ? 'error' : 'ready',\n errorMessage: query.isError && query.error instanceof Error ? query.error.message : undefined,\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"operations-query.js","sourceRoot":"","sources":["../../../src/lib/query/operations-query.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport {\n QueryClient,\n QueryClientProvider,\n useQuery,\n useQueryClient,\n} from '@tanstack/react-query'\nimport type { QueryKey, UseQueryResult } from '@tanstack/react-query'\nimport { getStudioOperations } from '../operations'\nimport type { OperationEvent, OperationsClient } from '../operations'\n\n/**\n * EC-308 / AD-17 — the ONE studio client read seam.\n *\n * Every studio **server read** goes through {@link useOperationQuery} over the\n * shared operations client, cached by TanStack Query; invalidation rides the\n * operations event bus into the same cache. This replaces the bespoke per-store\n * `useState` + epoch-guard + debounce + reload machinery that each store used to\n * hand-roll (the patchwork AD-17 calls out). See\n * [`docs/client-data-flow.md`](../../../../../docs/client-data-flow.md) and\n * [`docs/principles.md`](../../../../../docs/principles.md) §12.\n *\n * It is also the **Phase-2 prep** (EC-309): reactivity/multiplayer is added by\n * swapping pull → push *behind this hook* (an adapter `subscribe` capability\n * driving `queryClient.setQueryData`), never at the call sites — so call sites\n * never change when push lands.\n *\n * Deliberately NOT here: the optimistic editing session (the draft being typed,\n * live validation, the table working view-state in `cms-store`). That is client\n * state, not server state, and stays local (principle 12).\n */\n\n/**\n * Trailing-debounce window (ms) for event-driven invalidation: a burst of\n * commands — e.g. a run of `documents.patch` commits from one editor, or one\n * event per row in a bulk action — coalesces into a single refetch once the\n * writes settle. (The shared replacement for the per-store `*_RELOAD_DEBOUNCE_MS`.)\n */\nexport const OPERATION_INVALIDATE_DEBOUNCE_MS = 300\n\n/** Studio-tuned cache defaults: invalidation (not polling/focus) drives freshness. */\nfunction createStudioQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 5_000,\n refetchOnWindowFocus: false,\n retry: false,\n },\n },\n })\n}\n\n/**\n * Mounts the single studio `QueryClient` (one per studio lifetime, surviving\n * project switches). Mounted package-owned in `StudioProvider`, above every\n * per-project provider, so every studio hook can reach the cache.\n */\nexport function StudioQueryProvider({ children }: { children: ReactNode }): ReactNode {\n const [client] = useState(createStudioQueryClient)\n return <QueryClientProvider client={client}>{children}</QueryClientProvider>\n}\n\nexport interface UseOperationQueryOptions<T> {\n /** Stable query key; the first element is the domain tag used for grouping. */\n queryKey: QueryKey\n /** Loads the data from the shared operations client. */\n loader: (operations: OperationsClient) => Promise<T>\n /**\n * Which succeeded commands invalidate this query — a list of operation names\n * (`'documents.publish'`) and/or domains (`'documents'`). A succeeded command\n * matches when its `operation` OR `domain` is in the list. Keep it a stable\n * (module-level) array. Omit for a query that only loads once / refetches\n * manually.\n */\n invalidateOn?: readonly string[]\n /** Skip the query (e.g. fixture/playground sessions with no backend). */\n enabled?: boolean\n}\n\n/**\n * One operations-backed, cached, self-invalidating query. Thin wrapper over\n * `useQuery` whose `queryFn` resolves the shared operations client and runs the\n * loader, plus one shared invalidation subscriber (the only place that touches\n * the operations event bus for reads).\n */\nexport function useOperationQuery<T>(options: UseOperationQueryOptions<T>): UseQueryResult<T> {\n const { queryKey, loader, invalidateOn, enabled = true } = options\n const queryClient = useQueryClient()\n\n const result = useQuery<T>({\n queryKey,\n queryFn: async () => {\n const { operations } = await getStudioOperations()\n return loader(operations)\n },\n enabled,\n })\n\n // Stable serializations so an inline `queryKey`/`invalidateOn` doesn't churn the\n // subscription every render (re-subscribe only when their CONTENTS change).\n const keyJson = JSON.stringify(queryKey)\n const invalidateJson = JSON.stringify(invalidateOn ?? [])\n\n useEffect(() => {\n const triggers = new Set<string>(JSON.parse(invalidateJson) as string[])\n if (triggers.size === 0) return\n const key = JSON.parse(keyJson) as QueryKey\n let timer: ReturnType<typeof setTimeout> | undefined\n let unsubscribe: (() => void) | undefined\n let disposed = false\n const matches = (event: OperationEvent): boolean =>\n event.kind === 'command' &&\n event.status === 'succeeded' &&\n (triggers.has(event.operation) || triggers.has(event.domain))\n void getStudioOperations().then((studio) => {\n if (disposed) return\n unsubscribe = studio.operations.events.subscribe((event) => {\n if (!matches(event)) return\n // Trailing-debounce: a burst (per-keystroke autosave, bulk actions)\n // collapses into one invalidation; the active query then refetches once.\n if (timer) clearTimeout(timer)\n timer = setTimeout(() => {\n void queryClient.invalidateQueries({ queryKey: key })\n }, OPERATION_INVALIDATE_DEBOUNCE_MS)\n })\n })\n return () => {\n disposed = true\n if (timer) clearTimeout(timer)\n unsubscribe?.()\n }\n }, [keyJson, invalidateJson, queryClient])\n\n return result\n}\n\n/**\n * The write-side companion to {@link useOperationQuery}: an imperative handle on\n * the studio cache for a store that OWNS its writes. Lets a store apply an\n * EC-301-style optimistic patch (drop/replace one record) instead of re-listing\n * the whole collection on every edit — keeping `@tanstack/react-query` out of the\n * call sites so the seam stays the only place that knows the cache exists.\n */\nexport interface OperationCache {\n /**\n * Patch a cached query in place — no refetch. The EC-301 pattern: a focal-point\n * or alt edit patches the one changed record; it does NOT trigger a full\n * `*.list` round-trip. No-op if nothing is cached under `queryKey` yet.\n */\n patch<T>(queryKey: QueryKey, updater: (prev: T | undefined) => T): void\n /**\n * Invalidate a cached query, refetching it if it is currently active. Returns a\n * promise that settles when the refetch does, so a caller that must sequence a\n * read after a local write (e.g. flush-then-reload) can await it.\n */\n invalidate(queryKey: QueryKey): Promise<void>\n}\n\nexport function useOperationCache(): OperationCache {\n const queryClient = useQueryClient()\n const patch = useCallback(\n <T,>(queryKey: QueryKey, updater: (prev: T | undefined) => T): void => {\n queryClient.setQueryData<T>(queryKey, updater)\n },\n [queryClient],\n )\n const invalidate = useCallback(\n (queryKey: QueryKey): Promise<void> => queryClient.invalidateQueries({ queryKey }),\n [queryClient],\n )\n return useMemo(() => ({ patch, invalidate }), [patch, invalidate])\n}\n\n/**\n * EC-628 — a server read as a surface renders it: still loading, the value, or a failure.\n * A union so a failed read cannot be represented as an empty value: a surface reads\n * `value` only on `ready`, and has to say something on `error` (the published pool used to\n * turn a failed `documents.listPublished` into an empty map, and every live URL then read\n * \"not published\" — silently).\n */\nexport type ReadState<T> =\n | { readonly status: 'loading' }\n | { readonly status: 'ready'; readonly value: T }\n | { readonly status: 'error'; readonly message: string }\n\n/**\n * The {@link ReadState} of an operation query. A failure wins over a value: TanStack keeps\n * the last good `data` when a refetch fails, and serving it would present a stale answer\n * as the current one. `message` renders the failure (localized by the caller's catalog).\n */\nexport function readStateOf<T>(\n query: { readonly isError: boolean; readonly error: unknown; readonly data: T | undefined },\n message: (error: unknown) => string,\n): ReadState<T> {\n if (query.isError) return { status: 'error', message: message(query.error) }\n if (query.data === undefined) return { status: 'loading' }\n return { status: 'ready', value: query.data }\n}\n\n/**\n * Derive a list store's public `{ status, errorMessage }` from its operation query —\n * the identical mapping the assets/users/redirects rail stores (EC-319) each\n * hand-rolled. `errorMessage` is set only on an error; a store spreads it\n * conditionally onto its public shape.\n */\nexport function listStoreState(query: { isPending: boolean; isError: boolean; error: unknown }): {\n status: 'loading' | 'ready' | 'error'\n errorMessage: string | undefined\n} {\n return {\n status: query.isPending ? 'loading' : query.isError ? 'error' : 'ready',\n errorMessage: query.isError && query.error instanceof Error ? query.error.message : undefined,\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reference-model.js","sourceRoot":"","sources":["../../../src/lib/references/reference-model.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolveDocumentTitle, valuesInLocale } from '@elytracms/core/cms-core'\nimport { localizedLabel } from '@elytracms/studio/lib/i18n'\nimport type { CmsDocument, CollectionDef, LocaleConfig } from '@elytracms/core/cms-core'\nimport type {\n ReferenceEntryRecord,\n ReferenceKind,\n ReferenceSource,\n ReferenceTarget,\n} from '@elytracms/core/persistence'\n\n/**\n * Pure presentation model for the EC-172 references panels.\n *\n * The EC-155 index stores entries as `(source, path, target, kind)` per\n * variant; this module turns those records into renderable rows: variants\n * merged onto one row (draft/published badges), human-readable source/\n * location/target labels, and deep-link descriptors that pinpoint the exact\n * field (CMS) or graph node (builder) a path identifies. No data fetching, no\n * React — everything here is a deterministic function of its inputs, so it is\n * fully testable in the node environment.\n */\n\n// --- Path parsing -------------------------------------------------------------\n\n/** Split a `collection:id` document key (the index's document source/target id). */\nexport function parseDocumentKey(key: string): { collection: string; id: string } | null {\n const separator = key.indexOf(':')\n if (separator <= 0 || separator === key.length - 1) return null\n return { collection: key.slice(0, separator), id: key.slice(separator + 1) }\n}\n\nexport interface DocumentFieldPath {\n /** Present for `localized/<locale>/<field>` paths. */\n locale?: string\n field: string\n /** Tokens below the field value (array indices, rich-text node paths). */\n rest: readonly string[]\n}\n\n/**\n * Parse a document index path (`values/<field>/…` or\n * `localized/<locale>/<field>/…`) onto the owning field.\n */\nexport function parseDocumentFieldPath(path: string): DocumentFieldPath | null {\n const tokens = path.split('/')\n if (tokens[0] === 'values' && tokens[1]) {\n return { field: tokens[1], rest: tokens.slice(2) }\n }\n if (tokens[0] === 'localized' && tokens[1] && tokens[2]) {\n return { locale: tokens[1], field: tokens[2], rest: tokens.slice(3) }\n }\n return null\n}\n\n// --- Variant merging ----------------------------------------------------------\n\n/** One panel row: the same logical entry across both variants, with flags. */\nexport interface MergedReferenceEntry {\n /** Stable row key (source + path + target + kind). */\n key: string\n source: ReferenceSource\n path: string\n target: ReferenceTarget\n kind: ReferenceKind\n /** Present in the draft variant of the index. */\n draft: boolean\n /** Present in the published variant (a live reference). */\n published: boolean\n}\n\n/**\n * Merge per-variant index records onto one row per logical entry. A\n * draft-only row (`draft && !published`) is the Sanity hidden-draft-reference\n * case the panel must make visible. Order is deterministic:\n * source type → source id → path → target type → target id → kind.\n */\nexport function mergeReferenceVariants(\n records: readonly ReferenceEntryRecord[],\n): MergedReferenceEntry[] {\n const byKey = new Map<string, MergedReferenceEntry>()\n for (const record of records) {\n const key = `${record.source.type} ${record.source.id} ${record.path} ${record.target.type} ${record.target.id} ${record.kind}`\n const existing = byKey.get(key)\n if (existing) {\n if (record.variant === 'draft') existing.draft = true\n else existing.published = true\n continue\n }\n byKey.set(key, {\n key,\n source: record.source,\n path: record.path,\n target: record.target,\n kind: record.kind,\n draft: record.variant === 'draft',\n published: record.variant === 'published',\n })\n }\n return [...byKey.values()].sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0))\n}\n\n// --- Labels & deep links --------------------------------------------------------\n\n/** Human label per reference kind (entry-row badge). */\nexport const REFERENCE_KIND_LABELS: Record<ReferenceKind, string> = {\n 'relation-field': 'relation',\n 'asset-field': 'asset field',\n 'rich-text-link': 'rich-text link',\n 'rich-text-node': 'rich-text node',\n 'component-usage': 'component usage',\n}\n\n/**\n * A deep-link descriptor the panel renders as a router link. `cms-field`\n * carries the full index path so the document editor can highlight the exact\n * field control; `builder-node` carries the resolved node id so the builder\n * can select + scroll to the node.\n */\nexport type ReferenceDeepLink =\n | { kind: 'cms-field'; collectionId: string; documentId: string; fieldPath?: string }\n | { kind: 'builder-node'; pageId?: string; nodeId?: string }\n | { kind: 'component'; componentId: string }\n | { kind: 'asset'; assetId: string }\n\n/** Everything the model needs to label entries (loaded once per panel). */\nexport interface ReferenceLabelContext {\n collections: readonly CollectionDef[]\n /** Live documents keyed by `collection:id`. */\n documents: ReadonlyMap<string, CmsDocument>\n /** Asset metadata keyed by asset id. */\n assets: ReadonlyMap<string, { filename: string; title?: string | undefined }>\n /** Resolves a component id to its manifest title (live registry lookup). */\n componentTitle: (componentId: string) => string | undefined\n /**\n * The project's locale config (EC-580) — document titles read the default-locale\n * view through it. `null` when no CMS session has installed one.\n */\n locales: LocaleConfig | null\n}\n\n/** One fully-labelled panel row. */\nexport interface ReferenceEntryView extends MergedReferenceEntry {\n kindLabel: string\n /** The holding entity, e.g. `Post · Hello World` or `Page · Home`. */\n sourceLabel: string\n /** The exact location inside the source, e.g. `Author` or `Text (n-post-title)`. */\n locationLabel: string\n /** The referenced entity, e.g. `Ada Lovelace` or `cover.jpg`. */\n targetLabel: string\n /** Deep link to the exact source location (field control / graph node). */\n sourceLink: ReferenceDeepLink | null\n /** Link to the referenced entity's surface. */\n targetLink: ReferenceDeepLink | null\n}\n\nfunction fieldLabelOf(collection: CollectionDef | undefined, fieldName: string): string {\n const field = collection?.fields.find((f) => f.name === fieldName)\n // Non-hook `localizedLabel` reads the active UI locale (safe anywhere), so a\n // `{ en, de }` label resolves to the editor's language here too.\n return localizedLabel(field?.form?.label, fieldName)\n}\n\nfunction collectionLabelOf(collection: CollectionDef | undefined, collectionId: string): string {\n return (\n localizedLabel(collection?.form?.labelSingular) || localizedLabel(collection?.form?.label, collectionId)\n )\n}\n\n/**\n * Resolve a document's display title — the shared identity resolver\n * (`resolveDocumentTitle`, EC-242: a string `titleField` or a function pattern) over\n * the document's DEFAULT-locale view (EC-580: `valuesInLocale`, the one locale-value\n * reader; the project's config is the only default locale). A document authored\n * only in another locale has an empty default bucket, so its label is the fallback\n * id — the same thing every other default-locale surface shows (vision 3.5).\n * Without a project locale config (no CMS session installed one) only the shared\n * `values` can be read.\n */\nexport function documentTitleOf(\n collection: CollectionDef | undefined,\n document: CmsDocument | undefined,\n fallback: string,\n locales: LocaleConfig | null,\n): string {\n if (!collection || !document) return fallback\n const values = locales\n ? valuesInLocale(collection, document, locales.default, locales)\n : document.values\n return resolveDocumentTitle(collection, values, fallback)\n}\n\ninterface DocumentParts {\n collection: CollectionDef | undefined\n collectionId: string\n documentId: string\n title: string\n}\n\nfunction documentParts(context: ReferenceLabelContext, key: string): DocumentParts | null {\n const ref = parseDocumentKey(key)\n if (!ref) return null\n const collection = context.collections.find((c) => c.id === ref.collection)\n return {\n collection,\n collectionId: ref.collection,\n documentId: ref.id,\n title: documentTitleOf(collection, context.documents.get(key), ref.id, context.locales),\n }\n}\n\nfunction describeDocumentSource(\n context: ReferenceLabelContext,\n entry: MergedReferenceEntry,\n): { sourceLabel: string; locationLabel: string; sourceLink: ReferenceDeepLink | null } {\n const parts = documentParts(context, entry.source.id)\n if (!parts) {\n return { sourceLabel: entry.source.id, locationLabel: entry.path, sourceLink: null }\n }\n const parsed = parseDocumentFieldPath(entry.path)\n const fieldLabel = parsed ? fieldLabelOf(parts.collection, parsed.field) : entry.path\n const qualifiers: string[] = []\n if (parsed?.locale) qualifiers.push(parsed.locale)\n if (entry.kind === 'rich-text-link') qualifiers.push('link')\n if (entry.kind === 'rich-text-node') qualifiers.push('embedded node')\n if (\n entry.kind === 'relation-field' &&\n parsed &&\n parsed.rest.length === 1 &&\n /^\\d+$/.test(parsed.rest[0]!)\n ) {\n qualifiers.push(`item ${parsed.rest[0]}`)\n }\n return {\n sourceLabel: `${collectionLabelOf(parts.collection, parts.collectionId)} · ${parts.title}`,\n locationLabel: qualifiers.length > 0 ? `${fieldLabel} (${qualifiers.join(', ')})` : fieldLabel,\n sourceLink: {\n kind: 'cms-field',\n collectionId: parts.collectionId,\n documentId: parts.documentId,\n fieldPath: entry.path,\n },\n }\n}\n\n\nfunction describeTarget(\n context: ReferenceLabelContext,\n target: ReferenceTarget,\n): { targetLabel: string; targetLink: ReferenceDeepLink | null } {\n switch (target.type) {\n case 'document': {\n const parts = documentParts(context, target.id)\n if (!parts) return { targetLabel: target.id, targetLink: null }\n return {\n targetLabel: `${collectionLabelOf(parts.collection, parts.collectionId)} · ${parts.title}`,\n targetLink: {\n kind: 'cms-field',\n collectionId: parts.collectionId,\n documentId: parts.documentId,\n },\n }\n }\n case 'component':\n return {\n // EC-591: the manifest title, else the id itself — ids are the developer's\n // names now, with no engine prefix to strip.\n targetLabel: context.componentTitle(target.id) ?? target.id,\n targetLink: { kind: 'component', componentId: target.id },\n }\n case 'asset': {\n const asset = context.assets.get(target.id)\n return {\n targetLabel: asset ? (asset.title ?? asset.filename) : target.id,\n targetLink: { kind: 'asset', assetId: target.id },\n }\n }\n }\n}\n\n/** Label + link one merged entry (see {@link buildReferenceEntryViews}). */\nexport function buildReferenceEntryView(\n entry: MergedReferenceEntry,\n context: ReferenceLabelContext,\n): ReferenceEntryView {\n // EC-287: a reference source is only ever a CMS document (structure is code).\n const sourceParts = describeDocumentSource(context, entry)\n return {\n ...entry,\n kindLabel: REFERENCE_KIND_LABELS[entry.kind],\n ...sourceParts,\n ...describeTarget(context, entry.target),\n }\n}\n\n/** Merge variants and label every record — the panel's full render model. */\nexport function buildReferenceEntryViews(\n records: readonly ReferenceEntryRecord[],\n context: ReferenceLabelContext,\n): ReferenceEntryView[] {\n return mergeReferenceVariants(records).map((entry) => buildReferenceEntryView(entry, context))\n}\n"]}
|
|
1
|
+
{"version":3,"file":"reference-model.js","sourceRoot":"","sources":["../../../src/lib/references/reference-model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAC/E,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AASxC;;;;;;;;;;GAUG;AAEH,iFAAiF;AAEjF,oFAAoF;AACpF,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAClC,IAAI,SAAS,IAAI,CAAC,IAAI,SAAS,KAAK,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IAC/D,OAAO,EAAE,UAAU,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,CAAA;AAC9E,CAAC;AAUD;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC9B,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACxC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;IACpD,CAAC;IACD,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,WAAW,IAAI,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACxD,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;IACvE,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAkBD;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAAwC;IAExC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAgC,CAAA;IACrD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,IAAI,EAAE,CAAA;QAC/H,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC/B,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,MAAM,CAAC,OAAO,KAAK,OAAO;gBAAE,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAA;;gBAChD,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAA;YAC9B,SAAQ;QACV,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE;YACb,GAAG;YACH,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,KAAK,EAAE,MAAM,CAAC,OAAO,KAAK,OAAO;YACjC,SAAS,EAAE,MAAM,CAAC,OAAO,KAAK,WAAW;SAC1C,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACzF,CAAC;AAED,mFAAmF;AAEnF,wDAAwD;AACxD,MAAM,CAAC,MAAM,qBAAqB,GAAkC;IAClE,gBAAgB,EAAE,UAAU;IAC5B,aAAa,EAAE,aAAa;IAC5B,gBAAgB,EAAE,gBAAgB;IAClC,gBAAgB,EAAE,gBAAgB;IAClC,iBAAiB,EAAE,iBAAiB;CACrC,CAAA;AA6CD,SAAS,YAAY,CAAC,UAAqC,EAAE,SAAiB;IAC5E,MAAM,KAAK,GAAG,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAA;IAClE,6EAA6E;IAC7E,iEAAiE;IACjE,OAAO,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,CAAA;AACtD,CAAC;AAED,SAAS,iBAAiB,CAAC,UAAqC,EAAE,YAAoB;IACpF,OAAO,CACL,cAAc,CAAC,UAAU,EAAE,IAAI,EAAE,aAAa,CAAC,IAAI,cAAc,CAAC,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC,CACzG,CAAA;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAC7B,UAAqC,EACrC,QAAiC,EACjC,QAAgB,EAChB,OAA4B;IAE5B,IAAI,CAAC,UAAU,IAAI,CAAC,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAC7C,MAAM,MAAM,GAAG,OAAO;QACpB,CAAC,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC;QAChE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAA;IACnB,OAAO,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAA;AAC3D,CAAC;AASD,SAAS,aAAa,CAAC,OAA8B,EAAE,GAAW;IAChE,MAAM,GAAG,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;IACjC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IACrB,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,UAAU,CAAC,CAAA;IAC3E,OAAO;QACL,UAAU;QACV,YAAY,EAAE,GAAG,CAAC,UAAU;QAC5B,UAAU,EAAE,GAAG,CAAC,EAAE;QAClB,KAAK,EAAE,eAAe,CAAC,UAAU,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC;KACxF,CAAA;AACH,CAAC;AAED,SAAS,sBAAsB,CAC7B,OAA8B,EAC9B,KAA2B;IAE3B,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IACrD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,aAAa,EAAE,KAAK,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;IACtF,CAAC;IACD,MAAM,MAAM,GAAG,sBAAsB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACjD,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAA;IACrF,MAAM,UAAU,GAAa,EAAE,CAAA;IAC/B,IAAI,MAAM,EAAE,MAAM;QAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAClD,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;QAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAC5D,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;QAAE,UAAU,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;IACrE,IACE,KAAK,CAAC,IAAI,KAAK,gBAAgB;QAC/B,MAAM;QACN,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,EAC7B,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,QAAQ,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC3C,CAAC;IACD,OAAO;QACL,WAAW,EAAE,GAAG,iBAAiB,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,KAAK,EAAE;QAC1F,aAAa,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,KAAK,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU;QAC9F,UAAU,EAAE;YACV,IAAI,EAAE,WAAW;YACjB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,SAAS,EAAE,KAAK,CAAC,IAAI;SACtB;KACF,CAAA;AACH,CAAC;AAGD,SAAS,cAAc,CACrB,OAA8B,EAC9B,MAAuB;IAEvB,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,CAAA;YAC/C,IAAI,CAAC,KAAK;gBAAE,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;YAC/D,OAAO;gBACL,WAAW,EAAE,GAAG,iBAAiB,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,KAAK,EAAE;gBAC1F,UAAU,EAAE;oBACV,IAAI,EAAE,WAAW;oBACjB,YAAY,EAAE,KAAK,CAAC,YAAY;oBAChC,UAAU,EAAE,KAAK,CAAC,UAAU;iBAC7B;aACF,CAAA;QACH,CAAC;QACD,KAAK,WAAW;YACd,OAAO;gBACL,2EAA2E;gBAC3E,6CAA6C;gBAC7C,WAAW,EAAE,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,EAAE;gBAC3D,UAAU,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE;aAC1D,CAAA;QACH,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;YAC3C,OAAO;gBACL,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE;gBAChE,UAAU,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,EAAE;aAClD,CAAA;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,uBAAuB,CACrC,KAA2B,EAC3B,OAA8B;IAE9B,8EAA8E;IAC9E,MAAM,WAAW,GAAG,sBAAsB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IAC1D,OAAO;QACL,GAAG,KAAK;QACR,SAAS,EAAE,qBAAqB,CAAC,KAAK,CAAC,IAAI,CAAC;QAC5C,GAAG,WAAW;QACd,GAAG,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC;KACzC,CAAA;AACH,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,wBAAwB,CACtC,OAAwC,EACxC,OAA8B;IAE9B,OAAO,sBAAsB,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,uBAAuB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAA;AAChG,CAAC","sourcesContent":["import { resolveDocumentTitle, valuesInLocale } from '@elytracms/core/cms-core'\nimport { localizedLabel } from '../i18n'\nimport type { CmsDocument, CollectionDef, LocaleConfig } from '@elytracms/core/cms-core'\nimport type {\n ReferenceEntryRecord,\n ReferenceKind,\n ReferenceSource,\n ReferenceTarget,\n} from '@elytracms/core/persistence'\n\n/**\n * Pure presentation model for the EC-172 references panels.\n *\n * The EC-155 index stores entries as `(source, path, target, kind)` per\n * variant; this module turns those records into renderable rows: variants\n * merged onto one row (draft/published badges), human-readable source/\n * location/target labels, and deep-link descriptors that pinpoint the exact\n * field (CMS) or graph node (builder) a path identifies. No data fetching, no\n * React — everything here is a deterministic function of its inputs, so it is\n * fully testable in the node environment.\n */\n\n// --- Path parsing -------------------------------------------------------------\n\n/** Split a `collection:id` document key (the index's document source/target id). */\nexport function parseDocumentKey(key: string): { collection: string; id: string } | null {\n const separator = key.indexOf(':')\n if (separator <= 0 || separator === key.length - 1) return null\n return { collection: key.slice(0, separator), id: key.slice(separator + 1) }\n}\n\nexport interface DocumentFieldPath {\n /** Present for `localized/<locale>/<field>` paths. */\n locale?: string\n field: string\n /** Tokens below the field value (array indices, rich-text node paths). */\n rest: readonly string[]\n}\n\n/**\n * Parse a document index path (`values/<field>/…` or\n * `localized/<locale>/<field>/…`) onto the owning field.\n */\nexport function parseDocumentFieldPath(path: string): DocumentFieldPath | null {\n const tokens = path.split('/')\n if (tokens[0] === 'values' && tokens[1]) {\n return { field: tokens[1], rest: tokens.slice(2) }\n }\n if (tokens[0] === 'localized' && tokens[1] && tokens[2]) {\n return { locale: tokens[1], field: tokens[2], rest: tokens.slice(3) }\n }\n return null\n}\n\n// --- Variant merging ----------------------------------------------------------\n\n/** One panel row: the same logical entry across both variants, with flags. */\nexport interface MergedReferenceEntry {\n /** Stable row key (source + path + target + kind). */\n key: string\n source: ReferenceSource\n path: string\n target: ReferenceTarget\n kind: ReferenceKind\n /** Present in the draft variant of the index. */\n draft: boolean\n /** Present in the published variant (a live reference). */\n published: boolean\n}\n\n/**\n * Merge per-variant index records onto one row per logical entry. A\n * draft-only row (`draft && !published`) is the Sanity hidden-draft-reference\n * case the panel must make visible. Order is deterministic:\n * source type → source id → path → target type → target id → kind.\n */\nexport function mergeReferenceVariants(\n records: readonly ReferenceEntryRecord[],\n): MergedReferenceEntry[] {\n const byKey = new Map<string, MergedReferenceEntry>()\n for (const record of records) {\n const key = `${record.source.type} ${record.source.id} ${record.path} ${record.target.type} ${record.target.id} ${record.kind}`\n const existing = byKey.get(key)\n if (existing) {\n if (record.variant === 'draft') existing.draft = true\n else existing.published = true\n continue\n }\n byKey.set(key, {\n key,\n source: record.source,\n path: record.path,\n target: record.target,\n kind: record.kind,\n draft: record.variant === 'draft',\n published: record.variant === 'published',\n })\n }\n return [...byKey.values()].sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0))\n}\n\n// --- Labels & deep links --------------------------------------------------------\n\n/** Human label per reference kind (entry-row badge). */\nexport const REFERENCE_KIND_LABELS: Record<ReferenceKind, string> = {\n 'relation-field': 'relation',\n 'asset-field': 'asset field',\n 'rich-text-link': 'rich-text link',\n 'rich-text-node': 'rich-text node',\n 'component-usage': 'component usage',\n}\n\n/**\n * A deep-link descriptor the panel renders as a router link. `cms-field`\n * carries the full index path so the document editor can highlight the exact\n * field control; `builder-node` carries the resolved node id so the builder\n * can select + scroll to the node.\n */\nexport type ReferenceDeepLink =\n | { kind: 'cms-field'; collectionId: string; documentId: string; fieldPath?: string }\n | { kind: 'builder-node'; pageId?: string; nodeId?: string }\n | { kind: 'component'; componentId: string }\n | { kind: 'asset'; assetId: string }\n\n/** Everything the model needs to label entries (loaded once per panel). */\nexport interface ReferenceLabelContext {\n collections: readonly CollectionDef[]\n /** Live documents keyed by `collection:id`. */\n documents: ReadonlyMap<string, CmsDocument>\n /** Asset metadata keyed by asset id. */\n assets: ReadonlyMap<string, { filename: string; title?: string | undefined }>\n /** Resolves a component id to its manifest title (live registry lookup). */\n componentTitle: (componentId: string) => string | undefined\n /**\n * The project's locale config (EC-580) — document titles read the default-locale\n * view through it. `null` when no CMS session has installed one.\n */\n locales: LocaleConfig | null\n}\n\n/** One fully-labelled panel row. */\nexport interface ReferenceEntryView extends MergedReferenceEntry {\n kindLabel: string\n /** The holding entity, e.g. `Post · Hello World` or `Page · Home`. */\n sourceLabel: string\n /** The exact location inside the source, e.g. `Author` or `Text (n-post-title)`. */\n locationLabel: string\n /** The referenced entity, e.g. `Ada Lovelace` or `cover.jpg`. */\n targetLabel: string\n /** Deep link to the exact source location (field control / graph node). */\n sourceLink: ReferenceDeepLink | null\n /** Link to the referenced entity's surface. */\n targetLink: ReferenceDeepLink | null\n}\n\nfunction fieldLabelOf(collection: CollectionDef | undefined, fieldName: string): string {\n const field = collection?.fields.find((f) => f.name === fieldName)\n // Non-hook `localizedLabel` reads the active UI locale (safe anywhere), so a\n // `{ en, de }` label resolves to the editor's language here too.\n return localizedLabel(field?.form?.label, fieldName)\n}\n\nfunction collectionLabelOf(collection: CollectionDef | undefined, collectionId: string): string {\n return (\n localizedLabel(collection?.form?.labelSingular) || localizedLabel(collection?.form?.label, collectionId)\n )\n}\n\n/**\n * Resolve a document's display title — the shared identity resolver\n * (`resolveDocumentTitle`, EC-242: a string `titleField` or a function pattern) over\n * the document's DEFAULT-locale view (EC-580: `valuesInLocale`, the one locale-value\n * reader; the project's config is the only default locale). A document authored\n * only in another locale has an empty default bucket, so its label is the fallback\n * id — the same thing every other default-locale surface shows (vision 3.5).\n * Without a project locale config (no CMS session installed one) only the shared\n * `values` can be read.\n */\nexport function documentTitleOf(\n collection: CollectionDef | undefined,\n document: CmsDocument | undefined,\n fallback: string,\n locales: LocaleConfig | null,\n): string {\n if (!collection || !document) return fallback\n const values = locales\n ? valuesInLocale(collection, document, locales.default, locales)\n : document.values\n return resolveDocumentTitle(collection, values, fallback)\n}\n\ninterface DocumentParts {\n collection: CollectionDef | undefined\n collectionId: string\n documentId: string\n title: string\n}\n\nfunction documentParts(context: ReferenceLabelContext, key: string): DocumentParts | null {\n const ref = parseDocumentKey(key)\n if (!ref) return null\n const collection = context.collections.find((c) => c.id === ref.collection)\n return {\n collection,\n collectionId: ref.collection,\n documentId: ref.id,\n title: documentTitleOf(collection, context.documents.get(key), ref.id, context.locales),\n }\n}\n\nfunction describeDocumentSource(\n context: ReferenceLabelContext,\n entry: MergedReferenceEntry,\n): { sourceLabel: string; locationLabel: string; sourceLink: ReferenceDeepLink | null } {\n const parts = documentParts(context, entry.source.id)\n if (!parts) {\n return { sourceLabel: entry.source.id, locationLabel: entry.path, sourceLink: null }\n }\n const parsed = parseDocumentFieldPath(entry.path)\n const fieldLabel = parsed ? fieldLabelOf(parts.collection, parsed.field) : entry.path\n const qualifiers: string[] = []\n if (parsed?.locale) qualifiers.push(parsed.locale)\n if (entry.kind === 'rich-text-link') qualifiers.push('link')\n if (entry.kind === 'rich-text-node') qualifiers.push('embedded node')\n if (\n entry.kind === 'relation-field' &&\n parsed &&\n parsed.rest.length === 1 &&\n /^\\d+$/.test(parsed.rest[0]!)\n ) {\n qualifiers.push(`item ${parsed.rest[0]}`)\n }\n return {\n sourceLabel: `${collectionLabelOf(parts.collection, parts.collectionId)} · ${parts.title}`,\n locationLabel: qualifiers.length > 0 ? `${fieldLabel} (${qualifiers.join(', ')})` : fieldLabel,\n sourceLink: {\n kind: 'cms-field',\n collectionId: parts.collectionId,\n documentId: parts.documentId,\n fieldPath: entry.path,\n },\n }\n}\n\n\nfunction describeTarget(\n context: ReferenceLabelContext,\n target: ReferenceTarget,\n): { targetLabel: string; targetLink: ReferenceDeepLink | null } {\n switch (target.type) {\n case 'document': {\n const parts = documentParts(context, target.id)\n if (!parts) return { targetLabel: target.id, targetLink: null }\n return {\n targetLabel: `${collectionLabelOf(parts.collection, parts.collectionId)} · ${parts.title}`,\n targetLink: {\n kind: 'cms-field',\n collectionId: parts.collectionId,\n documentId: parts.documentId,\n },\n }\n }\n case 'component':\n return {\n // EC-591: the manifest title, else the id itself — ids are the developer's\n // names now, with no engine prefix to strip.\n targetLabel: context.componentTitle(target.id) ?? target.id,\n targetLink: { kind: 'component', componentId: target.id },\n }\n case 'asset': {\n const asset = context.assets.get(target.id)\n return {\n targetLabel: asset ? (asset.title ?? asset.filename) : target.id,\n targetLink: { kind: 'asset', assetId: target.id },\n }\n }\n }\n}\n\n/** Label + link one merged entry (see {@link buildReferenceEntryViews}). */\nexport function buildReferenceEntryView(\n entry: MergedReferenceEntry,\n context: ReferenceLabelContext,\n): ReferenceEntryView {\n // EC-287: a reference source is only ever a CMS document (structure is code).\n const sourceParts = describeDocumentSource(context, entry)\n return {\n ...entry,\n kindLabel: REFERENCE_KIND_LABELS[entry.kind],\n ...sourceParts,\n ...describeTarget(context, entry.target),\n }\n}\n\n/** Merge variants and label every record — the panel's full render model. */\nexport function buildReferenceEntryViews(\n records: readonly ReferenceEntryRecord[],\n context: ReferenceLabelContext,\n): ReferenceEntryView[] {\n return mergeReferenceVariants(records).map((entry) => buildReferenceEntryView(entry, context))\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import { documentKey } from '@elytracms/core/cms-core';
|
|
3
|
-
import { useOperationQuery } from '
|
|
3
|
+
import { useOperationQuery } from '../query/index.js';
|
|
4
4
|
import { localizedLabel } from '../i18n/index.js';
|
|
5
5
|
import { getStudioLocaleConfig } from '../locale/locale-store.js';
|
|
6
6
|
import { resolveDeploymentCollections, resolveDeploymentLocaleConfig } from '../workspace/index.js';
|
|
@@ -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 { ConflictReferrer } from '@elytracms/core/operations'\nimport type {\n ReferenceEntryRecord,\n ReferenceSource,\n ReferenceTarget,\n} from '@elytracms/core/persistence'\nimport { localizedLabel } from '../i18n'\nimport { getStudioLocaleConfig } from '../locale/locale-store'\nimport { resolveDeploymentCollections, 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 [documentsResult, assetsResult] = await Promise.all([\n operations.documents.list({ projectId }),\n operations.assets.list({ projectId }),\n ])\n\n // EC-583 / EC-559: the labels' schema is the config's (the store holds no schema).\n const collections = resolveDeploymentCollections()\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 * The strict referrers of each acted-on document (EC-589) — the core guard's OWN\n * computation (`references.strictReferrers`: THE reference walk over live drafts and\n * pinned snapshots, every position, severity off the declaration), so the dialog blocks\n * exactly when `documents.delete` / `unpublish` would refuse. One list per target, in\n * `targets` order; pair with {@link loadInboundReferenceViews} in `buildReferenceImpact`.\n */\nexport async function loadStrictReferrers(\n projectId: string,\n targets: readonly DocumentRef[],\n action: 'delete' | 'unpublish',\n): Promise<ConflictReferrer[][]> {\n if (targets.length === 0) return []\n const studio = await getStudioOperations()\n const result = await studio.operations.references.strictReferrers({\n projectId,\n targets: [...targets],\n mode: action,\n })\n if (!result.ok) throw new Error(result.error.message)\n return result.value.map((referrers) => [...referrers])\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 '../query'\nimport type { CmsDocument, DocumentRef } from '@elytracms/core/cms-core'\nimport type { ConflictReferrer } from '@elytracms/core/operations'\nimport type {\n ReferenceEntryRecord,\n ReferenceSource,\n ReferenceTarget,\n} from '@elytracms/core/persistence'\nimport { localizedLabel } from '../i18n'\nimport { getStudioLocaleConfig } from '../locale/locale-store'\nimport { resolveDeploymentCollections, 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 [documentsResult, assetsResult] = await Promise.all([\n operations.documents.list({ projectId }),\n operations.assets.list({ projectId }),\n ])\n\n // EC-583 / EC-559: the labels' schema is the config's (the store holds no schema).\n const collections = resolveDeploymentCollections()\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 * The strict referrers of each acted-on document (EC-589) — the core guard's OWN\n * computation (`references.strictReferrers`: THE reference walk over live drafts and\n * pinned snapshots, every position, severity off the declaration), so the dialog blocks\n * exactly when `documents.delete` / `unpublish` would refuse. One list per target, in\n * `targets` order; pair with {@link loadInboundReferenceViews} in `buildReferenceImpact`.\n */\nexport async function loadStrictReferrers(\n projectId: string,\n targets: readonly DocumentRef[],\n action: 'delete' | 'unpublish',\n): Promise<ConflictReferrer[][]> {\n if (targets.length === 0) return []\n const studio = await getStudioOperations()\n const result = await studio.operations.references.strictReferrers({\n projectId,\n targets: [...targets],\n mode: action,\n })\n if (!result.ok) throw new Error(result.error.message)\n return result.value.map((referrers) => [...referrers])\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,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { manifestValueProps } from '@elytracms/core/component-registry';
|
|
2
2
|
import { getImplementation, registerComponents } from '@elytracms/core/runtime-renderer';
|
|
3
3
|
import { resolveDeploymentComponents } from '../workspace/index.js';
|
|
4
4
|
/**
|
|
@@ -53,7 +53,7 @@ export function studioManifestLookup() {
|
|
|
53
53
|
const manifest = registry.getManifest(componentId);
|
|
54
54
|
if (!manifest)
|
|
55
55
|
return undefined;
|
|
56
|
-
return
|
|
56
|
+
return manifestValueProps(manifest);
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
/** A RenderContext for fixture previews (static props only; no asset/relation resolvers). */
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
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,kBAAkB,CAAC,QAAQ,CAAC,CAAA;IACrC,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 { manifestValueProps } 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 manifestValueProps(manifest)\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"]}
|
|
@@ -28,3 +28,13 @@ export declare function nodeFromProps(manifest: ComponentManifest, values: Recor
|
|
|
28
28
|
* Deterministic ids throughout.
|
|
29
29
|
*/
|
|
30
30
|
export declare function previewSpecs(manifest: ComponentManifest, registry: ComponentRegistry): PreviewSpec[];
|
|
31
|
+
/**
|
|
32
|
+
* The `Default` preview of a fixture-less component — the seeded node the blocks panel
|
|
33
|
+
* inserts (a layout container pre-filled through its role, EC-383) with the seed's
|
|
34
|
+
* placeholder ids re-minted deterministically (root `…-0`, then `…-0-1`, `…-0-2` …), so
|
|
35
|
+
* every node has a distinct, stable id. The builder store re-ids an insert itself; a
|
|
36
|
+
* preview renders the seed directly, and React keys slot children by node id — the
|
|
37
|
+
* seed's shared `tmp` placeholder was a duplicate key (EC-624). Both preview surfaces
|
|
38
|
+
* (the Components view and `elytra previews`) render this one node.
|
|
39
|
+
*/
|
|
40
|
+
export declare function defaultPreviewNode(manifest: ComponentManifest, registry: ComponentRegistry): ComponentNode;
|
|
@@ -37,11 +37,20 @@ export function previewSpecs(manifest, registry) {
|
|
|
37
37
|
node: nodeFromProps(manifest, fixture.props ?? {}, `preview-${manifest.id}-${i}`),
|
|
38
38
|
}));
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
return [{ name: 'Default', node: defaultPreviewNode(manifest, registry) }];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The `Default` preview of a fixture-less component — the seeded node the blocks panel
|
|
44
|
+
* inserts (a layout container pre-filled through its role, EC-383) with the seed's
|
|
45
|
+
* placeholder ids re-minted deterministically (root `…-0`, then `…-0-1`, `…-0-2` …), so
|
|
46
|
+
* every node has a distinct, stable id. The builder store re-ids an insert itself; a
|
|
47
|
+
* preview renders the seed directly, and React keys slot children by node id — the
|
|
48
|
+
* seed's shared `tmp` placeholder was a duplicate key (EC-624). Both preview surfaces
|
|
49
|
+
* (the Components view and `elytra previews`) render this one node.
|
|
50
|
+
*/
|
|
51
|
+
export function defaultPreviewNode(manifest, registry) {
|
|
42
52
|
const base = `preview-${manifest.id}-0`;
|
|
43
53
|
let n = 0;
|
|
44
|
-
|
|
45
|
-
return [{ name: 'Default', node }];
|
|
54
|
+
return cloneWithNewIds(seededNodeFromManifest(manifest, registry), () => (n++ === 0 ? base : `${base}-${n - 1}`));
|
|
46
55
|
}
|
|
47
56
|
//# sourceMappingURL=preview.js.map
|