@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,100 +1,479 @@
|
|
|
1
|
-
import { selfTypeDocument } from '@elytracms/core/cms-core';
|
|
1
|
+
import { applyDocumentPatches, documentKey, formatValuePath, selfTypeDocument, touchedPaths, valuePathsOverlap, } from '@elytracms/core/cms-core';
|
|
2
2
|
import { createPersistQueue } from './persist-queue.js';
|
|
3
|
+
/** A patch's identity within one call: op + target path (no two value writes of one path share a run). */
|
|
4
|
+
function targetKey(op, path) {
|
|
5
|
+
return `${op} ${formatValuePath(path)}`;
|
|
6
|
+
}
|
|
7
|
+
function patchTarget(patch) {
|
|
8
|
+
return patch.op === 'insert' ? [...patch.at.parent, { kind: 'node', id: patch.node.id }] : patch.path;
|
|
9
|
+
}
|
|
10
|
+
function conflictId(ref, path) {
|
|
11
|
+
return `${documentKey(ref)}|${formatValuePath(path)}`;
|
|
12
|
+
}
|
|
13
|
+
/** Paths two patches have in common (either touches a prefix of the other's). */
|
|
14
|
+
function patchesOverlap(a, b) {
|
|
15
|
+
return touchedPaths(a).some((p) => touchedPaths(b).some((q) => valuePathsOverlap(p, q)));
|
|
16
|
+
}
|
|
17
|
+
/** A `set`/`unset` on exactly one path — the shape a later edit of the same field supersedes. */
|
|
18
|
+
function isValueWrite(patch) {
|
|
19
|
+
return patch.op === 'set' || patch.op === 'unset';
|
|
20
|
+
}
|
|
21
|
+
function samePath(a, b) {
|
|
22
|
+
return a.length === b.length && valuePathsOverlap(a, b);
|
|
23
|
+
}
|
|
3
24
|
export function createCmsPersister(options) {
|
|
4
|
-
const queue = createPersistQueue()
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
25
|
+
const queue = createPersistQueue((error) => {
|
|
26
|
+
options.onError({
|
|
27
|
+
code: 'unknown',
|
|
28
|
+
message: error instanceof Error ? error.message : String(error),
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
const entries = new Map();
|
|
32
|
+
function entryOf(ref) {
|
|
33
|
+
const key = documentKey(ref);
|
|
34
|
+
let entry = entries.get(key);
|
|
35
|
+
if (!entry) {
|
|
36
|
+
entry = {
|
|
37
|
+
ref: { collection: ref.collection, id: ref.id },
|
|
38
|
+
base: undefined,
|
|
39
|
+
provisional: undefined,
|
|
40
|
+
queue: [],
|
|
41
|
+
inFlight: [],
|
|
42
|
+
busy: false,
|
|
43
|
+
conflicts: [],
|
|
44
|
+
held: undefined,
|
|
45
|
+
scheduled: false,
|
|
46
|
+
deleted: false,
|
|
47
|
+
};
|
|
48
|
+
entries.set(key, entry);
|
|
49
|
+
}
|
|
50
|
+
return entry;
|
|
51
|
+
}
|
|
52
|
+
/** EC-586: THE write canonicaliser over a whole document a writer hands us (create/import). */
|
|
53
|
+
function canonical(doc) {
|
|
16
54
|
const collection = options.getCollections().find((c) => c.id === doc.collection);
|
|
17
55
|
if (!collection)
|
|
18
56
|
return doc;
|
|
19
57
|
return selfTypeDocument(doc, collection, options.lookupManifest ? { lookupManifest: options.lookupManifest } : {});
|
|
20
58
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
59
|
+
/** The session's view: base ⊕ unresolved mine ⊕ in-flight ⊕ queued (the pure merge, no log). */
|
|
60
|
+
function display(entry) {
|
|
61
|
+
const baseDocument = entry.base?.document ?? entry.provisional;
|
|
62
|
+
if (entry.deleted || !baseDocument)
|
|
63
|
+
return undefined;
|
|
64
|
+
const patches = [
|
|
65
|
+
...entry.conflicts.map((conflict) => conflict.patch),
|
|
66
|
+
...entry.inFlight.map((queued) => queued.patch),
|
|
67
|
+
...entry.queue.map((queued) => queued.patch),
|
|
68
|
+
];
|
|
69
|
+
if (patches.length === 0)
|
|
70
|
+
return baseDocument;
|
|
71
|
+
return applyDocumentPatches({ document: baseDocument, patches, since: [] }).document;
|
|
72
|
+
}
|
|
73
|
+
function emitDocument(entry) {
|
|
74
|
+
if (entry.deleted)
|
|
75
|
+
return;
|
|
76
|
+
const shown = display(entry);
|
|
77
|
+
if (shown)
|
|
78
|
+
options.onDocument?.(entry.ref, shown);
|
|
79
|
+
}
|
|
80
|
+
function emitConflicts(entry) {
|
|
81
|
+
options.onConflicts?.(entry.ref, [...entry.conflicts]);
|
|
82
|
+
}
|
|
83
|
+
/** Adopt a server row as the base (never a lower revision), re-derive the view. */
|
|
84
|
+
function adopt(entry, record) {
|
|
85
|
+
if (entry.base && record.revision < entry.base.revision)
|
|
86
|
+
return;
|
|
87
|
+
entry.base = record;
|
|
88
|
+
options.onRecord?.(record);
|
|
89
|
+
emitDocument(entry);
|
|
90
|
+
}
|
|
91
|
+
/** After a write: apply a subscription row that arrived meanwhile, if newer. */
|
|
92
|
+
function adoptHeld(entry) {
|
|
93
|
+
const held = entry.held;
|
|
94
|
+
entry.held = undefined;
|
|
95
|
+
if (held === undefined)
|
|
96
|
+
return;
|
|
97
|
+
if (held === null) {
|
|
98
|
+
markGone(entry);
|
|
99
|
+
return;
|
|
32
100
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
101
|
+
if (!entry.base || held.revision > entry.base.revision)
|
|
102
|
+
adopt(entry, held);
|
|
103
|
+
}
|
|
104
|
+
function markGone(entry) {
|
|
105
|
+
if (entry.deleted)
|
|
106
|
+
return;
|
|
107
|
+
entry.deleted = true;
|
|
108
|
+
entry.queue = [];
|
|
109
|
+
options.onDocument?.(entry.ref, undefined);
|
|
110
|
+
}
|
|
111
|
+
function schedule(entry) {
|
|
112
|
+
if (entry.scheduled)
|
|
113
|
+
return;
|
|
114
|
+
entry.scheduled = true;
|
|
115
|
+
queue.push(async () => {
|
|
116
|
+
entry.scheduled = false;
|
|
117
|
+
await drain(entry);
|
|
118
|
+
adoptHeld(entry);
|
|
36
119
|
});
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
120
|
+
}
|
|
121
|
+
/** Who changed a refused path — the newest patch-log row in (base, current] touching it. Best-effort. */
|
|
122
|
+
async function resolveActors(entry, sentBase, refused) {
|
|
123
|
+
try {
|
|
124
|
+
const operations = await options.getOperations();
|
|
125
|
+
const log = await operations.documents.listPatches({
|
|
126
|
+
projectId: options.projectId,
|
|
127
|
+
ref: entry.ref,
|
|
128
|
+
afterRevision: sentBase,
|
|
129
|
+
});
|
|
130
|
+
if (!log.ok)
|
|
131
|
+
return;
|
|
132
|
+
let changed = false;
|
|
133
|
+
entry.conflicts = entry.conflicts.map((conflict) => {
|
|
134
|
+
if (conflict.actor !== undefined || !refused.some((r) => r.id === conflict.id))
|
|
135
|
+
return conflict;
|
|
136
|
+
const row = [...log.value]
|
|
137
|
+
.reverse()
|
|
138
|
+
.find((candidate) => candidate.kind === 'replace' ||
|
|
139
|
+
candidate.patches.some((patch) => touchedPaths(patch).some((path) => valuePathsOverlap(path, conflict.path))));
|
|
140
|
+
if (!row?.actor)
|
|
141
|
+
return conflict;
|
|
142
|
+
changed = true;
|
|
143
|
+
return { ...conflict, actor: row.actor };
|
|
144
|
+
});
|
|
145
|
+
if (changed)
|
|
146
|
+
emitConflicts(entry);
|
|
40
147
|
}
|
|
41
|
-
|
|
42
|
-
|
|
148
|
+
catch {
|
|
149
|
+
// The actor is decoration on a conflict the user can already resolve.
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/** Send the queue, one run of equal base at a time, until it is empty or a write fails. */
|
|
153
|
+
async function drain(entry) {
|
|
154
|
+
while (entry.queue.length > 0 && !entry.deleted) {
|
|
155
|
+
const operations = await options.getOperations();
|
|
156
|
+
if (!entry.base) {
|
|
157
|
+
// Untracked (a document the session never hydrated a revision for): read it.
|
|
158
|
+
// The user's view of it is the session's, so this is the narrowest window left.
|
|
159
|
+
const read = await operations.documents.get({ projectId: options.projectId, ref: entry.ref });
|
|
160
|
+
if (!read.ok) {
|
|
161
|
+
options.onError(read.error);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
entry.base = read.value;
|
|
165
|
+
}
|
|
166
|
+
const resolveBase = (queued) => queued.base ?? entry.base.revision;
|
|
167
|
+
const first = resolveBase(entry.queue[0]);
|
|
168
|
+
let end = 1;
|
|
169
|
+
while (end < entry.queue.length && resolveBase(entry.queue[end]) === first)
|
|
170
|
+
end += 1;
|
|
171
|
+
const run = entry.queue.splice(0, end);
|
|
172
|
+
entry.inFlight = run;
|
|
173
|
+
const result = await operations.documents.patch({
|
|
43
174
|
projectId: options.projectId,
|
|
44
|
-
|
|
175
|
+
ref: entry.ref,
|
|
176
|
+
baseRevision: first,
|
|
177
|
+
patches: run.map((queued) => queued.patch),
|
|
45
178
|
});
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
179
|
+
entry.inFlight = [];
|
|
180
|
+
if (result.ok) {
|
|
181
|
+
rebaseCovered(entry, run, result.value.revision);
|
|
182
|
+
adopt(entry, result.value);
|
|
183
|
+
adoptHeld(entry);
|
|
184
|
+
settleAll(run, { status: 'applied', record: result.value });
|
|
185
|
+
continue;
|
|
49
186
|
}
|
|
187
|
+
const error = result.error;
|
|
188
|
+
if (error.code === 'conflict' && error.refused && error.current) {
|
|
189
|
+
const current = error.current;
|
|
190
|
+
// Refusals are matched to the run by op + target path (the operation re-parses
|
|
191
|
+
// and canonicalises the patches, so object identity does not survive the call).
|
|
192
|
+
const refusedByKey = new Map(error.refused.map((refused) => [targetKey(refused.patch.op, refused.path), refused]));
|
|
193
|
+
const applied = [];
|
|
194
|
+
const conflicts = [];
|
|
195
|
+
for (const queued of run) {
|
|
196
|
+
const refused = refusedByKey.get(targetKey(queued.patch.op, patchTarget(queued.patch)));
|
|
197
|
+
if (!refused) {
|
|
198
|
+
applied.push(queued);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
// MINE is what the user wrote (the queued patch), not the canonicalised echo.
|
|
202
|
+
const conflict = upsertConflict(entry, { ...refused, patch: queued.patch }, current.revision);
|
|
203
|
+
conflicts.push(conflict);
|
|
204
|
+
for (const settle of queued.settle)
|
|
205
|
+
settle({ status: 'refused', conflicts: [conflict] });
|
|
206
|
+
}
|
|
207
|
+
rebaseCovered(entry, applied, current.revision);
|
|
208
|
+
adopt(entry, current);
|
|
209
|
+
adoptHeld(entry);
|
|
210
|
+
emitConflicts(entry);
|
|
211
|
+
settleAll(applied, { status: 'applied', record: current });
|
|
212
|
+
void resolveActors(entry, first, conflicts);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (error.code === 'conflict' && 'current' in error && error.current === null) {
|
|
216
|
+
// The document was deleted since — nothing to write into.
|
|
217
|
+
markGone(entry);
|
|
218
|
+
options.onError(error);
|
|
219
|
+
settleAll(run, { status: 'failed', error });
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
// A transport / authorization failure: the patches stay local (shown, unsent) and
|
|
223
|
+
// the error is visible; the next commit or forced flush retries them. The commits
|
|
224
|
+
// waiting on them learn of the failure now.
|
|
225
|
+
settleAll(run, { status: 'failed', error });
|
|
226
|
+
entry.queue.unshift(...run.map((queued) => ({ ...queued, settle: [] })));
|
|
227
|
+
adoptHeld(entry);
|
|
228
|
+
options.onError(error);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function settleAll(queued, outcome) {
|
|
233
|
+
for (const item of queued)
|
|
234
|
+
for (const settle of item.settle)
|
|
235
|
+
settle(outcome);
|
|
236
|
+
}
|
|
237
|
+
/** Queued patches on a path our own write just covered move their base to that write's revision. */
|
|
238
|
+
function rebaseCovered(entry, written, revision) {
|
|
239
|
+
if (written.length === 0)
|
|
240
|
+
return;
|
|
241
|
+
for (const queued of entry.queue) {
|
|
242
|
+
if (queued.base !== undefined && queued.base >= revision)
|
|
243
|
+
continue;
|
|
244
|
+
if (written.some((done) => patchesOverlap(done.patch, queued.patch)))
|
|
245
|
+
queued.base = revision;
|
|
50
246
|
}
|
|
51
|
-
baselineDone = true;
|
|
52
|
-
return true;
|
|
53
247
|
}
|
|
54
|
-
function
|
|
248
|
+
function upsertConflict(entry, refused, revision) {
|
|
249
|
+
const conflict = {
|
|
250
|
+
id: conflictId(entry.ref, refused.path),
|
|
251
|
+
ref: entry.ref,
|
|
252
|
+
patch: refused.patch,
|
|
253
|
+
path: refused.path,
|
|
254
|
+
theirs: refused.current,
|
|
255
|
+
reason: refused.reason,
|
|
256
|
+
revision,
|
|
257
|
+
};
|
|
258
|
+
entry.conflicts = [...entry.conflicts.filter((c) => c.id !== conflict.id), conflict];
|
|
259
|
+
return conflict;
|
|
260
|
+
}
|
|
261
|
+
function command(entry, run) {
|
|
55
262
|
queue.push(async () => {
|
|
56
|
-
// A failed baseline already surfaced through onError — running the
|
|
57
|
-
// command against missing records would only add a confusing follow-up.
|
|
58
|
-
if (!(await ensureBaselineNow()))
|
|
59
|
-
return;
|
|
60
263
|
const operations = await options.getOperations();
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
264
|
+
entry.busy = true;
|
|
265
|
+
try {
|
|
266
|
+
const result = await run(operations);
|
|
267
|
+
if (!result.ok)
|
|
268
|
+
options.onError(result.error);
|
|
269
|
+
}
|
|
270
|
+
finally {
|
|
271
|
+
entry.busy = false;
|
|
272
|
+
adoptHeld(entry);
|
|
273
|
+
}
|
|
64
274
|
});
|
|
65
275
|
}
|
|
66
276
|
return {
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
// EC-449: hand the authoritative saved record back to the session so it
|
|
72
|
-
// adopts the server-computed `hasUnpublishedChanges` verdict (the queue
|
|
73
|
-
// swallows successful results otherwise).
|
|
74
|
-
if (result.ok)
|
|
75
|
-
options.onDocumentSaved?.(result.value);
|
|
76
|
-
return result;
|
|
77
|
-
});
|
|
277
|
+
track(row) {
|
|
278
|
+
const entry = entryOf(row.document);
|
|
279
|
+
if (!entry.base || row.revision > entry.base.revision)
|
|
280
|
+
entry.base = row;
|
|
78
281
|
},
|
|
79
|
-
|
|
80
|
-
|
|
282
|
+
revisionOf: (ref) => entries.get(documentKey(ref))?.base?.revision,
|
|
283
|
+
documentOf: (ref) => {
|
|
284
|
+
const entry = entries.get(documentKey(ref));
|
|
285
|
+
return entry ? display(entry) : undefined;
|
|
81
286
|
},
|
|
82
|
-
|
|
83
|
-
|
|
287
|
+
adoptRecord(record) {
|
|
288
|
+
const entry = entryOf(record.document);
|
|
289
|
+
if (entry.deleted)
|
|
290
|
+
return;
|
|
291
|
+
if (!entry.base || record.revision > entry.base.revision)
|
|
292
|
+
adopt(entry, record);
|
|
84
293
|
},
|
|
85
|
-
|
|
86
|
-
|
|
294
|
+
commit(ref, patches, view, bases) {
|
|
295
|
+
const entry = entryOf(ref);
|
|
296
|
+
if (patches.length === 0 || entry.deleted) {
|
|
297
|
+
return Promise.resolve({
|
|
298
|
+
status: 'failed',
|
|
299
|
+
error: { code: 'not-found', message: 'The document is gone.', entity: 'cms-document', key: documentKey(ref) },
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
if (!entry.base && !entry.provisional && view)
|
|
303
|
+
entry.provisional = view;
|
|
304
|
+
let conflictsChanged = false;
|
|
305
|
+
const waits = [];
|
|
306
|
+
for (const [index, patch] of patches.entries()) {
|
|
307
|
+
const madeAt = bases?.[index] ?? entry.base?.revision;
|
|
308
|
+
let settle;
|
|
309
|
+
waits.push(new Promise((resolve) => (settle = resolve)));
|
|
310
|
+
// A field with an unresolved conflict: the new value replaces MINE, unsent —
|
|
311
|
+
// the user decides (keep mine sends it), never an implicit overwrite of theirs.
|
|
312
|
+
const open = entry.conflicts.find((conflict) => patchesOverlap(conflict.patch, patch));
|
|
313
|
+
if (open && isValueWrite(patch) && isValueWrite(open.patch) && samePath(open.patch.path, patch.path)) {
|
|
314
|
+
const replaced = { ...open, patch };
|
|
315
|
+
entry.conflicts = entry.conflicts.map((c) => (c === open ? replaced : c));
|
|
316
|
+
conflictsChanged = true;
|
|
317
|
+
settle({ status: 'refused', conflicts: [replaced] });
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
// A later value write of the same field supersedes an unsent one (keeping the
|
|
321
|
+
// EARLIER base — the user never saw a foreign value of a field they were typing in).
|
|
322
|
+
if (isValueWrite(patch)) {
|
|
323
|
+
const pending = entry.queue.findIndex((queued) => isValueWrite(queued.patch) && samePath(queued.patch.path, patch.path));
|
|
324
|
+
if (pending >= 0) {
|
|
325
|
+
const superseded = entry.queue[pending];
|
|
326
|
+
entry.queue[pending] = { patch, base: superseded.base, settle: [...superseded.settle, settle] };
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
entry.queue.push({ patch, base: madeAt, settle: [settle] });
|
|
331
|
+
}
|
|
332
|
+
emitDocument(entry);
|
|
333
|
+
if (conflictsChanged)
|
|
334
|
+
emitConflicts(entry);
|
|
335
|
+
schedule(entry);
|
|
336
|
+
// The commit's outcome: failed if any of its patches failed, refused if any was.
|
|
337
|
+
return Promise.all(waits).then((outcomes) => {
|
|
338
|
+
const failed = outcomes.find((o) => o.status === 'failed');
|
|
339
|
+
if (failed)
|
|
340
|
+
return failed;
|
|
341
|
+
const refused = outcomes.flatMap((o) => (o.status === 'refused' ? o.conflicts : []));
|
|
342
|
+
if (refused.length > 0)
|
|
343
|
+
return { status: 'refused', conflicts: refused };
|
|
344
|
+
return outcomes[outcomes.length - 1];
|
|
345
|
+
});
|
|
87
346
|
},
|
|
88
|
-
|
|
89
|
-
|
|
347
|
+
create(document) {
|
|
348
|
+
const doc = canonical(document);
|
|
349
|
+
const entry = entryOf(doc);
|
|
350
|
+
entry.deleted = false;
|
|
351
|
+
// Optimistic: the session shows the document at once; the base is the created row.
|
|
352
|
+
entry.provisional = doc;
|
|
353
|
+
options.onDocument?.(entry.ref, doc);
|
|
354
|
+
queue.push(async () => {
|
|
355
|
+
const operations = await options.getOperations();
|
|
356
|
+
entry.busy = true;
|
|
357
|
+
try {
|
|
358
|
+
const result = await operations.documents.replace({
|
|
359
|
+
projectId: options.projectId,
|
|
360
|
+
document: doc,
|
|
361
|
+
precondition: { ifAbsent: true },
|
|
362
|
+
});
|
|
363
|
+
if (result.ok)
|
|
364
|
+
adopt(entry, result.value);
|
|
365
|
+
else
|
|
366
|
+
options.onError(result.error);
|
|
367
|
+
}
|
|
368
|
+
finally {
|
|
369
|
+
entry.busy = false;
|
|
370
|
+
adoptHeld(entry);
|
|
371
|
+
}
|
|
372
|
+
if (entry.queue.length > 0)
|
|
373
|
+
await drain(entry);
|
|
374
|
+
});
|
|
90
375
|
},
|
|
91
|
-
|
|
376
|
+
replace(document) {
|
|
377
|
+
const doc = canonical(document);
|
|
378
|
+
const entry = entryOf(doc);
|
|
379
|
+
if (!entry.base)
|
|
380
|
+
entry.provisional = doc;
|
|
381
|
+
options.onDocument?.(entry.ref, doc);
|
|
92
382
|
queue.push(async () => {
|
|
93
|
-
await
|
|
383
|
+
await drain(entry);
|
|
384
|
+
const operations = await options.getOperations();
|
|
385
|
+
entry.busy = true;
|
|
386
|
+
try {
|
|
387
|
+
const result = await operations.documents.replace({
|
|
388
|
+
projectId: options.projectId,
|
|
389
|
+
document: doc,
|
|
390
|
+
precondition: entry.base ? { revision: entry.base.revision } : { ifAbsent: true },
|
|
391
|
+
});
|
|
392
|
+
if (result.ok)
|
|
393
|
+
adopt(entry, result.value);
|
|
394
|
+
else {
|
|
395
|
+
if (result.error.code === 'conflict' && 'current' in result.error && result.error.current) {
|
|
396
|
+
adopt(entry, result.error.current);
|
|
397
|
+
}
|
|
398
|
+
options.onError(result.error);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
finally {
|
|
402
|
+
entry.busy = false;
|
|
403
|
+
adoptHeld(entry);
|
|
404
|
+
}
|
|
94
405
|
});
|
|
406
|
+
},
|
|
407
|
+
deleteDocument(ref) {
|
|
408
|
+
const entry = entryOf(ref);
|
|
409
|
+
entry.queue = [];
|
|
410
|
+
entry.conflicts = [];
|
|
411
|
+
entry.deleted = true;
|
|
412
|
+
emitConflicts(entry);
|
|
413
|
+
command(entry, (operations) => operations.documents.delete({ projectId: options.projectId, ref }));
|
|
414
|
+
},
|
|
415
|
+
unpublishDocument(ref) {
|
|
416
|
+
const entry = entryOf(ref);
|
|
417
|
+
queue.push(async () => {
|
|
418
|
+
await drain(entry);
|
|
419
|
+
const operations = await options.getOperations();
|
|
420
|
+
entry.busy = true;
|
|
421
|
+
try {
|
|
422
|
+
const result = await operations.documents.unpublish({ projectId: options.projectId, ref });
|
|
423
|
+
if (result.ok)
|
|
424
|
+
adopt(entry, result.value);
|
|
425
|
+
else
|
|
426
|
+
options.onError(result.error);
|
|
427
|
+
}
|
|
428
|
+
finally {
|
|
429
|
+
entry.busy = false;
|
|
430
|
+
adoptHeld(entry);
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
},
|
|
434
|
+
adoptSnapshot(ref, record) {
|
|
435
|
+
const entry = entryOf(ref);
|
|
436
|
+
if (entry.inFlight.length > 0 || entry.busy || entry.scheduled) {
|
|
437
|
+
// Hold it: the write's own result decides first; the newer of the two wins.
|
|
438
|
+
if (record === undefined)
|
|
439
|
+
entry.held = null;
|
|
440
|
+
else if (!entry.held || record.revision > entry.held.revision)
|
|
441
|
+
entry.held = record;
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
if (record === undefined) {
|
|
445
|
+
if (entry.base)
|
|
446
|
+
markGone(entry);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
if (!entry.base || record.revision > entry.base.revision)
|
|
450
|
+
adopt(entry, record);
|
|
451
|
+
},
|
|
452
|
+
conflicts: (ref) => entries.get(documentKey(ref))?.conflicts ?? [],
|
|
453
|
+
resolveConflict(ref, id, choice) {
|
|
454
|
+
const entry = entries.get(documentKey(ref));
|
|
455
|
+
if (!entry)
|
|
456
|
+
return;
|
|
457
|
+
const conflict = entry.conflicts.find((c) => c.id === id);
|
|
458
|
+
if (!conflict)
|
|
459
|
+
return;
|
|
460
|
+
entry.conflicts = entry.conflicts.filter((c) => c !== conflict);
|
|
461
|
+
if (choice === 'mine') {
|
|
462
|
+
// Keep mine: re-send at the CURRENT base — a visible, explicit second write.
|
|
463
|
+
entry.queue.push({ patch: conflict.patch, base: entry.base?.revision, settle: [] });
|
|
464
|
+
schedule(entry);
|
|
465
|
+
}
|
|
466
|
+
emitDocument(entry);
|
|
467
|
+
emitConflicts(entry);
|
|
468
|
+
},
|
|
469
|
+
async flush(ref) {
|
|
470
|
+
if (ref) {
|
|
471
|
+
const entry = entries.get(documentKey(ref));
|
|
472
|
+
if (entry && entry.queue.length > 0)
|
|
473
|
+
schedule(entry);
|
|
474
|
+
}
|
|
95
475
|
await queue.flush();
|
|
96
476
|
},
|
|
97
|
-
flush: () => queue.flush(),
|
|
98
477
|
};
|
|
99
478
|
}
|
|
100
479
|
//# sourceMappingURL=cms-persistence.js.map
|