@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
package/convex/auth.ts
CHANGED
|
@@ -4,8 +4,9 @@ import { actionGeneric } from 'convex/server'
|
|
|
4
4
|
import type { AnyDataModel, GenericMutationCtx } from 'convex/server'
|
|
5
5
|
import { ConvexError, v } from 'convex/values'
|
|
6
6
|
import type { GenericId } from 'convex/values'
|
|
7
|
-
import { classifyInviteSignup, normalizeInviteEmail } from '@elytracms/core/persistence'
|
|
7
|
+
import { bootstrapRole, classifyInviteSignup, normalizeInviteEmail } from '@elytracms/core/persistence'
|
|
8
8
|
import type { InviteSignupDecision } from '@elytracms/core/persistence'
|
|
9
|
+
import { deployedRolePolicy } from './roles'
|
|
9
10
|
|
|
10
11
|
/** The provider list `convexAuth` accepts, derived from its own signature. */
|
|
11
12
|
type ConvexAuthProviders = Parameters<typeof convexAuth>[0]['providers']
|
|
@@ -210,7 +211,9 @@ export async function gateSessionCreation(
|
|
|
210
211
|
case 'bootstrap-admin':
|
|
211
212
|
await ctx.db.insert('members', {
|
|
212
213
|
userId: args.userId,
|
|
213
|
-
|
|
214
|
+
// EC-605: the deployed policy's strongest role (which the policy rules require
|
|
215
|
+
// to grant `project.administer`) — `admin` under the default policy.
|
|
216
|
+
role: bootstrapRole(deployedRolePolicy()),
|
|
214
217
|
createdAt: now,
|
|
215
218
|
updatedAt: now,
|
|
216
219
|
})
|
package/convex/authority.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { checkAuthorityFloor } from '@elytracms/core/operations'
|
|
2
2
|
import {
|
|
3
3
|
buildAuthorityAuditEntry,
|
|
4
|
-
|
|
4
|
+
isKnownRole,
|
|
5
|
+
policyRoleIds,
|
|
5
6
|
roleGrantsCapability,
|
|
6
7
|
} from '@elytracms/core/persistence'
|
|
7
8
|
import type { AuthorityAuditOutcome, ProjectRole } from '@elytracms/core/persistence'
|
|
8
9
|
import type { MutationCtx } from './functions'
|
|
9
10
|
import { memberRole, type GuardCtx } from './guard'
|
|
11
|
+
import { deployedRolePolicy } from './roles'
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* The engine integrity FLOOR at the Convex trust boundary (EC-240 / AD-18 tenet
|
|
@@ -16,9 +18,10 @@ import { memberRole, type GuardCtx } from './guard'
|
|
|
16
18
|
* invariants on authority-mutating handlers (`members:set`, `members:remove` —
|
|
17
19
|
* and therefore `users.save`/`users.remove`, which delegate to them):
|
|
18
20
|
*
|
|
19
|
-
* - **(a) assign only roles you hold** —
|
|
20
|
-
*
|
|
21
|
-
*
|
|
21
|
+
* - **(a) assign only roles you hold** — never fires under the default policy (the
|
|
22
|
+
* only administrator, `admin`, is the strongest role); under a declared policy
|
|
23
|
+
* with an administering role below the strongest (EC-605) it is what stops that
|
|
24
|
+
* role granting — or inviting someone into (`invites:create`) — a rank above its own.
|
|
22
25
|
* - **(b) the last admin can't be removed or demoted** — the hole this closes:
|
|
23
26
|
* before EC-240 the handlers did a blind upsert/delete, so an admin could
|
|
24
27
|
* remove the sole admin and lock everyone out (the empty-table bootstrap in
|
|
@@ -27,28 +30,32 @@ import { memberRole, type GuardCtx } from './guard'
|
|
|
27
30
|
* The decision logic is the ONE shared `checkAuthorityFloor` primitive that the
|
|
28
31
|
* engine resolver also calls — this file only supplies the primitives it reads
|
|
29
32
|
* from `ctx.db`, so client and server can never drift. Denials throw
|
|
30
|
-
* `unauthorized: …`, matching the guard convention.
|
|
33
|
+
* `unauthorized: …`, matching the guard convention. The policy is the deployment's
|
|
34
|
+
* (EC-605, `deployedRolePolicy`) — the one `requireCapability` granted by.
|
|
31
35
|
*/
|
|
32
36
|
export async function assertAuthorityFloor(
|
|
33
37
|
ctx: GuardCtx,
|
|
34
38
|
params: {
|
|
35
39
|
/** The caller (already `requireCapability('project.administer')`-checked). */
|
|
36
40
|
readonly callerId: string
|
|
37
|
-
/**
|
|
38
|
-
|
|
41
|
+
/**
|
|
42
|
+
* The member whose role/membership this op changes; absent for an invite (EC-605 —
|
|
43
|
+
* it grants a role to someone who is not a member yet, so only (a) can apply).
|
|
44
|
+
*/
|
|
45
|
+
readonly targetUserId?: string
|
|
39
46
|
/** Roles this op grants the target (empty for a pure removal). */
|
|
40
47
|
readonly rolesGranted: readonly ProjectRole[]
|
|
41
48
|
/** True when the op removes the membership entirely. */
|
|
42
49
|
readonly removesMembership: boolean
|
|
43
50
|
},
|
|
44
51
|
): Promise<void> {
|
|
52
|
+
const policy = deployedRolePolicy()
|
|
45
53
|
const callerRole = await memberRole(ctx, params.callerId)
|
|
46
|
-
const targetRole = await memberRole(ctx, params.targetUserId)
|
|
54
|
+
const targetRole = params.targetUserId !== undefined ? await memberRole(ctx, params.targetUserId) : null
|
|
47
55
|
const members = await ctx.db.query('members').collect()
|
|
48
56
|
// "admin" generalizes to "grants project.administer" (EC-240c) — the same
|
|
49
57
|
// policy the engine resolver uses, so client and server can never drift.
|
|
50
|
-
const isAdministrator = (role: string) =>
|
|
51
|
-
roleGrantsCapability(role, 'project.administer', defaultRolePolicy)
|
|
58
|
+
const isAdministrator = (role: string) => roleGrantsCapability(role, 'project.administer', policy)
|
|
52
59
|
const adminCount = members.filter((m) => isAdministrator(m.role)).length
|
|
53
60
|
|
|
54
61
|
const decision = checkAuthorityFloor(
|
|
@@ -59,11 +66,28 @@ export async function assertAuthorityFloor(
|
|
|
59
66
|
removesMembership: params.removesMembership,
|
|
60
67
|
adminCount,
|
|
61
68
|
},
|
|
62
|
-
|
|
69
|
+
policy,
|
|
63
70
|
)
|
|
64
71
|
if (!decision.allow) throw new Error(`unauthorized: ${decision.reason}`)
|
|
65
72
|
}
|
|
66
73
|
|
|
74
|
+
/**
|
|
75
|
+
* EC-605 — refuse a role the deployed policy does not declare, at the WRITE (a member's
|
|
76
|
+
* role, an invite's role), visibly: `invalid-role: …` naming the declared set. The
|
|
77
|
+
* studio validates against the project's config first; this is the trust boundary's own
|
|
78
|
+
* check, so a hand-crafted client or a drifted deployment can never store a role nobody
|
|
79
|
+
* can enforce (an unknown role grants nothing — it would fail closed on every read,
|
|
80
|
+
* silently).
|
|
81
|
+
*/
|
|
82
|
+
export function assertDeclaredRole(role: string): void {
|
|
83
|
+
const policy = deployedRolePolicy()
|
|
84
|
+
if (!isKnownRole(role, policy)) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`invalid-role: role "${role}" is not declared in this deployment's role policy (declared: ${policyRoleIds(policy).join(', ')}). Declare it in elytra.config.ts \`roles\` and re-provision (\`elytra init\`), or pick a declared role.`,
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
67
91
|
/**
|
|
68
92
|
* Append one {@link buildAuthorityAuditEntry} row to the `authorityAudit` table
|
|
69
93
|
* (EC-240 H-tail item 6). Called at the floor chokepoint (`members:set` /
|
|
@@ -4,6 +4,7 @@ import type { ConvexHttpClient } from 'convex/browser'
|
|
|
4
4
|
import type { FunctionReference } from 'convex/server'
|
|
5
5
|
import { fieldPath, measureMaxDepth, nodePropPath } from '@elytracms/core/cms-core'
|
|
6
6
|
import {
|
|
7
|
+
CONTRACT_RECORD_KINDS,
|
|
7
8
|
PERSISTENCE_CONTRACT_SCENARIOS,
|
|
8
9
|
publishCurrentDraft,
|
|
9
10
|
replaceAtCurrentRevision,
|
|
@@ -12,6 +13,7 @@ import type { ContractContext } from '@elytracms/core/persistence'
|
|
|
12
13
|
import { ConvexPersistenceAdapter } from '../src/lib/persistence/convex-adapter'
|
|
13
14
|
import type { ReactiveDocumentClient } from '../src/lib/persistence/convex-client'
|
|
14
15
|
import schema from './schema'
|
|
16
|
+
import { defineRecordFunctions } from './record-functions'
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* EC-454 — Convex-side contract parity, restored.
|
|
@@ -40,6 +42,11 @@ import schema from './schema'
|
|
|
40
42
|
const modules = {
|
|
41
43
|
...import.meta.glob(['./**/*.ts', '!./**/*.test.ts', '!./_generated/**']),
|
|
42
44
|
'./_generated/api.ts': async () => ({}),
|
|
45
|
+
// EC-616: the contract's FIXTURE record kinds, registered as test-only modules exactly as
|
|
46
|
+
// an addon's convex module registers its kind (one module per kind, named by the kind).
|
|
47
|
+
...Object.fromEntries(
|
|
48
|
+
CONTRACT_RECORD_KINDS.map((kind) => [`./${kind.module}.ts`, async () => defineRecordFunctions(kind)]),
|
|
49
|
+
),
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
/** The config-owned slug this deployment's records are stamped with (EC-179). */
|
package/convex/delivery.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { v } from 'convex/values'
|
|
2
|
-
import { DRAFT_PERSPECTIVE, DRAFT_VARIANT, unfoldValue, variantOf } from '@elytracms/core/cms-core'
|
|
2
|
+
import { DRAFT_PERSPECTIVE, DRAFT_VARIANT, liveRedirectRecords, unfoldValue, variantOf } from '@elytracms/core/cms-core'
|
|
3
3
|
import type { Perspective } from '@elytracms/core/cms-core'
|
|
4
4
|
import { assetRecordFromRow, deliveryAssetRecord } from '@elytracms/core/persistence'
|
|
5
|
+
import { constantTimeEquals } from '@elytracms/core/revalidation'
|
|
5
6
|
import type { AssetRecord } from '@elytracms/core/persistence'
|
|
6
7
|
import studioPackage from '../package.json'
|
|
7
8
|
import { query } from './functions'
|
|
8
9
|
import type { QueryCtx } from './functions'
|
|
9
10
|
import { requireCapability } from './guard'
|
|
11
|
+
import { loadRedirectEntries } from './redirects'
|
|
10
12
|
|
|
11
13
|
// EC-207: routes/redirects are config-sourced (authored in elytra.config.ts and
|
|
12
14
|
// resolved by studio-core), NOT backend data. The delivery snapshot no longer
|
|
@@ -156,13 +158,12 @@ export async function loadSnapshot(
|
|
|
156
158
|
|
|
157
159
|
const assets = await loadDeliveryAssets(ctx, projectId)
|
|
158
160
|
|
|
159
|
-
// Editorial redirects (EC-303): only ENABLED entries serve — a disabled redirect
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
.map((row) => ({ id: row.redirectId, from: row.from, to: row.to, permanent: row.permanent }))
|
|
161
|
+
// Editorial redirects (EC-303): only ENABLED entries serve — a disabled redirect is
|
|
162
|
+
// stored but never live, in either perspective. EC-616: read through the redirect
|
|
163
|
+
// kind's one server-side reader (records + the marked legacy tolerant read) and
|
|
164
|
+
// projected by the ONE enabled-redirect reader, `liveRedirectRecords` (EC-590 / EC-627 —
|
|
165
|
+
// this was the last hand-built copy on Convex).
|
|
166
|
+
const redirects = liveRedirectRecords(await loadRedirectEntries(ctx, projectId))
|
|
166
167
|
|
|
167
168
|
return {
|
|
168
169
|
projectId,
|
|
@@ -214,7 +215,9 @@ export const draftSnapshot = query({
|
|
|
214
215
|
if (expected === undefined || expected.length === 0) {
|
|
215
216
|
throw new Error('unauthorized: draft preview is not configured on this deployment')
|
|
216
217
|
}
|
|
217
|
-
|
|
218
|
+
// EC-617: the one timing-safe comparison (`constantTimeEquals`, the MAC-compare the
|
|
219
|
+
// revalidation and ingest verifiers rest on) — never a string `!==`.
|
|
220
|
+
if (!(await constantTimeEquals(args.token, expected))) {
|
|
218
221
|
throw new Error('unauthorized: invalid draft preview token')
|
|
219
222
|
}
|
|
220
223
|
return loadSnapshot(ctx, { projectId: args.projectId }, DRAFT_PERSPECTIVE)
|
package/convex/guard.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { getAuthUserId } from '@convex-dev/auth/server'
|
|
2
|
-
import {
|
|
2
|
+
import { roleGrantsCapability } from '@elytracms/core/persistence'
|
|
3
3
|
import type { OperationCapabilityId, ProjectRole, RolePolicy } from '@elytracms/core/persistence'
|
|
4
4
|
import type { QueryCtx } from './functions'
|
|
5
|
+
import { deployedRolePolicy } from './roles'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Server-side authorization guards (EC-150). The operations chokepoint in the
|
|
@@ -18,9 +19,10 @@ import type { QueryCtx } from './functions'
|
|
|
18
19
|
* imported here rather than re-declared. Handlers gate on CAPABILITIES
|
|
19
20
|
* (`content.read` / `content.write` / `project.administer`), not literal roles
|
|
20
21
|
* (EC-240c), so a project's custom role set enforces server-side by the same
|
|
21
|
-
* policy the operations layer uses.
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* policy the operations layer uses. EC-605: the policy is the project's `roles`
|
|
23
|
+
* declaration as DEPLOYED — the copy the `elytra` CLI provisions into
|
|
24
|
+
* `ELYTRA_ROLE_POLICY` (`./roles` `deployedRolePolicy`: the default when unset, a
|
|
25
|
+
* thrown, located error when malformed — never a silent default).
|
|
24
26
|
*
|
|
25
27
|
* Failures throw plain `Error('unauthorized: …')` — Convex transports the
|
|
26
28
|
* message; the app's adapters normalize thrown backend errors onto the
|
|
@@ -78,13 +80,13 @@ export async function memberRole(ctx: GuardCtx, userId: string): Promise<Project
|
|
|
78
80
|
* This is the ONE role-enforcement primitive at the trust boundary — every
|
|
79
81
|
* handler gates on a capability rather than a literal role, so a project's
|
|
80
82
|
* custom role set is honoured by the shared policy. `policy` defaults to the
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
+
* deployment's policy (EC-605, {@link deployedRolePolicy}); the parameter stays for a
|
|
84
|
+
* caller that must judge against another (tests).
|
|
83
85
|
*/
|
|
84
86
|
export async function requireCapability(
|
|
85
87
|
ctx: GuardCtx,
|
|
86
88
|
capability: OperationCapabilityId,
|
|
87
|
-
policy: RolePolicy =
|
|
89
|
+
policy: RolePolicy = deployedRolePolicy(),
|
|
88
90
|
): Promise<string> {
|
|
89
91
|
const userId = await requireUserId(ctx)
|
|
90
92
|
const actual = await memberRole(ctx, userId)
|
|
@@ -33,9 +33,20 @@ function routeTable(http: HttpRouter): string[] {
|
|
|
33
33
|
.sort()
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
/** EC-617: the engine's ONE route — the public-ingest receiver, an exact path (the EC-481 bar: a real client, a narrow prefix). */
|
|
37
|
+
const ENGINE_ROUTES = ['POST /elytra/ingest']
|
|
38
|
+
|
|
39
|
+
describe('defineStudioHttp — the engine registers exactly one HTTP route of its own (EC-481 → EC-617)', () => {
|
|
40
|
+
it('contributes ONLY the ingest receiver beyond what the injected auth registers', () => {
|
|
41
|
+
expect(routeTable(defineStudioHttp({ auth: silentAuth }))).toEqual(ENGINE_ROUTES)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('the receiver is an EXACT path, POST only — it claims no prefix', () => {
|
|
45
|
+
const http = defineStudioHttp({ auth: silentAuth })
|
|
46
|
+
expect(http.lookup('/elytra/ingest', 'POST')).not.toBeNull()
|
|
47
|
+
expect(http.lookup('/elytra/ingest/anything', 'POST')).toBeNull()
|
|
48
|
+
expect(http.lookup('/elytra/', 'POST')).toBeNull()
|
|
49
|
+
expect(http.lookup('/elytra/ingest', 'GET')).toBeNull()
|
|
39
50
|
})
|
|
40
51
|
|
|
41
52
|
it('control: the recorder DOES see routes, so the empty result above is real', () => {
|
|
@@ -45,10 +56,9 @@ describe('defineStudioHttp — the engine registers no HTTP routes of its own (E
|
|
|
45
56
|
http.route({ pathPrefix: '/api/auth/callback/', method: 'POST', handler })
|
|
46
57
|
},
|
|
47
58
|
}
|
|
48
|
-
expect(routeTable(defineStudioHttp({ auth }))).toEqual(
|
|
49
|
-
'GET /.well-known/jwks.json',
|
|
50
|
-
|
|
51
|
-
])
|
|
59
|
+
expect(routeTable(defineStudioHttp({ auth }))).toEqual(
|
|
60
|
+
['GET /.well-known/jwks.json', 'POST /api/auth/callback/*', ...ENGINE_ROUTES].sort(),
|
|
61
|
+
)
|
|
52
62
|
})
|
|
53
63
|
|
|
54
64
|
it('passes the auth routes through untouched — nothing is added, dropped, or reordered', () => {
|
|
@@ -61,7 +71,7 @@ describe('defineStudioHttp — the engine registers no HTTP routes of its own (E
|
|
|
61
71
|
}
|
|
62
72
|
},
|
|
63
73
|
}
|
|
64
|
-
expect(routeTable(defineStudioHttp({ auth }))).toEqual([...registered].sort())
|
|
74
|
+
expect(routeTable(defineStudioHttp({ auth }))).toEqual([...registered, ...ENGINE_ROUTES].sort())
|
|
65
75
|
})
|
|
66
76
|
})
|
|
67
77
|
|
package/convex/http.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { httpRouter } from 'convex/server'
|
|
2
2
|
import type { HttpRouter } from 'convex/server'
|
|
3
|
+
import { INGEST_RECEIVER_PATH } from '@elytracms/core/ingest'
|
|
4
|
+
import { ingestReceiver } from './ingest'
|
|
5
|
+
import type { IngestHandlers } from './ingest'
|
|
3
6
|
|
|
4
7
|
/** The slice of the Convex Auth object the studio's HTTP routes need. */
|
|
5
8
|
interface AuthHttpRoutes {
|
|
@@ -8,33 +11,36 @@ interface AuthHttpRoutes {
|
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
13
|
* EC-252 — the studio's HTTP routes as a factory. Pure engine infra ships in the
|
|
11
|
-
* package; only the consumer's `auth` (provider choice)
|
|
12
|
-
* `convex/http.ts
|
|
14
|
+
* package; only the consumer's `auth` (provider choice) and — since EC-617 — its ingest
|
|
15
|
+
* handlers are injected. The consumer's `convex/http.ts`:
|
|
13
16
|
*
|
|
14
17
|
* ```ts
|
|
15
18
|
* import { auth } from './auth'
|
|
16
19
|
* export default defineStudioHttp({ auth })
|
|
20
|
+
* // with an addon that handles ingest payloads (one import, one entry):
|
|
21
|
+
* // import { formsIngest } from '@elytracms/forms/convex'
|
|
22
|
+
* // export default defineStudioHttp({ auth, ingest: { submission: formsIngest } })
|
|
17
23
|
* ```
|
|
18
24
|
*
|
|
19
|
-
* ## The engine registers
|
|
25
|
+
* ## The engine registers exactly ONE route of its own (EC-481 → EC-617)
|
|
20
26
|
*
|
|
21
|
-
* Everything on this router comes from Convex Auth's `addHttpRoutes` — the JWT
|
|
22
|
-
* discovery endpoints (`/.well-known/*`, EC-150) and, only when a consumer supplies
|
|
23
|
-
*
|
|
27
|
+
* Everything else on this router comes from Convex Auth's `addHttpRoutes` — the JWT
|
|
28
|
+
* discovery endpoints (`/.well-known/*`, EC-150) and, only when a consumer supplies an
|
|
29
|
+
* OAuth provider, `/api/auth/signin/*` + `/api/auth/callback/*`.
|
|
24
30
|
*
|
|
25
|
-
*
|
|
26
|
-
* registry-sync endpoint
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* narrower prefix than `/api/projects/`.
|
|
31
|
+
* EC-481 removed the engine's former `GET /api/projects/*` (a Bearer-authenticated
|
|
32
|
+
* registry-sync endpoint with no client left) and set the bar for any future one: "a
|
|
33
|
+
* real client and a narrower prefix". EC-617's public-ingest RECEIVER is that route:
|
|
34
|
+
* `POST /elytra/ingest` — an EXACT path, not a prefix; its client is the site's
|
|
35
|
+
* `createIngestRoute` (`@elytracms/next`), which signs every payload with the
|
|
36
|
+
* server-held secret, and `elytra doctor`'s probe. It is mounted on every deployment,
|
|
37
|
+
* with or without handlers: closed (401) until `ELYTRA_INGEST_SECRET` is set, and a
|
|
38
|
+
* payload kind with no registered handler is refused visibly (422), never dropped.
|
|
39
|
+
* `http-routes.test.ts` pins the table.
|
|
35
40
|
*/
|
|
36
|
-
export function defineStudioHttp({ auth }: { auth: AuthHttpRoutes }): HttpRouter {
|
|
41
|
+
export function defineStudioHttp({ auth, ingest }: { auth: AuthHttpRoutes; ingest?: IngestHandlers }): HttpRouter {
|
|
37
42
|
const http = httpRouter()
|
|
38
43
|
auth.addHttpRoutes(http)
|
|
44
|
+
http.route({ path: INGEST_RECEIVER_PATH, method: 'POST', handler: ingestReceiver(ingest) })
|
|
39
45
|
return http
|
|
40
46
|
}
|