@elytracms/studio 0.0.91 → 0.0.93
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/asset-alt-migration.test.ts +127 -0
- package/convex/asset-replace.test.ts +168 -0
- package/convex/asset-revalidate.test.ts +6 -6
- package/convex/assets.ts +41 -5
- package/convex/cms.ts +537 -177
- package/convex/contract-parity.test.ts +173 -19
- package/convex/delivery.ts +26 -13
- package/convex/document-revalidate.test.ts +17 -16
- package/convex/orphan-hydration.test.ts +116 -0
- package/convex/presence-cost.test.ts +89 -0
- package/convex/rail-write-parity.test.ts +17 -14
- package/convex/referenceMaintenance.ts +15 -15
- package/convex/schema.ts +70 -5
- package/convex/triggers.ts +21 -8
- package/convex/write-limits.ts +1 -1
- package/dist/components/ai/ai-chat-panel.js +33 -22
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/ai/proposal-card.js +2 -1
- package/dist/components/ai/proposal-card.js.map +1 -1
- package/dist/components/ai/translate-document-action.d.ts +8 -2
- package/dist/components/ai/translate-document-action.js.map +1 -1
- package/dist/components/ai/translate-document-dialog.d.ts +1 -1
- package/dist/components/ai/translate-document-dialog.js +14 -8
- package/dist/components/ai/translate-document-dialog.js.map +1 -1
- package/dist/components/ai/translate-skipped.d.ts +19 -0
- package/dist/components/ai/translate-skipped.js +46 -0
- package/dist/components/ai/translate-skipped.js.map +1 -0
- package/dist/components/app-views/default-modules.d.ts +23 -6
- package/dist/components/app-views/default-modules.js +33 -14
- package/dist/components/app-views/default-modules.js.map +1 -1
- package/dist/components/assets/asset-detail.js +18 -8
- package/dist/components/assets/asset-detail.js.map +1 -1
- package/dist/components/assets/asset-metadata.d.ts +15 -4
- package/dist/components/assets/asset-metadata.js +15 -2
- package/dist/components/assets/asset-metadata.js.map +1 -1
- package/dist/components/assets/asset-search.js +4 -1
- package/dist/components/assets/asset-search.js.map +1 -1
- package/dist/components/assets/asset-selection.d.ts +0 -8
- package/dist/components/assets/asset-selection.js +4 -23
- package/dist/components/assets/asset-selection.js.map +1 -1
- package/dist/components/assets/asset-thumb.js +3 -2
- package/dist/components/assets/asset-thumb.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +3 -3
- package/dist/components/assets/assets-collection-view-source.js +9 -5
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/assets/assets-store.d.ts +14 -0
- package/dist/components/assets/assets-store.js +23 -1
- package/dist/components/assets/assets-store.js.map +1 -1
- package/dist/components/assets/replace-file.d.ts +22 -0
- package/dist/components/assets/replace-file.js +59 -0
- package/dist/components/assets/replace-file.js.map +1 -0
- package/dist/components/builder/blocks-model.d.ts +14 -26
- package/dist/components/builder/blocks-model.js +35 -41
- package/dist/components/builder/blocks-model.js.map +1 -1
- package/dist/components/builder/blocks-panel.d.ts +7 -3
- package/dist/components/builder/blocks-panel.js +6 -5
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/builder-store.d.ts +126 -30
- package/dist/components/builder/builder-store.js +169 -67
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-body.d.ts +30 -0
- package/dist/components/builder/canvas-body.js +38 -0
- package/dist/components/builder/canvas-body.js.map +1 -0
- package/dist/components/builder/canvas-chrome-css.js +7 -0
- package/dist/components/builder/canvas-chrome-css.js.map +1 -1
- package/dist/components/builder/canvas-dnd.d.ts +7 -7
- package/dist/components/builder/canvas-dnd.js +5 -5
- package/dist/components/builder/canvas-dnd.js.map +1 -1
- package/dist/components/builder/canvas-render-context.d.ts +30 -22
- package/dist/components/builder/canvas-render-context.js +44 -44
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/canvas-runs.d.ts +45 -0
- package/dist/components/builder/canvas-runs.js +51 -0
- package/dist/components/builder/canvas-runs.js.map +1 -0
- package/dist/components/builder/canvas.d.ts +5 -4
- package/dist/components/builder/canvas.js +34 -6
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/clipboard-actions.d.ts +15 -0
- package/dist/components/builder/clipboard-actions.js +50 -13
- package/dist/components/builder/clipboard-actions.js.map +1 -1
- package/dist/components/builder/composition-field-editor.d.ts +10 -16
- package/dist/components/builder/composition-field-editor.js +30 -40
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/composition-patches.d.ts +7 -0
- package/dist/components/builder/composition-patches.js +256 -0
- package/dist/components/builder/composition-patches.js.map +1 -0
- package/dist/components/builder/delete-key.d.ts +5 -0
- package/dist/components/builder/delete-key.js +4 -0
- package/dist/components/builder/delete-key.js.map +1 -1
- package/dist/components/builder/insert-context.d.ts +17 -25
- package/dist/components/builder/insert-context.js +45 -92
- package/dist/components/builder/insert-context.js.map +1 -1
- package/dist/components/builder/page-tree.d.ts +3 -3
- package/dist/components/builder/page-tree.js +3 -3
- package/dist/components/builder/page-tree.js.map +1 -1
- package/dist/components/builder/placement-refusal.d.ts +18 -0
- package/dist/components/builder/placement-refusal.js +46 -0
- package/dist/components/builder/placement-refusal.js.map +1 -0
- package/dist/components/builder/property-inspector.js +4 -0
- package/dist/components/builder/property-inspector.js.map +1 -1
- package/dist/components/builder/reuse-context.js +65 -26
- package/dist/components/builder/reuse-context.js.map +1 -1
- package/dist/components/builder/reuse.d.ts +79 -8
- package/dist/components/builder/reuse.js +71 -23
- package/dist/components/builder/reuse.js.map +1 -1
- package/dist/components/builder/run-inspector.d.ts +11 -0
- package/dist/components/builder/run-inspector.js +33 -0
- package/dist/components/builder/run-inspector.js.map +1 -0
- package/dist/components/cms/asset-field.d.ts +38 -1
- package/dist/components/cms/asset-field.js +182 -58
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/asset-picker-core.d.ts +16 -0
- package/dist/components/cms/asset-picker-core.js +20 -0
- package/dist/components/cms/asset-picker-core.js.map +1 -1
- package/dist/components/cms/asset-picker.js +12 -18
- package/dist/components/cms/asset-picker.js.map +1 -1
- package/dist/components/cms/bulk-actions.d.ts +4 -20
- package/dist/components/cms/bulk-actions.js +3 -24
- package/dist/components/cms/bulk-actions.js.map +1 -1
- package/dist/components/cms/cell-edit.d.ts +5 -5
- package/dist/components/cms/cell-edit.js.map +1 -1
- package/dist/components/cms/cell.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +87 -45
- package/dist/components/cms/cms-store.js +347 -203
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +7 -2
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/commit-policy.d.ts +24 -0
- package/dist/components/cms/commit-policy.js +37 -0
- package/dist/components/cms/commit-policy.js.map +1 -0
- package/dist/components/cms/detail-editor.js +83 -137
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/document-diff-view.d.ts +3 -1
- package/dist/components/cms/document-diff-view.js +12 -7
- package/dist/components/cms/document-diff-view.js.map +1 -1
- package/dist/components/cms/draft-commit.d.ts +70 -0
- package/dist/components/cms/draft-commit.js +82 -0
- package/dist/components/cms/draft-commit.js.map +1 -0
- package/dist/components/cms/duplicate-document.d.ts +11 -13
- package/dist/components/cms/duplicate-document.js +26 -34
- package/dist/components/cms/duplicate-document.js.map +1 -1
- package/dist/components/cms/field-anchors.d.ts +7 -4
- package/dist/components/cms/field-anchors.js +9 -7
- package/dist/components/cms/field-anchors.js.map +1 -1
- package/dist/components/cms/field-collaboration.d.ts +47 -0
- package/dist/components/cms/field-collaboration.js +87 -0
- package/dist/components/cms/field-collaboration.js.map +1 -0
- package/dist/components/cms/field-control-lib.d.ts +22 -1
- package/dist/components/cms/field-control-lib.js +33 -4
- package/dist/components/cms/field-control-lib.js.map +1 -1
- package/dist/components/cms/field-control.js +17 -14
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/gate-validation.d.ts +21 -30
- package/dist/components/cms/gate-validation.js +11 -22
- package/dist/components/cms/gate-validation.js.map +1 -1
- package/dist/components/cms/object-array-field.js +4 -4
- package/dist/components/cms/object-array-field.js.map +1 -1
- package/dist/components/cms/prop-fields.d.ts +7 -2
- package/dist/components/cms/prop-fields.js +13 -6
- package/dist/components/cms/prop-fields.js.map +1 -1
- package/dist/components/cms/reference-confirm.js +10 -4
- package/dist/components/cms/reference-confirm.js.map +1 -1
- package/dist/components/cms/rich-text/embed-picker.d.ts +1 -1
- package/dist/components/cms/rich-text/embed-picker.js.map +1 -1
- package/dist/components/cms/rich-text/insert.d.ts +20 -9
- package/dist/components/cms/rich-text/insert.js +25 -17
- package/dist/components/cms/rich-text/insert.js.map +1 -1
- package/dist/components/cms/use-document-draft.d.ts +96 -0
- package/dist/components/cms/use-document-draft.js +283 -0
- package/dist/components/cms/use-document-draft.js.map +1 -0
- package/dist/components/cms/use-document-versions.js +2 -2
- package/dist/components/cms/use-document-versions.js.map +1 -1
- package/dist/components/cms/use-live-url.d.ts +6 -12
- package/dist/components/cms/use-live-url.js +16 -15
- package/dist/components/cms/use-live-url.js.map +1 -1
- package/dist/components/cms/use-publish-controller.d.ts +13 -18
- package/dist/components/cms/use-publish-controller.js +24 -41
- package/dist/components/cms/use-publish-controller.js.map +1 -1
- package/dist/components/cms/use-published-perspective.d.ts +2 -3
- package/dist/components/cms/use-published-perspective.js +4 -3
- package/dist/components/cms/use-published-perspective.js.map +1 -1
- package/dist/components/cms/versions-pane.js +8 -1
- package/dist/components/cms/versions-pane.js.map +1 -1
- package/dist/components/collection-view/cms-collection-view-source.js +24 -7
- package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
- package/dist/components/components/transfer-dialog.js +28 -23
- package/dist/components/components/transfer-dialog.js.map +1 -1
- package/dist/components/components/transfer.d.ts +31 -21
- package/dist/components/components/transfer.js +49 -35
- package/dist/components/components/transfer.js.map +1 -1
- package/dist/components/project-list.js.map +1 -1
- package/dist/components/project-session.d.ts +6 -3
- package/dist/components/project-session.js +14 -8
- package/dist/components/project-session.js.map +1 -1
- package/dist/components/project-switcher.js.map +1 -1
- package/dist/components/rail-view-source.js +2 -1
- package/dist/components/rail-view-source.js.map +1 -1
- package/dist/components/section-nav.js +1 -1
- package/dist/components/section-nav.js.map +1 -1
- package/dist/lib/ai/apply-content.js +56 -116
- package/dist/lib/ai/apply-content.js.map +1 -1
- package/dist/lib/ai/chat-context.d.ts +8 -0
- package/dist/lib/ai/chat-context.js +37 -6
- package/dist/lib/ai/chat-context.js.map +1 -1
- package/dist/lib/ai/chat-state.d.ts +14 -1
- package/dist/lib/ai/chat-state.js.map +1 -1
- package/dist/lib/ai/constraints.d.ts +20 -5
- package/dist/lib/ai/constraints.js +125 -33
- package/dist/lib/ai/constraints.js.map +1 -1
- package/dist/lib/ai/fake-provider.d.ts +16 -3
- package/dist/lib/ai/fake-provider.js +22 -6
- package/dist/lib/ai/fake-provider.js.map +1 -1
- package/dist/lib/ai/index.d.ts +2 -0
- package/dist/lib/ai/index.js +2 -0
- package/dist/lib/ai/index.js.map +1 -1
- package/dist/lib/ai/leash.d.ts +34 -0
- package/dist/lib/ai/leash.js +28 -0
- package/dist/lib/ai/leash.js.map +1 -0
- package/dist/lib/ai/provenance.d.ts +1 -1
- package/dist/lib/ai/provenance.js.map +1 -1
- package/dist/lib/ai/rich-text-input.d.ts +8 -8
- package/dist/lib/ai/rich-text-input.js +2 -1
- package/dist/lib/ai/rich-text-input.js.map +1 -1
- package/dist/lib/ai/rich-text-translate.d.ts +27 -4
- package/dist/lib/ai/rich-text-translate.js +28 -10
- package/dist/lib/ai/rich-text-translate.js.map +1 -1
- package/dist/lib/ai/shape.d.ts +13 -13
- package/dist/lib/ai/shape.js +69 -93
- package/dist/lib/ai/shape.js.map +1 -1
- package/dist/lib/ai/tools.d.ts +36 -41
- package/dist/lib/ai/tools.js +85 -87
- package/dist/lib/ai/tools.js.map +1 -1
- package/dist/lib/ai/translate-document.d.ts +39 -5
- package/dist/lib/ai/translate-document.js +160 -51
- package/dist/lib/ai/translate-document.js.map +1 -1
- package/dist/lib/ai/types.d.ts +34 -23
- package/dist/lib/ai/types.js.map +1 -1
- package/dist/lib/ai/update-fields.d.ts +21 -12
- package/dist/lib/ai/update-fields.js +118 -177
- package/dist/lib/ai/update-fields.js.map +1 -1
- package/dist/lib/ai/writer-form.d.ts +116 -0
- package/dist/lib/ai/writer-form.js +429 -0
- package/dist/lib/ai/writer-form.js.map +1 -0
- package/dist/lib/ai-current-document.d.ts +11 -11
- package/dist/lib/ai-current-document.js +4 -4
- package/dist/lib/ai-current-document.js.map +1 -1
- package/dist/lib/assets/upload.d.ts +31 -2
- package/dist/lib/assets/upload.js +61 -6
- package/dist/lib/assets/upload.js.map +1 -1
- package/dist/lib/clipboard/os-clipboard.d.ts +10 -2
- package/dist/lib/clipboard/os-clipboard.js +8 -4
- package/dist/lib/clipboard/os-clipboard.js.map +1 -1
- package/dist/lib/clipboard/paste-target.d.ts +18 -9
- package/dist/lib/clipboard/paste-target.js +8 -7
- package/dist/lib/clipboard/paste-target.js.map +1 -1
- package/dist/lib/cms/field-patch.d.ts +15 -0
- package/dist/lib/cms/field-patch.js +25 -0
- package/dist/lib/cms/field-patch.js.map +1 -0
- package/dist/lib/cms/published-documents.d.ts +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 +3 -3
- package/dist/lib/collection-view/__fixtures__/document-writes.d.ts +6 -0
- package/dist/lib/collection-view/__fixtures__/document-writes.js +23 -0
- package/dist/lib/collection-view/__fixtures__/document-writes.js.map +1 -0
- package/dist/lib/collection-view/collection-view-source.d.ts +69 -6
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/data/collection-content.d.ts +4 -13
- package/dist/lib/data/collection-content.js +11 -5
- package/dist/lib/data/collection-content.js.map +1 -1
- package/dist/lib/dnd/drop-rules.d.ts +25 -31
- package/dist/lib/dnd/drop-rules.js +53 -59
- package/dist/lib/dnd/drop-rules.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/messages.de.js +72 -15
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +61 -12
- package/dist/lib/i18n/messages.en.js +76 -16
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/__fixtures__/write-document.d.ts +30 -0
- package/dist/lib/operations/__fixtures__/write-document.js +16 -0
- package/dist/lib/operations/__fixtures__/write-document.js.map +1 -0
- package/dist/lib/operations/cms-persistence.d.ts +124 -31
- package/dist/lib/operations/cms-persistence.js +447 -68
- package/dist/lib/operations/cms-persistence.js.map +1 -1
- package/dist/lib/operations/config-manifest-lookup.d.ts +35 -0
- package/dist/lib/operations/config-manifest-lookup.js +56 -0
- package/dist/lib/operations/config-manifest-lookup.js.map +1 -0
- package/dist/lib/operations/project-runtime-config.d.ts +39 -0
- package/dist/lib/operations/project-runtime-config.js +54 -0
- package/dist/lib/operations/project-runtime-config.js.map +1 -0
- package/dist/lib/operations/publish-with-revision.d.ts +64 -0
- package/dist/lib/operations/publish-with-revision.js +66 -0
- package/dist/lib/operations/publish-with-revision.js.map +1 -0
- package/dist/lib/operations/runtime-validator.d.ts +32 -0
- package/dist/lib/operations/runtime-validator.js +18 -0
- package/dist/lib/operations/runtime-validator.js.map +1 -0
- package/dist/lib/operations/session-hydration.d.ts +31 -14
- package/dist/lib/operations/session-hydration.js +12 -16
- package/dist/lib/operations/session-hydration.js.map +1 -1
- package/dist/lib/operations/studio-operations.d.ts +59 -2
- package/dist/lib/operations/studio-operations.js +76 -14
- package/dist/lib/operations/studio-operations.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.d.ts +13 -5
- package/dist/lib/persistence/convex-adapter.js +189 -74
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persistence/convex-client.d.ts +65 -5
- package/dist/lib/persistence/convex-client.js +106 -3
- package/dist/lib/persistence/convex-client.js.map +1 -1
- package/dist/lib/persistence/index.js +17 -18
- package/dist/lib/persistence/index.js.map +1 -1
- package/dist/lib/persona/labels.js +4 -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/impact.d.ts +21 -15
- package/dist/lib/references/impact.js +13 -26
- package/dist/lib/references/impact.js.map +1 -1
- package/dist/lib/references/references-store.d.ts +9 -0
- package/dist/lib/references/references-store.js +24 -4
- package/dist/lib/references/references-store.js.map +1 -1
- package/dist/lib/routes/route-issues.d.ts +6 -6
- package/dist/lib/routes/route-issues.js +26 -80
- package/dist/lib/routes/route-issues.js.map +1 -1
- package/dist/lib/workspace/config-issues.js +2 -1
- package/dist/lib/workspace/config-issues.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +31 -1
- package/dist/lib/workspace/index.js +64 -17
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/lib/workspace/switcher-source.d.ts +3 -3
- package/dist/lib/workspace/switcher-source.js.map +1 -1
- package/dist/lib/workspace/workspace-source.d.ts +2 -2
- package/dist/lib/workspace/workspace-source.js +2 -1
- package/dist/lib/workspace/workspace-source.js.map +1 -1
- package/package.json +3 -3
- package/dist/components/cms/cms-composition-validators.d.ts +0 -16
- package/dist/components/cms/cms-composition-validators.js +0 -133
- package/dist/components/cms/cms-composition-validators.js.map +0 -1
- package/dist/components/cms/publish-clean.d.ts +0 -28
- package/dist/components/cms/publish-clean.js +0 -38
- package/dist/components/cms/publish-clean.js.map +0 -1
- package/dist/components/cms/use-save-mode.d.ts +0 -35
- package/dist/components/cms/use-save-mode.js +0 -54
- package/dist/components/cms/use-save-mode.js.map +0 -1
- package/dist/lib/assets/asset-placements.d.ts +0 -28
- package/dist/lib/assets/asset-placements.js +0 -83
- package/dist/lib/assets/asset-placements.js.map +0 -1
- package/dist/lib/cms/document-gate.d.ts +0 -47
- package/dist/lib/cms/document-gate.js +0 -61
- package/dist/lib/cms/document-gate.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cms-persistence.js","sourceRoot":"","sources":["../../../src/lib/operations/cms-persistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { selfTypeDocument } from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CollectionDef,\n DocumentRef,\n PropFieldLookup,\n} from '@elytracms/core/cms-core'\nimport type { CmsDocumentRecord } from '@elytracms/core/persistence'\nimport type { OperationError, OperationResult, OperationsClient } from './operations-package'\nimport { createPersistQueue } from './persist-queue'\n\n/**\n * CMS session → operations persistence (EC-141). The CMS store keeps documents\n * and collections as optimistic session state (synchronous editing, validation\n * feedback while typing); every mutation is mirrored onto the operations layer\n * through `documents.*` / `schema.*` commands in dispatch order.\n *\n * Baseline: the persisted seed only covers the sample project. Before the\n * first mirrored command, the persister ensures the project has a persisted\n * schema + documents matching the session's starting state (kit imports,\n * sample fixtures), so deletes/updates address records that exist.\n */\nexport interface CmsPersister {\n upsertDocument(doc: CmsDocument): void\n deleteDocument(ref: DocumentRef): void\n /** Publish (EC-224): snapshot the current draft as a new version and pin it live. */\n publishDocument(ref: DocumentRef): void\n /** Unpublish (EC-224): clear the live version pin + any legacy published marker. */\n unpublishDocument(ref: DocumentRef): void\n upsertCollection(collection: CollectionDef): void\n /** Run the baseline seed now (e.g. before applying an AI changeset). */\n ensureBaseline(): Promise<void>\n /** Await every queued command. */\n flush(): Promise<void>\n}\n\nexport interface CmsPersisterOptions {\n projectId: string\n getOperations: () => Promise<OperationsClient>\n /** Session collections/documents at baseline time (read lazily via refs). */\n getCollections: () => readonly CollectionDef[]\n getDocuments: () => readonly CmsDocument[]\n /**\n * Component-prop field-defs by component id (EC-277): the manifest half of the\n * write-time self-typing lens, so a composition's static relation/asset props are\n * self-typed too (\"schema leads, component adopts\"). Omit → document fields are\n * still self-typed; composition static props pass through.\n */\n lookupManifest?: PropFieldLookup\n onError: (error: OperationError) => void\n /**\n * Called with the AUTHORITATIVE saved record after each queued upsert lands\n * (EC-449). The session adopts server-computed record state from it — today the\n * write-chokepoint `hasUnpublishedChanges` verdict behind the amber \"changed\"\n * list dot. Optional: omitting it changes nothing.\n */\n onDocumentSaved?: (record: CmsDocumentRecord) => void\n}\n\nexport function createCmsPersister(options: CmsPersisterOptions): CmsPersister {\n const queue = createPersistQueue()\n let baselineDone = false\n\n /**\n * EC-277 → EC-586: canonicalise a document (+ the manifest for composition static\n * props) before it is dispatched — THE write canonicaliser (`selfTypeDocument` →\n * `FIELD_TYPES`), the same function `documents.upsert` runs on arrival and `writeField`\n * runs on the first in-session write. It is idempotent and identity-preserving, so the\n * operation's own pass over an already-canonical document is a pointer walk; this call\n * is here because the studio's operations client carries no manifest lookup and the\n * session does. Drift-tolerant: an undeclared collection passes through unchanged.\n */\n function selfType(doc: CmsDocument): CmsDocument {\n const collection = options.getCollections().find((c) => c.id === doc.collection)\n if (!collection) return doc\n return selfTypeDocument(\n doc,\n collection,\n options.lookupManifest ? { lookupManifest: options.lookupManifest } : {},\n )\n }\n\n async function ensureBaselineNow(): Promise<boolean> {\n if (baselineDone) return true\n const operations = await options.getOperations()\n const schema = await operations.schema.get({ projectId: options.projectId })\n if (schema.ok) {\n // A persisted schema exists (seeded sample project or an earlier\n // session) — the session started from the same fixtures, so commands\n // can apply directly.\n baselineDone = true\n return true\n }\n const saved = await operations.schema.save({\n projectId: options.projectId,\n collections: [...options.getCollections()],\n })\n if (!saved.ok) {\n options.onError(saved.error)\n return false\n }\n for (const document of options.getDocuments()) {\n const result = await operations.documents.upsert({\n projectId: options.projectId,\n document: selfType(document),\n })\n if (!result.ok) {\n options.onError(result.error)\n return false\n }\n }\n baselineDone = true\n return true\n }\n\n function command(run: (operations: OperationsClient) => Promise<OperationResult<unknown>>): void {\n queue.push(async () => {\n // A failed baseline already surfaced through onError — running the\n // command against missing records would only add a confusing follow-up.\n if (!(await ensureBaselineNow())) return\n const operations = await options.getOperations()\n const result = await run(operations)\n if (!result.ok) options.onError(result.error)\n })\n }\n\n return {\n upsertDocument(doc) {\n const document = selfType(doc)\n command(async (operations) => {\n const result = await operations.documents.upsert({ projectId: options.projectId, document })\n // EC-449: hand the authoritative saved record back to the session so it\n // adopts the server-computed `hasUnpublishedChanges` verdict (the queue\n // swallows successful results otherwise).\n if (result.ok) options.onDocumentSaved?.(result.value)\n return result\n })\n },\n deleteDocument(ref) {\n command((operations) => operations.documents.delete({ projectId: options.projectId, ref }))\n },\n publishDocument(ref) {\n command((operations) =>\n operations.documents.publish({ projectId: options.projectId, ref }),\n )\n },\n unpublishDocument(ref) {\n command((operations) =>\n operations.documents.unpublish({ projectId: options.projectId, ref }),\n )\n },\n upsertCollection(collection) {\n command((operations) =>\n operations.schema.upsertCollection({ projectId: options.projectId, collection }),\n )\n },\n async ensureBaseline() {\n queue.push(async () => {\n await ensureBaselineNow()\n })\n await queue.flush()\n },\n flush: () => queue.flush(),\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cms-persistence.js","sourceRoot":"","sources":["../../../src/lib/operations/cms-persistence.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n applyDocumentPatches,\n documentKey,\n formatValuePath,\n selfTypeDocument,\n touchedPaths,\n valuePathsOverlap,\n} from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CollectionDef,\n DocumentPatch,\n DocumentRef,\n PropFieldLookup,\n RefusedPatch,\n ValuePath,\n} from '@elytracms/core/cms-core'\nimport type { CmsDocumentRecord } from '@elytracms/core/persistence'\nimport type { OperationError, OperationResult, OperationsClient } from './operations-package'\nimport { createPersistQueue } from './persist-queue'\n\n/**\n * CMS session → operations persistence (EC-141 → EC-595 part A2): the PATCH QUEUE.\n *\n * The CMS session keeps documents as optimistic, synchronous state; every write is\n * mirrored onto the operations layer through the revisioned aggregate's two verbs\n * (EC-595, ratified 2026-10-24): `documents.patch` for editing, `documents.replace`\n * for whole-document writers (create, import). There is no document diffing anywhere:\n * every writer EMITS the patches it made at its source (the form's field path, the\n * canvas's node patches, the AI proposal's field changes, bulk set-field, transfer) and\n * hands them to {@link CmsPersister.commit}.\n *\n * **Per document, the persister keeps** the server row it last adopted (`base` — its\n * revision is what a patch is made against), the committed-but-unacknowledged patches\n * (each with the revision the user's view was at when it was made), and the refused\n * paths awaiting a decision. It derives the session's view of the document from them:\n *\n * display = base.document ⊕ unresolved conflicts (mine) ⊕ in-flight ⊕ queued\n *\n * by re-applying the patches with THE merge (`applyDocumentPatches`, `since: []`) — the\n * same pure function both stores run. So local edits win on their paths until they are\n * flushed (EC-595 point 7), and a server row adopted underneath them (a write result,\n * a subscription push, a conflict's `current`) shows everywhere else at once.\n *\n * **Base revisions are per patch** (not per call): a patch is checked against the\n * changes in `(its base, current]`, so a colleague's change that arrived (by subscribe)\n * AFTER the user started typing in a field is still detected on that field. A patch\n * whose path our own successful write just covered moves its base to that write's\n * revision — the server guarantees no other session touched the path in between, so\n * a second edit of a field during the first one's flight never conflicts with itself.\n * Consecutive runs of equal base are sent in commit order, one `documents.patch` call\n * per run.\n *\n * **A refused path is a conflict ON THAT FIELD** ({@link DocumentConflict}): the\n * session shows \"changed by X — take theirs / keep mine\"; until the user decides, the\n * field keeps showing mine (and further edits of it replace mine without being sent);\n * keep-mine re-sends at the current base, take-theirs drops mine. Never a\n * whole-document dialog, never a silent overwrite.\n */\n\n/** One refused patch awaiting the user's decision (EC-595 binding point 5). */\nexport interface DocumentConflict {\n /** Stable id for the UI (`<key>|<path>`); one conflict per path. */\n readonly id: string\n readonly ref: DocumentRef\n /** The refused patch — \"mine\". */\n readonly patch: DocumentPatch\n /** The submitted target path. */\n readonly path: ValuePath\n /** The value at that path on the server NOW — \"theirs\" (`undefined` = absent). */\n readonly theirs: unknown\n /** `conflict` — changed since the base; `invalid` — never resolved (e.g. its node is gone). */\n readonly reason: RefusedPatch['reason']\n /** The server revision the refusal was made at. */\n readonly revision: number\n /** Who changed the path (the patch log's actor), once resolved; `undefined` while unknown. */\n readonly actor?: string\n}\n\n/** How one {@link CmsPersister.commit} ended once its patches were acknowledged. */\nexport type CommitOutcome =\n | { readonly status: 'applied'; readonly record: CmsDocumentRecord }\n /** Refused per field — the conflicts stay open on the document for the user to resolve. */\n | { readonly status: 'refused'; readonly conflicts: readonly DocumentConflict[] }\n /** A transport / authorization failure (also reported through `onError`). */\n | { readonly status: 'failed'; readonly error: OperationError }\n\nexport interface CmsPersister {\n /** Adopt a server row as this session's base for its document (hydration, a create result). */\n track(row: TrackedRow): void\n /** The revision the session's view of a document is at (`undefined` = untracked). */\n revisionOf(ref: DocumentRef): number | undefined\n /** The session's view of a tracked document (base ⊕ local), or `undefined`. */\n documentOf(ref: DocumentRef): CmsDocument | undefined\n /**\n * Commit patches a writer made (at the session's current view) and flush them. The\n * session's view updates synchronously (via `onDocument`); the patches persist in\n * order through `documents.patch`. `view` — the session's current document, the\n * provisional base of a document this session holds no revision for (a staged\n * transfer); its revision is read before the first send. `bases` — per patch, the\n * revision the user's view was at when they STARTED that edit (an editor field typed\n * across a colleague's change); absent → the revision the view is at now.\n */\n commit(\n ref: DocumentRef,\n patches: readonly DocumentPatch[],\n view?: CmsDocument,\n bases?: readonly (number | undefined)[],\n ): Promise<CommitOutcome>\n /** Adopt a server row one of the session's OWN direct writes returned (publish, restore). */\n adoptRecord(record: CmsDocumentRecord): void\n /** Create a document (insert-if-absent: `documents.replace` `{ ifAbsent: true }`). */\n create(document: CmsDocument): void\n /**\n * Write a whole document at the session's tracked revision (or insert-if-absent when\n * untracked): an import. A move since then is refused as a conflict, never overwritten.\n */\n replace(document: CmsDocument): void\n deleteDocument(ref: DocumentRef): void\n unpublishDocument(ref: DocumentRef): void\n /**\n * Adopt a server row pushed by the document subscription (EC-595 point 7): applied\n * under the local patches (local wins until flushed); held while a write is in flight\n * and applied after it, so an echo of our own write never races its result.\n * `undefined` = the document is gone.\n */\n adoptSnapshot(ref: DocumentRef, record: CmsDocumentRecord | undefined): void\n /** The unresolved conflicts on a document. */\n conflicts(ref: DocumentRef): readonly DocumentConflict[]\n /** Resolve a conflict: `theirs` adopts the server value; `mine` re-sends at the current base. */\n resolveConflict(ref: DocumentRef, conflictId: string, choice: 'theirs' | 'mine'): void\n /** Await every queued write (of one document, or all). The FORCED FLUSH's persistence half. */\n flush(ref?: DocumentRef): Promise<void>\n}\n\nexport interface CmsPersisterOptions {\n projectId: string\n getOperations: () => Promise<OperationsClient>\n /** The session's collections (read lazily via a ref) — the create/import canonicaliser's lens. */\n getCollections: () => readonly CollectionDef[]\n /** Component-prop field-defs (EC-277), so a created document's composition props are canonical too. */\n lookupManifest?: PropFieldLookup\n /** A failed / denied write that is not a per-field conflict — rendered by the session, never silent. */\n onError: (error: OperationError) => void\n /**\n * The session's view of a document changed (an optimistic commit, an adopted server\n * row, a resolved conflict) — `undefined` when the document is gone. Synchronous.\n */\n onDocument?: (ref: DocumentRef, document: CmsDocument | undefined) => void\n /**\n * A server row was adopted (EC-449 and friends): the session stamps the chokepoint's\n * `hasUnpublishedChanges`, `updatedAt`, `publishedAt` from it — and, since the row\n * carries the CANONICAL saved values, the session adopts those too (EC-586\n * found-but-left: the studio never adopted the saved canonical document).\n */\n onRecord?: (record: CmsDocumentRecord) => void\n /** A document's conflict list changed. */\n onConflicts?: (ref: DocumentRef, conflicts: readonly DocumentConflict[]) => void\n}\n\n/** What the session hydrated for a document: its value and the revision it is at. */\nexport type TrackedRow = Pick<CmsDocumentRecord, 'document' | 'revision'>\n\n/** A committed patch and the revision the user's view was at when it was made (`undefined`: on top of our own pending create). */\ninterface QueuedPatch {\n readonly patch: DocumentPatch\n base: number | undefined\n /** The commits waiting on this patch (a superseded value write hands its waiters on). */\n readonly settle: ((outcome: CommitOutcome) => void)[]\n}\n\n/** A patch's identity within one call: op + target path (no two value writes of one path share a run). */\nfunction targetKey(op: DocumentPatch['op'], path: ValuePath): string {\n return `${op} ${formatValuePath(path)}`\n}\n\nfunction patchTarget(patch: DocumentPatch): ValuePath {\n return patch.op === 'insert' ? [...patch.at.parent, { kind: 'node', id: patch.node.id }] : patch.path\n}\n\ninterface Entry {\n readonly ref: DocumentRef\n base: TrackedRow | undefined\n /** The session's document while no server row is adopted yet (a create in flight, an untracked doc). */\n provisional: CmsDocument | undefined\n /** Committed, not yet sent. */\n queue: QueuedPatch[]\n /** Sent, awaiting the result. */\n inFlight: QueuedPatch[]\n /** A whole-document write (create / replace / unpublish / delete) in flight. */\n busy: boolean\n conflicts: DocumentConflict[]\n /** A subscription row that arrived during a write (`null` = gone); adopted after it. */\n held: CmsDocumentRecord | null | undefined\n /** A drain is queued. */\n scheduled: boolean\n deleted: boolean\n}\n\nfunction conflictId(ref: DocumentRef, path: ValuePath): string {\n return `${documentKey(ref)}|${formatValuePath(path)}`\n}\n\n/** Paths two patches have in common (either touches a prefix of the other's). */\nfunction patchesOverlap(a: DocumentPatch, b: DocumentPatch): boolean {\n return touchedPaths(a).some((p) => touchedPaths(b).some((q) => valuePathsOverlap(p, q)))\n}\n\n/** A `set`/`unset` on exactly one path — the shape a later edit of the same field supersedes. */\nfunction isValueWrite(patch: DocumentPatch): patch is Extract<DocumentPatch, { op: 'set' | 'unset' }> {\n return patch.op === 'set' || patch.op === 'unset'\n}\n\nfunction samePath(a: ValuePath, b: ValuePath): boolean {\n return a.length === b.length && valuePathsOverlap(a, b)\n}\n\nexport function createCmsPersister(options: CmsPersisterOptions): CmsPersister {\n const queue = createPersistQueue((error) => {\n options.onError({\n code: 'unknown',\n message: error instanceof Error ? error.message : String(error),\n })\n })\n const entries = new Map<string, Entry>()\n\n function entryOf(ref: DocumentRef): Entry {\n const key = documentKey(ref)\n let entry = entries.get(key)\n if (!entry) {\n entry = {\n ref: { collection: ref.collection, id: ref.id },\n base: undefined,\n provisional: undefined,\n queue: [],\n inFlight: [],\n busy: false,\n conflicts: [],\n held: undefined,\n scheduled: false,\n deleted: false,\n }\n entries.set(key, entry)\n }\n return entry\n }\n\n /** EC-586: THE write canonicaliser over a whole document a writer hands us (create/import). */\n function canonical(doc: CmsDocument): CmsDocument {\n const collection = options.getCollections().find((c) => c.id === doc.collection)\n if (!collection) return doc\n return selfTypeDocument(doc, collection, options.lookupManifest ? { lookupManifest: options.lookupManifest } : {})\n }\n\n /** The session's view: base ⊕ unresolved mine ⊕ in-flight ⊕ queued (the pure merge, no log). */\n function display(entry: Entry): CmsDocument | undefined {\n const baseDocument = entry.base?.document ?? entry.provisional\n if (entry.deleted || !baseDocument) return undefined\n const patches = [\n ...entry.conflicts.map((conflict) => conflict.patch),\n ...entry.inFlight.map((queued) => queued.patch),\n ...entry.queue.map((queued) => queued.patch),\n ]\n if (patches.length === 0) return baseDocument\n return applyDocumentPatches({ document: baseDocument, patches, since: [] }).document\n }\n\n function emitDocument(entry: Entry): void {\n if (entry.deleted) return\n const shown = display(entry)\n if (shown) options.onDocument?.(entry.ref, shown)\n }\n\n function emitConflicts(entry: Entry): void {\n options.onConflicts?.(entry.ref, [...entry.conflicts])\n }\n\n /** Adopt a server row as the base (never a lower revision), re-derive the view. */\n function adopt(entry: Entry, record: CmsDocumentRecord): void {\n if (entry.base && record.revision < entry.base.revision) return\n entry.base = record\n options.onRecord?.(record)\n emitDocument(entry)\n }\n\n /** After a write: apply a subscription row that arrived meanwhile, if newer. */\n function adoptHeld(entry: Entry): void {\n const held = entry.held\n entry.held = undefined\n if (held === undefined) return\n if (held === null) {\n markGone(entry)\n return\n }\n if (!entry.base || held.revision > entry.base.revision) adopt(entry, held)\n }\n\n function markGone(entry: Entry): void {\n if (entry.deleted) return\n entry.deleted = true\n entry.queue = []\n options.onDocument?.(entry.ref, undefined)\n }\n\n function schedule(entry: Entry): void {\n if (entry.scheduled) return\n entry.scheduled = true\n queue.push(async () => {\n entry.scheduled = false\n await drain(entry)\n adoptHeld(entry)\n })\n }\n\n /** Who changed a refused path — the newest patch-log row in (base, current] touching it. Best-effort. */\n async function resolveActors(entry: Entry, sentBase: number, refused: readonly DocumentConflict[]): Promise<void> {\n try {\n const operations = await options.getOperations()\n const log = await operations.documents.listPatches({\n projectId: options.projectId,\n ref: entry.ref,\n afterRevision: sentBase,\n })\n if (!log.ok) return\n let changed = false\n entry.conflicts = entry.conflicts.map((conflict) => {\n if (conflict.actor !== undefined || !refused.some((r) => r.id === conflict.id)) return conflict\n const row = [...log.value]\n .reverse()\n .find(\n (candidate) =>\n candidate.kind === 'replace' ||\n candidate.patches.some((patch) =>\n touchedPaths(patch).some((path) => valuePathsOverlap(path, conflict.path)),\n ),\n )\n if (!row?.actor) return conflict\n changed = true\n return { ...conflict, actor: row.actor }\n })\n if (changed) emitConflicts(entry)\n } catch {\n // The actor is decoration on a conflict the user can already resolve.\n }\n }\n\n /** Send the queue, one run of equal base at a time, until it is empty or a write fails. */\n async function drain(entry: Entry): Promise<void> {\n while (entry.queue.length > 0 && !entry.deleted) {\n const operations = await options.getOperations()\n if (!entry.base) {\n // Untracked (a document the session never hydrated a revision for): read it.\n // The user's view of it is the session's, so this is the narrowest window left.\n const read = await operations.documents.get({ projectId: options.projectId, ref: entry.ref })\n if (!read.ok) {\n options.onError(read.error)\n return\n }\n entry.base = read.value\n }\n const resolveBase = (queued: QueuedPatch) => queued.base ?? entry.base!.revision\n const first = resolveBase(entry.queue[0]!)\n let end = 1\n while (end < entry.queue.length && resolveBase(entry.queue[end]!) === first) end += 1\n const run = entry.queue.splice(0, end)\n entry.inFlight = run\n const result = await operations.documents.patch({\n projectId: options.projectId,\n ref: entry.ref,\n baseRevision: first,\n patches: run.map((queued) => queued.patch),\n })\n entry.inFlight = []\n if (result.ok) {\n rebaseCovered(entry, run, result.value.revision)\n adopt(entry, result.value)\n adoptHeld(entry)\n settleAll(run, { status: 'applied', record: result.value })\n continue\n }\n const error = result.error\n if (error.code === 'conflict' && error.refused && error.current) {\n const current = error.current\n // Refusals are matched to the run by op + target path (the operation re-parses\n // and canonicalises the patches, so object identity does not survive the call).\n const refusedByKey = new Map(\n error.refused.map((refused) => [targetKey(refused.patch.op, refused.path), refused]),\n )\n const applied: QueuedPatch[] = []\n const conflicts: DocumentConflict[] = []\n for (const queued of run) {\n const refused = refusedByKey.get(targetKey(queued.patch.op, patchTarget(queued.patch)))\n if (!refused) {\n applied.push(queued)\n continue\n }\n // MINE is what the user wrote (the queued patch), not the canonicalised echo.\n const conflict = upsertConflict(entry, { ...refused, patch: queued.patch }, current.revision)\n conflicts.push(conflict)\n for (const settle of queued.settle) settle({ status: 'refused', conflicts: [conflict] })\n }\n rebaseCovered(entry, applied, current.revision)\n adopt(entry, current)\n adoptHeld(entry)\n emitConflicts(entry)\n settleAll(applied, { status: 'applied', record: current })\n void resolveActors(entry, first, conflicts)\n continue\n }\n if (error.code === 'conflict' && 'current' in error && error.current === null) {\n // The document was deleted since — nothing to write into.\n markGone(entry)\n options.onError(error)\n settleAll(run, { status: 'failed', error })\n return\n }\n // A transport / authorization failure: the patches stay local (shown, unsent) and\n // the error is visible; the next commit or forced flush retries them. The commits\n // waiting on them learn of the failure now.\n settleAll(run, { status: 'failed', error })\n entry.queue.unshift(...run.map((queued) => ({ ...queued, settle: [] })))\n adoptHeld(entry)\n options.onError(error)\n return\n }\n }\n\n function settleAll(queued: readonly QueuedPatch[], outcome: CommitOutcome): void {\n for (const item of queued) for (const settle of item.settle) settle(outcome)\n }\n\n /** Queued patches on a path our own write just covered move their base to that write's revision. */\n function rebaseCovered(entry: Entry, written: readonly QueuedPatch[], revision: number): void {\n if (written.length === 0) return\n for (const queued of entry.queue) {\n if (queued.base !== undefined && queued.base >= revision) continue\n if (written.some((done) => patchesOverlap(done.patch, queued.patch))) queued.base = revision\n }\n }\n\n function upsertConflict(entry: Entry, refused: RefusedPatch, revision: number): DocumentConflict {\n const conflict: DocumentConflict = {\n id: conflictId(entry.ref, refused.path),\n ref: entry.ref,\n patch: refused.patch,\n path: refused.path,\n theirs: refused.current,\n reason: refused.reason,\n revision,\n }\n entry.conflicts = [...entry.conflicts.filter((c) => c.id !== conflict.id), conflict]\n return conflict\n }\n\n function command(entry: Entry, run: (operations: OperationsClient) => Promise<OperationResult<unknown>>): void {\n queue.push(async () => {\n const operations = await options.getOperations()\n entry.busy = true\n try {\n const result = await run(operations)\n if (!result.ok) options.onError(result.error)\n } finally {\n entry.busy = false\n adoptHeld(entry)\n }\n })\n }\n\n return {\n track(row) {\n const entry = entryOf(row.document)\n if (!entry.base || row.revision > entry.base.revision) entry.base = row\n },\n revisionOf: (ref) => entries.get(documentKey(ref))?.base?.revision,\n documentOf: (ref) => {\n const entry = entries.get(documentKey(ref))\n return entry ? display(entry) : undefined\n },\n adoptRecord(record) {\n const entry = entryOf(record.document)\n if (entry.deleted) return\n if (!entry.base || record.revision > entry.base.revision) adopt(entry, record)\n },\n commit(ref, patches, view, bases) {\n const entry = entryOf(ref)\n if (patches.length === 0 || entry.deleted) {\n return Promise.resolve<CommitOutcome>({\n status: 'failed',\n error: { code: 'not-found', message: 'The document is gone.', entity: 'cms-document', key: documentKey(ref) },\n })\n }\n if (!entry.base && !entry.provisional && view) entry.provisional = view\n let conflictsChanged = false\n const waits: Promise<CommitOutcome>[] = []\n for (const [index, patch] of patches.entries()) {\n const madeAt = bases?.[index] ?? entry.base?.revision\n let settle!: (outcome: CommitOutcome) => void\n waits.push(new Promise<CommitOutcome>((resolve) => (settle = resolve)))\n // A field with an unresolved conflict: the new value replaces MINE, unsent —\n // the user decides (keep mine sends it), never an implicit overwrite of theirs.\n const open = entry.conflicts.find((conflict) => patchesOverlap(conflict.patch, patch))\n if (open && isValueWrite(patch) && isValueWrite(open.patch) && samePath(open.patch.path, patch.path)) {\n const replaced: DocumentConflict = { ...open, patch }\n entry.conflicts = entry.conflicts.map((c) => (c === open ? replaced : c))\n conflictsChanged = true\n settle({ status: 'refused', conflicts: [replaced] })\n continue\n }\n // A later value write of the same field supersedes an unsent one (keeping the\n // EARLIER base — the user never saw a foreign value of a field they were typing in).\n if (isValueWrite(patch)) {\n const pending = entry.queue.findIndex(\n (queued) => isValueWrite(queued.patch) && samePath(queued.patch.path, patch.path),\n )\n if (pending >= 0) {\n const superseded = entry.queue[pending]!\n entry.queue[pending] = { patch, base: superseded.base, settle: [...superseded.settle, settle] }\n continue\n }\n }\n entry.queue.push({ patch, base: madeAt, settle: [settle] })\n }\n emitDocument(entry)\n if (conflictsChanged) emitConflicts(entry)\n schedule(entry)\n // The commit's outcome: failed if any of its patches failed, refused if any was.\n return Promise.all(waits).then((outcomes): CommitOutcome => {\n const failed = outcomes.find((o) => o.status === 'failed')\n if (failed) return failed\n const refused = outcomes.flatMap((o) => (o.status === 'refused' ? o.conflicts : []))\n if (refused.length > 0) return { status: 'refused', conflicts: refused }\n return outcomes[outcomes.length - 1]!\n })\n },\n create(document) {\n const doc = canonical(document)\n const entry = entryOf(doc)\n entry.deleted = false\n // Optimistic: the session shows the document at once; the base is the created row.\n entry.provisional = doc\n options.onDocument?.(entry.ref, doc)\n queue.push(async () => {\n const operations = await options.getOperations()\n entry.busy = true\n try {\n const result = await operations.documents.replace({\n projectId: options.projectId,\n document: doc,\n precondition: { ifAbsent: true },\n })\n if (result.ok) adopt(entry, result.value)\n else options.onError(result.error)\n } finally {\n entry.busy = false\n adoptHeld(entry)\n }\n if (entry.queue.length > 0) await drain(entry)\n })\n },\n replace(document) {\n const doc = canonical(document)\n const entry = entryOf(doc)\n if (!entry.base) entry.provisional = doc\n options.onDocument?.(entry.ref, doc)\n queue.push(async () => {\n await drain(entry)\n const operations = await options.getOperations()\n entry.busy = true\n try {\n const result = await operations.documents.replace({\n projectId: options.projectId,\n document: doc,\n precondition: entry.base ? { revision: entry.base.revision } : { ifAbsent: true },\n })\n if (result.ok) adopt(entry, result.value)\n else {\n if (result.error.code === 'conflict' && 'current' in result.error && result.error.current) {\n adopt(entry, result.error.current)\n }\n options.onError(result.error)\n }\n } finally {\n entry.busy = false\n adoptHeld(entry)\n }\n })\n },\n deleteDocument(ref) {\n const entry = entryOf(ref)\n entry.queue = []\n entry.conflicts = []\n entry.deleted = true\n emitConflicts(entry)\n command(entry, (operations) => operations.documents.delete({ projectId: options.projectId, ref }))\n },\n unpublishDocument(ref) {\n const entry = entryOf(ref)\n queue.push(async () => {\n await drain(entry)\n const operations = await options.getOperations()\n entry.busy = true\n try {\n const result = await operations.documents.unpublish({ projectId: options.projectId, ref })\n if (result.ok) adopt(entry, result.value)\n else options.onError(result.error)\n } finally {\n entry.busy = false\n adoptHeld(entry)\n }\n })\n },\n adoptSnapshot(ref, record) {\n const entry = entryOf(ref)\n if (entry.inFlight.length > 0 || entry.busy || entry.scheduled) {\n // Hold it: the write's own result decides first; the newer of the two wins.\n if (record === undefined) entry.held = null\n else if (!entry.held || record.revision > entry.held.revision) entry.held = record\n return\n }\n if (record === undefined) {\n if (entry.base) markGone(entry)\n return\n }\n if (!entry.base || record.revision > entry.base.revision) adopt(entry, record)\n },\n conflicts: (ref) => entries.get(documentKey(ref))?.conflicts ?? [],\n resolveConflict(ref, id, choice) {\n const entry = entries.get(documentKey(ref))\n if (!entry) return\n const conflict = entry.conflicts.find((c) => c.id === id)\n if (!conflict) return\n entry.conflicts = entry.conflicts.filter((c) => c !== conflict)\n if (choice === 'mine') {\n // Keep mine: re-send at the CURRENT base — a visible, explicit second write.\n entry.queue.push({ patch: conflict.patch, base: entry.base?.revision, settle: [] })\n schedule(entry)\n }\n emitDocument(entry)\n emitConflicts(entry)\n },\n async flush(ref) {\n if (ref) {\n const entry = entries.get(documentKey(ref))\n if (entry && entry.queue.length > 0) schedule(entry)\n }\n await queue.flush()\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ComponentManifest } from '@elytracms/core/component-registry';
|
|
2
|
+
import type { PropFieldLookup } from '@elytracms/core/cms-core';
|
|
3
|
+
/** The one part of a config component entry the lookup reads. */
|
|
4
|
+
interface ManifestEntry {
|
|
5
|
+
readonly manifest: ComponentManifest;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* EC-595 A1 (critic correction 4) — the operations context's manifest half of the write
|
|
9
|
+
* canonicaliser, built from the CONFIG's component entries (`elytra.config.ts`
|
|
10
|
+
* `components`: `{ manifest, implementation }` pairs).
|
|
11
|
+
*
|
|
12
|
+
* Why not the studio registry's `studioManifestLookup()`: `lib/registry` registers the
|
|
13
|
+
* implementations through `runtime-renderer`, and the operations bootstrap deliberately
|
|
14
|
+
* stays off that edge (`lib/cms/document-gate.ts` explains the same constraint for the
|
|
15
|
+
* validator seam). The manifests ARE the config's — the registry reads the very same
|
|
16
|
+
* entries — so reading them here is the same declaration, one reader per layer, not a
|
|
17
|
+
* second source. Only `component-registry`'s `manifestPropFields` (the one prop-def
|
|
18
|
+
* projection, name-injected) is imported.
|
|
19
|
+
*
|
|
20
|
+
* `entries` is read lazily on every lookup (the active project's list can be
|
|
21
|
+
* re-resolved); the per-list cache keeps a hot path a map read. A duplicate component id
|
|
22
|
+
* resolves to the FIRST entry — the registry's `registerComponents` reports duplicates as
|
|
23
|
+
* a visible issue, so a duplicate is already a broken config the studio surfaces.
|
|
24
|
+
*/
|
|
25
|
+
export declare function configManifestLookup(entries: () => readonly ManifestEntry[] | undefined): PropFieldLookup;
|
|
26
|
+
/**
|
|
27
|
+
* EC-595 A2 (A1 found-but-left #1) — the manifest lookup for the operation's OWN
|
|
28
|
+
* project: the operations context's `manifestLookup` resolver. One operations bundle is
|
|
29
|
+
* cached per deployment key and may serve several config projects with different
|
|
30
|
+
* component lists, so a write to an inactive project canonicalises its props with THAT
|
|
31
|
+
* project's manifests, never the active one's. `entriesFor` reads the config project
|
|
32
|
+
* by id (see `configProjectFor`); one cached lookup per project id.
|
|
33
|
+
*/
|
|
34
|
+
export declare function projectManifestLookup(entriesFor: (projectId: string) => readonly ManifestEntry[] | undefined): (projectId: string) => PropFieldLookup;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { manifestPropFields } from '@elytracms/core/component-registry';
|
|
2
|
+
/**
|
|
3
|
+
* EC-595 A1 (critic correction 4) — the operations context's manifest half of the write
|
|
4
|
+
* canonicaliser, built from the CONFIG's component entries (`elytra.config.ts`
|
|
5
|
+
* `components`: `{ manifest, implementation }` pairs).
|
|
6
|
+
*
|
|
7
|
+
* Why not the studio registry's `studioManifestLookup()`: `lib/registry` registers the
|
|
8
|
+
* implementations through `runtime-renderer`, and the operations bootstrap deliberately
|
|
9
|
+
* stays off that edge (`lib/cms/document-gate.ts` explains the same constraint for the
|
|
10
|
+
* validator seam). The manifests ARE the config's — the registry reads the very same
|
|
11
|
+
* entries — so reading them here is the same declaration, one reader per layer, not a
|
|
12
|
+
* second source. Only `component-registry`'s `manifestPropFields` (the one prop-def
|
|
13
|
+
* projection, name-injected) is imported.
|
|
14
|
+
*
|
|
15
|
+
* `entries` is read lazily on every lookup (the active project's list can be
|
|
16
|
+
* re-resolved); the per-list cache keeps a hot path a map read. A duplicate component id
|
|
17
|
+
* resolves to the FIRST entry — the registry's `registerComponents` reports duplicates as
|
|
18
|
+
* a visible issue, so a duplicate is already a broken config the studio surfaces.
|
|
19
|
+
*/
|
|
20
|
+
export function configManifestLookup(entries) {
|
|
21
|
+
let cachedList;
|
|
22
|
+
let cachedById = new Map();
|
|
23
|
+
return (componentId) => {
|
|
24
|
+
const list = entries();
|
|
25
|
+
if (list !== cachedList) {
|
|
26
|
+
cachedList = list;
|
|
27
|
+
cachedById = new Map();
|
|
28
|
+
for (const entry of list ?? []) {
|
|
29
|
+
if (cachedById.has(entry.manifest.id))
|
|
30
|
+
continue;
|
|
31
|
+
cachedById.set(entry.manifest.id, Object.fromEntries(manifestPropFields(entry.manifest).map((field) => [field.name, field])));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return cachedById.get(componentId);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* EC-595 A2 (A1 found-but-left #1) — the manifest lookup for the operation's OWN
|
|
39
|
+
* project: the operations context's `manifestLookup` resolver. One operations bundle is
|
|
40
|
+
* cached per deployment key and may serve several config projects with different
|
|
41
|
+
* component lists, so a write to an inactive project canonicalises its props with THAT
|
|
42
|
+
* project's manifests, never the active one's. `entriesFor` reads the config project
|
|
43
|
+
* by id (see `configProjectFor`); one cached lookup per project id.
|
|
44
|
+
*/
|
|
45
|
+
export function projectManifestLookup(entriesFor) {
|
|
46
|
+
const byProject = new Map();
|
|
47
|
+
return (projectId) => {
|
|
48
|
+
let lookup = byProject.get(projectId);
|
|
49
|
+
if (!lookup) {
|
|
50
|
+
lookup = configManifestLookup(() => entriesFor(projectId));
|
|
51
|
+
byProject.set(projectId, lookup);
|
|
52
|
+
}
|
|
53
|
+
return lookup;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=config-manifest-lookup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-manifest-lookup.js","sourceRoot":"","sources":["../../../src/lib/operations/config-manifest-lookup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAA;AASvE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAmD;IAEnD,IAAI,UAAgD,CAAA;IACpD,IAAI,UAAU,GAAG,IAAI,GAAG,EAAoC,CAAA;IAC5D,OAAO,CAAC,WAAW,EAAE,EAAE;QACrB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAA;QACtB,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;YACxB,UAAU,GAAG,IAAI,CAAA;YACjB,UAAU,GAAG,IAAI,GAAG,EAAE,CAAA;YACtB,KAAK,MAAM,KAAK,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC;gBAC/B,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAAE,SAAQ;gBAC/C,UAAU,CAAC,GAAG,CACZ,KAAK,CAAC,QAAQ,CAAC,EAAE,EACjB,MAAM,CAAC,WAAW,CAAC,kBAAkB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAC3F,CAAA;YACH,CAAC;QACH,CAAC;QACD,OAAO,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IACpC,CAAC,CAAA;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACnC,UAAuE;IAEvE,MAAM,SAAS,GAAG,IAAI,GAAG,EAA2B,CAAA;IACpD,OAAO,CAAC,SAAS,EAAE,EAAE;QACnB,IAAI,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QACrC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,GAAG,oBAAoB,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAA;YAC1D,SAAS,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;QAClC,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC,CAAA;AACH,CAAC","sourcesContent":["import { manifestPropFields } from '@elytracms/core/component-registry'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport type { FieldDef, PropFieldLookup } from '@elytracms/core/cms-core'\n\n/** The one part of a config component entry the lookup reads. */\ninterface ManifestEntry {\n readonly manifest: ComponentManifest\n}\n\n/**\n * EC-595 A1 (critic correction 4) — the operations context's manifest half of the write\n * canonicaliser, built from the CONFIG's component entries (`elytra.config.ts`\n * `components`: `{ manifest, implementation }` pairs).\n *\n * Why not the studio registry's `studioManifestLookup()`: `lib/registry` registers the\n * implementations through `runtime-renderer`, and the operations bootstrap deliberately\n * stays off that edge (`lib/cms/document-gate.ts` explains the same constraint for the\n * validator seam). The manifests ARE the config's — the registry reads the very same\n * entries — so reading them here is the same declaration, one reader per layer, not a\n * second source. Only `component-registry`'s `manifestPropFields` (the one prop-def\n * projection, name-injected) is imported.\n *\n * `entries` is read lazily on every lookup (the active project's list can be\n * re-resolved); the per-list cache keeps a hot path a map read. A duplicate component id\n * resolves to the FIRST entry — the registry's `registerComponents` reports duplicates as\n * a visible issue, so a duplicate is already a broken config the studio surfaces.\n */\nexport function configManifestLookup(\n entries: () => readonly ManifestEntry[] | undefined,\n): PropFieldLookup {\n let cachedList: readonly ManifestEntry[] | undefined\n let cachedById = new Map<string, Record<string, FieldDef>>()\n return (componentId) => {\n const list = entries()\n if (list !== cachedList) {\n cachedList = list\n cachedById = new Map()\n for (const entry of list ?? []) {\n if (cachedById.has(entry.manifest.id)) continue\n cachedById.set(\n entry.manifest.id,\n Object.fromEntries(manifestPropFields(entry.manifest).map((field) => [field.name, field])),\n )\n }\n }\n return cachedById.get(componentId)\n }\n}\n\n/**\n * EC-595 A2 (A1 found-but-left #1) — the manifest lookup for the operation's OWN\n * project: the operations context's `manifestLookup` resolver. One operations bundle is\n * cached per deployment key and may serve several config projects with different\n * component lists, so a write to an inactive project canonicalises its props with THAT\n * project's manifests, never the active one's. `entriesFor` reads the config project\n * by id (see `configProjectFor`); one cached lookup per project id.\n */\nexport function projectManifestLookup(\n entriesFor: (projectId: string) => readonly ManifestEntry[] | undefined,\n): (projectId: string) => PropFieldLookup {\n const byProject = new Map<string, PropFieldLookup>()\n return (projectId) => {\n let lookup = byProject.get(projectId)\n if (!lookup) {\n lookup = configManifestLookup(() => entriesFor(projectId))\n byProject.set(projectId, lookup)\n }\n return lookup\n }\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { CollectionDef, LocaleConfig } from '@elytracms/core/cms-core';
|
|
2
|
+
import { ComponentRegistry } from '@elytracms/core/component-registry';
|
|
3
|
+
import { configProjectFor } from '../workspace/index.js';
|
|
4
|
+
export { configProjectFor };
|
|
5
|
+
/**
|
|
6
|
+
* EC-595 A2 — the config project an OPERATION runs for, by its project id (the slug),
|
|
7
|
+
* whether or not it is the ACTIVE project.
|
|
8
|
+
*
|
|
9
|
+
* One operations bundle is cached per deployment KEY (EC-179) and a deployment may
|
|
10
|
+
* serve several config projects, so everything an operation reads from config — its
|
|
11
|
+
* collections (A1's `resolveProjectCollections`), its component manifests (the write
|
|
12
|
+
* canonicaliser), its locales (the publish gate's runtime validator) — is resolved for
|
|
13
|
+
* the operation's own `projectId`, never for whichever project the UI has active (A1
|
|
14
|
+
* found-but-left #1: the manifest lookup read the active project's components).
|
|
15
|
+
*
|
|
16
|
+
* The lookup itself is the workspace's ONE reader of "the config project with this slug"
|
|
17
|
+
* (`configProjectFor`, `lib/workspace` — EC-582 folded the copy that lived here into it);
|
|
18
|
+
* this module derives what an operation reads from it.
|
|
19
|
+
*/
|
|
20
|
+
/** The locale config of the operation's project (see {@link configProjectFor}). */
|
|
21
|
+
export declare function projectLocaleConfig(projectId: string): LocaleConfig | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* The collections of the operation's project (see {@link configProjectFor}); `[]` for an
|
|
24
|
+
* id no config project declares (env-only wiring, engine fixtures). EC-585: the ONE
|
|
25
|
+
* reader of "the config project with this slug" — it replaced `lib/workspace`'s
|
|
26
|
+
* `resolveProjectCollections`, a second copy of the same lookup (EC-595 A2 found-but-left).
|
|
27
|
+
*/
|
|
28
|
+
export declare function projectCollections(projectId: string): CollectionDef[];
|
|
29
|
+
/**
|
|
30
|
+
* EC-582 — the component registry of the operation's project (see
|
|
31
|
+
* {@link configProjectFor}): its config component entries' MANIFESTS, registered once per
|
|
32
|
+
* list (the implementations are the renderer's, not the validator's — this stays off
|
|
33
|
+
* `runtime-renderer`, like the manifest lookup). What the publish gate's canonical
|
|
34
|
+
* validator judges compositions and props with — never the ACTIVE project's registry
|
|
35
|
+
* (critic row 13: on a deployment shared by projects with different component lists the
|
|
36
|
+
* gate judged an inactive project's props with the wrong manifests). `undefined` for a
|
|
37
|
+
* project that lists no components (or an id no config declares).
|
|
38
|
+
*/
|
|
39
|
+
export declare function projectComponentRegistry(projectId: string): ComponentRegistry | undefined;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ComponentRegistry } from '@elytracms/core/component-registry';
|
|
2
|
+
import { configProjectFor } from '../workspace/index.js';
|
|
3
|
+
export { configProjectFor };
|
|
4
|
+
/**
|
|
5
|
+
* EC-595 A2 — the config project an OPERATION runs for, by its project id (the slug),
|
|
6
|
+
* whether or not it is the ACTIVE project.
|
|
7
|
+
*
|
|
8
|
+
* One operations bundle is cached per deployment KEY (EC-179) and a deployment may
|
|
9
|
+
* serve several config projects, so everything an operation reads from config — its
|
|
10
|
+
* collections (A1's `resolveProjectCollections`), its component manifests (the write
|
|
11
|
+
* canonicaliser), its locales (the publish gate's runtime validator) — is resolved for
|
|
12
|
+
* the operation's own `projectId`, never for whichever project the UI has active (A1
|
|
13
|
+
* found-but-left #1: the manifest lookup read the active project's components).
|
|
14
|
+
*
|
|
15
|
+
* The lookup itself is the workspace's ONE reader of "the config project with this slug"
|
|
16
|
+
* (`configProjectFor`, `lib/workspace` — EC-582 folded the copy that lived here into it);
|
|
17
|
+
* this module derives what an operation reads from it.
|
|
18
|
+
*/
|
|
19
|
+
/** The locale config of the operation's project (see {@link configProjectFor}). */
|
|
20
|
+
export function projectLocaleConfig(projectId) {
|
|
21
|
+
return configProjectFor(projectId)?.locales;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The collections of the operation's project (see {@link configProjectFor}); `[]` for an
|
|
25
|
+
* id no config project declares (env-only wiring, engine fixtures). EC-585: the ONE
|
|
26
|
+
* reader of "the config project with this slug" — it replaced `lib/workspace`'s
|
|
27
|
+
* `resolveProjectCollections`, a second copy of the same lookup (EC-595 A2 found-but-left).
|
|
28
|
+
*/
|
|
29
|
+
export function projectCollections(projectId) {
|
|
30
|
+
return configProjectFor(projectId)?.collections ?? [];
|
|
31
|
+
}
|
|
32
|
+
const registries = new WeakMap();
|
|
33
|
+
/**
|
|
34
|
+
* EC-582 — the component registry of the operation's project (see
|
|
35
|
+
* {@link configProjectFor}): its config component entries' MANIFESTS, registered once per
|
|
36
|
+
* list (the implementations are the renderer's, not the validator's — this stays off
|
|
37
|
+
* `runtime-renderer`, like the manifest lookup). What the publish gate's canonical
|
|
38
|
+
* validator judges compositions and props with — never the ACTIVE project's registry
|
|
39
|
+
* (critic row 13: on a deployment shared by projects with different component lists the
|
|
40
|
+
* gate judged an inactive project's props with the wrong manifests). `undefined` for a
|
|
41
|
+
* project that lists no components (or an id no config declares).
|
|
42
|
+
*/
|
|
43
|
+
export function projectComponentRegistry(projectId) {
|
|
44
|
+
const entries = configProjectFor(projectId)?.components;
|
|
45
|
+
if (!entries)
|
|
46
|
+
return undefined;
|
|
47
|
+
let registry = registries.get(entries);
|
|
48
|
+
if (!registry) {
|
|
49
|
+
registry = new ComponentRegistry(entries.map((entry) => entry.manifest));
|
|
50
|
+
registries.set(entries, registry);
|
|
51
|
+
}
|
|
52
|
+
return registry;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=project-runtime-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-runtime-config.js","sourceRoot":"","sources":["../../../src/lib/operations/project-runtime-config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAA;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAE/C,OAAO,EAAE,gBAAgB,EAAE,CAAA;AAE3B;;;;;;;;;;;;;;GAcG;AAEH,mFAAmF;AACnF,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACnD,OAAO,gBAAgB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;AAC7C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,OAAO,gBAAgB,CAAC,SAAS,CAAC,EAAE,WAAW,IAAI,EAAE,CAAA;AACvD,CAAC;AAED,MAAM,UAAU,GAAG,IAAI,OAAO,EAA6B,CAAA;AAE3D;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CAAC,SAAiB;IACxD,MAAM,OAAO,GAAG,gBAAgB,CAAC,SAAS,CAAC,EAAE,UAAU,CAAA;IACvD,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAA;IAC9B,IAAI,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,IAAI,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAA;QACxE,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IACnC,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC","sourcesContent":["import type { CollectionDef, LocaleConfig } from '@elytracms/core/cms-core'\nimport { ComponentRegistry } from '@elytracms/core/component-registry'\nimport { configProjectFor } from '../workspace'\n\nexport { configProjectFor }\n\n/**\n * EC-595 A2 — the config project an OPERATION runs for, by its project id (the slug),\n * whether or not it is the ACTIVE project.\n *\n * One operations bundle is cached per deployment KEY (EC-179) and a deployment may\n * serve several config projects, so everything an operation reads from config — its\n * collections (A1's `resolveProjectCollections`), its component manifests (the write\n * canonicaliser), its locales (the publish gate's runtime validator) — is resolved for\n * the operation's own `projectId`, never for whichever project the UI has active (A1\n * found-but-left #1: the manifest lookup read the active project's components).\n *\n * The lookup itself is the workspace's ONE reader of \"the config project with this slug\"\n * (`configProjectFor`, `lib/workspace` — EC-582 folded the copy that lived here into it);\n * this module derives what an operation reads from it.\n */\n\n/** The locale config of the operation's project (see {@link configProjectFor}). */\nexport function projectLocaleConfig(projectId: string): LocaleConfig | undefined {\n return configProjectFor(projectId)?.locales\n}\n\n/**\n * The collections of the operation's project (see {@link configProjectFor}); `[]` for an\n * id no config project declares (env-only wiring, engine fixtures). EC-585: the ONE\n * reader of \"the config project with this slug\" — it replaced `lib/workspace`'s\n * `resolveProjectCollections`, a second copy of the same lookup (EC-595 A2 found-but-left).\n */\nexport function projectCollections(projectId: string): CollectionDef[] {\n return configProjectFor(projectId)?.collections ?? []\n}\n\nconst registries = new WeakMap<object, ComponentRegistry>()\n\n/**\n * EC-582 — the component registry of the operation's project (see\n * {@link configProjectFor}): its config component entries' MANIFESTS, registered once per\n * list (the implementations are the renderer's, not the validator's — this stays off\n * `runtime-renderer`, like the manifest lookup). What the publish gate's canonical\n * validator judges compositions and props with — never the ACTIVE project's registry\n * (critic row 13: on a deployment shared by projects with different component lists the\n * gate judged an inactive project's props with the wrong manifests). `undefined` for a\n * project that lists no components (or an id no config declares).\n */\nexport function projectComponentRegistry(projectId: string): ComponentRegistry | undefined {\n const entries = configProjectFor(projectId)?.components\n if (!entries) return undefined\n let registry = registries.get(entries)\n if (!registry) {\n registry = new ComponentRegistry(entries.map((entry) => entry.manifest))\n registries.set(entries, registry)\n }\n return registry\n}\n"]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { CmsDocument, CmsValidationIssue, DocumentRef } from '@elytracms/core/cms-core';
|
|
2
|
+
import type { CmsDocumentRecord } from '@elytracms/core/persistence';
|
|
3
|
+
import type { OperationError, OperationResult } from './operations-package.js';
|
|
4
|
+
/** EC-595 point 6: a publish refused because the draft moved is re-validated and retried this often. */
|
|
5
|
+
export declare const PUBLISH_ATTEMPTS = 3;
|
|
6
|
+
export interface PublishWithRevisionDeps {
|
|
7
|
+
/** The FORCED FLUSH: every queued patch of the document lands (the session adopts the committed draft). */
|
|
8
|
+
flush(ref: DocumentRef): Promise<void>;
|
|
9
|
+
/** The revision the session's view of the document is at. */
|
|
10
|
+
revisionOf(ref: DocumentRef): number | undefined;
|
|
11
|
+
/** `documents.publish({ ref, expectedRevision })`. */
|
|
12
|
+
publish(ref: DocumentRef, expectedRevision: number | undefined): Promise<OperationResult<CmsDocumentRecord>>;
|
|
13
|
+
/** Adopt a server row (the publish result, or a refusal's `current`). */
|
|
14
|
+
adopt(record: CmsDocumentRecord): void;
|
|
15
|
+
/** The session's view of the document after an adoption. */
|
|
16
|
+
documentOf(ref: DocumentRef): CmsDocument | undefined;
|
|
17
|
+
/** The session's (display) validation — re-run on the moved draft before a retry. */
|
|
18
|
+
validate(document: CmsDocument): readonly CmsValidationIssue[];
|
|
19
|
+
attempts?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* EC-595 binding point 6 — publish the revision the client validated (pure, deps
|
|
23
|
+
* injected — unit-tested without a React tree).
|
|
24
|
+
*
|
|
25
|
+
* FORCED FLUSH, then `documents.publish({ expectedRevision })`: the store refuses when
|
|
26
|
+
* the draft moved since (a colleague's save landed between the flush and the publish —
|
|
27
|
+
* Codex P05). On that refusal the session adopts the current row, RE-VALIDATES what
|
|
28
|
+
* would now be published, and retries — bounded by {@link PUBLISH_ATTEMPTS}, and never
|
|
29
|
+
* past a blocking issue the moved draft carries (the refusal is returned instead, so
|
|
30
|
+
* nothing unvalidated is published). Any other failure is returned as is.
|
|
31
|
+
*/
|
|
32
|
+
export declare function publishWithRevision(ref: DocumentRef, deps: PublishWithRevisionDeps): Promise<OperationResult<CmsDocumentRecord>>;
|
|
33
|
+
/**
|
|
34
|
+
* EC-585 — a publish the operation REFUSED (validation, a strict target, a conflict that
|
|
35
|
+
* did not settle, an unready asset library…), raised to the caller instead of resolving
|
|
36
|
+
* as if it had landed. Before this, the session's `publishDocument` recorded the error
|
|
37
|
+
* on the persist banner and RESOLVED, so the editor's controller set "Published" and
|
|
38
|
+
* promote toasted success on a refusal (read on the base: `cms-store.tsx:1065-1075`,
|
|
39
|
+
* `use-publish-controller.ts:94-96`). `message` is the localized sentence for a toast;
|
|
40
|
+
* `error` carries the full refusal (issues included).
|
|
41
|
+
*/
|
|
42
|
+
export declare class PublishRefusedError extends Error {
|
|
43
|
+
readonly error: OperationError;
|
|
44
|
+
constructor(error: OperationError, message: string);
|
|
45
|
+
}
|
|
46
|
+
export interface PublishInSessionDeps {
|
|
47
|
+
/** The optimistic flip of the session's published set (reconciled below). */
|
|
48
|
+
markPublished(ref: DocumentRef): void;
|
|
49
|
+
/** {@link publishWithRevision} for this session. */
|
|
50
|
+
publish(ref: DocumentRef): Promise<OperationResult<unknown>>;
|
|
51
|
+
/** Re-read the authoritative record (the pin, the flags) — after success AND refusal. */
|
|
52
|
+
reconcile(ref: DocumentRef): Promise<void>;
|
|
53
|
+
/** Surface the refusal on the session (the persist banner). */
|
|
54
|
+
report(error: OperationError): void;
|
|
55
|
+
/** The localized sentence for a refusal. */
|
|
56
|
+
describe(error: OperationError): string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* EC-585 — the session's publish of one document (pure, deps injected): optimistic flip
|
|
60
|
+
* → the one publish operation → reconcile against the backend record — and on a refusal,
|
|
61
|
+
* report it AND reject with {@link PublishRefusedError}, so no caller (the editor's
|
|
62
|
+
* Publish, the versions pane, promote) can show success for a publish that did not land.
|
|
63
|
+
*/
|
|
64
|
+
export declare function publishInSession(ref: DocumentRef, deps: PublishInSessionDeps): Promise<void>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** EC-595 point 6: a publish refused because the draft moved is re-validated and retried this often. */
|
|
2
|
+
export const PUBLISH_ATTEMPTS = 3;
|
|
3
|
+
/**
|
|
4
|
+
* EC-595 binding point 6 — publish the revision the client validated (pure, deps
|
|
5
|
+
* injected — unit-tested without a React tree).
|
|
6
|
+
*
|
|
7
|
+
* FORCED FLUSH, then `documents.publish({ expectedRevision })`: the store refuses when
|
|
8
|
+
* the draft moved since (a colleague's save landed between the flush and the publish —
|
|
9
|
+
* Codex P05). On that refusal the session adopts the current row, RE-VALIDATES what
|
|
10
|
+
* would now be published, and retries — bounded by {@link PUBLISH_ATTEMPTS}, and never
|
|
11
|
+
* past a blocking issue the moved draft carries (the refusal is returned instead, so
|
|
12
|
+
* nothing unvalidated is published). Any other failure is returned as is.
|
|
13
|
+
*/
|
|
14
|
+
export async function publishWithRevision(ref, deps) {
|
|
15
|
+
const attempts = deps.attempts ?? PUBLISH_ATTEMPTS;
|
|
16
|
+
let result;
|
|
17
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
18
|
+
await deps.flush(ref);
|
|
19
|
+
result = await deps.publish(ref, deps.revisionOf(ref));
|
|
20
|
+
if (result.ok) {
|
|
21
|
+
deps.adopt(result.value);
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
const error = result.error;
|
|
25
|
+
if (error.code !== 'conflict' || !('current' in error) || !error.current)
|
|
26
|
+
return result;
|
|
27
|
+
deps.adopt(error.current);
|
|
28
|
+
const view = deps.documentOf(ref) ?? error.current.document;
|
|
29
|
+
if (deps.validate(view).some((issue) => issue.severity === 'error'))
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* EC-585 — a publish the operation REFUSED (validation, a strict target, a conflict that
|
|
36
|
+
* did not settle, an unready asset library…), raised to the caller instead of resolving
|
|
37
|
+
* as if it had landed. Before this, the session's `publishDocument` recorded the error
|
|
38
|
+
* on the persist banner and RESOLVED, so the editor's controller set "Published" and
|
|
39
|
+
* promote toasted success on a refusal (read on the base: `cms-store.tsx:1065-1075`,
|
|
40
|
+
* `use-publish-controller.ts:94-96`). `message` is the localized sentence for a toast;
|
|
41
|
+
* `error` carries the full refusal (issues included).
|
|
42
|
+
*/
|
|
43
|
+
export class PublishRefusedError extends Error {
|
|
44
|
+
error;
|
|
45
|
+
constructor(error, message) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.error = error;
|
|
48
|
+
this.name = 'PublishRefusedError';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* EC-585 — the session's publish of one document (pure, deps injected): optimistic flip
|
|
53
|
+
* → the one publish operation → reconcile against the backend record — and on a refusal,
|
|
54
|
+
* report it AND reject with {@link PublishRefusedError}, so no caller (the editor's
|
|
55
|
+
* Publish, the versions pane, promote) can show success for a publish that did not land.
|
|
56
|
+
*/
|
|
57
|
+
export async function publishInSession(ref, deps) {
|
|
58
|
+
deps.markPublished(ref);
|
|
59
|
+
const result = await deps.publish(ref);
|
|
60
|
+
await deps.reconcile(ref);
|
|
61
|
+
if (!result.ok) {
|
|
62
|
+
deps.report(result.error);
|
|
63
|
+
throw new PublishRefusedError(result.error, deps.describe(result.error));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=publish-with-revision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-with-revision.js","sourceRoot":"","sources":["../../../src/lib/operations/publish-with-revision.ts"],"names":[],"mappings":"AAIA,wGAAwG;AACxG,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAA;AAkBjC;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,GAAgB,EAChB,IAA6B;IAE7B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,gBAAgB,CAAA;IAClD,IAAI,MAAsD,CAAA;IAC1D,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,QAAQ,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACrB,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAA;QACtD,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACxB,OAAO,MAAM,CAAA;QACf,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAA;QAC1B,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO;YAAE,OAAO,MAAM,CAAA;QACvF,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAA;QAC3D,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC;YAAE,OAAO,MAAM,CAAA;IACpF,CAAC;IACD,OAAO,MAAO,CAAA;AAChB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAEjC;IADX,YACW,KAAqB,EAC9B,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAA;QAHL,UAAK,GAAL,KAAK,CAAgB;QAI9B,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAA;IACnC,CAAC;CACF;AAeD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAgB,EAAE,IAA0B;IACjF,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAA;IACvB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACtC,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;IACzB,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACzB,MAAM,IAAI,mBAAmB,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;IAC1E,CAAC;AACH,CAAC","sourcesContent":["import type { CmsDocument, CmsValidationIssue, DocumentRef } from '@elytracms/core/cms-core'\nimport type { CmsDocumentRecord } from '@elytracms/core/persistence'\nimport type { OperationError, OperationResult } from './operations-package'\n\n/** EC-595 point 6: a publish refused because the draft moved is re-validated and retried this often. */\nexport const PUBLISH_ATTEMPTS = 3\n\nexport interface PublishWithRevisionDeps {\n /** The FORCED FLUSH: every queued patch of the document lands (the session adopts the committed draft). */\n flush(ref: DocumentRef): Promise<void>\n /** The revision the session's view of the document is at. */\n revisionOf(ref: DocumentRef): number | undefined\n /** `documents.publish({ ref, expectedRevision })`. */\n publish(ref: DocumentRef, expectedRevision: number | undefined): Promise<OperationResult<CmsDocumentRecord>>\n /** Adopt a server row (the publish result, or a refusal's `current`). */\n adopt(record: CmsDocumentRecord): void\n /** The session's view of the document after an adoption. */\n documentOf(ref: DocumentRef): CmsDocument | undefined\n /** The session's (display) validation — re-run on the moved draft before a retry. */\n validate(document: CmsDocument): readonly CmsValidationIssue[]\n attempts?: number\n}\n\n/**\n * EC-595 binding point 6 — publish the revision the client validated (pure, deps\n * injected — unit-tested without a React tree).\n *\n * FORCED FLUSH, then `documents.publish({ expectedRevision })`: the store refuses when\n * the draft moved since (a colleague's save landed between the flush and the publish —\n * Codex P05). On that refusal the session adopts the current row, RE-VALIDATES what\n * would now be published, and retries — bounded by {@link PUBLISH_ATTEMPTS}, and never\n * past a blocking issue the moved draft carries (the refusal is returned instead, so\n * nothing unvalidated is published). Any other failure is returned as is.\n */\nexport async function publishWithRevision(\n ref: DocumentRef,\n deps: PublishWithRevisionDeps,\n): Promise<OperationResult<CmsDocumentRecord>> {\n const attempts = deps.attempts ?? PUBLISH_ATTEMPTS\n let result: OperationResult<CmsDocumentRecord> | undefined\n for (let attempt = 0; attempt < attempts; attempt += 1) {\n await deps.flush(ref)\n result = await deps.publish(ref, deps.revisionOf(ref))\n if (result.ok) {\n deps.adopt(result.value)\n return result\n }\n const error = result.error\n if (error.code !== 'conflict' || !('current' in error) || !error.current) return result\n deps.adopt(error.current)\n const view = deps.documentOf(ref) ?? error.current.document\n if (deps.validate(view).some((issue) => issue.severity === 'error')) return result\n }\n return result!\n}\n\n/**\n * EC-585 — a publish the operation REFUSED (validation, a strict target, a conflict that\n * did not settle, an unready asset library…), raised to the caller instead of resolving\n * as if it had landed. Before this, the session's `publishDocument` recorded the error\n * on the persist banner and RESOLVED, so the editor's controller set \"Published\" and\n * promote toasted success on a refusal (read on the base: `cms-store.tsx:1065-1075`,\n * `use-publish-controller.ts:94-96`). `message` is the localized sentence for a toast;\n * `error` carries the full refusal (issues included).\n */\nexport class PublishRefusedError extends Error {\n constructor(\n readonly error: OperationError,\n message: string,\n ) {\n super(message)\n this.name = 'PublishRefusedError'\n }\n}\n\nexport interface PublishInSessionDeps {\n /** The optimistic flip of the session's published set (reconciled below). */\n markPublished(ref: DocumentRef): void\n /** {@link publishWithRevision} for this session. */\n publish(ref: DocumentRef): Promise<OperationResult<unknown>>\n /** Re-read the authoritative record (the pin, the flags) — after success AND refusal. */\n reconcile(ref: DocumentRef): Promise<void>\n /** Surface the refusal on the session (the persist banner). */\n report(error: OperationError): void\n /** The localized sentence for a refusal. */\n describe(error: OperationError): string\n}\n\n/**\n * EC-585 — the session's publish of one document (pure, deps injected): optimistic flip\n * → the one publish operation → reconcile against the backend record — and on a refusal,\n * report it AND reject with {@link PublishRefusedError}, so no caller (the editor's\n * Publish, the versions pane, promote) can show success for a publish that did not land.\n */\nexport async function publishInSession(ref: DocumentRef, deps: PublishInSessionDeps): Promise<void> {\n deps.markPublished(ref)\n const result = await deps.publish(ref)\n await deps.reconcile(ref)\n if (!result.ok) {\n deps.report(result.error)\n throw new PublishRefusedError(result.error, deps.describe(result.error))\n }\n}\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { LocaleConfig } from '@elytracms/core/cms-core';
|
|
2
|
+
import type { ComponentRegistry } from '@elytracms/core/component-registry';
|
|
3
|
+
import type { CollectionsResolver, DocumentValidator } from './operations-package.js';
|
|
4
|
+
/**
|
|
5
|
+
* EC-595 A2 → EC-582 — THE studio publish gate's validator, bound to the OPERATIONS
|
|
6
|
+
* RUNTIME (Codex F23, ruled EC-565 1f): built once per operations bundle, never read from
|
|
7
|
+
* a React mount. Since EC-582 it is core's `createDocumentValidator`
|
|
8
|
+
* (`@elytracms/core/validation` — the same gate a headless embedder and the CLI build):
|
|
9
|
+
* per call it resolves the OPERATION's project — its collections, locales and component
|
|
10
|
+
* registry (`registry(projectId)`: THAT project's manifests, never the active project's —
|
|
11
|
+
* a deployment may serve several projects with different component lists) — prefetches
|
|
12
|
+
* the oracles through the operation's adapter and runs the canonical validator.
|
|
13
|
+
*
|
|
14
|
+
* `@elytracms/core/validation` (the composition leash pulls in the rich-text parser) is
|
|
15
|
+
* imported DYNAMICALLY inside the async validator, so the operations bootstrap's static
|
|
16
|
+
* module graph does not grow by it (measured before/after, EC-595 Done part A2 and EC-582
|
|
17
|
+
* Done).
|
|
18
|
+
*
|
|
19
|
+
* The editor's live validation keeps reading the session (that is display); the GATE
|
|
20
|
+
* reads this. The refusals and their catalog keys (`GATE_*_MESSAGE_KEY`) are declared
|
|
21
|
+
* once, in core.
|
|
22
|
+
*/
|
|
23
|
+
export interface RuntimeValidatorOptions {
|
|
24
|
+
/** The resolved config's collections, per project (the operations context's resolver). */
|
|
25
|
+
readonly collections: CollectionsResolver;
|
|
26
|
+
/** The project's locale config; `undefined` → the project declares none (a blocking refusal). */
|
|
27
|
+
readonly localeConfig: (projectId: string) => LocaleConfig | undefined;
|
|
28
|
+
/** The project's component registry (its manifests); `undefined` → it lists no components. */
|
|
29
|
+
readonly registry: (projectId: string) => ComponentRegistry | undefined;
|
|
30
|
+
}
|
|
31
|
+
/** Build THE gate validator for an operations bundle. */
|
|
32
|
+
export declare function createRuntimeDocumentValidator(options: RuntimeValidatorOptions): DocumentValidator;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Build THE gate validator for an operations bundle. */
|
|
2
|
+
export function createRuntimeDocumentValidator(options) {
|
|
3
|
+
let gate;
|
|
4
|
+
return async (document, context) => {
|
|
5
|
+
gate ??= import('@elytracms/core/validation').then(({ createDocumentValidator }) => createDocumentValidator({
|
|
6
|
+
project: (projectId) => {
|
|
7
|
+
const registry = options.registry(projectId);
|
|
8
|
+
return {
|
|
9
|
+
collections: options.collections(projectId),
|
|
10
|
+
locales: options.localeConfig(projectId),
|
|
11
|
+
...(registry ? { registry } : {}),
|
|
12
|
+
};
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
15
|
+
return (await gate)(document, context);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=runtime-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-validator.js","sourceRoot":"","sources":["../../../src/lib/operations/runtime-validator.ts"],"names":[],"mappings":"AAiCA,yDAAyD;AACzD,MAAM,UAAU,8BAA8B,CAAC,OAAgC;IAC7E,IAAI,IAA8H,CAAA;IAClI,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;QACjC,IAAI,KAAK,MAAM,CAAC,4BAA4B,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,uBAAuB,EAAE,EAAE,EAAE,CACjF,uBAAuB,CAAC;YACtB,OAAO,EAAE,CAAC,SAAS,EAAE,EAAE;gBACrB,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;gBAC5C,OAAO;oBACL,WAAW,EAAE,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC;oBAC3C,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,SAAS,CAAC;oBACxC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAClC,CAAA;YACH,CAAC;SACF,CAAC,CACH,CAAA;QACD,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IACxC,CAAC,CAAA;AACH,CAAC","sourcesContent":["import type { CmsDocument, CmsValidationIssue, LocaleConfig } from '@elytracms/core/cms-core'\nimport type { ComponentRegistry } from '@elytracms/core/component-registry'\nimport type { CollectionsResolver, DocumentValidator } from './operations-package'\n\n/**\n * EC-595 A2 → EC-582 — THE studio publish gate's validator, bound to the OPERATIONS\n * RUNTIME (Codex F23, ruled EC-565 1f): built once per operations bundle, never read from\n * a React mount. Since EC-582 it is core's `createDocumentValidator`\n * (`@elytracms/core/validation` — the same gate a headless embedder and the CLI build):\n * per call it resolves the OPERATION's project — its collections, locales and component\n * registry (`registry(projectId)`: THAT project's manifests, never the active project's —\n * a deployment may serve several projects with different component lists) — prefetches\n * the oracles through the operation's adapter and runs the canonical validator.\n *\n * `@elytracms/core/validation` (the composition leash pulls in the rich-text parser) is\n * imported DYNAMICALLY inside the async validator, so the operations bootstrap's static\n * module graph does not grow by it (measured before/after, EC-595 Done part A2 and EC-582\n * Done).\n *\n * The editor's live validation keeps reading the session (that is display); the GATE\n * reads this. The refusals and their catalog keys (`GATE_*_MESSAGE_KEY`) are declared\n * once, in core.\n */\n\nexport interface RuntimeValidatorOptions {\n /** The resolved config's collections, per project (the operations context's resolver). */\n readonly collections: CollectionsResolver\n /** The project's locale config; `undefined` → the project declares none (a blocking refusal). */\n readonly localeConfig: (projectId: string) => LocaleConfig | undefined\n /** The project's component registry (its manifests); `undefined` → it lists no components. */\n readonly registry: (projectId: string) => ComponentRegistry | undefined\n}\n\n/** Build THE gate validator for an operations bundle. */\nexport function createRuntimeDocumentValidator(options: RuntimeValidatorOptions): DocumentValidator {\n let gate: Promise<(document: CmsDocument, context: Parameters<DocumentValidator>[1]) => Promise<CmsValidationIssue[]>> | undefined\n return async (document, context) => {\n gate ??= import('@elytracms/core/validation').then(({ createDocumentValidator }) =>\n createDocumentValidator({\n project: (projectId) => {\n const registry = options.registry(projectId)\n return {\n collections: options.collections(projectId),\n locales: options.localeConfig(projectId),\n ...(registry ? { registry } : {}),\n }\n },\n }),\n )\n return (await gate)(document, context)\n }\n}\n"]}
|