@elytracms/studio 0.0.1
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/LICENSE +21 -0
- package/README.md +3 -0
- package/convex/assets.ts +107 -0
- package/convex/auth.config.ts +15 -0
- package/convex/auth.ts +50 -0
- package/convex/cliTokens.ts +63 -0
- package/convex/cms.ts +174 -0
- package/convex/content.ts +316 -0
- package/convex/delivery.ts +266 -0
- package/convex/functions.ts +35 -0
- package/convex/graphs.ts +51 -0
- package/convex/guard.ts +67 -0
- package/convex/http.ts +30 -0
- package/convex/members.ts +83 -0
- package/convex/publishing.ts +102 -0
- package/convex/references.ts +168 -0
- package/convex/schema.ts +173 -0
- package/convex/sync.ts +322 -0
- package/convex/tsconfig.json +12 -0
- package/convex/users.ts +23 -0
- package/convex/webhooks.ts +85 -0
- package/dist/components/assets/asset-library.d.ts +14 -0
- package/dist/components/assets/asset-library.js +191 -0
- package/dist/components/assets/asset-library.js.map +1 -0
- package/dist/components/assets/asset-metadata.d.ts +34 -0
- package/dist/components/assets/asset-metadata.js +40 -0
- package/dist/components/assets/asset-metadata.js.map +1 -0
- package/dist/components/assets/asset-search.d.ts +7 -0
- package/dist/components/assets/asset-search.js +12 -0
- package/dist/components/assets/asset-search.js.map +1 -0
- package/dist/components/assets/asset-selection.d.ts +17 -0
- package/dist/components/assets/asset-selection.js +33 -0
- package/dist/components/assets/asset-selection.js.map +1 -0
- package/dist/components/assets/assets-store.d.ts +51 -0
- package/dist/components/assets/assets-store.js +153 -0
- package/dist/components/assets/assets-store.js.map +1 -0
- package/dist/components/auth-context.d.ts +38 -0
- package/dist/components/auth-context.js +88 -0
- package/dist/components/auth-context.js.map +1 -0
- package/dist/components/backend-gate.d.ts +19 -0
- package/dist/components/backend-gate.js +63 -0
- package/dist/components/backend-gate.js.map +1 -0
- package/dist/components/breadcrumb-context.d.ts +39 -0
- package/dist/components/breadcrumb-context.js +38 -0
- package/dist/components/breadcrumb-context.js.map +1 -0
- package/dist/components/builder/blocks-model.d.ts +80 -0
- package/dist/components/builder/blocks-model.js +181 -0
- package/dist/components/builder/blocks-model.js.map +1 -0
- package/dist/components/builder/blocks-panel.d.ts +44 -0
- package/dist/components/builder/blocks-panel.js +147 -0
- package/dist/components/builder/blocks-panel.js.map +1 -0
- package/dist/components/builder/builder-store.d.ts +69 -0
- package/dist/components/builder/builder-store.js +294 -0
- package/dist/components/builder/builder-store.js.map +1 -0
- package/dist/components/builder/canvas-stylesheet.d.ts +4 -0
- package/dist/components/builder/canvas-stylesheet.js +24 -0
- package/dist/components/builder/canvas-stylesheet.js.map +1 -0
- package/dist/components/builder/canvas.d.ts +8 -0
- package/dist/components/builder/canvas.js +723 -0
- package/dist/components/builder/canvas.js.map +1 -0
- package/dist/components/builder/clipboard-actions.d.ts +93 -0
- package/dist/components/builder/clipboard-actions.js +246 -0
- package/dist/components/builder/clipboard-actions.js.map +1 -0
- package/dist/components/builder/composition-field-editor.d.ts +11 -0
- package/dist/components/builder/composition-field-editor.js +150 -0
- package/dist/components/builder/composition-field-editor.js.map +1 -0
- package/dist/components/builder/composition-scope.d.ts +16 -0
- package/dist/components/builder/composition-scope.js +7 -0
- package/dist/components/builder/composition-scope.js.map +1 -0
- package/dist/components/builder/drag-session.d.ts +41 -0
- package/dist/components/builder/drag-session.js +51 -0
- package/dist/components/builder/drag-session.js.map +1 -0
- package/dist/components/builder/fixture-thumbnail.d.ts +18 -0
- package/dist/components/builder/fixture-thumbnail.js +97 -0
- package/dist/components/builder/fixture-thumbnail.js.map +1 -0
- package/dist/components/builder/inline-edit.d.ts +142 -0
- package/dist/components/builder/inline-edit.js +136 -0
- package/dist/components/builder/inline-edit.js.map +1 -0
- package/dist/components/builder/insert-context.d.ts +14 -0
- package/dist/components/builder/insert-context.js +65 -0
- package/dist/components/builder/insert-context.js.map +1 -0
- package/dist/components/builder/insert-menu.d.ts +9 -0
- package/dist/components/builder/insert-menu.js +9 -0
- package/dist/components/builder/insert-menu.js.map +1 -0
- package/dist/components/builder/layout-inspector.d.ts +14 -0
- package/dist/components/builder/layout-inspector.js +84 -0
- package/dist/components/builder/layout-inspector.js.map +1 -0
- package/dist/components/builder/node-factory.d.ts +17 -0
- package/dist/components/builder/node-factory.js +90 -0
- package/dist/components/builder/node-factory.js.map +1 -0
- package/dist/components/builder/page-tree.d.ts +3 -0
- package/dist/components/builder/page-tree.js +207 -0
- package/dist/components/builder/page-tree.js.map +1 -0
- package/dist/components/builder/property-inspector.d.ts +1 -0
- package/dist/components/builder/property-inspector.js +233 -0
- package/dist/components/builder/property-inspector.js.map +1 -0
- package/dist/components/builder/rich-text-toolbar.d.ts +4 -0
- package/dist/components/builder/rich-text-toolbar.js +104 -0
- package/dist/components/builder/rich-text-toolbar.js.map +1 -0
- package/dist/components/builder/spacing-handles.d.ts +22 -0
- package/dist/components/builder/spacing-handles.js +213 -0
- package/dist/components/builder/spacing-handles.js.map +1 -0
- package/dist/components/cms/asset-picker.d.ts +12 -0
- package/dist/components/cms/asset-picker.js +60 -0
- package/dist/components/cms/asset-picker.js.map +1 -0
- package/dist/components/cms/bulk-actions.d.ts +60 -0
- package/dist/components/cms/bulk-actions.js +95 -0
- package/dist/components/cms/bulk-actions.js.map +1 -0
- package/dist/components/cms/bulk-bar.d.ts +23 -0
- package/dist/components/cms/bulk-bar.js +26 -0
- package/dist/components/cms/bulk-bar.js.map +1 -0
- package/dist/components/cms/cell-edit.d.ts +91 -0
- package/dist/components/cms/cell-edit.js +144 -0
- package/dist/components/cms/cell-edit.js.map +1 -0
- package/dist/components/cms/cell.d.ts +29 -0
- package/dist/components/cms/cell.js +127 -0
- package/dist/components/cms/cell.js.map +1 -0
- package/dist/components/cms/cms-index.d.ts +8 -0
- package/dist/components/cms/cms-index.js +23 -0
- package/dist/components/cms/cms-index.js.map +1 -0
- package/dist/components/cms/cms-store.d.ts +188 -0
- package/dist/components/cms/cms-store.js +702 -0
- package/dist/components/cms/cms-store.js.map +1 -0
- package/dist/components/cms/collection-table.d.ts +6 -0
- package/dist/components/cms/collection-table.js +250 -0
- package/dist/components/cms/collection-table.js.map +1 -0
- package/dist/components/cms/collections-sidebar.d.ts +9 -0
- package/dist/components/cms/collections-sidebar.js +25 -0
- package/dist/components/cms/collections-sidebar.js.map +1 -0
- package/dist/components/cms/detail-editor.d.ts +7 -0
- package/dist/components/cms/detail-editor.js +386 -0
- package/dist/components/cms/detail-editor.js.map +1 -0
- package/dist/components/cms/doc-state.d.ts +13 -0
- package/dist/components/cms/doc-state.js +18 -0
- package/dist/components/cms/doc-state.js.map +1 -0
- package/dist/components/cms/document-editor-context.d.ts +48 -0
- package/dist/components/cms/document-editor-context.js +23 -0
- package/dist/components/cms/document-editor-context.js.map +1 -0
- package/dist/components/cms/editable-cell.d.ts +22 -0
- package/dist/components/cms/editable-cell.js +176 -0
- package/dist/components/cms/editable-cell.js.map +1 -0
- package/dist/components/cms/field-control.d.ts +8 -0
- package/dist/components/cms/field-control.js +149 -0
- package/dist/components/cms/field-control.js.map +1 -0
- package/dist/components/cms/filter-builder.d.ts +12 -0
- package/dist/components/cms/filter-builder.js +84 -0
- package/dist/components/cms/filter-builder.js.map +1 -0
- package/dist/components/cms/import-export.d.ts +6 -0
- package/dist/components/cms/import-export.js +33 -0
- package/dist/components/cms/import-export.js.map +1 -0
- package/dist/components/cms/new-page-dialog.d.ts +22 -0
- package/dist/components/cms/new-page-dialog.js +42 -0
- package/dist/components/cms/new-page-dialog.js.map +1 -0
- package/dist/components/cms/reference-confirm.d.ts +25 -0
- package/dist/components/cms/reference-confirm.js +124 -0
- package/dist/components/cms/reference-confirm.js.map +1 -0
- package/dist/components/cms/relation-picker.d.ts +16 -0
- package/dist/components/cms/relation-picker.js +47 -0
- package/dist/components/cms/relation-picker.js.map +1 -0
- package/dist/components/cms/use-cell-edit.d.ts +33 -0
- package/dist/components/cms/use-cell-edit.js +125 -0
- package/dist/components/cms/use-cell-edit.js.map +1 -0
- package/dist/components/cms/versions-model.d.ts +15 -0
- package/dist/components/cms/versions-model.js +23 -0
- package/dist/components/cms/versions-model.js.map +1 -0
- package/dist/components/cms/versions-pane.d.ts +14 -0
- package/dist/components/cms/versions-pane.js +86 -0
- package/dist/components/cms/versions-pane.js.map +1 -0
- package/dist/components/cms/view-bar.d.ts +11 -0
- package/dist/components/cms/view-bar.js +80 -0
- package/dist/components/cms/view-bar.js.map +1 -0
- package/dist/components/collab/collab-context.d.ts +34 -0
- package/dist/components/collab/collab-context.js +52 -0
- package/dist/components/collab/collab-context.js.map +1 -0
- package/dist/components/collab/collab-seed.d.ts +27 -0
- package/dist/components/collab/collab-seed.js +42 -0
- package/dist/components/collab/collab-seed.js.map +1 -0
- package/dist/components/components/component-detail.d.ts +9 -0
- package/dist/components/components/component-detail.js +30 -0
- package/dist/components/components/component-detail.js.map +1 -0
- package/dist/components/components/component-status.d.ts +10 -0
- package/dist/components/components/component-status.js +30 -0
- package/dist/components/components/component-status.js.map +1 -0
- package/dist/components/components/components-index.d.ts +8 -0
- package/dist/components/components/components-index.js +42 -0
- package/dist/components/components/components-index.js.map +1 -0
- package/dist/components/components/field-inspector.d.ts +9 -0
- package/dist/components/components/field-inspector.js +38 -0
- package/dist/components/components/field-inspector.js.map +1 -0
- package/dist/components/components/fixture-runner.d.ts +10 -0
- package/dist/components/components/fixture-runner.js +24 -0
- package/dist/components/components/fixture-runner.js.map +1 -0
- package/dist/components/components/registry-browser.d.ts +4 -0
- package/dist/components/components/registry-browser.js +64 -0
- package/dist/components/components/registry-browser.js.map +1 -0
- package/dist/components/current-user-menu.d.ts +13 -0
- package/dist/components/current-user-menu.js +31 -0
- package/dist/components/current-user-menu.js.map +1 -0
- package/dist/components/dev-login-badge.d.ts +9 -0
- package/dist/components/dev-login-badge.js +18 -0
- package/dist/components/dev-login-badge.js.map +1 -0
- package/dist/components/extensions/extensions-view.d.ts +7 -0
- package/dist/components/extensions/extensions-view.js +126 -0
- package/dist/components/extensions/extensions-view.js.map +1 -0
- package/dist/components/guards.d.ts +27 -0
- package/dist/components/guards.js +46 -0
- package/dist/components/guards.js.map +1 -0
- package/dist/components/inspector/inspector-sidebar.d.ts +26 -0
- package/dist/components/inspector/inspector-sidebar.js +17 -0
- package/dist/components/inspector/inspector-sidebar.js.map +1 -0
- package/dist/components/operation-error.d.ts +17 -0
- package/dist/components/operation-error.js +54 -0
- package/dist/components/operation-error.js.map +1 -0
- package/dist/components/playground-badge.d.ts +10 -0
- package/dist/components/playground-badge.js +19 -0
- package/dist/components/playground-badge.js.map +1 -0
- package/dist/components/project-list.d.ts +15 -0
- package/dist/components/project-list.js +52 -0
- package/dist/components/project-list.js.map +1 -0
- package/dist/components/project-session.d.ts +46 -0
- package/dist/components/project-session.js +107 -0
- package/dist/components/project-session.js.map +1 -0
- package/dist/components/project-switcher.d.ts +11 -0
- package/dist/components/project-switcher.js +44 -0
- package/dist/components/project-switcher.js.map +1 -0
- package/dist/components/references/builder-deep-link.d.ts +12 -0
- package/dist/components/references/builder-deep-link.js +53 -0
- package/dist/components/references/builder-deep-link.js.map +1 -0
- package/dist/components/references/references-panel.d.ts +17 -0
- package/dist/components/references/references-panel.js +66 -0
- package/dist/components/references/references-panel.js.map +1 -0
- package/dist/components/routes/redirect-chain.d.ts +9 -0
- package/dist/components/routes/redirect-chain.js +16 -0
- package/dist/components/routes/redirect-chain.js.map +1 -0
- package/dist/components/routes/redirect-manager.d.ts +13 -0
- package/dist/components/routes/redirect-manager.js +24 -0
- package/dist/components/routes/redirect-manager.js.map +1 -0
- package/dist/components/routes/route-list.d.ts +35 -0
- package/dist/components/routes/route-list.js +69 -0
- package/dist/components/routes/route-list.js.map +1 -0
- package/dist/components/routes/routes-shell.d.ts +14 -0
- package/dist/components/routes/routes-shell.js +68 -0
- package/dist/components/routes/routes-shell.js.map +1 -0
- package/dist/components/section-nav.d.ts +17 -0
- package/dist/components/section-nav.js +33 -0
- package/dist/components/section-nav.js.map +1 -0
- package/dist/components/section-state.d.ts +24 -0
- package/dist/components/section-state.js +78 -0
- package/dist/components/section-state.js.map +1 -0
- package/dist/components/section-surface.d.ts +31 -0
- package/dist/components/section-surface.js +46 -0
- package/dist/components/section-surface.js.map +1 -0
- package/dist/components/sections.d.ts +38 -0
- package/dist/components/sections.js +72 -0
- package/dist/components/sections.js.map +1 -0
- package/dist/components/settings-form.d.ts +10 -0
- package/dist/components/settings-form.js +17 -0
- package/dist/components/settings-form.js.map +1 -0
- package/dist/components/studio-context.d.ts +22 -0
- package/dist/components/studio-context.js +48 -0
- package/dist/components/studio-context.js.map +1 -0
- package/dist/components/studio-project-screen.d.ts +19 -0
- package/dist/components/studio-project-screen.js +195 -0
- package/dist/components/studio-project-screen.js.map +1 -0
- package/dist/components/studio-shell.d.ts +18 -0
- package/dist/components/studio-shell.js +64 -0
- package/dist/components/studio-shell.js.map +1 -0
- package/dist/components/theme-context.d.ts +17 -0
- package/dist/components/theme-context.js +26 -0
- package/dist/components/theme-context.js.map +1 -0
- package/dist/components/workspace-gate.d.ts +12 -0
- package/dist/components/workspace-gate.js +27 -0
- package/dist/components/workspace-gate.js.map +1 -0
- package/dist/components/workspace-projects.d.ts +13 -0
- package/dist/components/workspace-projects.js +18 -0
- package/dist/components/workspace-projects.js.map +1 -0
- package/dist/lib/assets/delete-asset.d.ts +34 -0
- package/dist/lib/assets/delete-asset.js +34 -0
- package/dist/lib/assets/delete-asset.js.map +1 -0
- package/dist/lib/assets/image-dimensions.d.ts +18 -0
- package/dist/lib/assets/image-dimensions.js +26 -0
- package/dist/lib/assets/image-dimensions.js.map +1 -0
- package/dist/lib/assets/index.d.ts +9 -0
- package/dist/lib/assets/index.js +10 -0
- package/dist/lib/assets/index.js.map +1 -0
- package/dist/lib/assets/upload-state.d.ts +53 -0
- package/dist/lib/assets/upload-state.js +59 -0
- package/dist/lib/assets/upload-state.js.map +1 -0
- package/dist/lib/assets/upload.d.ts +56 -0
- package/dist/lib/assets/upload.js +125 -0
- package/dist/lib/assets/upload.js.map +1 -0
- package/dist/lib/auth/auth-adapter.d.ts +57 -0
- package/dist/lib/auth/auth-adapter.js +13 -0
- package/dist/lib/auth/auth-adapter.js.map +1 -0
- package/dist/lib/auth/convex-auth-adapter.d.ts +39 -0
- package/dist/lib/auth/convex-auth-adapter.js +171 -0
- package/dist/lib/auth/convex-auth-adapter.js.map +1 -0
- package/dist/lib/auth/dev-auto-login-adapter.d.ts +25 -0
- package/dist/lib/auth/dev-auto-login-adapter.js +40 -0
- package/dist/lib/auth/dev-auto-login-adapter.js.map +1 -0
- package/dist/lib/auth/index.d.ts +28 -0
- package/dist/lib/auth/index.js +61 -0
- package/dist/lib/auth/index.js.map +1 -0
- package/dist/lib/auth/local-auth-adapter.d.ts +57 -0
- package/dist/lib/auth/local-auth-adapter.js +78 -0
- package/dist/lib/auth/local-auth-adapter.js.map +1 -0
- package/dist/lib/auth/route-guard.d.ts +33 -0
- package/dist/lib/auth/route-guard.js +51 -0
- package/dist/lib/auth/route-guard.js.map +1 -0
- package/dist/lib/auth-errors.d.ts +42 -0
- package/dist/lib/auth-errors.js +74 -0
- package/dist/lib/auth-errors.js.map +1 -0
- package/dist/lib/bindings/section-bindings.d.ts +24 -0
- package/dist/lib/bindings/section-bindings.js +9 -0
- package/dist/lib/bindings/section-bindings.js.map +1 -0
- package/dist/lib/clipboard/index.d.ts +7 -0
- package/dist/lib/clipboard/index.js +8 -0
- package/dist/lib/clipboard/index.js.map +1 -0
- package/dist/lib/clipboard/os-clipboard.d.ts +46 -0
- package/dist/lib/clipboard/os-clipboard.js +105 -0
- package/dist/lib/clipboard/os-clipboard.js.map +1 -0
- package/dist/lib/clipboard/paste-issues.d.ts +34 -0
- package/dist/lib/clipboard/paste-issues.js +67 -0
- package/dist/lib/clipboard/paste-issues.js.map +1 -0
- package/dist/lib/clipboard/paste-target.d.ts +20 -0
- package/dist/lib/clipboard/paste-target.js +28 -0
- package/dist/lib/clipboard/paste-target.js.map +1 -0
- package/dist/lib/cms/published-documents.d.ts +30 -0
- package/dist/lib/cms/published-documents.js +75 -0
- package/dist/lib/cms/published-documents.js.map +1 -0
- package/dist/lib/cms-views/index.d.ts +9 -0
- package/dist/lib/cms-views/index.js +10 -0
- package/dist/lib/cms-views/index.js.map +1 -0
- package/dist/lib/cms-views/selection.d.ts +29 -0
- package/dist/lib/cms-views/selection.js +46 -0
- package/dist/lib/cms-views/selection.js.map +1 -0
- package/dist/lib/cms-views/view-model.d.ts +192 -0
- package/dist/lib/cms-views/view-model.js +353 -0
- package/dist/lib/cms-views/view-model.js.map +1 -0
- package/dist/lib/cms-views/views-store.d.ts +70 -0
- package/dist/lib/cms-views/views-store.js +129 -0
- package/dist/lib/cms-views/views-store.js.map +1 -0
- package/dist/lib/data/collection-content.d.ts +105 -0
- package/dist/lib/data/collection-content.js +196 -0
- package/dist/lib/data/collection-content.js.map +1 -0
- package/dist/lib/data/data-store.d.ts +97 -0
- package/dist/lib/data/data-store.js +205 -0
- package/dist/lib/data/data-store.js.map +1 -0
- package/dist/lib/data/index.d.ts +4 -0
- package/dist/lib/data/index.js +8 -0
- package/dist/lib/data/index.js.map +1 -0
- package/dist/lib/data/providers.d.ts +143 -0
- package/dist/lib/data/providers.js +255 -0
- package/dist/lib/data/providers.js.map +1 -0
- package/dist/lib/data/sample-capture.d.ts +30 -0
- package/dist/lib/data/sample-capture.js +70 -0
- package/dist/lib/data/sample-capture.js.map +1 -0
- package/dist/lib/data/sources-store.d.ts +42 -0
- package/dist/lib/data/sources-store.js +120 -0
- package/dist/lib/data/sources-store.js.map +1 -0
- package/dist/lib/dnd/drop-rules.d.ts +219 -0
- package/dist/lib/dnd/drop-rules.js +257 -0
- package/dist/lib/dnd/drop-rules.js.map +1 -0
- package/dist/lib/dnd/gap-drag.d.ts +66 -0
- package/dist/lib/dnd/gap-drag.js +95 -0
- package/dist/lib/dnd/gap-drag.js.map +1 -0
- package/dist/lib/locale/binding-fallback.d.ts +28 -0
- package/dist/lib/locale/binding-fallback.js +66 -0
- package/dist/lib/locale/binding-fallback.js.map +1 -0
- package/dist/lib/locale/fallback.d.ts +43 -0
- package/dist/lib/locale/fallback.js +52 -0
- package/dist/lib/locale/fallback.js.map +1 -0
- package/dist/lib/locale/index.d.ts +6 -0
- package/dist/lib/locale/index.js +7 -0
- package/dist/lib/locale/index.js.map +1 -0
- package/dist/lib/locale/locale-store.d.ts +40 -0
- package/dist/lib/locale/locale-store.js +61 -0
- package/dist/lib/locale/locale-store.js.map +1 -0
- package/dist/lib/locale/locale-switcher.d.ts +10 -0
- package/dist/lib/locale/locale-switcher.js +24 -0
- package/dist/lib/locale/locale-switcher.js.map +1 -0
- package/dist/lib/locale/route-variant.d.ts +14 -0
- package/dist/lib/locale/route-variant.js +11 -0
- package/dist/lib/locale/route-variant.js.map +1 -0
- package/dist/lib/locale/use-studio-locale.d.ts +10 -0
- package/dist/lib/locale/use-studio-locale.js +8 -0
- package/dist/lib/locale/use-studio-locale.js.map +1 -0
- package/dist/lib/operations/asset-repository.d.ts +15 -0
- package/dist/lib/operations/asset-repository.js +56 -0
- package/dist/lib/operations/asset-repository.js.map +1 -0
- package/dist/lib/operations/cms-persistence.d.ts +35 -0
- package/dist/lib/operations/cms-persistence.js +75 -0
- package/dist/lib/operations/cms-persistence.js.map +1 -0
- package/dist/lib/operations/graph-commands.d.ts +140 -0
- package/dist/lib/operations/graph-commands.js +86 -0
- package/dist/lib/operations/graph-commands.js.map +1 -0
- package/dist/lib/operations/graph-persistence.d.ts +27 -0
- package/dist/lib/operations/graph-persistence.js +35 -0
- package/dist/lib/operations/graph-persistence.js.map +1 -0
- package/dist/lib/operations/index.d.ts +13 -0
- package/dist/lib/operations/index.js +14 -0
- package/dist/lib/operations/index.js.map +1 -0
- package/dist/lib/operations/operations-package.d.ts +6 -0
- package/dist/lib/operations/operations-package.js +7 -0
- package/dist/lib/operations/operations-package.js.map +1 -0
- package/dist/lib/operations/persist-queue.d.ts +16 -0
- package/dist/lib/operations/persist-queue.js +18 -0
- package/dist/lib/operations/persist-queue.js.map +1 -0
- package/dist/lib/operations/session-hydration.d.ts +84 -0
- package/dist/lib/operations/session-hydration.js +112 -0
- package/dist/lib/operations/session-hydration.js.map +1 -0
- package/dist/lib/operations/studio-operations.d.ts +59 -0
- package/dist/lib/operations/studio-operations.js +92 -0
- package/dist/lib/operations/studio-operations.js.map +1 -0
- package/dist/lib/persistence/backend-mode.d.ts +69 -0
- package/dist/lib/persistence/backend-mode.js +120 -0
- package/dist/lib/persistence/backend-mode.js.map +1 -0
- package/dist/lib/persistence/convex-adapter.d.ts +34 -0
- package/dist/lib/persistence/convex-adapter.js +681 -0
- package/dist/lib/persistence/convex-adapter.js.map +1 -0
- package/dist/lib/persistence/convex-blob-storage.d.ts +57 -0
- package/dist/lib/persistence/convex-blob-storage.js +97 -0
- package/dist/lib/persistence/convex-blob-storage.js.map +1 -0
- package/dist/lib/persistence/convex-client.d.ts +19 -0
- package/dist/lib/persistence/convex-client.js +40 -0
- package/dist/lib/persistence/convex-client.js.map +1 -0
- package/dist/lib/persistence/index.d.ts +30 -0
- package/dist/lib/persistence/index.js +60 -0
- package/dist/lib/persistence/index.js.map +1 -0
- package/dist/lib/persona/labels.d.ts +23 -0
- package/dist/lib/persona/labels.js +122 -0
- package/dist/lib/persona/labels.js.map +1 -0
- package/dist/lib/rail.d.ts +11 -0
- package/dist/lib/rail.js +26 -0
- package/dist/lib/rail.js.map +1 -0
- package/dist/lib/references/impact.d.ts +64 -0
- package/dist/lib/references/impact.js +62 -0
- package/dist/lib/references/impact.js.map +1 -0
- package/dist/lib/references/index.d.ts +7 -0
- package/dist/lib/references/index.js +8 -0
- package/dist/lib/references/index.js.map +1 -0
- package/dist/lib/references/reference-model.d.ts +125 -0
- package/dist/lib/references/reference-model.js +242 -0
- package/dist/lib/references/reference-model.js.map +1 -0
- package/dist/lib/references/references-store.d.ts +65 -0
- package/dist/lib/references/references-store.js +221 -0
- package/dist/lib/references/references-store.js.map +1 -0
- package/dist/lib/registry/index.d.ts +65 -0
- package/dist/lib/registry/index.js +114 -0
- package/dist/lib/registry/index.js.map +1 -0
- package/dist/lib/registry/preview.d.ts +15 -0
- package/dist/lib/registry/preview.js +110 -0
- package/dist/lib/registry/preview.js.map +1 -0
- package/dist/lib/registry/project-components.d.ts +16 -0
- package/dist/lib/registry/project-components.js +152 -0
- package/dist/lib/registry/project-components.js.map +1 -0
- package/dist/lib/routing/index.d.ts +10 -0
- package/dist/lib/routing/index.js +11 -0
- package/dist/lib/routing/index.js.map +1 -0
- package/dist/lib/routing/routing-analysis.d.ts +26 -0
- package/dist/lib/routing/routing-analysis.js +37 -0
- package/dist/lib/routing/routing-analysis.js.map +1 -0
- package/dist/lib/routing/routing-store.d.ts +50 -0
- package/dist/lib/routing/routing-store.js +72 -0
- package/dist/lib/routing/routing-store.js.map +1 -0
- package/dist/lib/starters/kit-session.d.ts +5 -0
- package/dist/lib/starters/kit-session.js +17 -0
- package/dist/lib/starters/kit-session.js.map +1 -0
- package/dist/lib/theme.d.ts +21 -0
- package/dist/lib/theme.js +32 -0
- package/dist/lib/theme.js.map +1 -0
- package/dist/lib/utils.d.ts +11 -0
- package/dist/lib/utils.js +28 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/lib/webhooks/config.d.ts +15 -0
- package/dist/lib/webhooks/config.js +64 -0
- package/dist/lib/webhooks/config.js.map +1 -0
- package/dist/lib/webhooks/dispatcher.d.ts +67 -0
- package/dist/lib/webhooks/dispatcher.js +61 -0
- package/dist/lib/webhooks/dispatcher.js.map +1 -0
- package/dist/lib/webhooks/index.d.ts +11 -0
- package/dist/lib/webhooks/index.js +10 -0
- package/dist/lib/webhooks/index.js.map +1 -0
- package/dist/lib/webhooks/signing.d.ts +15 -0
- package/dist/lib/webhooks/signing.js +24 -0
- package/dist/lib/webhooks/signing.js.map +1 -0
- package/dist/lib/webhooks/tags.d.ts +25 -0
- package/dist/lib/webhooks/tags.js +173 -0
- package/dist/lib/webhooks/tags.js.map +1 -0
- package/dist/lib/working-graph.d.ts +82 -0
- package/dist/lib/working-graph.js +113 -0
- package/dist/lib/working-graph.js.map +1 -0
- package/dist/lib/workspace/index.d.ts +109 -0
- package/dist/lib/workspace/index.js +206 -0
- package/dist/lib/workspace/index.js.map +1 -0
- package/dist/lib/workspace/switcher-source.d.ts +39 -0
- package/dist/lib/workspace/switcher-source.js +23 -0
- package/dist/lib/workspace/switcher-source.js.map +1 -0
- package/dist/lib/workspace/workspace-source.d.ts +47 -0
- package/dist/lib/workspace/workspace-source.js +67 -0
- package/dist/lib/workspace/workspace-source.js.map +1 -0
- package/dist/router/index.d.ts +32 -0
- package/dist/router/index.js +32 -0
- package/dist/router/index.js.map +1 -0
- package/dist/router/match.d.ts +75 -0
- package/dist/router/match.js +87 -0
- package/dist/router/match.js.map +1 -0
- package/dist/router/search.d.ts +22 -0
- package/dist/router/search.js +16 -0
- package/dist/router/search.js.map +1 -0
- package/dist/ui/avatar.d.ts +17 -0
- package/dist/ui/avatar.js +38 -0
- package/dist/ui/avatar.js.map +1 -0
- package/dist/ui/badge.d.ts +9 -0
- package/dist/ui/badge.js +23 -0
- package/dist/ui/badge.js.map +1 -0
- package/dist/ui/button.d.ts +11 -0
- package/dist/ui/button.js +30 -0
- package/dist/ui/button.js.map +1 -0
- package/dist/ui/card.d.ts +11 -0
- package/dist/ui/card.js +23 -0
- package/dist/ui/card.js.map +1 -0
- package/dist/ui/dialog.d.ts +12 -0
- package/dist/ui/dialog.js +27 -0
- package/dist/ui/dialog.js.map +1 -0
- package/dist/ui/dropdown-menu.d.ts +15 -0
- package/dist/ui/dropdown-menu.js +24 -0
- package/dist/ui/dropdown-menu.js.map +1 -0
- package/dist/ui/field.d.ts +15 -0
- package/dist/ui/field.js +14 -0
- package/dist/ui/field.js.map +1 -0
- package/dist/ui/index.d.ts +14 -0
- package/dist/ui/index.js +15 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/input.d.ts +3 -0
- package/dist/ui/input.js +7 -0
- package/dist/ui/input.js.map +1 -0
- package/dist/ui/inspector.d.ts +18 -0
- package/dist/ui/inspector.js +14 -0
- package/dist/ui/inspector.js.map +1 -0
- package/dist/ui/layout.d.ts +45 -0
- package/dist/ui/layout.js +48 -0
- package/dist/ui/layout.js.map +1 -0
- package/dist/ui/panel.d.ts +12 -0
- package/dist/ui/panel.js +20 -0
- package/dist/ui/panel.js.map +1 -0
- package/dist/ui/separator.d.ts +6 -0
- package/dist/ui/separator.js +7 -0
- package/dist/ui/separator.js.map +1 -0
- package/dist/ui/toolbar.d.ts +9 -0
- package/dist/ui/toolbar.js +16 -0
- package/dist/ui/toolbar.js.map +1 -0
- package/dist/ui/typography.d.ts +50 -0
- package/dist/ui/typography.js +43 -0
- package/dist/ui/typography.js.map +1 -0
- package/package.json +149 -0
- package/src/styles/studio.css +101 -0
- package/src/styles/tokens.css +63 -0
package/convex/guard.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { getAuthUserId } from '@convex-dev/auth/server'
|
|
2
|
+
import type { QueryCtx } from './functions'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Server-side authorization guards (EC-150). The operations chokepoint in the
|
|
6
|
+
* app enforces the role model for UX, but Convex functions are the actual
|
|
7
|
+
* trust boundary — every mutation and read re-checks here, so a hand-crafted
|
|
8
|
+
* client cannot bypass the rules.
|
|
9
|
+
*
|
|
10
|
+
* Single-tenant (EC-179): a deployment IS one project, so roles are
|
|
11
|
+
* deployment-scoped — a `members` row grants a user a role on this deployment.
|
|
12
|
+
* No `projectId` is threaded.
|
|
13
|
+
*
|
|
14
|
+
* Role ranks mirror `@elytracms/persistence`'s `roleAtLeast`:
|
|
15
|
+
* viewer (0) < editor (1) < admin (2).
|
|
16
|
+
*
|
|
17
|
+
* Failures throw plain `Error('unauthorized: …')` — Convex transports the
|
|
18
|
+
* message; the app's adapters normalize thrown backend errors onto the
|
|
19
|
+
* persistence error taxonomy, and the operations pipeline turns them into
|
|
20
|
+
* structured `OperationError`s (never crashes).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
export type ProjectRole = 'admin' | 'editor' | 'viewer'
|
|
24
|
+
|
|
25
|
+
const ROLE_RANK: Record<ProjectRole, number> = { viewer: 0, editor: 1, admin: 2 }
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The ctx slice the guards need: identity + a readable `members` table.
|
|
29
|
+
* Both `QueryCtx` and `MutationCtx` satisfy it (a writer is a reader).
|
|
30
|
+
*/
|
|
31
|
+
export type GuardCtx = Pick<QueryCtx, 'auth' | 'db'>
|
|
32
|
+
|
|
33
|
+
/** Resolve the authenticated user id or reject. */
|
|
34
|
+
export async function requireUserId(ctx: GuardCtx): Promise<string> {
|
|
35
|
+
const userId = await getAuthUserId(ctx)
|
|
36
|
+
if (userId === null) {
|
|
37
|
+
throw new Error('unauthorized: this function requires an authenticated user')
|
|
38
|
+
}
|
|
39
|
+
return userId
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Look up the caller's role on this deployment; `null` when not a member. */
|
|
43
|
+
export async function memberRole(ctx: GuardCtx, userId: string): Promise<ProjectRole | null> {
|
|
44
|
+
const membership = await ctx.db
|
|
45
|
+
.query('members')
|
|
46
|
+
.withIndex('by_user', (q) => q.eq('userId', userId))
|
|
47
|
+
.first()
|
|
48
|
+
return membership?.role ?? null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Require an authenticated caller with at least `role` on this deployment.
|
|
53
|
+
* Returns the user id so handlers can attribute writes.
|
|
54
|
+
*/
|
|
55
|
+
export async function requireRole(ctx: GuardCtx, role: ProjectRole): Promise<string> {
|
|
56
|
+
const userId = await requireUserId(ctx)
|
|
57
|
+
const actual = await memberRole(ctx, userId)
|
|
58
|
+
if (actual === null) {
|
|
59
|
+
throw new Error('unauthorized: not a member of this deployment')
|
|
60
|
+
}
|
|
61
|
+
if (ROLE_RANK[actual] < ROLE_RANK[role]) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`unauthorized: this deployment requires the "${role}" role (caller is "${actual}")`,
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
return userId
|
|
67
|
+
}
|
package/convex/http.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { httpRouter } from 'convex/server'
|
|
2
|
+
import type { HttpRouter } from 'convex/server'
|
|
3
|
+
import { registrySync } from './sync'
|
|
4
|
+
|
|
5
|
+
/** The slice of the Convex Auth object the studio's HTTP routes need. */
|
|
6
|
+
interface AuthHttpRoutes {
|
|
7
|
+
addHttpRoutes(http: HttpRouter): void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* EC-252 — the studio's HTTP routes as a factory. `http`/`sync` are pure engine
|
|
12
|
+
* infra, so they ship in the package; only the consumer's `auth` (provider choice)
|
|
13
|
+
* is injected. The consumer's `convex/http.ts` is a two-liner:
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { auth } from './auth'
|
|
17
|
+
* export default defineStudioHttp({ auth })
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* Registers Convex Auth's JWT discovery endpoints (`/.well-known/*`, EC-150) plus the
|
|
21
|
+
* EC-157 Bearer-token registry-sync endpoint. Convex routes by exact path or prefix
|
|
22
|
+
* only, so the dynamic `/api/projects/:projectId/registry-sync/snapshot` segment is
|
|
23
|
+
* parsed inside the action (`sync.ts`).
|
|
24
|
+
*/
|
|
25
|
+
export function defineStudioHttp({ auth }: { auth: AuthHttpRoutes }): HttpRouter {
|
|
26
|
+
const http = httpRouter()
|
|
27
|
+
auth.addHttpRoutes(http)
|
|
28
|
+
http.route({ pathPrefix: '/api/projects/', method: 'GET', handler: registrySync })
|
|
29
|
+
return http
|
|
30
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { v } from 'convex/values'
|
|
2
|
+
import { mutation, query } from './functions'
|
|
3
|
+
import { requireRole, requireUserId } from './guard'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Deployment membership persistence (EC-150, single-tenant per EC-179). The
|
|
7
|
+
* Convex side of the `MembersRepository` contract; the app's
|
|
8
|
+
* `ConvexMembersRepository` maps these onto `@elytracms/persistence` records.
|
|
9
|
+
* Member *management* is admin-only, server-side — UI gating alone is never
|
|
10
|
+
* trusted. One row per user grants their role on this deployment's one project.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const roleValidator = v.union(v.literal('admin'), v.literal('editor'), v.literal('viewer'))
|
|
14
|
+
|
|
15
|
+
export const list = query({
|
|
16
|
+
args: {},
|
|
17
|
+
handler: async (ctx) => {
|
|
18
|
+
await requireRole(ctx, 'viewer')
|
|
19
|
+
const rows = await ctx.db.query('members').collect()
|
|
20
|
+
return rows.sort((a, b) => (a.userId < b.userId ? -1 : a.userId > b.userId ? 1 : 0))
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
export const get = query({
|
|
25
|
+
args: { userId: v.string() },
|
|
26
|
+
handler: async (ctx, { userId }) => {
|
|
27
|
+
// Members may look up memberships of this deployment; everyone may look up
|
|
28
|
+
// their *own* membership (the studio needs it to render the role badge).
|
|
29
|
+
const caller = await requireUserId(ctx)
|
|
30
|
+
if (caller !== userId) await requireRole(ctx, 'viewer')
|
|
31
|
+
return ctx.db
|
|
32
|
+
.query('members')
|
|
33
|
+
.withIndex('by_user', (q) => q.eq('userId', userId))
|
|
34
|
+
.first()
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
/** The authenticated caller's membership on this deployment (studio bootstrap). */
|
|
39
|
+
export const mine = query({
|
|
40
|
+
args: {},
|
|
41
|
+
handler: async (ctx) => {
|
|
42
|
+
const userId = await requireUserId(ctx)
|
|
43
|
+
return ctx.db
|
|
44
|
+
.query('members')
|
|
45
|
+
.withIndex('by_user', (q) => q.eq('userId', userId))
|
|
46
|
+
.first()
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
export const set = mutation({
|
|
51
|
+
args: {
|
|
52
|
+
userId: v.string(),
|
|
53
|
+
role: roleValidator,
|
|
54
|
+
createdAt: v.string(),
|
|
55
|
+
updatedAt: v.string(),
|
|
56
|
+
},
|
|
57
|
+
handler: async (ctx, args) => {
|
|
58
|
+
await requireRole(ctx, 'admin')
|
|
59
|
+
const existing = await ctx.db
|
|
60
|
+
.query('members')
|
|
61
|
+
.withIndex('by_user', (q) => q.eq('userId', args.userId))
|
|
62
|
+
.first()
|
|
63
|
+
if (existing) {
|
|
64
|
+
await ctx.db.patch(existing._id, { role: args.role, updatedAt: args.updatedAt })
|
|
65
|
+
return ctx.db.get(existing._id)
|
|
66
|
+
}
|
|
67
|
+
const id = await ctx.db.insert('members', args)
|
|
68
|
+
return ctx.db.get(id)
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
export const remove = mutation({
|
|
73
|
+
args: { userId: v.string() },
|
|
74
|
+
handler: async (ctx, { userId }) => {
|
|
75
|
+
await requireRole(ctx, 'admin')
|
|
76
|
+
const existing = await ctx.db
|
|
77
|
+
.query('members')
|
|
78
|
+
.withIndex('by_user', (q) => q.eq('userId', userId))
|
|
79
|
+
.first()
|
|
80
|
+
if (!existing) throw new Error(`not-found: member "${userId}"`)
|
|
81
|
+
await ctx.db.delete(existing._id)
|
|
82
|
+
},
|
|
83
|
+
})
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { makeFunctionReference } from 'convex/server'
|
|
2
|
+
import { v } from 'convex/values'
|
|
3
|
+
import { mutation, query } from './functions'
|
|
4
|
+
import type { MutationCtx } from './functions'
|
|
5
|
+
import { requireRole } from './guard'
|
|
6
|
+
|
|
7
|
+
// EC-223: the studio's convex functions are package-shipped and deployment-agnostic
|
|
8
|
+
// — they address each other by STRING ref (resolved against the consumer's deployment),
|
|
9
|
+
// never the per-deployment `_generated/api`. Mirrors the lib's `queryRef`/`actionRef`.
|
|
10
|
+
const webhooksDeliver = makeFunctionReference<'action'>('webhooks:deliver')
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Publishing state (EC-045). Single-environment (EC-179, vision AD-8): a
|
|
14
|
+
* deployment IS one environment, so there is exactly ONE publishingState row —
|
|
15
|
+
* see `env-is-deployment-boundary`.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* EC-180 / EC-146 — fire the host revalidation webhook on a publish-state change.
|
|
20
|
+
*
|
|
21
|
+
* A mutation cannot `fetch`, so we hand off to the `webhooks.deliver` internal
|
|
22
|
+
* action (it signs + POSTs server-side; the secret never leaves Convex). A graph
|
|
23
|
+
* publish/unpublish can affect any page, so the honest v1 invalidation is the
|
|
24
|
+
* whole-project sweep tag `p:<scope>:project` — a mirror of
|
|
25
|
+
* `projectSweepWebhookTag` (`src/lib/webhooks/tags.ts`) / `@elytracms/next`'s
|
|
26
|
+
* `projectSweepTag`; the tests on those two sides pin the literal.
|
|
27
|
+
*
|
|
28
|
+
* `scope` must equal the host's `cache.scope` (its `ELYTRA_PROJECT_ID`) for the
|
|
29
|
+
* tag to match what the host stamped on its cached fetches; it comes from the
|
|
30
|
+
* `CANVAS_PROJECT_SCOPE` Convex env var. Unconfigured deployments (local dev,
|
|
31
|
+
* no env) are a silent no-op — publishing must never depend on a reachable host.
|
|
32
|
+
*/
|
|
33
|
+
async function scheduleRevalidate(
|
|
34
|
+
ctx: MutationCtx,
|
|
35
|
+
event: 'publishing.publish' | 'publishing.unpublish',
|
|
36
|
+
): Promise<void> {
|
|
37
|
+
const env =
|
|
38
|
+
(globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env ?? {}
|
|
39
|
+
const scope = env['CANVAS_PROJECT_SCOPE']
|
|
40
|
+
if (!scope) return
|
|
41
|
+
await ctx.scheduler.runAfter(0, webhooksDeliver, {
|
|
42
|
+
projectId: scope,
|
|
43
|
+
event,
|
|
44
|
+
tags: [`p:${scope}:project`],
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const publish = mutation({
|
|
49
|
+
args: {
|
|
50
|
+
revisionId: v.string(),
|
|
51
|
+
revision: v.number(),
|
|
52
|
+
publishedAt: v.string(),
|
|
53
|
+
},
|
|
54
|
+
handler: async (ctx, args) => {
|
|
55
|
+
await requireRole(ctx, 'editor') // EC-150
|
|
56
|
+
const existing = await ctx.db.query('publishingState').first()
|
|
57
|
+
const record = {
|
|
58
|
+
status: 'published' as const,
|
|
59
|
+
publishedRevisionId: args.revisionId,
|
|
60
|
+
publishedRevision: args.revision,
|
|
61
|
+
publishedAt: args.publishedAt,
|
|
62
|
+
createdAt: existing?.createdAt ?? args.publishedAt,
|
|
63
|
+
updatedAt: args.publishedAt,
|
|
64
|
+
}
|
|
65
|
+
// EC-153 parity: republish preserves `createdAt`; return the stored record.
|
|
66
|
+
const id = existing ? existing._id : await ctx.db.insert('publishingState', record)
|
|
67
|
+
if (existing) await ctx.db.patch(existing._id, record)
|
|
68
|
+
// EC-180: drop the host's cached pages for this project (fire-and-forget).
|
|
69
|
+
await scheduleRevalidate(ctx, 'publishing.publish')
|
|
70
|
+
return ctx.db.get(id)
|
|
71
|
+
},
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
export const unpublish = mutation({
|
|
75
|
+
args: { updatedAt: v.string() },
|
|
76
|
+
handler: async (ctx, args) => {
|
|
77
|
+
await requireRole(ctx, 'editor') // EC-150
|
|
78
|
+
const existing = await ctx.db.query('publishingState').first()
|
|
79
|
+
if (!existing || existing.status !== 'published') {
|
|
80
|
+
throw new Error('invalid-transition: nothing is currently published')
|
|
81
|
+
}
|
|
82
|
+
await ctx.db.patch(existing._id, {
|
|
83
|
+
status: 'draft',
|
|
84
|
+
publishedRevisionId: undefined,
|
|
85
|
+
publishedRevision: undefined,
|
|
86
|
+
publishedAt: undefined,
|
|
87
|
+
updatedAt: args.updatedAt,
|
|
88
|
+
})
|
|
89
|
+
// EC-180: a graph going dark must also drop the host's cached pages.
|
|
90
|
+
await scheduleRevalidate(ctx, 'publishing.unpublish')
|
|
91
|
+
// EC-153 parity: return the stored record (createdAt preserved).
|
|
92
|
+
return ctx.db.get(existing._id)
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
export const getState = query({
|
|
97
|
+
args: {},
|
|
98
|
+
handler: async (ctx) => {
|
|
99
|
+
await requireRole(ctx, 'viewer') // EC-150
|
|
100
|
+
return ctx.db.query('publishingState').first()
|
|
101
|
+
},
|
|
102
|
+
})
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { v } from 'convex/values'
|
|
2
|
+
import { mutation, query } from './functions'
|
|
3
|
+
import { requireRole } from './guard'
|
|
4
|
+
import type { MutationCtx } from './functions'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Reference-index persistence (EC-155, vision AD-6). Entries are derived data
|
|
8
|
+
* maintained replace-all-for-source by the operations layer; both lookup
|
|
9
|
+
* directions are index-backed (`by_source` outbound, `by_target` inbound).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const variantValidator = v.union(v.literal('draft'), v.literal('published'))
|
|
13
|
+
const sourceTypeValidator = v.union(v.literal('document'), v.literal('page'), v.literal('layout'))
|
|
14
|
+
const targetTypeValidator = v.union(
|
|
15
|
+
v.literal('document'),
|
|
16
|
+
v.literal('component'),
|
|
17
|
+
v.literal('asset'),
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
/** One outbound reference, before attribution to its source. */
|
|
21
|
+
const outboundReferenceValidator = v.object({
|
|
22
|
+
path: v.string(),
|
|
23
|
+
targetType: targetTypeValidator,
|
|
24
|
+
targetId: v.string(),
|
|
25
|
+
kind: v.string(),
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
/** An outbound reference attributed to its holding source. */
|
|
29
|
+
const entryValidator = v.object({
|
|
30
|
+
sourceType: sourceTypeValidator,
|
|
31
|
+
sourceId: v.string(),
|
|
32
|
+
path: v.string(),
|
|
33
|
+
targetType: targetTypeValidator,
|
|
34
|
+
targetId: v.string(),
|
|
35
|
+
kind: v.string(),
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
async function deleteForSource(
|
|
39
|
+
ctx: MutationCtx,
|
|
40
|
+
variant: 'draft' | 'published',
|
|
41
|
+
sourceType: 'document' | 'page' | 'layout',
|
|
42
|
+
sourceId?: string,
|
|
43
|
+
): Promise<void> {
|
|
44
|
+
const existing = await ctx.db
|
|
45
|
+
.query('referenceEntries')
|
|
46
|
+
.withIndex('by_source', (q) => {
|
|
47
|
+
const scoped = q.eq('variant', variant).eq('sourceType', sourceType)
|
|
48
|
+
return sourceId === undefined ? scoped : scoped.eq('sourceId', sourceId)
|
|
49
|
+
})
|
|
50
|
+
.collect()
|
|
51
|
+
for (const doc of existing) await ctx.db.delete(doc._id)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const replaceForSource = mutation({
|
|
55
|
+
args: {
|
|
56
|
+
variant: variantValidator,
|
|
57
|
+
sourceType: sourceTypeValidator,
|
|
58
|
+
sourceId: v.string(),
|
|
59
|
+
references: v.array(outboundReferenceValidator),
|
|
60
|
+
},
|
|
61
|
+
handler: async (ctx, args) => {
|
|
62
|
+
await requireRole(ctx, 'editor') // EC-150
|
|
63
|
+
await deleteForSource(ctx, args.variant, args.sourceType, args.sourceId)
|
|
64
|
+
for (const reference of args.references) {
|
|
65
|
+
await ctx.db.insert('referenceEntries', {
|
|
66
|
+
variant: args.variant,
|
|
67
|
+
sourceType: args.sourceType,
|
|
68
|
+
sourceId: args.sourceId,
|
|
69
|
+
...reference,
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
export const replaceForSourceTypes = mutation({
|
|
76
|
+
args: {
|
|
77
|
+
variant: variantValidator,
|
|
78
|
+
sourceTypes: v.array(sourceTypeValidator),
|
|
79
|
+
entries: v.array(entryValidator),
|
|
80
|
+
},
|
|
81
|
+
handler: async (ctx, args) => {
|
|
82
|
+
await requireRole(ctx, 'editor') // EC-150
|
|
83
|
+
for (const sourceType of args.sourceTypes) {
|
|
84
|
+
await deleteForSource(ctx, args.variant, sourceType)
|
|
85
|
+
}
|
|
86
|
+
for (const entry of args.entries) {
|
|
87
|
+
await ctx.db.insert('referenceEntries', {
|
|
88
|
+
variant: args.variant,
|
|
89
|
+
...entry,
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
export const removeForSource = mutation({
|
|
96
|
+
args: {
|
|
97
|
+
sourceType: sourceTypeValidator,
|
|
98
|
+
sourceId: v.string(),
|
|
99
|
+
variant: v.optional(variantValidator),
|
|
100
|
+
},
|
|
101
|
+
handler: async (ctx, args) => {
|
|
102
|
+
await requireRole(ctx, 'editor') // EC-150
|
|
103
|
+
const variants = args.variant ? [args.variant] : (['draft', 'published'] as const)
|
|
104
|
+
for (const variant of variants) {
|
|
105
|
+
await deleteForSource(ctx, variant, args.sourceType, args.sourceId)
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
export const clearProject = mutation({
|
|
111
|
+
args: {},
|
|
112
|
+
handler: async (ctx) => {
|
|
113
|
+
await requireRole(ctx, 'editor') // EC-150
|
|
114
|
+
const existing = await ctx.db.query('referenceEntries').collect()
|
|
115
|
+
for (const doc of existing) await ctx.db.delete(doc._id)
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
export const outbound = query({
|
|
120
|
+
args: {
|
|
121
|
+
sourceType: sourceTypeValidator,
|
|
122
|
+
sourceId: v.string(),
|
|
123
|
+
variant: v.optional(variantValidator),
|
|
124
|
+
},
|
|
125
|
+
handler: async (ctx, args) => {
|
|
126
|
+
await requireRole(ctx, 'viewer') // EC-150
|
|
127
|
+
const variants = args.variant ? [args.variant] : (['draft', 'published'] as const)
|
|
128
|
+
const out = []
|
|
129
|
+
for (const variant of variants) {
|
|
130
|
+
out.push(
|
|
131
|
+
...(await ctx.db
|
|
132
|
+
.query('referenceEntries')
|
|
133
|
+
.withIndex('by_source', (q) =>
|
|
134
|
+
q.eq('variant', variant).eq('sourceType', args.sourceType).eq('sourceId', args.sourceId),
|
|
135
|
+
)
|
|
136
|
+
.collect()),
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
return out
|
|
140
|
+
},
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
export const inbound = query({
|
|
144
|
+
args: {
|
|
145
|
+
targetType: targetTypeValidator,
|
|
146
|
+
targetId: v.string(),
|
|
147
|
+
variant: v.optional(variantValidator),
|
|
148
|
+
},
|
|
149
|
+
handler: async (ctx, args) => {
|
|
150
|
+
await requireRole(ctx, 'viewer') // EC-150
|
|
151
|
+
return ctx.db
|
|
152
|
+
.query('referenceEntries')
|
|
153
|
+
.withIndex('by_target', (q) => {
|
|
154
|
+
const scoped = q.eq('targetType', args.targetType).eq('targetId', args.targetId)
|
|
155
|
+
return args.variant ? scoped.eq('variant', args.variant) : scoped
|
|
156
|
+
})
|
|
157
|
+
.collect()
|
|
158
|
+
},
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
export const list = query({
|
|
162
|
+
args: { variant: v.optional(variantValidator) },
|
|
163
|
+
handler: async (ctx, args) => {
|
|
164
|
+
await requireRole(ctx, 'viewer') // EC-150
|
|
165
|
+
const docs = await ctx.db.query('referenceEntries').collect()
|
|
166
|
+
return args.variant ? docs.filter((d) => d.variant === args.variant) : docs
|
|
167
|
+
},
|
|
168
|
+
})
|
package/convex/schema.ts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { defineSchema, defineTable } from 'convex/server'
|
|
2
|
+
import { v } from 'convex/values'
|
|
3
|
+
import { authTables } from '@convex-dev/auth/server'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Convex schema for the Elytra hosted builder (brief §10).
|
|
7
|
+
*
|
|
8
|
+
* ## Single-tenant: one deployment = one project (EC-179, vision AD-8)
|
|
9
|
+
*
|
|
10
|
+
* Project identity lives in `elytra.config.ts` (EC-178); a Convex deployment
|
|
11
|
+
* hosts exactly ONE project, so content tables carry NO `projectId` — the
|
|
12
|
+
* deployment itself IS the scope (the Payload model). Membership is likewise
|
|
13
|
+
* deployment-scoped: a `members` row grants a user a role on this deployment.
|
|
14
|
+
* Multiple projects = multiple config entries + multiple deployments.
|
|
15
|
+
*
|
|
16
|
+
* Tables mirror the persisted record shapes defined (and Zod-validated) in
|
|
17
|
+
* `@elytracms/persistence`; nested structures (graph JSON, documents) are stored
|
|
18
|
+
* as validated blobs (`v.any()`) since the Zod schemas in the packages are the
|
|
19
|
+
* canonical source of truth for their shape.
|
|
20
|
+
*
|
|
21
|
+
* ## Backend is data-only (AD-11, C0: EC-181/182/183)
|
|
22
|
+
*
|
|
23
|
+
* Project **structure is code**, not backend rows: component manifests live in
|
|
24
|
+
* the repo registry (EC-181), project identity/settings/locales/routes and CMS
|
|
25
|
+
* **collection schemas** all live in `elytra.config.ts` (EC-182/EC-183). So
|
|
26
|
+
* there is no `projects`, `componentManifests`, or `cmsSchemas` table — the
|
|
27
|
+
* remaining tables hold **data only**: CMS documents, assets, graph revisions
|
|
28
|
+
* (the page composition is a field value — data, per AD-12), publishing state,
|
|
29
|
+
* the derived reference index, deployment membership, CLI tokens, and auth.
|
|
30
|
+
*
|
|
31
|
+
* This schema is provider configuration only — the Studio and tests talk to the
|
|
32
|
+
* provider-neutral `PersistenceAdapter`, never to Convex directly (brief §4.10).
|
|
33
|
+
*/
|
|
34
|
+
export default defineSchema({
|
|
35
|
+
/**
|
|
36
|
+
* Convex Auth tables (EC-150): `users`, `authAccounts`, `authSessions`,
|
|
37
|
+
* `authRefreshTokens`, `authVerificationCodes`, `authVerifiers`,
|
|
38
|
+
* `authRateLimits`. Required by `convexAuth` in `auth.ts`; see
|
|
39
|
+
* https://labs.convex.dev/auth/setup/schema.
|
|
40
|
+
*/
|
|
41
|
+
...authTables,
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Deployment membership roles (EC-150, vision AD-7). Single-tenant: one row
|
|
45
|
+
* per user grants their role on THIS deployment's one project. `userId` is the
|
|
46
|
+
* Convex Auth `users` id (stored as a string so the table shape matches the
|
|
47
|
+
* provider-neutral `@elytracms/persistence` record). Server-side enforcement
|
|
48
|
+
* happens in `guard.ts`; the operations chokepoint enforces the same rules
|
|
49
|
+
* client-side.
|
|
50
|
+
*/
|
|
51
|
+
members: defineTable({
|
|
52
|
+
userId: v.string(),
|
|
53
|
+
role: v.union(v.literal('admin'), v.literal('editor'), v.literal('viewer')),
|
|
54
|
+
createdAt: v.string(),
|
|
55
|
+
updatedAt: v.string(),
|
|
56
|
+
}).index('by_user', ['userId']),
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Revocable CLI sync tokens (EC-150, consumed by the EC-147 push CLI).
|
|
60
|
+
* Only the SHA-256 hash of a token is ever stored; revoked tokens keep
|
|
61
|
+
* their row (with `revokedAt`) for auditability.
|
|
62
|
+
*
|
|
63
|
+
* Single-tenant (EC-182/EC-183): a deployment hosts one project, so a token
|
|
64
|
+
* carries no per-project scope — `projectId` is gone (it was `optional`
|
|
65
|
+
* through EC-182; this drops the field now that both deployments are migrated).
|
|
66
|
+
*/
|
|
67
|
+
cliTokens: defineTable({
|
|
68
|
+
tokenId: v.string(),
|
|
69
|
+
label: v.string(),
|
|
70
|
+
tokenHash: v.string(),
|
|
71
|
+
createdAt: v.string(),
|
|
72
|
+
revokedAt: v.optional(v.string()),
|
|
73
|
+
})
|
|
74
|
+
.index('by_token', ['tokenId'])
|
|
75
|
+
.index('by_hash', ['tokenHash']),
|
|
76
|
+
|
|
77
|
+
/** Project graph revision records — append-only (EC-041). */
|
|
78
|
+
graphRevisions: defineTable({
|
|
79
|
+
revision: v.number(),
|
|
80
|
+
serializedGraph: v.string(),
|
|
81
|
+
schemaVersion: v.number(),
|
|
82
|
+
label: v.optional(v.string()),
|
|
83
|
+
message: v.optional(v.string()),
|
|
84
|
+
createdAt: v.string(),
|
|
85
|
+
}).index('by_revision', ['revision']),
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* CMS documents (EC-043). The stored `document` is the continuous working
|
|
89
|
+
* **draft**; `publishedVersion` pins which `documentVersions` row is live
|
|
90
|
+
* (the Sanity draft/published model, EC-224). Absent = never published.
|
|
91
|
+
* Localized variants ride on the document itself.
|
|
92
|
+
*/
|
|
93
|
+
cmsDocuments: defineTable({
|
|
94
|
+
collection: v.string(),
|
|
95
|
+
docId: v.string(),
|
|
96
|
+
document: v.any(),
|
|
97
|
+
publishedVersion: v.optional(v.number()),
|
|
98
|
+
createdAt: v.string(),
|
|
99
|
+
updatedAt: v.string(),
|
|
100
|
+
})
|
|
101
|
+
.index('by_collection', ['collection'])
|
|
102
|
+
.index('by_doc', ['collection', 'docId']),
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* CMS document version snapshots — append-only (EC-224), the document-scoped
|
|
106
|
+
* analogue of `graphRevisions`. One row per recorded version (created on
|
|
107
|
+
* publish or a pre-restore snapshot, never on a plain draft edit), keyed per
|
|
108
|
+
* document via `by_doc`. `cmsDocuments.publishedVersion` points at the live one.
|
|
109
|
+
*/
|
|
110
|
+
documentVersions: defineTable({
|
|
111
|
+
collection: v.string(),
|
|
112
|
+
docId: v.string(),
|
|
113
|
+
version: v.number(),
|
|
114
|
+
snapshot: v.any(),
|
|
115
|
+
label: v.optional(v.string()),
|
|
116
|
+
createdAt: v.string(),
|
|
117
|
+
}).index('by_doc', ['collection', 'docId']),
|
|
118
|
+
|
|
119
|
+
/** Asset metadata; bytes live in Convex/file storage behind the blob adapter (EC-044). */
|
|
120
|
+
assets: defineTable({
|
|
121
|
+
assetId: v.string(),
|
|
122
|
+
filename: v.string(),
|
|
123
|
+
contentType: v.string(),
|
|
124
|
+
byteSize: v.number(),
|
|
125
|
+
width: v.optional(v.number()),
|
|
126
|
+
height: v.optional(v.number()),
|
|
127
|
+
alt: v.optional(v.string()),
|
|
128
|
+
/** Editable display title (EC-152; persisted for parity with in-memory — EC-153). */
|
|
129
|
+
title: v.optional(v.string()),
|
|
130
|
+
storageKey: v.string(),
|
|
131
|
+
/** Stable serve URL resolved from Convex file storage on upload (EC-151). */
|
|
132
|
+
url: v.optional(v.string()),
|
|
133
|
+
/** Normalized image focal point `{ x, y }` in 0–1 (EC-230); CSS object-position. */
|
|
134
|
+
focalPoint: v.optional(v.object({ x: v.number(), y: v.number() })),
|
|
135
|
+
createdAt: v.string(),
|
|
136
|
+
updatedAt: v.string(),
|
|
137
|
+
}).index('by_asset', ['assetId']),
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Reference index entries (EC-155, vision AD-6) — derived data, recomputed
|
|
141
|
+
* replace-all-for-source on every write. Indexed for efficient lookup in both
|
|
142
|
+
* directions: outbound ("what does X reference") via `by_source`, inbound
|
|
143
|
+
* ("what references X") via `by_target`.
|
|
144
|
+
*/
|
|
145
|
+
referenceEntries: defineTable({
|
|
146
|
+
variant: v.union(v.literal('draft'), v.literal('published')),
|
|
147
|
+
sourceType: v.union(v.literal('document'), v.literal('page'), v.literal('layout')),
|
|
148
|
+
sourceId: v.string(),
|
|
149
|
+
/** Exact field/node path inside the source, e.g. `values/author`. */
|
|
150
|
+
path: v.string(),
|
|
151
|
+
targetType: v.union(v.literal('document'), v.literal('component'), v.literal('asset')),
|
|
152
|
+
targetId: v.string(),
|
|
153
|
+
/** Which reference-bearing structure produced the entry. */
|
|
154
|
+
kind: v.string(),
|
|
155
|
+
})
|
|
156
|
+
// `by_source` also serves source-type prefix scans (the graph recompute path).
|
|
157
|
+
.index('by_source', ['variant', 'sourceType', 'sourceId'])
|
|
158
|
+
.index('by_target', ['targetType', 'targetId', 'variant']),
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Publishing state (EC-045). Single-environment (EC-179, vision AD-8): a
|
|
162
|
+
* deployment IS one environment, so this is a single-row table — no
|
|
163
|
+
* `environment`, no index.
|
|
164
|
+
*/
|
|
165
|
+
publishingState: defineTable({
|
|
166
|
+
status: v.union(v.literal('draft'), v.literal('published')),
|
|
167
|
+
publishedRevisionId: v.optional(v.string()),
|
|
168
|
+
publishedRevision: v.optional(v.number()),
|
|
169
|
+
publishedAt: v.optional(v.string()),
|
|
170
|
+
createdAt: v.string(),
|
|
171
|
+
updatedAt: v.string(),
|
|
172
|
+
}),
|
|
173
|
+
})
|