@elytracms/studio 0.0.93 → 0.0.94
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/auth.ts +5 -2
- package/convex/authority.ts +35 -11
- package/convex/contract-parity.test.ts +7 -0
- package/convex/delivery.ts +12 -9
- 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/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 +30 -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 +2 -2
- 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-thumb.js +3 -3
- package/dist/components/assets/asset-thumb.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +2 -2
- 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 +6 -6
- 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.js +2 -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-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-render-context.d.ts +1 -1
- package/dist/components/builder/canvas-render-context.js +4 -4
- 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 +1 -1
- package/dist/components/builder/composition-field-editor.js +3 -3
- 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-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 +4 -4
- 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 +1 -1
- 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.js +2 -2
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/asset-picker.js +2 -2
- 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/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 +7 -7
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/detail-editor.d.ts +5 -2
- package/dist/components/cms/detail-editor.js +20 -14
- 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 +7 -1
- package/dist/components/cms/use-document-draft.js +16 -2
- 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 +1 -1
- package/dist/components/cms/use-live-url.js +3 -3
- 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 +2 -2
- 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 +3 -3
- 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 +156 -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/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.js +1 -1
- package/dist/lib/cms/published-documents.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +12 -12
- package/dist/lib/cms-views/view-model.js +2 -2
- package/dist/lib/cms-views/view-model.js.map +1 -1
- package/dist/lib/collection-view/collection-view-source.d.ts +7 -2
- 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 +4 -0
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +4 -0
- package/dist/lib/i18n/messages.en.js +8 -0
- package/dist/lib/i18n/messages.en.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/index.js +14 -1
- package/dist/lib/persistence/index.js.map +1 -1
- package/dist/lib/persona/labels.js +1 -1
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/query/operations-query.d.ts +1 -1
- package/dist/lib/query/operations-query.js +1 -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/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/mount.d.ts +28 -0
- package/dist/mount.js +27 -0
- package/dist/mount.js.map +1 -0
- 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,5 +1,6 @@
|
|
|
1
1
|
import type { CollectionDef, LocaleConfig } from '@elytracms/core/cms-core';
|
|
2
2
|
import { ComponentRegistry } from '@elytracms/core/component-registry';
|
|
3
|
+
import type { RolePolicy } from '@elytracms/core/persistence';
|
|
3
4
|
import { configProjectFor } from '../workspace/index.js';
|
|
4
5
|
export { configProjectFor };
|
|
5
6
|
/**
|
|
@@ -26,6 +27,14 @@ export declare function projectLocaleConfig(projectId: string): LocaleConfig | u
|
|
|
26
27
|
* `resolveProjectCollections`, a second copy of the same lookup (EC-595 A2 found-but-left).
|
|
27
28
|
*/
|
|
28
29
|
export declare function projectCollections(projectId: string): CollectionDef[];
|
|
30
|
+
/**
|
|
31
|
+
* EC-605 — the role policy of the operation's project (see {@link configProjectFor}):
|
|
32
|
+
* its `roles` declaration, else the default (core's one reader, `projectRolePolicy`).
|
|
33
|
+
* What the operations runtime validates a granted role against and the role hook grants
|
|
34
|
+
* by, what the users and invite pickers list, and what the studio's drift check compares
|
|
35
|
+
* with the deployment's copy. The default for an id no config project declares.
|
|
36
|
+
*/
|
|
37
|
+
export declare function projectRolePolicyFor(projectId: string): RolePolicy;
|
|
29
38
|
/**
|
|
30
39
|
* EC-582 — the component registry of the operation's project (see
|
|
31
40
|
* {@link configProjectFor}): its config component entries' MANIFESTS, registered once per
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { ComponentRegistry } from '@elytracms/core/component-registry';
|
|
2
|
+
import { defaultRolePolicy } from '@elytracms/core/persistence';
|
|
3
|
+
import { projectRolePolicy } from '@elytracms/core/studio-core';
|
|
2
4
|
import { configProjectFor } from '../workspace/index.js';
|
|
3
5
|
export { configProjectFor };
|
|
4
6
|
/**
|
|
@@ -29,6 +31,17 @@ export function projectLocaleConfig(projectId) {
|
|
|
29
31
|
export function projectCollections(projectId) {
|
|
30
32
|
return configProjectFor(projectId)?.collections ?? [];
|
|
31
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* EC-605 — the role policy of the operation's project (see {@link configProjectFor}):
|
|
36
|
+
* its `roles` declaration, else the default (core's one reader, `projectRolePolicy`).
|
|
37
|
+
* What the operations runtime validates a granted role against and the role hook grants
|
|
38
|
+
* by, what the users and invite pickers list, and what the studio's drift check compares
|
|
39
|
+
* with the deployment's copy. The default for an id no config project declares.
|
|
40
|
+
*/
|
|
41
|
+
export function projectRolePolicyFor(projectId) {
|
|
42
|
+
const project = configProjectFor(projectId);
|
|
43
|
+
return project ? projectRolePolicy(project) : defaultRolePolicy;
|
|
44
|
+
}
|
|
32
45
|
const registries = new WeakMap();
|
|
33
46
|
/**
|
|
34
47
|
* EC-582 — the component registry of the operation's project (see
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-runtime-config.js","sourceRoot":"","sources":["../../../src/lib/operations/project-runtime-config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAA;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAE/C,OAAO,EAAE,gBAAgB,EAAE,CAAA;AAE3B;;;;;;;;;;;;;;GAcG;AAEH,mFAAmF;AACnF,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACnD,OAAO,gBAAgB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;AAC7C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,OAAO,gBAAgB,CAAC,SAAS,CAAC,EAAE,WAAW,IAAI,EAAE,CAAA;AACvD,CAAC;AAED,MAAM,UAAU,GAAG,IAAI,OAAO,EAA6B,CAAA;AAE3D;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CAAC,SAAiB;IACxD,MAAM,OAAO,GAAG,gBAAgB,CAAC,SAAS,CAAC,EAAE,UAAU,CAAA;IACvD,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAA;IAC9B,IAAI,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,IAAI,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAA;QACxE,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IACnC,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC","sourcesContent":["import type { CollectionDef, LocaleConfig } from '@elytracms/core/cms-core'\nimport { ComponentRegistry } from '@elytracms/core/component-registry'\nimport { configProjectFor } from '../workspace'\n\nexport { configProjectFor }\n\n/**\n * EC-595 A2 — the config project an OPERATION runs for, by its project id (the slug),\n * whether or not it is the ACTIVE project.\n *\n * One operations bundle is cached per deployment KEY (EC-179) and a deployment may\n * serve several config projects, so everything an operation reads from config — its\n * collections (A1's `resolveProjectCollections`), its component manifests (the write\n * canonicaliser), its locales (the publish gate's runtime validator) — is resolved for\n * the operation's own `projectId`, never for whichever project the UI has active (A1\n * found-but-left #1: the manifest lookup read the active project's components).\n *\n * The lookup itself is the workspace's ONE reader of \"the config project with this slug\"\n * (`configProjectFor`, `lib/workspace` — EC-582 folded the copy that lived here into it);\n * this module derives what an operation reads from it.\n */\n\n/** The locale config of the operation's project (see {@link configProjectFor}). */\nexport function projectLocaleConfig(projectId: string): LocaleConfig | undefined {\n return configProjectFor(projectId)?.locales\n}\n\n/**\n * The collections of the operation's project (see {@link configProjectFor}); `[]` for an\n * id no config project declares (env-only wiring, engine fixtures). EC-585: the ONE\n * reader of \"the config project with this slug\" — it replaced `lib/workspace`'s\n * `resolveProjectCollections`, a second copy of the same lookup (EC-595 A2 found-but-left).\n */\nexport function projectCollections(projectId: string): CollectionDef[] {\n return configProjectFor(projectId)?.collections ?? []\n}\n\nconst registries = new WeakMap<object, ComponentRegistry>()\n\n/**\n * EC-582 — the component registry of the operation's project (see\n * {@link configProjectFor}): its config component entries' MANIFESTS, registered once per\n * list (the implementations are the renderer's, not the validator's — this stays off\n * `runtime-renderer`, like the manifest lookup). What the publish gate's canonical\n * validator judges compositions and props with — never the ACTIVE project's registry\n * (critic row 13: on a deployment shared by projects with different component lists the\n * gate judged an inactive project's props with the wrong manifests). `undefined` for a\n * project that lists no components (or an id no config declares).\n */\nexport function projectComponentRegistry(projectId: string): ComponentRegistry | undefined {\n const entries = configProjectFor(projectId)?.components\n if (!entries) return undefined\n let registry = registries.get(entries)\n if (!registry) {\n registry = new ComponentRegistry(entries.map((entry) => entry.manifest))\n registries.set(entries, registry)\n }\n return registry\n}\n"]}
|
|
1
|
+
{"version":3,"file":"project-runtime-config.js","sourceRoot":"","sources":["../../../src/lib/operations/project-runtime-config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAA;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AAE/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAE/C,OAAO,EAAE,gBAAgB,EAAE,CAAA;AAE3B;;;;;;;;;;;;;;GAcG;AAEH,mFAAmF;AACnF,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACnD,OAAO,gBAAgB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;AAC7C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,OAAO,gBAAgB,CAAC,SAAS,CAAC,EAAE,WAAW,IAAI,EAAE,CAAA;AACvD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,SAAiB;IACpD,MAAM,OAAO,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAA;IAC3C,OAAO,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAA;AACjE,CAAC;AAED,MAAM,UAAU,GAAG,IAAI,OAAO,EAA6B,CAAA;AAE3D;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CAAC,SAAiB;IACxD,MAAM,OAAO,GAAG,gBAAgB,CAAC,SAAS,CAAC,EAAE,UAAU,CAAA;IACvD,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAA;IAC9B,IAAI,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,IAAI,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAA;QACxE,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IACnC,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC","sourcesContent":["import type { CollectionDef, LocaleConfig } from '@elytracms/core/cms-core'\nimport { ComponentRegistry } from '@elytracms/core/component-registry'\nimport { defaultRolePolicy } from '@elytracms/core/persistence'\nimport type { RolePolicy } from '@elytracms/core/persistence'\nimport { projectRolePolicy } from '@elytracms/core/studio-core'\nimport { configProjectFor } from '../workspace'\n\nexport { configProjectFor }\n\n/**\n * EC-595 A2 — the config project an OPERATION runs for, by its project id (the slug),\n * whether or not it is the ACTIVE project.\n *\n * One operations bundle is cached per deployment KEY (EC-179) and a deployment may\n * serve several config projects, so everything an operation reads from config — its\n * collections (A1's `resolveProjectCollections`), its component manifests (the write\n * canonicaliser), its locales (the publish gate's runtime validator) — is resolved for\n * the operation's own `projectId`, never for whichever project the UI has active (A1\n * found-but-left #1: the manifest lookup read the active project's components).\n *\n * The lookup itself is the workspace's ONE reader of \"the config project with this slug\"\n * (`configProjectFor`, `lib/workspace` — EC-582 folded the copy that lived here into it);\n * this module derives what an operation reads from it.\n */\n\n/** The locale config of the operation's project (see {@link configProjectFor}). */\nexport function projectLocaleConfig(projectId: string): LocaleConfig | undefined {\n return configProjectFor(projectId)?.locales\n}\n\n/**\n * The collections of the operation's project (see {@link configProjectFor}); `[]` for an\n * id no config project declares (env-only wiring, engine fixtures). EC-585: the ONE\n * reader of \"the config project with this slug\" — it replaced `lib/workspace`'s\n * `resolveProjectCollections`, a second copy of the same lookup (EC-595 A2 found-but-left).\n */\nexport function projectCollections(projectId: string): CollectionDef[] {\n return configProjectFor(projectId)?.collections ?? []\n}\n\n/**\n * EC-605 — the role policy of the operation's project (see {@link configProjectFor}):\n * its `roles` declaration, else the default (core's one reader, `projectRolePolicy`).\n * What the operations runtime validates a granted role against and the role hook grants\n * by, what the users and invite pickers list, and what the studio's drift check compares\n * with the deployment's copy. The default for an id no config project declares.\n */\nexport function projectRolePolicyFor(projectId: string): RolePolicy {\n const project = configProjectFor(projectId)\n return project ? projectRolePolicy(project) : defaultRolePolicy\n}\n\nconst registries = new WeakMap<object, ComponentRegistry>()\n\n/**\n * EC-582 — the component registry of the operation's project (see\n * {@link configProjectFor}): its config component entries' MANIFESTS, registered once per\n * list (the implementations are the renderer's, not the validator's — this stays off\n * `runtime-renderer`, like the manifest lookup). What the publish gate's canonical\n * validator judges compositions and props with — never the ACTIVE project's registry\n * (critic row 13: on a deployment shared by projects with different component lists the\n * gate judged an inactive project's props with the wrong manifests). `undefined` for a\n * project that lists no components (or an id no config declares).\n */\nexport function projectComponentRegistry(projectId: string): ComponentRegistry | undefined {\n const entries = configProjectFor(projectId)?.components\n if (!entries) return undefined\n let registry = registries.get(entries)\n if (!registry) {\n registry = new ComponentRegistry(entries.map((entry) => entry.manifest))\n registries.set(entries, registry)\n }\n return registry\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DocumentListener, IdFactory, PersistenceAdapter, PresenceInput } from '@elytracms/core/persistence';
|
|
2
2
|
import type { DocumentRef } from '@elytracms/core/cms-core';
|
|
3
|
-
import type { ChangesetOperations, CollectionsResolver, DocumentValidator, LocalesResolver, ManifestLookupResolver, OperationsClient, Principal, ReferenceIndexAttachment } from './operations-package.js';
|
|
3
|
+
import type { ChangesetOperations, CollectionsResolver, DocumentValidator, LocalesResolver, ManifestLookupResolver, OperationsClient, Principal, ReferenceIndexAttachment, RolePolicyResolver } from './operations-package.js';
|
|
4
4
|
/**
|
|
5
5
|
* The studio's single operations bootstrap (EC-141, vision AD-7).
|
|
6
6
|
*
|
|
@@ -88,6 +88,14 @@ export interface CreateStudioOperationsOptions {
|
|
|
88
88
|
* collections, the manifests and the gate's validator.
|
|
89
89
|
*/
|
|
90
90
|
locales?: LocalesResolver;
|
|
91
|
+
/**
|
|
92
|
+
* EC-605 — the role policy, per project: what a role an operation grants is validated
|
|
93
|
+
* against and what the role hook (`createRoleAuthorization`, which reads it from the
|
|
94
|
+
* runtime) grants by. Defaults to that config project's `roles` declaration
|
|
95
|
+
* (`projectRolePolicyFor`; the default policy when it declares none) — read the same
|
|
96
|
+
* way as the collections and locales.
|
|
97
|
+
*/
|
|
98
|
+
rolePolicy?: RolePolicyResolver;
|
|
91
99
|
}
|
|
92
100
|
/** Build a studio operations bundle over an adapter (exported for tests). */
|
|
93
101
|
export declare function createStudioOperations(adapter: PersistenceAdapter, options?: CreateStudioOperationsOptions): StudioOperations;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { activeDeploymentKey, selectPersistenceAdapter } from '../persistence/index.js';
|
|
2
2
|
import { attachSessionExpiryReporting } from '../session-expiry.js';
|
|
3
3
|
import { projectManifestLookup } from './config-manifest-lookup.js';
|
|
4
|
-
import { configProjectFor, projectCollections, projectComponentRegistry, projectLocaleConfig, } from './project-runtime-config.js';
|
|
4
|
+
import { configProjectFor, projectCollections, projectComponentRegistry, projectLocaleConfig, projectRolePolicyFor, } from './project-runtime-config.js';
|
|
5
5
|
import { createRuntimeDocumentValidator } from './runtime-validator.js';
|
|
6
6
|
import { attachReferenceIndex, createChangesets, createOperations, createRoleAuthorization, summarizeInput, toOperationError, } from './operations-package.js';
|
|
7
7
|
/**
|
|
@@ -14,6 +14,9 @@ function noopReferenceIndexAttachment() {
|
|
|
14
14
|
}
|
|
15
15
|
/** Build a studio operations bundle over an adapter (exported for tests). */
|
|
16
16
|
export function createStudioOperations(adapter, options = {}) {
|
|
17
|
+
// EC-605: the role hook is built WITHOUT a fixed policy — it grants by the policy the
|
|
18
|
+
// runtime resolves for each operation's project (`rolePolicy` below), the same one the
|
|
19
|
+
// operation's roles were validated against: one injection point, not two copies.
|
|
17
20
|
const authorized = options.actor
|
|
18
21
|
? { actor: options.actor, authorize: createRoleAuthorization(adapter.members) }
|
|
19
22
|
: {};
|
|
@@ -23,6 +26,7 @@ export function createStudioOperations(adapter, options = {}) {
|
|
|
23
26
|
// with the operation's slug (`configProjectFor`): collections, manifests, locales.
|
|
24
27
|
const collections = options.collections ?? projectCollections;
|
|
25
28
|
const locales = options.locales ?? projectLocaleConfig;
|
|
29
|
+
const rolePolicy = options.rolePolicy ?? projectRolePolicyFor;
|
|
26
30
|
const idFactory = options.idFactory ? { idFactory: options.idFactory } : {};
|
|
27
31
|
const manifestLookup = options.manifestLookup ?? projectManifestLookup((projectId) => configProjectFor(projectId)?.components);
|
|
28
32
|
// EC-595 A2 (F23) → EC-582: the publish gate's validator is bound to THIS bundle —
|
|
@@ -41,6 +45,7 @@ export function createStudioOperations(adapter, options = {}) {
|
|
|
41
45
|
validateDocument,
|
|
42
46
|
manifestLookup,
|
|
43
47
|
locales,
|
|
48
|
+
rolePolicy,
|
|
44
49
|
...idFactory,
|
|
45
50
|
...authorized,
|
|
46
51
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"studio-operations.js","sourceRoot":"","sources":["../../../src/lib/operations/studio-operations.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n DocumentListener,\n IdFactory,\n PersistenceAdapter,\n PresenceInput,\n} from '@elytracms/core/persistence'\nimport type { DocumentRef } from '@elytracms/core/cms-core'\nimport { activeDeploymentKey, selectPersistenceAdapter } from '../persistence'\nimport { attachSessionExpiryReporting } from '../session-expiry'\nimport { projectManifestLookup } from './config-manifest-lookup'\nimport {\n configProjectFor,\n projectCollections,\n projectComponentRegistry,\n projectLocaleConfig,\n} from './project-runtime-config'\nimport { createRuntimeDocumentValidator } from './runtime-validator'\nimport {\n attachReferenceIndex,\n createChangesets,\n createOperations,\n createRoleAuthorization,\n summarizeInput,\n toOperationError,\n} from './operations-package'\nimport type {\n ChangesetOperations,\n CollectionsResolver,\n DocumentValidator,\n LocalesResolver,\n ManifestLookupResolver,\n OperationsClient,\n Principal,\n ReferenceIndexAttachment,\n} from './operations-package'\n\n/**\n * The studio's single operations bootstrap (EC-141, vision AD-7).\n *\n * One `OperationsClient` + one `ChangesetOperations` instance, built over the\n * selected persistence adapter, shared by every store and surface in the app.\n * The UI never talks to a repository for writes — every mutation is a typed\n * operation command (validate → authorize → mutate → event), so the studio\n * dogfoods the same seam plugins, AI changesets, and the CLI use.\n *\n * The EC-155 reference index is attached to the shared event bus here, so any\n * document/graph/publishing command issued anywhere in the studio keeps the\n * index maintained.\n */\nexport interface StudioOperations {\n readonly operations: OperationsClient\n readonly changesets: ChangesetOperations\n /**\n * The raw adapter, exposed for *transport and read paths only*: blob byte\n * upload/download (`adapter.blobs`) and existing read stores. Studio code\n * must not call repository write methods on it — writes go through\n * `operations`.\n */\n readonly adapter: PersistenceAdapter\n /** EC-155 maintained-on-write index attachment (flushable in tests). */\n readonly references: ReferenceIndexAttachment\n /**\n * EC-595 binding point 7 — subscribe to ONE document (the narrow EC-309 slice): the\n * listener gets the current row + the presence on it, then one snapshot per change\n * (Convex: the reactive client's server push; playground: the in-memory store's own\n * writes). An adapter capability exposed through the in-process bundle — the UI never\n * holds a reactive Convex client. A subscription failure is ALSO emitted as a failed\n * `documents.subscribe` event on the operations bus, so an expired session reaches\n * EC-403's one session-expiry chokepoint like any failed operation (never a silently\n * stopped stream). Returns the unsubscribe function.\n */\n subscribeDocument(projectId: string, ref: DocumentRef, listener: DocumentListener): () => void\n /** EC-595 — this session's field-presence heartbeat on a document (thin presence, Stage A). */\n reportPresence(projectId: string, ref: DocumentRef, presence: PresenceInput): Promise<void>\n /** EC-595 — remove this session's presence from a document. */\n clearPresence(projectId: string, ref: DocumentRef, sessionId: string): Promise<void>\n}\n\nexport interface CreateStudioOperationsOptions {\n /**\n * The signed-in principal (EC-150). When present, the EC-150 role hook\n * (`createRoleAuthorization` over `adapter.members`) becomes the\n * authorization chokepoint for every command and query — viewers get\n * structured denials on mutations, editors on administration. Without an\n * actor the client runs pass-through (seeds, fixtures, tests that exercise\n * domain logic rather than permissions); the route guard keeps\n * unauthenticated users away from every operation surface.\n */\n actor?: Principal\n /**\n * EC-583 / EC-559 — the collection definitions on the operations context, resolved for\n * each operation's OWN project id. Defaults to the workspace config's project with that\n * slug (`projectCollections` — the resolved `elytra.config.ts`, functions\n * included), read on every call: the bundle is cached per deployment KEY (EC-179) and\n * one deployment may serve several config projects, so capturing one project's\n * collections at build time would canonicalise another project's documents against\n * them (operator correction to phase 1). The store holds no schema.\n */\n collections?: CollectionsResolver\n /** EC-578 — the document-id factory (`documents.create`); UUIDs by default. */\n idFactory?: IdFactory\n /**\n * EC-595 A1 (critic correction 4) → A2 — the manifest half of the write canonicaliser\n * on the operations context, resolved for each operation's OWN project. Defaults to a\n * lookup over that config project's component entries (`projectManifestLookup`), so\n * every patch / replace canonicalises a composition's props with its project's\n * manifests — never the active project's (A1 found-but-left #1).\n */\n manifestLookup?: ManifestLookupResolver\n /**\n * EC-595 A2 (F23) — the publish gate's validator, bound to THIS bundle. Defaults to\n * the runtime validator (`createRuntimeDocumentValidator`: the operation's project's\n * config + oracles prefetched through the adapter). Tests that pin persistence\n * routing rather than gate strength inject their own here — at construction, never\n * through a mount.\n */\n validateDocument?: DocumentValidator\n /**\n * EC-585 — the locale config on the operations context, per project: the view\n * `documents.publish` runs its hidden-clear in. Defaults to that config project's\n * `locales` (`projectLocaleConfig`) — the same project, read the same way, as the\n * collections, the manifests and the gate's validator.\n */\n locales?: LocalesResolver\n}\n\n/**\n * A no-op {@link ReferenceIndexAttachment} for self-maintaining adapters (EC-277):\n * nothing is subscribed, so `detach` and `flush` are trivially satisfied (the index\n * is kept current in the adapter's write path, not by a queued subscriber).\n */\nfunction noopReferenceIndexAttachment(): ReferenceIndexAttachment {\n return { detach: () => {}, flush: async () => {} }\n}\n\n/** Build a studio operations bundle over an adapter (exported for tests). */\nexport function createStudioOperations(\n adapter: PersistenceAdapter,\n options: CreateStudioOperationsOptions = {},\n): StudioOperations {\n const authorized = options.actor\n ? { actor: options.actor, authorize: createRoleAuthorization(adapter.members) }\n : {}\n // EC-583: the collections come from the config (never the store), per project id;\n // EC-595 A1: so does the manifest lookup. Both ride the changesets runtime too.\n // EC-585: every per-project fact below is read from ONE source — the config project\n // with the operation's slug (`configProjectFor`): collections, manifests, locales.\n const collections: CollectionsResolver = options.collections ?? projectCollections\n const locales: LocalesResolver = options.locales ?? projectLocaleConfig\n const idFactory = options.idFactory ? { idFactory: options.idFactory } : {}\n const manifestLookup =\n options.manifestLookup ?? projectManifestLookup((projectId) => configProjectFor(projectId)?.components)\n // EC-595 A2 (F23) → EC-582: the publish gate's validator is bound to THIS bundle —\n // core's canonical gate (`@elytracms/core/validation`) over the OPERATION's project:\n // its collections, locales and component registry (never the active project's) +\n // oracles prefetched through the adapter — never whichever CMS session is mounted.\n const validateDocument =\n options.validateDocument ??\n createRuntimeDocumentValidator({\n collections,\n localeConfig: locales,\n registry: projectComponentRegistry,\n })\n const operations = createOperations({\n adapter,\n collections,\n validateDocument,\n manifestLookup,\n locales,\n ...idFactory,\n ...authorized,\n })\n // EC-277: when the adapter maintains the index in its own write path (AD-6 \"at the\n // source\" — the Convex provider does this server-side, so a raw `convex run` is\n // caught too), the client-side subscriber would only double the work. Attach it\n // ONLY for providers that don't self-maintain. Where it does run, extraction is a\n // schema-free, manifest-free walk over self-describing content, so it needs no\n // registry (compositions self-typed their relation/asset props on write).\n const references = adapter.maintainsReferenceIndex\n ? noopReferenceIndexAttachment()\n : attachReferenceIndex(operations.events, adapter)\n const changesets = createChangesets({\n adapter,\n collections,\n events: operations.events,\n // EC-497 / EC-595 A2: a changeset-driven publish is gated identically — the same\n // runtime validator, prefetching through the step's own (scratch or real) adapter.\n validateDocument,\n manifestLookup,\n locales,\n ...idFactory,\n ...authorized,\n })\n // EC-579: no revalidation emitter attaches here. Host revalidation is a property\n // of the Convex write chokepoint (`convex/triggers.ts`), the one emitter — it\n // catches every writer, keeps the signing secret server-side, and the playground's\n // in-browser adapter is read by no host, so there is nothing to emit there.\n // EC-403: session-expiry detection rides the SAME lifecycle bus — every failed\n // command/query/changeset operation is checked against the session-expired\n // predicate here, so expiry raises the ONE app-level blocking re-auth state no\n // matter which surface issued the operation (per-surface banners can't cover\n // surfaces that render no banner).\n attachSessionExpiryReporting(operations.events)\n\n /** Report a capability failure on the bus (EC-403 sees an expiry like any failed operation). */\n const reportFailure = (operation: string, input: unknown, error: unknown) => {\n operations.events.emit({\n operation,\n domain: 'documents',\n kind: 'query',\n status: 'failed',\n inputSummary: summarizeInput(input),\n error: toOperationError(error),\n })\n }\n return {\n operations,\n changesets,\n adapter,\n references,\n subscribeDocument: (projectId, ref, listener) =>\n adapter.cms.subscribeDocument(projectId, ref, {\n onSnapshot: (snapshot) => listener.onSnapshot(snapshot),\n onError: (error) => {\n reportFailure('documents.subscribe', { projectId, ref }, error)\n listener.onError?.(error)\n },\n }),\n reportPresence: async (projectId, ref, presence) => {\n try {\n await adapter.cms.reportPresence(projectId, ref, presence)\n } catch (error) {\n reportFailure('documents.presence', { projectId, ref }, error)\n throw error\n }\n },\n clearPresence: (projectId, ref, sessionId) => adapter.cms.clearPresence(projectId, ref, sessionId),\n }\n}\n\n// One adapter + one operations bundle per backend (EC-179): keyed by the active\n// deployment so switching to a project on another Convex deployment selects (or\n// lazily builds) that backend's client, rather than reusing the first one.\nconst adaptersByKey = new Map<string, Promise<PersistenceAdapter>>()\nconst instancesByKey = new Map<string, Promise<StudioOperations>>()\nlet currentActor: Principal | null = null\n\n/**\n * The active backend key, tolerant of an unconfigured env: the real app never\n * reaches `getStudioOperations` while misconfigured (the `DeploymentSetupGate` renders a\n * boot error first), but tests inject a bundle directly via the seam below — so\n * a missing backend resolves to one stable key rather than throwing.\n */\nfunction deploymentKey(): string {\n try {\n return activeDeploymentKey()\n } catch {\n return '__unconfigured__'\n }\n}\n\nfunction getAdapter(key: string): Promise<PersistenceAdapter> {\n let adapter = adaptersByKey.get(key)\n if (!adapter) {\n adapter = selectPersistenceAdapter()\n adaptersByKey.set(key, adapter)\n }\n return adapter\n}\n\n/**\n * The shared studio operations client for the active backend (lazily\n * bootstrapped, cached per deployment). In-memory mode this owns the one seeded\n * adapter the whole session mutates; against Convex it owns one client per\n * deployment — the active project (EC-179) selects which.\n */\nexport function getStudioOperations(): Promise<StudioOperations> {\n const key = deploymentKey()\n let instance = instancesByKey.get(key)\n if (!instance) {\n const actor = currentActor\n instance = getAdapter(key).then((adapter) =>\n createStudioOperations(adapter, actor ? { actor } : {}),\n )\n instancesByKey.set(key, instance)\n }\n return instance\n}\n\n/**\n * Bind (or clear) the session principal (EC-150). Called by the auth context\n * on sign-in/sign-out. Rebuilds the operations clients over the *same* adapters\n * — the persisted state (and, in-memory, the whole session's edits) survives;\n * only the authorization identity changes. All cached backends are rebuilt so a\n * later switch picks up the new actor too.\n */\nexport function setStudioSession(actor: Principal | null): void {\n if (currentActor?.id === actor?.id) return // same user (or both null) — keep the clients\n currentActor = actor\n instancesByKey.clear()\n}\n\n/**\n * EC-595 A2 — the signed-in principal the operations clients act as (`null` signed out):\n * what this session's presence heartbeat names (\"X is in this field\") and how a conflict\n * tells \"you, in another tab\" from a colleague. Read-only; set by {@link setStudioSession}.\n */\nexport function getStudioActor(): Principal | null {\n return currentActor\n}\n\n/** Test seam: replace (or clear) the singleton for the active backend. */\nexport function setStudioOperationsForTests(next: StudioOperations | null): void {\n if (next) {\n instancesByKey.set(deploymentKey(), Promise.resolve(next))\n return\n }\n instancesByKey.clear()\n adaptersByKey.clear()\n currentActor = null\n}\n"]}
|
|
1
|
+
{"version":3,"file":"studio-operations.js","sourceRoot":"","sources":["../../../src/lib/operations/studio-operations.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n DocumentListener,\n IdFactory,\n PersistenceAdapter,\n PresenceInput,\n} from '@elytracms/core/persistence'\nimport type { DocumentRef } from '@elytracms/core/cms-core'\nimport { activeDeploymentKey, selectPersistenceAdapter } from '../persistence'\nimport { attachSessionExpiryReporting } from '../session-expiry'\nimport { projectManifestLookup } from './config-manifest-lookup'\nimport {\n configProjectFor,\n projectCollections,\n projectComponentRegistry,\n projectLocaleConfig,\n projectRolePolicyFor,\n} from './project-runtime-config'\nimport { createRuntimeDocumentValidator } from './runtime-validator'\nimport {\n attachReferenceIndex,\n createChangesets,\n createOperations,\n createRoleAuthorization,\n summarizeInput,\n toOperationError,\n} from './operations-package'\nimport type {\n ChangesetOperations,\n CollectionsResolver,\n DocumentValidator,\n LocalesResolver,\n ManifestLookupResolver,\n OperationsClient,\n Principal,\n ReferenceIndexAttachment,\n RolePolicyResolver,\n} from './operations-package'\n\n/**\n * The studio's single operations bootstrap (EC-141, vision AD-7).\n *\n * One `OperationsClient` + one `ChangesetOperations` instance, built over the\n * selected persistence adapter, shared by every store and surface in the app.\n * The UI never talks to a repository for writes — every mutation is a typed\n * operation command (validate → authorize → mutate → event), so the studio\n * dogfoods the same seam plugins, AI changesets, and the CLI use.\n *\n * The EC-155 reference index is attached to the shared event bus here, so any\n * document/graph/publishing command issued anywhere in the studio keeps the\n * index maintained.\n */\nexport interface StudioOperations {\n readonly operations: OperationsClient\n readonly changesets: ChangesetOperations\n /**\n * The raw adapter, exposed for *transport and read paths only*: blob byte\n * upload/download (`adapter.blobs`) and existing read stores. Studio code\n * must not call repository write methods on it — writes go through\n * `operations`.\n */\n readonly adapter: PersistenceAdapter\n /** EC-155 maintained-on-write index attachment (flushable in tests). */\n readonly references: ReferenceIndexAttachment\n /**\n * EC-595 binding point 7 — subscribe to ONE document (the narrow EC-309 slice): the\n * listener gets the current row + the presence on it, then one snapshot per change\n * (Convex: the reactive client's server push; playground: the in-memory store's own\n * writes). An adapter capability exposed through the in-process bundle — the UI never\n * holds a reactive Convex client. A subscription failure is ALSO emitted as a failed\n * `documents.subscribe` event on the operations bus, so an expired session reaches\n * EC-403's one session-expiry chokepoint like any failed operation (never a silently\n * stopped stream). Returns the unsubscribe function.\n */\n subscribeDocument(projectId: string, ref: DocumentRef, listener: DocumentListener): () => void\n /** EC-595 — this session's field-presence heartbeat on a document (thin presence, Stage A). */\n reportPresence(projectId: string, ref: DocumentRef, presence: PresenceInput): Promise<void>\n /** EC-595 — remove this session's presence from a document. */\n clearPresence(projectId: string, ref: DocumentRef, sessionId: string): Promise<void>\n}\n\nexport interface CreateStudioOperationsOptions {\n /**\n * The signed-in principal (EC-150). When present, the EC-150 role hook\n * (`createRoleAuthorization` over `adapter.members`) becomes the\n * authorization chokepoint for every command and query — viewers get\n * structured denials on mutations, editors on administration. Without an\n * actor the client runs pass-through (seeds, fixtures, tests that exercise\n * domain logic rather than permissions); the route guard keeps\n * unauthenticated users away from every operation surface.\n */\n actor?: Principal\n /**\n * EC-583 / EC-559 — the collection definitions on the operations context, resolved for\n * each operation's OWN project id. Defaults to the workspace config's project with that\n * slug (`projectCollections` — the resolved `elytra.config.ts`, functions\n * included), read on every call: the bundle is cached per deployment KEY (EC-179) and\n * one deployment may serve several config projects, so capturing one project's\n * collections at build time would canonicalise another project's documents against\n * them (operator correction to phase 1). The store holds no schema.\n */\n collections?: CollectionsResolver\n /** EC-578 — the document-id factory (`documents.create`); UUIDs by default. */\n idFactory?: IdFactory\n /**\n * EC-595 A1 (critic correction 4) → A2 — the manifest half of the write canonicaliser\n * on the operations context, resolved for each operation's OWN project. Defaults to a\n * lookup over that config project's component entries (`projectManifestLookup`), so\n * every patch / replace canonicalises a composition's props with its project's\n * manifests — never the active project's (A1 found-but-left #1).\n */\n manifestLookup?: ManifestLookupResolver\n /**\n * EC-595 A2 (F23) — the publish gate's validator, bound to THIS bundle. Defaults to\n * the runtime validator (`createRuntimeDocumentValidator`: the operation's project's\n * config + oracles prefetched through the adapter). Tests that pin persistence\n * routing rather than gate strength inject their own here — at construction, never\n * through a mount.\n */\n validateDocument?: DocumentValidator\n /**\n * EC-585 — the locale config on the operations context, per project: the view\n * `documents.publish` runs its hidden-clear in. Defaults to that config project's\n * `locales` (`projectLocaleConfig`) — the same project, read the same way, as the\n * collections, the manifests and the gate's validator.\n */\n locales?: LocalesResolver\n /**\n * EC-605 — the role policy, per project: what a role an operation grants is validated\n * against and what the role hook (`createRoleAuthorization`, which reads it from the\n * runtime) grants by. Defaults to that config project's `roles` declaration\n * (`projectRolePolicyFor`; the default policy when it declares none) — read the same\n * way as the collections and locales.\n */\n rolePolicy?: RolePolicyResolver\n}\n\n/**\n * A no-op {@link ReferenceIndexAttachment} for self-maintaining adapters (EC-277):\n * nothing is subscribed, so `detach` and `flush` are trivially satisfied (the index\n * is kept current in the adapter's write path, not by a queued subscriber).\n */\nfunction noopReferenceIndexAttachment(): ReferenceIndexAttachment {\n return { detach: () => {}, flush: async () => {} }\n}\n\n/** Build a studio operations bundle over an adapter (exported for tests). */\nexport function createStudioOperations(\n adapter: PersistenceAdapter,\n options: CreateStudioOperationsOptions = {},\n): StudioOperations {\n // EC-605: the role hook is built WITHOUT a fixed policy — it grants by the policy the\n // runtime resolves for each operation's project (`rolePolicy` below), the same one the\n // operation's roles were validated against: one injection point, not two copies.\n const authorized = options.actor\n ? { actor: options.actor, authorize: createRoleAuthorization(adapter.members) }\n : {}\n // EC-583: the collections come from the config (never the store), per project id;\n // EC-595 A1: so does the manifest lookup. Both ride the changesets runtime too.\n // EC-585: every per-project fact below is read from ONE source — the config project\n // with the operation's slug (`configProjectFor`): collections, manifests, locales.\n const collections: CollectionsResolver = options.collections ?? projectCollections\n const locales: LocalesResolver = options.locales ?? projectLocaleConfig\n const rolePolicy: RolePolicyResolver = options.rolePolicy ?? projectRolePolicyFor\n const idFactory = options.idFactory ? { idFactory: options.idFactory } : {}\n const manifestLookup =\n options.manifestLookup ?? projectManifestLookup((projectId) => configProjectFor(projectId)?.components)\n // EC-595 A2 (F23) → EC-582: the publish gate's validator is bound to THIS bundle —\n // core's canonical gate (`@elytracms/core/validation`) over the OPERATION's project:\n // its collections, locales and component registry (never the active project's) +\n // oracles prefetched through the adapter — never whichever CMS session is mounted.\n const validateDocument =\n options.validateDocument ??\n createRuntimeDocumentValidator({\n collections,\n localeConfig: locales,\n registry: projectComponentRegistry,\n })\n const operations = createOperations({\n adapter,\n collections,\n validateDocument,\n manifestLookup,\n locales,\n rolePolicy,\n ...idFactory,\n ...authorized,\n })\n // EC-277: when the adapter maintains the index in its own write path (AD-6 \"at the\n // source\" — the Convex provider does this server-side, so a raw `convex run` is\n // caught too), the client-side subscriber would only double the work. Attach it\n // ONLY for providers that don't self-maintain. Where it does run, extraction is a\n // schema-free, manifest-free walk over self-describing content, so it needs no\n // registry (compositions self-typed their relation/asset props on write).\n const references = adapter.maintainsReferenceIndex\n ? noopReferenceIndexAttachment()\n : attachReferenceIndex(operations.events, adapter)\n const changesets = createChangesets({\n adapter,\n collections,\n events: operations.events,\n // EC-497 / EC-595 A2: a changeset-driven publish is gated identically — the same\n // runtime validator, prefetching through the step's own (scratch or real) adapter.\n validateDocument,\n manifestLookup,\n locales,\n ...idFactory,\n ...authorized,\n })\n // EC-579: no revalidation emitter attaches here. Host revalidation is a property\n // of the Convex write chokepoint (`convex/triggers.ts`), the one emitter — it\n // catches every writer, keeps the signing secret server-side, and the playground's\n // in-browser adapter is read by no host, so there is nothing to emit there.\n // EC-403: session-expiry detection rides the SAME lifecycle bus — every failed\n // command/query/changeset operation is checked against the session-expired\n // predicate here, so expiry raises the ONE app-level blocking re-auth state no\n // matter which surface issued the operation (per-surface banners can't cover\n // surfaces that render no banner).\n attachSessionExpiryReporting(operations.events)\n\n /** Report a capability failure on the bus (EC-403 sees an expiry like any failed operation). */\n const reportFailure = (operation: string, input: unknown, error: unknown) => {\n operations.events.emit({\n operation,\n domain: 'documents',\n kind: 'query',\n status: 'failed',\n inputSummary: summarizeInput(input),\n error: toOperationError(error),\n })\n }\n return {\n operations,\n changesets,\n adapter,\n references,\n subscribeDocument: (projectId, ref, listener) =>\n adapter.cms.subscribeDocument(projectId, ref, {\n onSnapshot: (snapshot) => listener.onSnapshot(snapshot),\n onError: (error) => {\n reportFailure('documents.subscribe', { projectId, ref }, error)\n listener.onError?.(error)\n },\n }),\n reportPresence: async (projectId, ref, presence) => {\n try {\n await adapter.cms.reportPresence(projectId, ref, presence)\n } catch (error) {\n reportFailure('documents.presence', { projectId, ref }, error)\n throw error\n }\n },\n clearPresence: (projectId, ref, sessionId) => adapter.cms.clearPresence(projectId, ref, sessionId),\n }\n}\n\n// One adapter + one operations bundle per backend (EC-179): keyed by the active\n// deployment so switching to a project on another Convex deployment selects (or\n// lazily builds) that backend's client, rather than reusing the first one.\nconst adaptersByKey = new Map<string, Promise<PersistenceAdapter>>()\nconst instancesByKey = new Map<string, Promise<StudioOperations>>()\nlet currentActor: Principal | null = null\n\n/**\n * The active backend key, tolerant of an unconfigured env: the real app never\n * reaches `getStudioOperations` while misconfigured (the `DeploymentSetupGate` renders a\n * boot error first), but tests inject a bundle directly via the seam below — so\n * a missing backend resolves to one stable key rather than throwing.\n */\nfunction deploymentKey(): string {\n try {\n return activeDeploymentKey()\n } catch {\n return '__unconfigured__'\n }\n}\n\nfunction getAdapter(key: string): Promise<PersistenceAdapter> {\n let adapter = adaptersByKey.get(key)\n if (!adapter) {\n adapter = selectPersistenceAdapter()\n adaptersByKey.set(key, adapter)\n }\n return adapter\n}\n\n/**\n * The shared studio operations client for the active backend (lazily\n * bootstrapped, cached per deployment). In-memory mode this owns the one seeded\n * adapter the whole session mutates; against Convex it owns one client per\n * deployment — the active project (EC-179) selects which.\n */\nexport function getStudioOperations(): Promise<StudioOperations> {\n const key = deploymentKey()\n let instance = instancesByKey.get(key)\n if (!instance) {\n const actor = currentActor\n instance = getAdapter(key).then((adapter) =>\n createStudioOperations(adapter, actor ? { actor } : {}),\n )\n instancesByKey.set(key, instance)\n }\n return instance\n}\n\n/**\n * Bind (or clear) the session principal (EC-150). Called by the auth context\n * on sign-in/sign-out. Rebuilds the operations clients over the *same* adapters\n * — the persisted state (and, in-memory, the whole session's edits) survives;\n * only the authorization identity changes. All cached backends are rebuilt so a\n * later switch picks up the new actor too.\n */\nexport function setStudioSession(actor: Principal | null): void {\n if (currentActor?.id === actor?.id) return // same user (or both null) — keep the clients\n currentActor = actor\n instancesByKey.clear()\n}\n\n/**\n * EC-595 A2 — the signed-in principal the operations clients act as (`null` signed out):\n * what this session's presence heartbeat names (\"X is in this field\") and how a conflict\n * tells \"you, in another tab\" from a colleague. Read-only; set by {@link setStudioSession}.\n */\nexport function getStudioActor(): Principal | null {\n return currentActor\n}\n\n/** Test seam: replace (or clear) the singleton for the active backend. */\nexport function setStudioOperationsForTests(next: StudioOperations | null): void {\n if (next) {\n instancesByKey.set(deploymentKey(), Promise.resolve(next))\n return\n }\n instancesByKey.clear()\n adaptersByKey.clear()\n currentActor = null\n}\n"]}
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import type { ConvexHttpClient } from 'convex/browser';
|
|
2
|
-
import type { AssetRepository, BackendStatus, BackendValidationInput, BackendValidationResult, BlobStorageAdapter, CmsRepository, DeliveryRepository, MembersRepository, PersistenceAdapter,
|
|
2
|
+
import type { RecordStore, AssetRepository, BackendStatus, BackendValidationInput, BackendValidationResult, BlobStorageAdapter, CmsRepository, DeliveryRepository, MembersRepository, PersistenceAdapter, ProjectRole, ReferenceIndexRepository, UserRepository, InviteRepository } from '@elytracms/core/persistence';
|
|
3
3
|
import type { ReactiveDocumentClient } from './convex-client.js';
|
|
4
|
+
/**
|
|
5
|
+
* The single `members.role` column a deployment stores for the 240c-ready `roles[]`
|
|
6
|
+
* (the 240a→240c bridge). EC-605: exactly ONE role, passed through as given — the
|
|
7
|
+
* operations layer validated it against the project's policy, and `members:set`
|
|
8
|
+
* refuses one the DEPLOYED policy does not declare, visibly. This used to filter
|
|
9
|
+
* against the default policy and floor an unknown or empty set to `viewer` — a silent
|
|
10
|
+
* rewrite that turned a project's own role into a viewer. Several roles cannot be
|
|
11
|
+
* collapsed here without a policy, so they are refused rather than guessed at.
|
|
12
|
+
*/
|
|
13
|
+
export declare function singleStoredRole(roles: readonly string[]): ProjectRole;
|
|
4
14
|
/**
|
|
5
15
|
* Convex-backed {@link PersistenceAdapter} (brief §10, EC-039). Every repository
|
|
6
16
|
* is implemented against the Convex functions in `apps/builder/convex/*`, mapping
|
|
@@ -17,8 +27,11 @@ export declare class ConvexPersistenceAdapter implements PersistenceAdapter {
|
|
|
17
27
|
readonly invites: InviteRepository;
|
|
18
28
|
/** Studio users rail — role + custom fields joined to auth identity (EC-240). */
|
|
19
29
|
readonly users: UserRepository;
|
|
20
|
-
/**
|
|
21
|
-
|
|
30
|
+
/**
|
|
31
|
+
* EC-616 — the generic record store: one repository per declared kind, each over its
|
|
32
|
+
* kind's Convex module (cached per kind object, so a rail and its foot share one).
|
|
33
|
+
*/
|
|
34
|
+
readonly records: RecordStore;
|
|
22
35
|
/** Convex file storage behind the EC-133 blob boundary (EC-151). */
|
|
23
36
|
readonly blobs: BlobStorageAdapter;
|
|
24
37
|
/** Delivery-pipeline (instant-publish) health, read-only (EC-268/EC-329). */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ConvexError } from 'convex/values';
|
|
2
2
|
import { DRAFT_VARIANT, documentKey, documentSchema } from '@elytracms/core/cms-core';
|
|
3
|
-
import { PRESENCE_TTL_MS, RecordNotFoundError, PersistenceValidationError, presenceInputSchema, assetCreateDefaults, assetRecordFromRow, compareReferenceEntries,
|
|
3
|
+
import { PRESENCE_TTL_MS, RecordNotFoundError, PersistenceValidationError, presenceInputSchema, assetCreateDefaults, assetRecordFromRow, compareReferenceEntries, persistenceIssueSchema, presentWriteKeys, recordEntryFromWire, validateForPersistence, versionNotPinnableError, } from '@elytracms/core/persistence';
|
|
4
4
|
import { normalizeInviteEmail } from '@elytracms/core/persistence';
|
|
5
5
|
import { createUuidIdFactory } from '@elytracms/core/determinism';
|
|
6
6
|
import { SUBSCRIPTION_UNAUTHENTICATED_MESSAGE, queryRef, mutationRef } from './convex-client.js';
|
|
@@ -594,15 +594,26 @@ function userRecordFromDoc(doc, projectId) {
|
|
|
594
594
|
};
|
|
595
595
|
}
|
|
596
596
|
/**
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
*
|
|
597
|
+
* The single `members.role` column a deployment stores for the 240c-ready `roles[]`
|
|
598
|
+
* (the 240a→240c bridge). EC-605: exactly ONE role, passed through as given — the
|
|
599
|
+
* operations layer validated it against the project's policy, and `members:set`
|
|
600
|
+
* refuses one the DEPLOYED policy does not declare, visibly. This used to filter
|
|
601
|
+
* against the default policy and floor an unknown or empty set to `viewer` — a silent
|
|
602
|
+
* rewrite that turned a project's own role into a viewer. Several roles cannot be
|
|
603
|
+
* collapsed here without a policy, so they are refused rather than guessed at.
|
|
600
604
|
*/
|
|
601
|
-
function
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
605
|
+
export function singleStoredRole(roles) {
|
|
606
|
+
if (roles.length !== 1) {
|
|
607
|
+
throw new PersistenceValidationError('A deployment stores exactly one role per member.', [
|
|
608
|
+
{
|
|
609
|
+
code: 'invalid-role-count',
|
|
610
|
+
message: `Expected exactly one role, got ${roles.length} (${roles.join(', ') || 'none'}).`,
|
|
611
|
+
path: ['roles'],
|
|
612
|
+
domain: 'users',
|
|
613
|
+
},
|
|
614
|
+
]);
|
|
615
|
+
}
|
|
616
|
+
return roles[0];
|
|
606
617
|
}
|
|
607
618
|
/**
|
|
608
619
|
* Convex-backed {@link UserRepository} (EC-240, 240a). Reads are the admin-gated
|
|
@@ -643,7 +654,7 @@ class ConvexUserRepository {
|
|
|
643
654
|
const now = this.clock();
|
|
644
655
|
await this.client.mutation(mutationRef('members:set'), {
|
|
645
656
|
userId: input.id,
|
|
646
|
-
role:
|
|
657
|
+
role: singleStoredRole(input.roles),
|
|
647
658
|
...(input.meta !== undefined ? { meta: input.meta } : {}),
|
|
648
659
|
createdAt: now,
|
|
649
660
|
updatedAt: now,
|
|
@@ -722,56 +733,65 @@ class ConvexInviteRepository {
|
|
|
722
733
|
}
|
|
723
734
|
}
|
|
724
735
|
/**
|
|
725
|
-
* Convex-backed {@link
|
|
726
|
-
* (
|
|
727
|
-
*
|
|
728
|
-
*
|
|
729
|
-
*
|
|
736
|
+
* Convex-backed {@link RecordRepository} (EC-616) — one record kind over its own Convex
|
|
737
|
+
* module (`<kind.module>:save|get|list|remove`, built by `defineRecordFunctions(kind)`),
|
|
738
|
+
* stored in the one `records` table. The write is sent as written (EC-577): the handler
|
|
739
|
+
* resolves it against the stored record in its transaction with the rule the in-memory
|
|
740
|
+
* store uses. Reads come back in the ONE order (`orderRecords`, applied server-side), and
|
|
741
|
+
* a refused query or write arrives as the same `PersistenceValidationError` the in-memory
|
|
742
|
+
* store throws.
|
|
730
743
|
*/
|
|
731
|
-
class
|
|
744
|
+
class ConvexRecordRepository {
|
|
732
745
|
client;
|
|
746
|
+
kind;
|
|
733
747
|
clock;
|
|
734
|
-
constructor(client, clock = realClock) {
|
|
748
|
+
constructor(client, kind, clock = realClock) {
|
|
735
749
|
this.client = client;
|
|
750
|
+
this.kind = kind;
|
|
736
751
|
this.clock = clock;
|
|
737
752
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
const
|
|
743
|
-
.
|
|
744
|
-
redirectId: id,
|
|
745
|
-
...presentWriteKeys(write),
|
|
746
|
-
createdAt: now,
|
|
747
|
-
updatedAt: now,
|
|
748
|
-
})
|
|
753
|
+
ref(fn) {
|
|
754
|
+
return `${this.kind.module}:${fn}`;
|
|
755
|
+
}
|
|
756
|
+
async list(projectId, query = {}) {
|
|
757
|
+
const rows = (await this.client
|
|
758
|
+
.query(queryRef(this.ref('list')), presentWriteKeys({ ...query }))
|
|
749
759
|
.catch((error) => {
|
|
750
760
|
throw asRailWriteError(error);
|
|
751
761
|
}));
|
|
752
|
-
return
|
|
762
|
+
return rows.map((row) => recordEntryFromWire(this.kind, row, projectId));
|
|
753
763
|
}
|
|
754
|
-
async
|
|
755
|
-
const found = await this.
|
|
764
|
+
async get(projectId, id) {
|
|
765
|
+
const found = await this.find(projectId, id);
|
|
756
766
|
if (!found)
|
|
757
|
-
throw new RecordNotFoundError(
|
|
767
|
+
throw new RecordNotFoundError(this.kind.kind, `${projectId}/${id}`);
|
|
758
768
|
return found;
|
|
759
769
|
}
|
|
760
|
-
async
|
|
761
|
-
const
|
|
762
|
-
|
|
770
|
+
async find(projectId, id) {
|
|
771
|
+
const row = (await this.client.query(queryRef(this.ref('get')), {
|
|
772
|
+
[this.kind.idKey]: id,
|
|
763
773
|
}));
|
|
764
|
-
return
|
|
774
|
+
return row ? recordEntryFromWire(this.kind, row, projectId) : undefined;
|
|
765
775
|
}
|
|
766
|
-
async
|
|
767
|
-
const
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
776
|
+
async save(projectId, input) {
|
|
777
|
+
const now = this.clock();
|
|
778
|
+
const { id: givenId, ...write } = input;
|
|
779
|
+
const id = givenId ?? newId(this.kind.idPrefix);
|
|
780
|
+
const row = (await this.client
|
|
781
|
+
.mutation(mutationRef(this.ref('save')), {
|
|
782
|
+
[this.kind.idKey]: id,
|
|
783
|
+
...presentWriteKeys(write),
|
|
784
|
+
createdAt: now,
|
|
785
|
+
updatedAt: now,
|
|
786
|
+
})
|
|
787
|
+
.catch((error) => {
|
|
788
|
+
throw asRailWriteError(error);
|
|
789
|
+
}));
|
|
790
|
+
return recordEntryFromWire(this.kind, row, projectId);
|
|
771
791
|
}
|
|
772
|
-
async
|
|
773
|
-
await this.
|
|
774
|
-
await this.client.mutation(mutationRef('
|
|
792
|
+
async remove(projectId, id) {
|
|
793
|
+
await this.get(projectId, id); // not-found parity with the in-memory store
|
|
794
|
+
await this.client.mutation(mutationRef(this.ref('remove')), { [this.kind.idKey]: id });
|
|
775
795
|
}
|
|
776
796
|
}
|
|
777
797
|
/**
|
|
@@ -807,8 +827,11 @@ export class ConvexPersistenceAdapter {
|
|
|
807
827
|
invites;
|
|
808
828
|
/** Studio users rail — role + custom fields joined to auth identity (EC-240). */
|
|
809
829
|
users;
|
|
810
|
-
/**
|
|
811
|
-
|
|
830
|
+
/**
|
|
831
|
+
* EC-616 — the generic record store: one repository per declared kind, each over its
|
|
832
|
+
* kind's Convex module (cached per kind object, so a rail and its foot share one).
|
|
833
|
+
*/
|
|
834
|
+
records;
|
|
812
835
|
/** Convex file storage behind the EC-133 blob boundary (EC-151). */
|
|
813
836
|
blobs;
|
|
814
837
|
/** Delivery-pipeline (instant-publish) health, read-only (EC-268/EC-329). */
|
|
@@ -842,7 +865,15 @@ export class ConvexPersistenceAdapter {
|
|
|
842
865
|
this.members = new ConvexMembersRepository(client, projectId);
|
|
843
866
|
this.users = new ConvexUserRepository(client);
|
|
844
867
|
this.invites = new ConvexInviteRepository(client);
|
|
845
|
-
|
|
868
|
+
const recordRepositories = new Map();
|
|
869
|
+
this.records = (kind) => {
|
|
870
|
+
let repository = recordRepositories.get(kind);
|
|
871
|
+
if (!repository) {
|
|
872
|
+
repository = new ConvexRecordRepository(client, kind);
|
|
873
|
+
recordRepositories.set(kind, repository);
|
|
874
|
+
}
|
|
875
|
+
return repository;
|
|
876
|
+
};
|
|
846
877
|
this.blobs = new ConvexBlobStorage(client);
|
|
847
878
|
this.delivery = new ConvexDeliveryRepository(client);
|
|
848
879
|
}
|