@elytracms/studio 0.0.96 → 0.0.97
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/cms.ts +153 -13
- package/convex/delivery-folded-drift.test.ts +104 -0
- package/convex/delivery.ts +24 -8
- package/convex/depth-codec-lookalike.test.ts +192 -0
- package/convex/fixture-accounts.test.ts +305 -0
- package/convex/fixtureAccounts.ts +298 -0
- package/convex/incarnation.test.ts +149 -0
- package/convex/ingest.test.ts +145 -3
- package/convex/ingest.ts +123 -17
- package/convex/receipt-races.test.ts +184 -0
- package/convex/records-zod-copy.test.ts +76 -0
- package/convex/schema.ts +20 -1
- package/convex/strict-delete.test.ts +129 -0
- package/dist/components/cms/cms-store.d.ts +4 -1
- package/dist/components/cms/cms-store.js +11 -13
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/use-document-draft.d.ts +41 -2
- package/dist/components/cms/use-document-draft.js +60 -18
- package/dist/components/cms/use-document-draft.js.map +1 -1
- package/dist/lib/collection-view/collection-view-source.d.ts +5 -2
- package/dist/lib/collection-view/collection-view-source.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 +2 -0
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +2 -0
- package/dist/lib/i18n/messages.en.js +2 -0
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/cms-persistence.d.ts +41 -2
- package/dist/lib/operations/cms-persistence.js +111 -24
- package/dist/lib/operations/cms-persistence.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.js +27 -1
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persona/labels.js +1 -0
- package/dist/lib/persona/labels.js.map +1 -1
- package/package.json +4 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PresenceEntry } from '@elytracms/core/persistence';
|
|
2
2
|
import type { CmsDocument, CollectionDef, DocumentPatch, FieldDef, Locale, PropFieldLookup, ValuePath } from '@elytracms/core/cms-core';
|
|
3
|
-
import type { CollectionViewSource } from '../../lib/collection-view/collection-view-source.js';
|
|
3
|
+
import type { CollectionViewSource, DocumentWrites } from '../../lib/collection-view/collection-view-source.js';
|
|
4
4
|
import type { CanvasFieldChange, CanvasLift } from '../builder/canvas-rebase.js';
|
|
5
5
|
import type { CommitPolicy } from './commit-policy.js';
|
|
6
6
|
import type { CanvasControl, CanvasControlPosition } from './canvas-commit.js';
|
|
@@ -45,11 +45,44 @@ export declare const documentVisibility: VisibilitySource;
|
|
|
45
45
|
* onto, as ONE value: every edit made on the draft was authored against that revision (an
|
|
46
46
|
* uncommitted position keeps the revision its first keystroke saw). `undefined`: the view
|
|
47
47
|
* had no revision (a fixture session, a record rail, a document still being created).
|
|
48
|
+
*
|
|
49
|
+
* EC-667 — that session view is a COMPOSITE while it shows a pending write (Codex F18):
|
|
50
|
+
* `revision` is its whole-document token (the EARLIEST authoring revision of any position
|
|
51
|
+
* — what a whole-document writer, the slot alias or the translation, writes at), and
|
|
52
|
+
* `shown` is the view itself, the token a position's edit reads ITS provenance from at
|
|
53
|
+
* the first keystroke ({@link positionBase}).
|
|
48
54
|
*/
|
|
49
55
|
export interface DraftView {
|
|
50
56
|
readonly document: CmsDocument;
|
|
51
57
|
readonly revision: number | undefined;
|
|
58
|
+
/** EC-667 — the session view the draft was last rebased onto (absent: none — a fixture, a record rail). */
|
|
59
|
+
readonly shown?: CmsDocument;
|
|
52
60
|
}
|
|
61
|
+
/** What a draft reads its tokens from: the write model's view registry (EC-647 / EC-667). */
|
|
62
|
+
export type ViewTokens = Pick<DocumentWrites, 'revisionOfView'>;
|
|
63
|
+
/** EC-667 — the draft of a session view as it is shown (pure): its value, its whole-document token, the view itself. */
|
|
64
|
+
export declare function sessionDraftView(doc: CmsDocument, tokens: ViewTokens | undefined): DraftView;
|
|
65
|
+
/**
|
|
66
|
+
* EC-667 — the base an edit of `path` that STARTS now on `draft` is authored against
|
|
67
|
+
* (pure; the committer captures it at the first keystroke): the revision the content at
|
|
68
|
+
* that position of the shown session view was authored against — under a pending
|
|
69
|
+
* whole-document snapshot the snapshot's revision, under a pending own write that write's
|
|
70
|
+
* base — never the revision of a row adopted underneath it. Captured at the start, it
|
|
71
|
+
* survives the pending write's refusal or failure (looking it up when the commit arrives
|
|
72
|
+
* would be too late). Without a view token: the draft's whole-document revision.
|
|
73
|
+
*/
|
|
74
|
+
export declare function positionBase(draft: DraftView, path: ValuePath, tokens: ViewTokens | undefined): number | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* THE draft rebase as one value (pure — what the editor runs when the session's document
|
|
77
|
+
* changes): the session view with the uncommitted positions laid over it ({@link
|
|
78
|
+
* rebaseDraft}), its whole-document token, the view itself (EC-647 → EC-667: the rebase
|
|
79
|
+
* moves the value AND its tokens), and — when the screen has a canvas — the token of the
|
|
80
|
+
* canvas field's position, which the canvas's next edit is authored at.
|
|
81
|
+
*/
|
|
82
|
+
export declare function rebaseDraftView(doc: CmsDocument, previous: DraftView, uncommitted: readonly UncommittedPosition[], tokens: ViewTokens, canvasPath?: ValuePath): {
|
|
83
|
+
view: DraftView;
|
|
84
|
+
canvasRevision: number | undefined;
|
|
85
|
+
};
|
|
53
86
|
/**
|
|
54
87
|
* EC-647 — the slot alias write (pure): the draft with `alias` set (or removed), written
|
|
55
88
|
* whole AT THE DRAFT'S REVISION — never the session's tracked one, which may already hold
|
|
@@ -99,6 +132,11 @@ export declare function canvasWrite(input: {
|
|
|
99
132
|
* reported when only THAT moved too (`from` ≡ `to`): the canvas's agreed value is valid
|
|
100
133
|
* at the newer revision, so its next edit is authored there — the revision follows the
|
|
101
134
|
* value, never runs ahead of it.
|
|
135
|
+
*
|
|
136
|
+
* EC-667: `revision` is the token of the canvas FIELD's position in the shown view
|
|
137
|
+
* ({@link rebaseDraftView}) — under a pending snapshot the snapshot's revision. The canvas
|
|
138
|
+
* mounts at the draft's whole-document token (the earliest, possibly earlier than its
|
|
139
|
+
* field's), so the first rebase with a token reports it even when nothing moved.
|
|
102
140
|
*/
|
|
103
141
|
export declare function canvasFieldChange(previous: CmsDocument, next: CmsDocument, path: ValuePath, last: CanvasFieldChange | undefined, revision: number | undefined): CanvasFieldChange | undefined;
|
|
104
142
|
/**
|
|
@@ -140,7 +178,8 @@ export interface DocumentDraft {
|
|
|
140
178
|
/**
|
|
141
179
|
* EC-647 — the revision of the session view the draft was last rebased onto (see
|
|
142
180
|
* {@link DraftView}): a writer that authors from {@link draft} (the translation) writes
|
|
143
|
-
* at this revision.
|
|
181
|
+
* at this revision. EC-667: the view's WHOLE-DOCUMENT token — the earliest authoring
|
|
182
|
+
* revision of any position it shows (a field's own edit reads its position's token).
|
|
144
183
|
*/
|
|
145
184
|
revision: number | undefined;
|
|
146
185
|
/** Uncommitted (document model) / unsaved (record model) edits exist. */
|
|
@@ -47,6 +47,35 @@ export const documentVisibility = {
|
|
|
47
47
|
return () => document.removeEventListener('visibilitychange', listener);
|
|
48
48
|
},
|
|
49
49
|
};
|
|
50
|
+
/** EC-667 — the draft of a session view as it is shown (pure): its value, its whole-document token, the view itself. */
|
|
51
|
+
export function sessionDraftView(doc, tokens) {
|
|
52
|
+
return { document: doc, revision: tokens?.revisionOfView(doc), shown: doc };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* EC-667 — the base an edit of `path` that STARTS now on `draft` is authored against
|
|
56
|
+
* (pure; the committer captures it at the first keystroke): the revision the content at
|
|
57
|
+
* that position of the shown session view was authored against — under a pending
|
|
58
|
+
* whole-document snapshot the snapshot's revision, under a pending own write that write's
|
|
59
|
+
* base — never the revision of a row adopted underneath it. Captured at the start, it
|
|
60
|
+
* survives the pending write's refusal or failure (looking it up when the commit arrives
|
|
61
|
+
* would be too late). Without a view token: the draft's whole-document revision.
|
|
62
|
+
*/
|
|
63
|
+
export function positionBase(draft, path, tokens) {
|
|
64
|
+
return tokens && draft.shown ? tokens.revisionOfView(draft.shown, path) : draft.revision;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* THE draft rebase as one value (pure — what the editor runs when the session's document
|
|
68
|
+
* changes): the session view with the uncommitted positions laid over it ({@link
|
|
69
|
+
* rebaseDraft}), its whole-document token, the view itself (EC-647 → EC-667: the rebase
|
|
70
|
+
* moves the value AND its tokens), and — when the screen has a canvas — the token of the
|
|
71
|
+
* canvas field's position, which the canvas's next edit is authored at.
|
|
72
|
+
*/
|
|
73
|
+
export function rebaseDraftView(doc, previous, uncommitted, tokens, canvasPath) {
|
|
74
|
+
return {
|
|
75
|
+
view: { document: rebaseDraft(doc, previous.document, uncommitted), revision: tokens.revisionOfView(doc), shown: doc },
|
|
76
|
+
canvasRevision: canvasPath ? tokens.revisionOfView(doc, canvasPath) : undefined,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
50
79
|
/**
|
|
51
80
|
* EC-647 — the slot alias write (pure): the draft with `alias` set (or removed), written
|
|
52
81
|
* whole AT THE DRAFT'S REVISION — never the session's tracked one, which may already hold
|
|
@@ -108,11 +137,17 @@ export function canvasWrite(input) {
|
|
|
108
137
|
* reported when only THAT moved too (`from` ≡ `to`): the canvas's agreed value is valid
|
|
109
138
|
* at the newer revision, so its next edit is authored there — the revision follows the
|
|
110
139
|
* value, never runs ahead of it.
|
|
140
|
+
*
|
|
141
|
+
* EC-667: `revision` is the token of the canvas FIELD's position in the shown view
|
|
142
|
+
* ({@link rebaseDraftView}) — under a pending snapshot the snapshot's revision. The canvas
|
|
143
|
+
* mounts at the draft's whole-document token (the earliest, possibly earlier than its
|
|
144
|
+
* field's), so the first rebase with a token reports it even when nothing moved.
|
|
111
145
|
*/
|
|
112
146
|
export function canvasFieldChange(previous, next, path, last, revision) {
|
|
113
147
|
const from = readValueAt(previous, path);
|
|
114
148
|
const to = readValueAt(next, path);
|
|
115
|
-
|
|
149
|
+
const tokenHeld = last === undefined ? revision === undefined : last.revision === revision;
|
|
150
|
+
if (semanticValueEqual(from, to) && tokenHeld)
|
|
116
151
|
return undefined;
|
|
117
152
|
return { from, to, seq: (last?.seq ?? 0) + 1, revision };
|
|
118
153
|
}
|
|
@@ -178,11 +213,9 @@ export function useDocumentDraft(input) {
|
|
|
178
213
|
const writes = source.writes;
|
|
179
214
|
const documentModel = writes.kind === 'document' ? writes : undefined;
|
|
180
215
|
const ref = useMemo(() => ({ collection: doc.collection, id: doc.id }), [doc.collection, doc.id]);
|
|
181
|
-
// EC-647: the draft and the revision of the view it rebased onto travel as ONE value
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
revision: documentModel?.revisionOfView(doc),
|
|
185
|
-
}));
|
|
216
|
+
// EC-647: the draft and the revision of the view it rebased onto travel as ONE value
|
|
217
|
+
// (EC-667: with the view itself — the per-position token).
|
|
218
|
+
const [view, setViewState] = useState(() => sessionDraftView(doc, documentModel));
|
|
186
219
|
const draft = view.document;
|
|
187
220
|
const draftRef = useRef(view);
|
|
188
221
|
// EC-616: the record as the editor opened it (or as of their last successful save) — the
|
|
@@ -195,8 +228,8 @@ export function useDocumentDraft(input) {
|
|
|
195
228
|
draftRef.current = next;
|
|
196
229
|
setViewState(next);
|
|
197
230
|
}, []);
|
|
198
|
-
/** An edit of the draft: its value moves, the
|
|
199
|
-
const setDraft = useCallback((next) => setView({ document: next
|
|
231
|
+
/** An edit of the draft: its value moves, the tokens it is authored against do not. */
|
|
232
|
+
const setDraft = useCallback((next) => setView({ ...draftRef.current, document: next }), [setView]);
|
|
200
233
|
const scheduler = input.scheduler ?? browserScheduler;
|
|
201
234
|
const now = input.now ?? (() => new Date().toISOString());
|
|
202
235
|
const fieldsByName = useMemo(() => new Map(collection.fields.map((f) => [f.name, f])), [collection]);
|
|
@@ -220,7 +253,9 @@ export function useDocumentDraft(input) {
|
|
|
220
253
|
},
|
|
221
254
|
// EC-647: a position's base is the revision of the DRAFT its first keystroke was
|
|
222
255
|
// made on — never the session's tracked revision, which may already hold a
|
|
223
|
-
// colleague's change the draft has not rebased onto yet.
|
|
256
|
+
// colleague's change the draft has not rebased onto yet. EC-667: the form's
|
|
257
|
+
// writers name the POSITION's token ({@link positionBase}, passed to `edited`);
|
|
258
|
+
// this whole-document token (the earliest) is the fallback.
|
|
224
259
|
baseOf: () => draftRef.current.revision,
|
|
225
260
|
scheduler,
|
|
226
261
|
}),
|
|
@@ -249,29 +284,36 @@ export function useDocumentDraft(input) {
|
|
|
249
284
|
if (!documentModel)
|
|
250
285
|
return;
|
|
251
286
|
const previous = draftRef.current.document;
|
|
252
|
-
|
|
253
|
-
// EC-
|
|
254
|
-
const
|
|
255
|
-
setView(
|
|
287
|
+
// EC-647: the rebase moves the value AND the revision — the session view's own
|
|
288
|
+
// (EC-667: its tokens, whole-document and per position).
|
|
289
|
+
const { view: next, canvasRevision } = rebaseDraftView(doc, draftRef.current, committer.uncommitted(), documentModel, canvasPath);
|
|
290
|
+
setView(next);
|
|
256
291
|
// EC-630: the canvas field moved underneath the canvas — the canvas rebases onto the
|
|
257
292
|
// change (its own edits are in both values: the session's view holds every patch it
|
|
258
293
|
// committed, and the draft overlays every position it has not). EC-647: the change
|
|
259
|
-
// carries the revision
|
|
294
|
+
// carries the revision — EC-667: the canvas FIELD's token — so the canvas's next edit
|
|
295
|
+
// is authored where its value is.
|
|
260
296
|
if (!canvasPath)
|
|
261
297
|
return;
|
|
262
|
-
setCanvasChange((last) => canvasFieldChange(previous, next, canvasPath, last,
|
|
298
|
+
setCanvasChange((last) => canvasFieldChange(previous, next.document, canvasPath, last, canvasRevision) ?? last);
|
|
263
299
|
}, [doc]);
|
|
264
300
|
// --- Writers ----------------------------------------------------------------------------
|
|
265
301
|
const setFieldValue = useCallback((field, value) => {
|
|
302
|
+
// EC-667: the position's token is read BEFORE the draft moves — from the view this
|
|
303
|
+
// keystroke was made on (a re-edit keeps its first keystroke's either way).
|
|
304
|
+
const path = fieldPositionPath(collection, field.name, locale);
|
|
305
|
+
const authored = { base: positionBase(draftRef.current, path, documentModel) };
|
|
266
306
|
const next = writeField(collection, draftRef.current.document, field.name, locale, value, { lookupManifest });
|
|
267
307
|
setDraft(next);
|
|
268
308
|
if (documentModel)
|
|
269
|
-
committer.edited(field.name,
|
|
309
|
+
committer.edited(field.name, path, authored);
|
|
270
310
|
}, [collection, locale, lookupManifest, setDraft, documentModel, committer]);
|
|
271
311
|
const editField = useCallback((field, update) => {
|
|
312
|
+
const path = fieldPositionPath(collection, field.name, locale);
|
|
313
|
+
const authored = { base: positionBase(draftRef.current, path, documentModel) };
|
|
272
314
|
setDraft(update(draftRef.current.document));
|
|
273
315
|
if (documentModel)
|
|
274
|
-
committer.edited(field.name,
|
|
316
|
+
committer.edited(field.name, path, authored);
|
|
275
317
|
}, [collection, locale, setDraft, documentModel, committer]);
|
|
276
318
|
const setCanvasValue = useCallback((field, lift) => {
|
|
277
319
|
const focus = committer.focusedField();
|
|
@@ -407,7 +449,7 @@ export function useDocumentDraft(input) {
|
|
|
407
449
|
// canvas remounts from it (its history belonged to the replaced value).
|
|
408
450
|
const moved = canvasPath && !semanticValueEqual(readValueAt(draftRef.current.document, canvasPath), readValueAt(next, canvasPath));
|
|
409
451
|
// EC-647: the restored value comes with the revision the session showed it at.
|
|
410
|
-
setView(
|
|
452
|
+
setView(sessionDraftView(next, documentModel));
|
|
411
453
|
if (moved)
|
|
412
454
|
setCanvasNonce((n) => n + 1);
|
|
413
455
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-document-draft.js","sourceRoot":"","sources":["../../../src/components/cms/use-document-draft.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport {\n PRESENCE_TTL_MS,\n livePresence,\n} from '@elytracms/core/persistence'\nimport type { PresenceEntry } from '@elytracms/core/persistence'\nimport {\n applyDocumentPatches,\n formatValuePath,\n readValueAt,\n semanticValueEqual,\n writeField,\n} from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CollectionDef,\n DocumentPatch,\n DocumentRef,\n FieldDef,\n Locale,\n PropFieldLookup,\n ValuePath,\n} from '@elytracms/core/cms-core'\nimport type { CollectionViewSource } from '../../lib/collection-view/collection-view-source'\nimport { fieldPositionPath } from '../../lib/cms/field-patch'\nimport { compositionPatches } from '../builder/composition-patches'\nimport type { CanvasFieldChange, CanvasLift } from '../builder/canvas-rebase'\nimport { commitPolicyOf } from './commit-policy'\nimport type { CommitPolicy } from './commit-policy'\nimport { canvasControl, routeCanvasWrite } from './canvas-commit'\nimport type { CanvasControl, CanvasControlPosition } from './canvas-commit'\nimport { browserScheduler, createDraftCommitter } from './draft-commit'\nimport type { DraftCommitter, Scheduler, UncommittedPosition } from './draft-commit'\n\n/**\n * EC-595 A2 — the detail editor's draft over the revisioned document (and, for the\n * explicit-save rails, over a record).\n *\n * **Document write model** (the CMS): the draft changes per keystroke (live validation\n * reads it); a field's edit is COMMITTED per its control kind's policy\n * ({@link commitPolicyOf} / {@link createDraftCommitter}) as the patch of the position it\n * wrote — never a document. The canvas commits each action's node patches at once\n * (`compositionPatches`); a burst of typing in one of its inspector controls is that\n * control's uncommitted position, committed on its kind's policy like a form field\n * (EC-630, `canvas-commit.ts`). The draft REBASES whenever the session's document changes\n * (a write result, a colleague's change by subscription, a resolved conflict): it is the\n * session's document with this editor's uncommitted positions laid over it — local wins\n * until committed, then the per-path check decides (EC-595 point 7).\n *\n * **Forced flushes** (the ruling: before unmount, tab hide, publish, preview, an AI\n * proposal, duplicate, transfer): {@link DocumentDraft.flush} commits every uncommitted\n * position and awaits the persister. Unmount and tab hide run it here; the editor's\n * publish, duplicate and the AI tap call it.\n *\n * **Thin presence** (Stage A): a heartbeat naming the focused field's path on focus\n * change and every {@link PRESENCE_HEARTBEAT_MS} (under the 30 s TTL), cleared on\n * unmount; the other sessions' live heartbeats arrive with the subscription.\n *\n * **Record write model** (users, redirects, assets, the account): the draft is edited\n * in place and written whole on the editor's Save button (EC-240) — unchanged.\n */\n\n/** The presence heartbeat interval — under {@link PRESENCE_TTL_MS} so a present editor never lapses. */\nexport const PRESENCE_HEARTBEAT_MS = 10_000\n\n/** An injectable visibility source (the tab-hide flush; tests drive it). */\nexport interface VisibilitySource {\n /** Call `onHidden` whenever the tab becomes hidden; returns the unsubscribe. */\n onHidden(onHidden: () => void): () => void\n}\n\nexport const documentVisibility: VisibilitySource = {\n onHidden(onHidden) {\n if (typeof document === 'undefined') return () => {}\n const listener = () => {\n if (document.visibilityState === 'hidden') onHidden()\n }\n document.addEventListener('visibilitychange', listener)\n return () => document.removeEventListener('visibilitychange', listener)\n },\n}\n\n/**\n * EC-647 — the editor's draft and the revision of the session view it was last rebased\n * onto, as ONE value: every edit made on the draft was authored against that revision (an\n * uncommitted position keeps the revision its first keystroke saw). `undefined`: the view\n * had no revision (a fixture session, a record rail, a document still being created).\n */\nexport interface DraftView {\n readonly document: CmsDocument\n readonly revision: number | undefined\n}\n\n/**\n * EC-647 — the slot alias write (pure): the draft with `alias` set (or removed), written\n * whole AT THE DRAFT'S REVISION — never the session's tracked one, which may already hold\n * a colleague's change the draft has not absorbed (Codex F10). There is no identity-\n * metadata operation for the alias (the value-position grammar has no position for it,\n * measured on the base), so it stays a whole-document `replace` with its precondition.\n */\nexport function aliasWrite(\n draft: DraftView,\n alias: string | undefined,\n writes: { replace(doc: CmsDocument, revision: number | undefined): void },\n): CmsDocument {\n const next = { ...draft.document }\n if (alias === undefined) delete next.alias\n else next.alias = alias\n writes.replace(next, draft.revision)\n return next\n}\n\n/** The patch that writes the value `doc` holds at `path` (unset when absent). */\nexport function positionPatch(doc: CmsDocument, path: ValuePath): DocumentPatch {\n const value = readValueAt(doc, path)\n return value === undefined ? { op: 'unset', path } : { op: 'set', path, value }\n}\n\n/**\n * THE rebase (pure): the session's document with the editor's uncommitted positions laid\n * over it, values from the editor's current draft.\n */\nexport function rebaseDraft(\n sessionDoc: CmsDocument,\n draft: CmsDocument,\n uncommitted: readonly UncommittedPosition[],\n): CmsDocument {\n if (uncommitted.length === 0) return sessionDoc\n const patches = uncommitted.map((position) => positionPatch(draft, position.path))\n return applyDocumentPatches({ document: sessionDoc, patches, since: [] }).document\n}\n\n/**\n * EC-630 — the canvas's write into the draft (pure). The canvas writer's node patches,\n * keyed by node id, are the canvas's OWN delta: from `base`, the value it last agreed on\n * with the draft, to `value` — never a document diff, and never measured against the\n * draft's CURRENT value (a colleague's change that reached the draft a moment before the\n * edit lifted would read as part of the edit, and its patches would revert it). The draft\n * takes the delta over whatever it holds now (normally `base` itself) and canonicalises the\n * field through the write lens. `undefined` when the canvas changed nothing.\n */\nexport function canvasWrite(input: {\n collection: CollectionDef\n draft: CmsDocument\n field: string\n locale: Locale\n value: unknown\n base: unknown\n lookupManifest: PropFieldLookup\n}): { next: CmsDocument; patches: DocumentPatch[] } | undefined {\n const { collection, draft, field, locale, lookupManifest } = input\n const path = fieldPositionPath(collection, field, locale)\n const localized = path.length > 1 ? locale : undefined\n const patches = compositionPatches(input.base, input.value, field, localized)\n if (patches.length === 0) return undefined\n const applied = applyDocumentPatches({ document: draft, patches, since: [] }).document\n return { next: writeField(collection, draft, field, locale, readValueAt(applied, path), { lookupManifest }), patches }\n}\n\n/**\n * EC-630 — the change of the canvas field made UNDERNEATH the canvas when the draft\n * rebased from `previous` to `next` (pure), numbered after `last`; `undefined` when the\n * field did not move. The canvas's own edits are in both values (the session's view holds\n * every patch it committed, the draft overlays every position it has not), so the change\n * is someone else's: a colleague's patch, a write's canonical row, a resolved conflict.\n *\n * EC-647: it carries `revision`, the revision the draft is at after the rebase, and is\n * reported when only THAT moved too (`from` ≡ `to`): the canvas's agreed value is valid\n * at the newer revision, so its next edit is authored there — the revision follows the\n * value, never runs ahead of it.\n */\nexport function canvasFieldChange(\n previous: CmsDocument,\n next: CmsDocument,\n path: ValuePath,\n last: CanvasFieldChange | undefined,\n revision: number | undefined,\n): CanvasFieldChange | undefined {\n const from = readValueAt(previous, path)\n const to = readValueAt(next, path)\n if (semanticValueEqual(from, to) && (last === undefined || last.revision === revision)) return undefined\n return { from, to, seq: (last?.seq ?? 0) + 1, revision }\n}\n\n/**\n * EC-647 — the canvas's write path (pure; what {@link DocumentDraft.setCanvasValue} runs):\n * the canvas writer's delta into the draft ({@link canvasWrite}, handed to `apply`), then\n * routed ({@link routeCanvasWrite}) — committed at once, or held as a focused inspector\n * control's uncommitted position — and in both cases paired with the revision of the\n * canvas value the edit was authored on (`lift.revision`), never the draft's: the draft\n * may already hold a colleague's change the canvas has not been rebased onto (Codex F12),\n * and the store must check the edit from the canvas's revision to refuse it where it\n * overlaps that change. `commit` absent → a draft without a revisioned write model (the\n * draft moves, nothing is committed). `undefined` when the canvas changed nothing.\n */\nexport function canvasCommit(input: {\n collection: CollectionDef\n draft: CmsDocument\n field: string\n locale: Locale\n lift: CanvasLift\n lookupManifest: PropFieldLookup\n /** The focused canvas inspector control, if focus is on one. */\n focused: CanvasControl | undefined\n committer: DraftCommitter\n /** The draft takes the canvas's write (before it is routed — a forced commit reads it). */\n apply: (next: CmsDocument) => void\n commit?: (patches: readonly DocumentPatch[], bases: readonly (number | undefined)[]) => void\n}): 'deferred' | 'committed' | 'draft' | undefined {\n const { lift, commit } = input\n const write = canvasWrite({\n collection: input.collection,\n draft: input.draft,\n field: input.field,\n locale: input.locale,\n value: lift.value,\n base: lift.base,\n lookupManifest: input.lookupManifest,\n })\n if (!write) return undefined\n input.apply(write.next)\n if (!commit) return 'draft'\n // EC-630: a burst of typing in a focused inspector control is that control's\n // uncommitted position (its policy commits it); a canvas action commits now.\n return routeCanvasWrite({\n patches: write.patches,\n focused: input.focused,\n committer: input.committer,\n revision: lift.revision,\n commitNow: (patches) => commit(patches, patches.map(() => lift.revision)),\n })\n}\n\n/**\n * The reader's \"now\" for presence liveness: THIS session's own latest heartbeat `at`\n * (written by the same store clock as everyone else's — the playground store runs a\n * deterministic sequential clock, Convex the writers' wall clocks), falling back to the\n * injected clock before the first heartbeat lands. Comparing others' `at` against a\n * different time base would call every colleague stale (measured live in the\n * playground: the store's clock is months behind the browser's).\n */\nexport function presenceReaderNow(\n entries: readonly PresenceEntry[],\n sessionId: string,\n fallback: string,\n): string {\n const own = entries.filter((entry) => entry.sessionId === sessionId).map((entry) => entry.at)\n return own.length > 0 ? own.sort().at(-1)! : fallback\n}\n\nexport interface DocumentDraft {\n draft: CmsDocument\n /**\n * EC-647 — the revision of the session view the draft was last rebased onto (see\n * {@link DraftView}): a writer that authors from {@link draft} (the translation) writes\n * at this revision.\n */\n revision: number | undefined\n /** Uncommitted (document model) / unsaved (record model) edits exist. */\n dirty: boolean\n /** The record model: the editor renders a Save button + navigation guard. */\n explicitSave: boolean\n /** A field's control wrote `value` (the active locale for a localized field). */\n setFieldValue(field: FieldDef, value: unknown): void\n /** A writer that transforms ONE field position (fill / reset from base). */\n editField(field: FieldDef, update: (draft: CmsDocument) => CmsDocument): void\n /**\n * The canvas emitted a new value for its field (node patches, committed at once). The\n * lift's `base` is the value the canvas last agreed on with the draft: the canvas\n * writer's delta is `base` → `value`, applied over whatever the draft holds now (EC-630 —\n * a change that landed underneath meanwhile is never reverted by the canvas's own\n * write); its `revision` is the revision that value was agreed at — the revision the\n * edit is committed at (EC-647, {@link canvasCommit}).\n */\n setCanvasValue(field: FieldDef, lift: CanvasLift): void\n /** The slot alias (a document-level key: a whole-document write at the held revision). */\n setAlias(alias: string | undefined): void\n /** Field boundary events (focus / blur / Enter) — the commit policy's triggers. */\n focused(field: FieldDef): void\n blurred(field: FieldDef): void\n entered(field: FieldDef): void\n /**\n * EC-630 — the canvas inspector's boundary events: a control writing `position` of the\n * canvas field `field`, committing on `policy` (THE declaration for its kind). A burst of\n * typing in it is its uncommitted position until blur / Enter / the idle pause.\n */\n canvasFocused(field: FieldDef, position: CanvasControlPosition, policy: CommitPolicy): void\n canvasBlurred(field: FieldDef, position: CanvasControlPosition): void\n canvasEntered(field: FieldDef, position: CanvasControlPosition): void\n /** The forced commit (sync; the persister serialises what follows) / the record model's Save. */\n save(): void\n /** The FORCED FLUSH: commit everything uncommitted and await persistence. */\n flush(): Promise<void>\n /** Adopt a draft from outside (a restored version). */\n replaceDraft(next: CmsDocument): void\n /**\n * Bumped when the canvas must remount from the draft: a restored version replaced the\n * document, or a locale switch pointed the canvas at another bucket. (A change made\n * underneath the canvas is NOT a remount — {@link canvasChange}.)\n */\n canvasNonce: number\n /**\n * EC-630 — the latest change of the canvas field's value made UNDERNEATH the canvas (a\n * colleague's patch by subscription, a write's canonical row, a resolved conflict): the\n * draft's value before and after it. The canvas REBASES onto it — the change lands, its\n * undo history and selection survive — where it used to remount and lose both.\n */\n canvasChange: CanvasFieldChange | undefined\n /** The OTHER sessions present on this document, live by the reader's clock. */\n presence: readonly PresenceEntry[]\n}\n\nexport interface UseDocumentDraftInput {\n source: CollectionViewSource\n collection: CollectionDef\n doc: CmsDocument\n locale: Locale\n lookupManifest: PropFieldLookup\n /** The composition field the canvas edits on this screen, if any. */\n canvasField?: FieldDef\n scheduler?: Scheduler\n visibility?: VisibilitySource\n /** The reader's clock for presence liveness (ISO); `new Date().toISOString()` by default. */\n now?: () => string\n}\n\n/**\n * EC-616 — the base a record save is diffed against: the source's value whenever the draft\n * equals it (the record as opened, or as a save came back), else the previous base (the\n * editor has unsaved changes; a change from another writer meanwhile is not theirs). Pure.\n */\nexport function recordSaveBase(previous: CmsDocument, draft: CmsDocument, doc: CmsDocument): CmsDocument {\n return semanticValueEqual(draft, doc) ? doc : previous\n}\n\nexport function useDocumentDraft(input: UseDocumentDraftInput): DocumentDraft {\n const { source, collection, doc, locale, lookupManifest, canvasField } = input\n const writes = source.writes\n const documentModel = writes.kind === 'document' ? writes : undefined\n const ref: DocumentRef = useMemo(() => ({ collection: doc.collection, id: doc.id }), [doc.collection, doc.id])\n // EC-647: the draft and the revision of the view it rebased onto travel as ONE value.\n const [view, setViewState] = useState<DraftView>(() => ({\n document: doc,\n revision: documentModel?.revisionOfView(doc),\n }))\n const draft = view.document\n const draftRef = useRef(view)\n // EC-616: the record as the editor opened it (or as of their last successful save) — the\n // BASE a record rail diffs a save against, so it writes only the editor's changes. It moves\n // only while the draft equals the source's value (at open, and once a save is reflected\n // back): another writer's change arriving mid-edit never becomes the editor's base.\n const baseRef = useRef<CmsDocument>(doc)\n baseRef.current = recordSaveBase(baseRef.current, draftRef.current.document, doc)\n const setView = useCallback((next: DraftView) => {\n draftRef.current = next\n setViewState(next)\n }, [])\n /** An edit of the draft: its value moves, the revision it is authored against does not. */\n const setDraft = useCallback(\n (next: CmsDocument) => setView({ document: next, revision: draftRef.current.revision }),\n [setView],\n )\n const scheduler = input.scheduler ?? browserScheduler\n const now = input.now ?? (() => new Date().toISOString())\n const fieldsByName = useMemo(() => new Map(collection.fields.map((f) => [f.name, f])), [collection])\n\n // --- Committing --------------------------------------------------------------------\n const writesRef = useRef(documentModel)\n writesRef.current = documentModel\n /** EC-630 — the canvas inspector controls that took focus, by committer key (their position + policy). */\n const canvasControls = useRef(new Map<string, CanvasControl>())\n const committer = useMemo<DraftCommitter>(\n () =>\n createDraftCommitter({\n policyOf: (name) => {\n const field = fieldsByName.get(name)\n if (field) return commitPolicyOf(field)\n return canvasControls.current.get(name)?.policy ?? 'blur-or-idle'\n },\n commit: (positions) => {\n const target = writesRef.current\n if (!target) return\n void target.commit(\n ref,\n positions.map((position) => positionPatch(draftRef.current.document, position.path)),\n positions.map((position) => position.base),\n )\n },\n // EC-647: a position's base is the revision of the DRAFT its first keystroke was\n // made on — never the session's tracked revision, which may already hold a\n // colleague's change the draft has not rebased onto yet.\n baseOf: () => draftRef.current.revision,\n scheduler,\n }),\n // One committer per open document (the editor is keyed by it).\n [ref],\n )\n\n // --- The canvas's value: its remount and its rebase ------------------------------------\n const canvasPath = canvasField ? fieldPositionPath(collection, canvasField.name, locale) : undefined\n const canvasPathKey = canvasPath ? formatValuePath(canvasPath) : ''\n const [canvasNonce, setCanvasNonce] = useState(0)\n const [canvasChange, setCanvasChange] = useState<CanvasFieldChange | undefined>(undefined)\n // A locale switch points the canvas at another bucket: remount from it.\n const lastCanvasPathKey = useRef(canvasPathKey)\n useEffect(() => {\n if (lastCanvasPathKey.current === canvasPathKey) return\n lastCanvasPathKey.current = canvasPathKey\n setCanvasNonce((n) => n + 1)\n }, [canvasPathKey])\n\n // --- The rebase (document model) -------------------------------------------------------\n const firstDoc = useRef(true)\n useEffect(() => {\n if (firstDoc.current) {\n firstDoc.current = false\n return\n }\n if (!documentModel) return\n const previous = draftRef.current.document\n const next = rebaseDraft(doc, previous, committer.uncommitted())\n // EC-647: the rebase moves the value AND the revision — the session view's own.\n const revision = documentModel.revisionOfView(doc)\n setView({ document: next, revision })\n // EC-630: the canvas field moved underneath the canvas — the canvas rebases onto the\n // change (its own edits are in both values: the session's view holds every patch it\n // committed, and the draft overlays every position it has not). EC-647: the change\n // carries the revision, so the canvas's next edit is authored where its value is.\n if (!canvasPath) return\n setCanvasChange((last) => canvasFieldChange(previous, next, canvasPath, last, revision) ?? last)\n }, [doc])\n\n // --- Writers ----------------------------------------------------------------------------\n const setFieldValue = useCallback(\n (field: FieldDef, value: unknown) => {\n const next = writeField(collection, draftRef.current.document, field.name, locale, value, { lookupManifest })\n setDraft(next)\n if (documentModel) committer.edited(field.name, fieldPositionPath(collection, field.name, locale))\n },\n [collection, locale, lookupManifest, setDraft, documentModel, committer],\n )\n const editField = useCallback(\n (field: FieldDef, update: (current: CmsDocument) => CmsDocument) => {\n setDraft(update(draftRef.current.document))\n if (documentModel) committer.edited(field.name, fieldPositionPath(collection, field.name, locale))\n },\n [collection, locale, setDraft, documentModel, committer],\n )\n const setCanvasValue = useCallback(\n (field: FieldDef, lift: CanvasLift) => {\n const focus = committer.focusedField()\n canvasCommit({\n collection,\n draft: draftRef.current.document,\n field: field.name,\n locale,\n lift,\n lookupManifest,\n focused: focus === undefined ? undefined : canvasControls.current.get(focus),\n committer,\n apply: setDraft,\n ...(documentModel\n ? { commit: (patches: readonly DocumentPatch[], bases: readonly (number | undefined)[]) => void documentModel.commit(ref, patches, bases) }\n : {}),\n })\n },\n [collection, locale, lookupManifest, setDraft, documentModel, ref, committer],\n )\n const setAlias = useCallback(\n (alias: string | undefined) => {\n if (!documentModel) {\n aliasWrite(draftRef.current, alias, { replace: (next) => setDraft(next) })\n return\n }\n // The uncommitted positions first (the snapshot holds them), then the snapshot AT\n // THE DRAFT'S REVISION (EC-647 — Codex F10).\n committer.commitAll()\n setDraft(aliasWrite(draftRef.current, alias, documentModel))\n },\n [setDraft, documentModel, committer],\n )\n\n // --- Forced flush ----------------------------------------------------------------------\n const save = useCallback(() => {\n if (documentModel) committer.commitAll()\n else if (writes.kind === 'record') writes.save(draftRef.current.document, baseRef.current)\n }, [documentModel, committer, writes])\n const flush = useCallback(async () => {\n save()\n if (documentModel) await documentModel.flush(ref)\n }, [save, documentModel, ref])\n\n // Tab hide: commit and flush. Unmount: the same, and leave the document.\n const visibility = input.visibility ?? documentVisibility\n useEffect(() => {\n if (!documentModel) return\n return visibility.onHidden(() => {\n committer.commitAll()\n void documentModel.flush(ref)\n })\n }, [ref])\n useEffect(\n () => () => {\n const target = writesRef.current\n if (!target) return\n committer.commitAll()\n committer.dispose()\n void target.flush(ref)\n target.clearPresence(ref)\n },\n [ref],\n )\n\n // --- Subscription + presence ------------------------------------------------------------\n const [presenceEntries, setPresenceEntries] = useState<readonly PresenceEntry[]>([])\n const [clock, setClock] = useState(now)\n useEffect(() => {\n if (!documentModel) return\n return documentModel.subscribe(ref, setPresenceEntries)\n }, [ref])\n const focusedPathRef = useRef<ValuePath | undefined>(undefined)\n const heartbeat = useCallback(() => {\n writesRef.current?.reportPresence(ref, focusedPathRef.current)\n setClock(now())\n }, [ref])\n useEffect(() => {\n if (!documentModel) return\n heartbeat()\n let handle: unknown\n const tick = () => {\n heartbeat()\n handle = scheduler.setTimeout(tick, PRESENCE_HEARTBEAT_MS)\n }\n handle = scheduler.setTimeout(tick, PRESENCE_HEARTBEAT_MS)\n return () => scheduler.clearTimeout(handle)\n }, [ref])\n const presence = useMemo(\n () =>\n documentModel\n ? livePresence(\n presenceEntries,\n presenceReaderNow(presenceEntries, documentModel.presenceSessionId, clock),\n PRESENCE_TTL_MS,\n ).filter((entry) => entry.sessionId !== documentModel.presenceSessionId)\n : [],\n [presenceEntries, clock, documentModel],\n )\n\n const focused = useCallback(\n (field: FieldDef) => {\n if (!documentModel) return\n committer.focused(field.name)\n focusedPathRef.current = fieldPositionPath(collection, field.name, locale)\n heartbeat()\n },\n [documentModel, committer, collection, locale, heartbeat],\n )\n const blurred = useCallback(\n (field: FieldDef) => {\n if (!documentModel) return\n committer.blurred(field.name)\n focusedPathRef.current = undefined\n heartbeat()\n },\n [documentModel, committer, heartbeat],\n )\n const entered = useCallback(\n (field: FieldDef) => {\n if (documentModel) committer.entered(field.name)\n },\n [documentModel, committer],\n )\n\n // EC-630 — the canvas inspector's commit boundary: a control at a position of the canvas\n // field gained focus, lost it, or saw Enter. Its policy is THE declaration's for its kind.\n const canvasControlOf = useCallback(\n (field: FieldDef, position: CanvasControlPosition, policy: CommitPolicy) =>\n canvasControl(fieldPositionPath(collection, field.name, locale), position, policy),\n [collection, locale],\n )\n const canvasFocused = useCallback(\n (field: FieldDef, position: CanvasControlPosition, policy: CommitPolicy) => {\n if (!documentModel) return\n const control = canvasControlOf(field, position, policy)\n canvasControls.current.set(control.key, control)\n committer.focused(control.key)\n focusedPathRef.current = control.path\n heartbeat()\n },\n [documentModel, committer, canvasControlOf, heartbeat],\n )\n const canvasBlurred = useCallback(\n (field: FieldDef, position: CanvasControlPosition) => {\n if (!documentModel) return\n const key = canvasControlOf(field, position, 'change').key\n committer.blurred(key)\n canvasControls.current.delete(key)\n focusedPathRef.current = undefined\n heartbeat()\n },\n [documentModel, committer, canvasControlOf, heartbeat],\n )\n const canvasEntered = useCallback(\n (field: FieldDef, position: CanvasControlPosition) => {\n if (documentModel) committer.entered(canvasControlOf(field, position, 'change').key)\n },\n [documentModel, committer, canvasControlOf],\n )\n\n const replaceDraft = useCallback(\n (next: CmsDocument) => {\n // A restored version replaces the document: when it moved the canvas field, the\n // canvas remounts from it (its history belonged to the replaced value).\n const moved = canvasPath && !semanticValueEqual(readValueAt(draftRef.current.document, canvasPath), readValueAt(next, canvasPath))\n // EC-647: the restored value comes with the revision the session showed it at.\n setView({ document: next, revision: documentModel?.revisionOfView(next) })\n if (moved) setCanvasNonce((n) => n + 1)\n },\n // The path is keyed by its string form.\n [setView, documentModel, canvasPathKey],\n )\n\n const dirty = useMemo(() => !semanticValueEqual(draft, doc), [draft, doc])\n\n return {\n draft,\n revision: view.revision,\n dirty,\n explicitSave: writes.kind === 'record',\n setFieldValue,\n editField,\n setCanvasValue,\n setAlias,\n focused,\n blurred,\n entered,\n canvasFocused,\n canvasBlurred,\n canvasEntered,\n save,\n flush,\n replaceDraft,\n canvasNonce,\n canvasChange,\n presence,\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"use-document-draft.js","sourceRoot":"","sources":["../../../src/components/cms/use-document-draft.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport {\n PRESENCE_TTL_MS,\n livePresence,\n} from '@elytracms/core/persistence'\nimport type { PresenceEntry } from '@elytracms/core/persistence'\nimport {\n applyDocumentPatches,\n formatValuePath,\n readValueAt,\n semanticValueEqual,\n writeField,\n} from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CollectionDef,\n DocumentPatch,\n DocumentRef,\n FieldDef,\n Locale,\n PropFieldLookup,\n ValuePath,\n} from '@elytracms/core/cms-core'\nimport type { CollectionViewSource, DocumentWrites } from '../../lib/collection-view/collection-view-source'\nimport { fieldPositionPath } from '../../lib/cms/field-patch'\nimport { compositionPatches } from '../builder/composition-patches'\nimport type { CanvasFieldChange, CanvasLift } from '../builder/canvas-rebase'\nimport { commitPolicyOf } from './commit-policy'\nimport type { CommitPolicy } from './commit-policy'\nimport { canvasControl, routeCanvasWrite } from './canvas-commit'\nimport type { CanvasControl, CanvasControlPosition } from './canvas-commit'\nimport { browserScheduler, createDraftCommitter } from './draft-commit'\nimport type { DraftCommitter, Scheduler, UncommittedPosition } from './draft-commit'\n\n/**\n * EC-595 A2 — the detail editor's draft over the revisioned document (and, for the\n * explicit-save rails, over a record).\n *\n * **Document write model** (the CMS): the draft changes per keystroke (live validation\n * reads it); a field's edit is COMMITTED per its control kind's policy\n * ({@link commitPolicyOf} / {@link createDraftCommitter}) as the patch of the position it\n * wrote — never a document. The canvas commits each action's node patches at once\n * (`compositionPatches`); a burst of typing in one of its inspector controls is that\n * control's uncommitted position, committed on its kind's policy like a form field\n * (EC-630, `canvas-commit.ts`). The draft REBASES whenever the session's document changes\n * (a write result, a colleague's change by subscription, a resolved conflict): it is the\n * session's document with this editor's uncommitted positions laid over it — local wins\n * until committed, then the per-path check decides (EC-595 point 7).\n *\n * **Forced flushes** (the ruling: before unmount, tab hide, publish, preview, an AI\n * proposal, duplicate, transfer): {@link DocumentDraft.flush} commits every uncommitted\n * position and awaits the persister. Unmount and tab hide run it here; the editor's\n * publish, duplicate and the AI tap call it.\n *\n * **Thin presence** (Stage A): a heartbeat naming the focused field's path on focus\n * change and every {@link PRESENCE_HEARTBEAT_MS} (under the 30 s TTL), cleared on\n * unmount; the other sessions' live heartbeats arrive with the subscription.\n *\n * **Record write model** (users, redirects, assets, the account): the draft is edited\n * in place and written whole on the editor's Save button (EC-240) — unchanged.\n */\n\n/** The presence heartbeat interval — under {@link PRESENCE_TTL_MS} so a present editor never lapses. */\nexport const PRESENCE_HEARTBEAT_MS = 10_000\n\n/** An injectable visibility source (the tab-hide flush; tests drive it). */\nexport interface VisibilitySource {\n /** Call `onHidden` whenever the tab becomes hidden; returns the unsubscribe. */\n onHidden(onHidden: () => void): () => void\n}\n\nexport const documentVisibility: VisibilitySource = {\n onHidden(onHidden) {\n if (typeof document === 'undefined') return () => {}\n const listener = () => {\n if (document.visibilityState === 'hidden') onHidden()\n }\n document.addEventListener('visibilitychange', listener)\n return () => document.removeEventListener('visibilitychange', listener)\n },\n}\n\n/**\n * EC-647 — the editor's draft and the revision of the session view it was last rebased\n * onto, as ONE value: every edit made on the draft was authored against that revision (an\n * uncommitted position keeps the revision its first keystroke saw). `undefined`: the view\n * had no revision (a fixture session, a record rail, a document still being created).\n *\n * EC-667 — that session view is a COMPOSITE while it shows a pending write (Codex F18):\n * `revision` is its whole-document token (the EARLIEST authoring revision of any position\n * — what a whole-document writer, the slot alias or the translation, writes at), and\n * `shown` is the view itself, the token a position's edit reads ITS provenance from at\n * the first keystroke ({@link positionBase}).\n */\nexport interface DraftView {\n readonly document: CmsDocument\n readonly revision: number | undefined\n /** EC-667 — the session view the draft was last rebased onto (absent: none — a fixture, a record rail). */\n readonly shown?: CmsDocument\n}\n\n/** What a draft reads its tokens from: the write model's view registry (EC-647 / EC-667). */\nexport type ViewTokens = Pick<DocumentWrites, 'revisionOfView'>\n\n/** EC-667 — the draft of a session view as it is shown (pure): its value, its whole-document token, the view itself. */\nexport function sessionDraftView(doc: CmsDocument, tokens: ViewTokens | undefined): DraftView {\n return { document: doc, revision: tokens?.revisionOfView(doc), shown: doc }\n}\n\n/**\n * EC-667 — the base an edit of `path` that STARTS now on `draft` is authored against\n * (pure; the committer captures it at the first keystroke): the revision the content at\n * that position of the shown session view was authored against — under a pending\n * whole-document snapshot the snapshot's revision, under a pending own write that write's\n * base — never the revision of a row adopted underneath it. Captured at the start, it\n * survives the pending write's refusal or failure (looking it up when the commit arrives\n * would be too late). Without a view token: the draft's whole-document revision.\n */\nexport function positionBase(draft: DraftView, path: ValuePath, tokens: ViewTokens | undefined): number | undefined {\n return tokens && draft.shown ? tokens.revisionOfView(draft.shown, path) : draft.revision\n}\n\n/**\n * THE draft rebase as one value (pure — what the editor runs when the session's document\n * changes): the session view with the uncommitted positions laid over it ({@link\n * rebaseDraft}), its whole-document token, the view itself (EC-647 → EC-667: the rebase\n * moves the value AND its tokens), and — when the screen has a canvas — the token of the\n * canvas field's position, which the canvas's next edit is authored at.\n */\nexport function rebaseDraftView(\n doc: CmsDocument,\n previous: DraftView,\n uncommitted: readonly UncommittedPosition[],\n tokens: ViewTokens,\n canvasPath?: ValuePath,\n): { view: DraftView; canvasRevision: number | undefined } {\n return {\n view: { document: rebaseDraft(doc, previous.document, uncommitted), revision: tokens.revisionOfView(doc), shown: doc },\n canvasRevision: canvasPath ? tokens.revisionOfView(doc, canvasPath) : undefined,\n }\n}\n\n/**\n * EC-647 — the slot alias write (pure): the draft with `alias` set (or removed), written\n * whole AT THE DRAFT'S REVISION — never the session's tracked one, which may already hold\n * a colleague's change the draft has not absorbed (Codex F10). There is no identity-\n * metadata operation for the alias (the value-position grammar has no position for it,\n * measured on the base), so it stays a whole-document `replace` with its precondition.\n */\nexport function aliasWrite(\n draft: DraftView,\n alias: string | undefined,\n writes: { replace(doc: CmsDocument, revision: number | undefined): void },\n): CmsDocument {\n const next = { ...draft.document }\n if (alias === undefined) delete next.alias\n else next.alias = alias\n writes.replace(next, draft.revision)\n return next\n}\n\n/** The patch that writes the value `doc` holds at `path` (unset when absent). */\nexport function positionPatch(doc: CmsDocument, path: ValuePath): DocumentPatch {\n const value = readValueAt(doc, path)\n return value === undefined ? { op: 'unset', path } : { op: 'set', path, value }\n}\n\n/**\n * THE rebase (pure): the session's document with the editor's uncommitted positions laid\n * over it, values from the editor's current draft.\n */\nexport function rebaseDraft(\n sessionDoc: CmsDocument,\n draft: CmsDocument,\n uncommitted: readonly UncommittedPosition[],\n): CmsDocument {\n if (uncommitted.length === 0) return sessionDoc\n const patches = uncommitted.map((position) => positionPatch(draft, position.path))\n return applyDocumentPatches({ document: sessionDoc, patches, since: [] }).document\n}\n\n/**\n * EC-630 — the canvas's write into the draft (pure). The canvas writer's node patches,\n * keyed by node id, are the canvas's OWN delta: from `base`, the value it last agreed on\n * with the draft, to `value` — never a document diff, and never measured against the\n * draft's CURRENT value (a colleague's change that reached the draft a moment before the\n * edit lifted would read as part of the edit, and its patches would revert it). The draft\n * takes the delta over whatever it holds now (normally `base` itself) and canonicalises the\n * field through the write lens. `undefined` when the canvas changed nothing.\n */\nexport function canvasWrite(input: {\n collection: CollectionDef\n draft: CmsDocument\n field: string\n locale: Locale\n value: unknown\n base: unknown\n lookupManifest: PropFieldLookup\n}): { next: CmsDocument; patches: DocumentPatch[] } | undefined {\n const { collection, draft, field, locale, lookupManifest } = input\n const path = fieldPositionPath(collection, field, locale)\n const localized = path.length > 1 ? locale : undefined\n const patches = compositionPatches(input.base, input.value, field, localized)\n if (patches.length === 0) return undefined\n const applied = applyDocumentPatches({ document: draft, patches, since: [] }).document\n return { next: writeField(collection, draft, field, locale, readValueAt(applied, path), { lookupManifest }), patches }\n}\n\n/**\n * EC-630 — the change of the canvas field made UNDERNEATH the canvas when the draft\n * rebased from `previous` to `next` (pure), numbered after `last`; `undefined` when the\n * field did not move. The canvas's own edits are in both values (the session's view holds\n * every patch it committed, the draft overlays every position it has not), so the change\n * is someone else's: a colleague's patch, a write's canonical row, a resolved conflict.\n *\n * EC-647: it carries `revision`, the revision the draft is at after the rebase, and is\n * reported when only THAT moved too (`from` ≡ `to`): the canvas's agreed value is valid\n * at the newer revision, so its next edit is authored there — the revision follows the\n * value, never runs ahead of it.\n *\n * EC-667: `revision` is the token of the canvas FIELD's position in the shown view\n * ({@link rebaseDraftView}) — under a pending snapshot the snapshot's revision. The canvas\n * mounts at the draft's whole-document token (the earliest, possibly earlier than its\n * field's), so the first rebase with a token reports it even when nothing moved.\n */\nexport function canvasFieldChange(\n previous: CmsDocument,\n next: CmsDocument,\n path: ValuePath,\n last: CanvasFieldChange | undefined,\n revision: number | undefined,\n): CanvasFieldChange | undefined {\n const from = readValueAt(previous, path)\n const to = readValueAt(next, path)\n const tokenHeld = last === undefined ? revision === undefined : last.revision === revision\n if (semanticValueEqual(from, to) && tokenHeld) return undefined\n return { from, to, seq: (last?.seq ?? 0) + 1, revision }\n}\n\n/**\n * EC-647 — the canvas's write path (pure; what {@link DocumentDraft.setCanvasValue} runs):\n * the canvas writer's delta into the draft ({@link canvasWrite}, handed to `apply`), then\n * routed ({@link routeCanvasWrite}) — committed at once, or held as a focused inspector\n * control's uncommitted position — and in both cases paired with the revision of the\n * canvas value the edit was authored on (`lift.revision`), never the draft's: the draft\n * may already hold a colleague's change the canvas has not been rebased onto (Codex F12),\n * and the store must check the edit from the canvas's revision to refuse it where it\n * overlaps that change. `commit` absent → a draft without a revisioned write model (the\n * draft moves, nothing is committed). `undefined` when the canvas changed nothing.\n */\nexport function canvasCommit(input: {\n collection: CollectionDef\n draft: CmsDocument\n field: string\n locale: Locale\n lift: CanvasLift\n lookupManifest: PropFieldLookup\n /** The focused canvas inspector control, if focus is on one. */\n focused: CanvasControl | undefined\n committer: DraftCommitter\n /** The draft takes the canvas's write (before it is routed — a forced commit reads it). */\n apply: (next: CmsDocument) => void\n commit?: (patches: readonly DocumentPatch[], bases: readonly (number | undefined)[]) => void\n}): 'deferred' | 'committed' | 'draft' | undefined {\n const { lift, commit } = input\n const write = canvasWrite({\n collection: input.collection,\n draft: input.draft,\n field: input.field,\n locale: input.locale,\n value: lift.value,\n base: lift.base,\n lookupManifest: input.lookupManifest,\n })\n if (!write) return undefined\n input.apply(write.next)\n if (!commit) return 'draft'\n // EC-630: a burst of typing in a focused inspector control is that control's\n // uncommitted position (its policy commits it); a canvas action commits now.\n return routeCanvasWrite({\n patches: write.patches,\n focused: input.focused,\n committer: input.committer,\n revision: lift.revision,\n commitNow: (patches) => commit(patches, patches.map(() => lift.revision)),\n })\n}\n\n/**\n * The reader's \"now\" for presence liveness: THIS session's own latest heartbeat `at`\n * (written by the same store clock as everyone else's — the playground store runs a\n * deterministic sequential clock, Convex the writers' wall clocks), falling back to the\n * injected clock before the first heartbeat lands. Comparing others' `at` against a\n * different time base would call every colleague stale (measured live in the\n * playground: the store's clock is months behind the browser's).\n */\nexport function presenceReaderNow(\n entries: readonly PresenceEntry[],\n sessionId: string,\n fallback: string,\n): string {\n const own = entries.filter((entry) => entry.sessionId === sessionId).map((entry) => entry.at)\n return own.length > 0 ? own.sort().at(-1)! : fallback\n}\n\nexport interface DocumentDraft {\n draft: CmsDocument\n /**\n * EC-647 — the revision of the session view the draft was last rebased onto (see\n * {@link DraftView}): a writer that authors from {@link draft} (the translation) writes\n * at this revision. EC-667: the view's WHOLE-DOCUMENT token — the earliest authoring\n * revision of any position it shows (a field's own edit reads its position's token).\n */\n revision: number | undefined\n /** Uncommitted (document model) / unsaved (record model) edits exist. */\n dirty: boolean\n /** The record model: the editor renders a Save button + navigation guard. */\n explicitSave: boolean\n /** A field's control wrote `value` (the active locale for a localized field). */\n setFieldValue(field: FieldDef, value: unknown): void\n /** A writer that transforms ONE field position (fill / reset from base). */\n editField(field: FieldDef, update: (draft: CmsDocument) => CmsDocument): void\n /**\n * The canvas emitted a new value for its field (node patches, committed at once). The\n * lift's `base` is the value the canvas last agreed on with the draft: the canvas\n * writer's delta is `base` → `value`, applied over whatever the draft holds now (EC-630 —\n * a change that landed underneath meanwhile is never reverted by the canvas's own\n * write); its `revision` is the revision that value was agreed at — the revision the\n * edit is committed at (EC-647, {@link canvasCommit}).\n */\n setCanvasValue(field: FieldDef, lift: CanvasLift): void\n /** The slot alias (a document-level key: a whole-document write at the held revision). */\n setAlias(alias: string | undefined): void\n /** Field boundary events (focus / blur / Enter) — the commit policy's triggers. */\n focused(field: FieldDef): void\n blurred(field: FieldDef): void\n entered(field: FieldDef): void\n /**\n * EC-630 — the canvas inspector's boundary events: a control writing `position` of the\n * canvas field `field`, committing on `policy` (THE declaration for its kind). A burst of\n * typing in it is its uncommitted position until blur / Enter / the idle pause.\n */\n canvasFocused(field: FieldDef, position: CanvasControlPosition, policy: CommitPolicy): void\n canvasBlurred(field: FieldDef, position: CanvasControlPosition): void\n canvasEntered(field: FieldDef, position: CanvasControlPosition): void\n /** The forced commit (sync; the persister serialises what follows) / the record model's Save. */\n save(): void\n /** The FORCED FLUSH: commit everything uncommitted and await persistence. */\n flush(): Promise<void>\n /** Adopt a draft from outside (a restored version). */\n replaceDraft(next: CmsDocument): void\n /**\n * Bumped when the canvas must remount from the draft: a restored version replaced the\n * document, or a locale switch pointed the canvas at another bucket. (A change made\n * underneath the canvas is NOT a remount — {@link canvasChange}.)\n */\n canvasNonce: number\n /**\n * EC-630 — the latest change of the canvas field's value made UNDERNEATH the canvas (a\n * colleague's patch by subscription, a write's canonical row, a resolved conflict): the\n * draft's value before and after it. The canvas REBASES onto it — the change lands, its\n * undo history and selection survive — where it used to remount and lose both.\n */\n canvasChange: CanvasFieldChange | undefined\n /** The OTHER sessions present on this document, live by the reader's clock. */\n presence: readonly PresenceEntry[]\n}\n\nexport interface UseDocumentDraftInput {\n source: CollectionViewSource\n collection: CollectionDef\n doc: CmsDocument\n locale: Locale\n lookupManifest: PropFieldLookup\n /** The composition field the canvas edits on this screen, if any. */\n canvasField?: FieldDef\n scheduler?: Scheduler\n visibility?: VisibilitySource\n /** The reader's clock for presence liveness (ISO); `new Date().toISOString()` by default. */\n now?: () => string\n}\n\n/**\n * EC-616 — the base a record save is diffed against: the source's value whenever the draft\n * equals it (the record as opened, or as a save came back), else the previous base (the\n * editor has unsaved changes; a change from another writer meanwhile is not theirs). Pure.\n */\nexport function recordSaveBase(previous: CmsDocument, draft: CmsDocument, doc: CmsDocument): CmsDocument {\n return semanticValueEqual(draft, doc) ? doc : previous\n}\n\nexport function useDocumentDraft(input: UseDocumentDraftInput): DocumentDraft {\n const { source, collection, doc, locale, lookupManifest, canvasField } = input\n const writes = source.writes\n const documentModel = writes.kind === 'document' ? writes : undefined\n const ref: DocumentRef = useMemo(() => ({ collection: doc.collection, id: doc.id }), [doc.collection, doc.id])\n // EC-647: the draft and the revision of the view it rebased onto travel as ONE value\n // (EC-667: with the view itself — the per-position token).\n const [view, setViewState] = useState<DraftView>(() => sessionDraftView(doc, documentModel))\n const draft = view.document\n const draftRef = useRef(view)\n // EC-616: the record as the editor opened it (or as of their last successful save) — the\n // BASE a record rail diffs a save against, so it writes only the editor's changes. It moves\n // only while the draft equals the source's value (at open, and once a save is reflected\n // back): another writer's change arriving mid-edit never becomes the editor's base.\n const baseRef = useRef<CmsDocument>(doc)\n baseRef.current = recordSaveBase(baseRef.current, draftRef.current.document, doc)\n const setView = useCallback((next: DraftView) => {\n draftRef.current = next\n setViewState(next)\n }, [])\n /** An edit of the draft: its value moves, the tokens it is authored against do not. */\n const setDraft = useCallback(\n (next: CmsDocument) => setView({ ...draftRef.current, document: next }),\n [setView],\n )\n const scheduler = input.scheduler ?? browserScheduler\n const now = input.now ?? (() => new Date().toISOString())\n const fieldsByName = useMemo(() => new Map(collection.fields.map((f) => [f.name, f])), [collection])\n\n // --- Committing --------------------------------------------------------------------\n const writesRef = useRef(documentModel)\n writesRef.current = documentModel\n /** EC-630 — the canvas inspector controls that took focus, by committer key (their position + policy). */\n const canvasControls = useRef(new Map<string, CanvasControl>())\n const committer = useMemo<DraftCommitter>(\n () =>\n createDraftCommitter({\n policyOf: (name) => {\n const field = fieldsByName.get(name)\n if (field) return commitPolicyOf(field)\n return canvasControls.current.get(name)?.policy ?? 'blur-or-idle'\n },\n commit: (positions) => {\n const target = writesRef.current\n if (!target) return\n void target.commit(\n ref,\n positions.map((position) => positionPatch(draftRef.current.document, position.path)),\n positions.map((position) => position.base),\n )\n },\n // EC-647: a position's base is the revision of the DRAFT its first keystroke was\n // made on — never the session's tracked revision, which may already hold a\n // colleague's change the draft has not rebased onto yet. EC-667: the form's\n // writers name the POSITION's token ({@link positionBase}, passed to `edited`);\n // this whole-document token (the earliest) is the fallback.\n baseOf: () => draftRef.current.revision,\n scheduler,\n }),\n // One committer per open document (the editor is keyed by it).\n [ref],\n )\n\n // --- The canvas's value: its remount and its rebase ------------------------------------\n const canvasPath = canvasField ? fieldPositionPath(collection, canvasField.name, locale) : undefined\n const canvasPathKey = canvasPath ? formatValuePath(canvasPath) : ''\n const [canvasNonce, setCanvasNonce] = useState(0)\n const [canvasChange, setCanvasChange] = useState<CanvasFieldChange | undefined>(undefined)\n // A locale switch points the canvas at another bucket: remount from it.\n const lastCanvasPathKey = useRef(canvasPathKey)\n useEffect(() => {\n if (lastCanvasPathKey.current === canvasPathKey) return\n lastCanvasPathKey.current = canvasPathKey\n setCanvasNonce((n) => n + 1)\n }, [canvasPathKey])\n\n // --- The rebase (document model) -------------------------------------------------------\n const firstDoc = useRef(true)\n useEffect(() => {\n if (firstDoc.current) {\n firstDoc.current = false\n return\n }\n if (!documentModel) return\n const previous = draftRef.current.document\n // EC-647: the rebase moves the value AND the revision — the session view's own\n // (EC-667: its tokens, whole-document and per position).\n const { view: next, canvasRevision } = rebaseDraftView(doc, draftRef.current, committer.uncommitted(), documentModel, canvasPath)\n setView(next)\n // EC-630: the canvas field moved underneath the canvas — the canvas rebases onto the\n // change (its own edits are in both values: the session's view holds every patch it\n // committed, and the draft overlays every position it has not). EC-647: the change\n // carries the revision — EC-667: the canvas FIELD's token — so the canvas's next edit\n // is authored where its value is.\n if (!canvasPath) return\n setCanvasChange((last) => canvasFieldChange(previous, next.document, canvasPath, last, canvasRevision) ?? last)\n }, [doc])\n\n // --- Writers ----------------------------------------------------------------------------\n const setFieldValue = useCallback(\n (field: FieldDef, value: unknown) => {\n // EC-667: the position's token is read BEFORE the draft moves — from the view this\n // keystroke was made on (a re-edit keeps its first keystroke's either way).\n const path = fieldPositionPath(collection, field.name, locale)\n const authored = { base: positionBase(draftRef.current, path, documentModel) }\n const next = writeField(collection, draftRef.current.document, field.name, locale, value, { lookupManifest })\n setDraft(next)\n if (documentModel) committer.edited(field.name, path, authored)\n },\n [collection, locale, lookupManifest, setDraft, documentModel, committer],\n )\n const editField = useCallback(\n (field: FieldDef, update: (current: CmsDocument) => CmsDocument) => {\n const path = fieldPositionPath(collection, field.name, locale)\n const authored = { base: positionBase(draftRef.current, path, documentModel) }\n setDraft(update(draftRef.current.document))\n if (documentModel) committer.edited(field.name, path, authored)\n },\n [collection, locale, setDraft, documentModel, committer],\n )\n const setCanvasValue = useCallback(\n (field: FieldDef, lift: CanvasLift) => {\n const focus = committer.focusedField()\n canvasCommit({\n collection,\n draft: draftRef.current.document,\n field: field.name,\n locale,\n lift,\n lookupManifest,\n focused: focus === undefined ? undefined : canvasControls.current.get(focus),\n committer,\n apply: setDraft,\n ...(documentModel\n ? { commit: (patches: readonly DocumentPatch[], bases: readonly (number | undefined)[]) => void documentModel.commit(ref, patches, bases) }\n : {}),\n })\n },\n [collection, locale, lookupManifest, setDraft, documentModel, ref, committer],\n )\n const setAlias = useCallback(\n (alias: string | undefined) => {\n if (!documentModel) {\n aliasWrite(draftRef.current, alias, { replace: (next) => setDraft(next) })\n return\n }\n // The uncommitted positions first (the snapshot holds them), then the snapshot AT\n // THE DRAFT'S REVISION (EC-647 — Codex F10).\n committer.commitAll()\n setDraft(aliasWrite(draftRef.current, alias, documentModel))\n },\n [setDraft, documentModel, committer],\n )\n\n // --- Forced flush ----------------------------------------------------------------------\n const save = useCallback(() => {\n if (documentModel) committer.commitAll()\n else if (writes.kind === 'record') writes.save(draftRef.current.document, baseRef.current)\n }, [documentModel, committer, writes])\n const flush = useCallback(async () => {\n save()\n if (documentModel) await documentModel.flush(ref)\n }, [save, documentModel, ref])\n\n // Tab hide: commit and flush. Unmount: the same, and leave the document.\n const visibility = input.visibility ?? documentVisibility\n useEffect(() => {\n if (!documentModel) return\n return visibility.onHidden(() => {\n committer.commitAll()\n void documentModel.flush(ref)\n })\n }, [ref])\n useEffect(\n () => () => {\n const target = writesRef.current\n if (!target) return\n committer.commitAll()\n committer.dispose()\n void target.flush(ref)\n target.clearPresence(ref)\n },\n [ref],\n )\n\n // --- Subscription + presence ------------------------------------------------------------\n const [presenceEntries, setPresenceEntries] = useState<readonly PresenceEntry[]>([])\n const [clock, setClock] = useState(now)\n useEffect(() => {\n if (!documentModel) return\n return documentModel.subscribe(ref, setPresenceEntries)\n }, [ref])\n const focusedPathRef = useRef<ValuePath | undefined>(undefined)\n const heartbeat = useCallback(() => {\n writesRef.current?.reportPresence(ref, focusedPathRef.current)\n setClock(now())\n }, [ref])\n useEffect(() => {\n if (!documentModel) return\n heartbeat()\n let handle: unknown\n const tick = () => {\n heartbeat()\n handle = scheduler.setTimeout(tick, PRESENCE_HEARTBEAT_MS)\n }\n handle = scheduler.setTimeout(tick, PRESENCE_HEARTBEAT_MS)\n return () => scheduler.clearTimeout(handle)\n }, [ref])\n const presence = useMemo(\n () =>\n documentModel\n ? livePresence(\n presenceEntries,\n presenceReaderNow(presenceEntries, documentModel.presenceSessionId, clock),\n PRESENCE_TTL_MS,\n ).filter((entry) => entry.sessionId !== documentModel.presenceSessionId)\n : [],\n [presenceEntries, clock, documentModel],\n )\n\n const focused = useCallback(\n (field: FieldDef) => {\n if (!documentModel) return\n committer.focused(field.name)\n focusedPathRef.current = fieldPositionPath(collection, field.name, locale)\n heartbeat()\n },\n [documentModel, committer, collection, locale, heartbeat],\n )\n const blurred = useCallback(\n (field: FieldDef) => {\n if (!documentModel) return\n committer.blurred(field.name)\n focusedPathRef.current = undefined\n heartbeat()\n },\n [documentModel, committer, heartbeat],\n )\n const entered = useCallback(\n (field: FieldDef) => {\n if (documentModel) committer.entered(field.name)\n },\n [documentModel, committer],\n )\n\n // EC-630 — the canvas inspector's commit boundary: a control at a position of the canvas\n // field gained focus, lost it, or saw Enter. Its policy is THE declaration's for its kind.\n const canvasControlOf = useCallback(\n (field: FieldDef, position: CanvasControlPosition, policy: CommitPolicy) =>\n canvasControl(fieldPositionPath(collection, field.name, locale), position, policy),\n [collection, locale],\n )\n const canvasFocused = useCallback(\n (field: FieldDef, position: CanvasControlPosition, policy: CommitPolicy) => {\n if (!documentModel) return\n const control = canvasControlOf(field, position, policy)\n canvasControls.current.set(control.key, control)\n committer.focused(control.key)\n focusedPathRef.current = control.path\n heartbeat()\n },\n [documentModel, committer, canvasControlOf, heartbeat],\n )\n const canvasBlurred = useCallback(\n (field: FieldDef, position: CanvasControlPosition) => {\n if (!documentModel) return\n const key = canvasControlOf(field, position, 'change').key\n committer.blurred(key)\n canvasControls.current.delete(key)\n focusedPathRef.current = undefined\n heartbeat()\n },\n [documentModel, committer, canvasControlOf, heartbeat],\n )\n const canvasEntered = useCallback(\n (field: FieldDef, position: CanvasControlPosition) => {\n if (documentModel) committer.entered(canvasControlOf(field, position, 'change').key)\n },\n [documentModel, committer, canvasControlOf],\n )\n\n const replaceDraft = useCallback(\n (next: CmsDocument) => {\n // A restored version replaces the document: when it moved the canvas field, the\n // canvas remounts from it (its history belonged to the replaced value).\n const moved = canvasPath && !semanticValueEqual(readValueAt(draftRef.current.document, canvasPath), readValueAt(next, canvasPath))\n // EC-647: the restored value comes with the revision the session showed it at.\n setView(sessionDraftView(next, documentModel))\n if (moved) setCanvasNonce((n) => n + 1)\n },\n // The path is keyed by its string form.\n [setView, documentModel, canvasPathKey],\n )\n\n const dirty = useMemo(() => !semanticValueEqual(draft, doc), [draft, doc])\n\n return {\n draft,\n revision: view.revision,\n dirty,\n explicitSave: writes.kind === 'record',\n setFieldValue,\n editField,\n setCanvasValue,\n setAlias,\n focused,\n blurred,\n entered,\n canvasFocused,\n canvasBlurred,\n canvasEntered,\n save,\n flush,\n replaceDraft,\n canvasNonce,\n canvasChange,\n presence,\n }\n}\n"]}
|
|
@@ -211,9 +211,12 @@ export interface DocumentWrites {
|
|
|
211
211
|
/**
|
|
212
212
|
* EC-647 — the revision a document value the session SHOWED is at (`undefined` for a
|
|
213
213
|
* value it never showed, or a provisional one): a writer holding the value it displayed
|
|
214
|
-
* holds its revision.
|
|
214
|
+
* holds its revision. EC-667 — a view showing a pending write is a composite: with
|
|
215
|
+
* `path`, the revision the content at that position was authored against (an editor's
|
|
216
|
+
* first keystroke there); without it, the EARLIEST of any position the view shows — the
|
|
217
|
+
* token of a whole-document writer.
|
|
215
218
|
*/
|
|
216
|
-
revisionOfView(view: CmsDocument): number | undefined;
|
|
219
|
+
revisionOfView(view: CmsDocument, path?: ValuePath): number | undefined;
|
|
217
220
|
/** Create a document from a whole value (insert-if-absent): duplicate. */
|
|
218
221
|
insert(doc: CmsDocument): void;
|
|
219
222
|
/** A fresh document id from the session's injected factory (EC-578). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection-view-source.js","sourceRoot":"","sources":["../../../src/lib/collection-view/collection-view-source.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAiTvD;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAA4B,EAC5B,KAAe,EACf,MAAoB;IAEpB,OAAO,MAAM,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;AACjG,CAAC;AAyDD;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAA4B;IACjE,OAAO,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE;QAClC,UAAU,EAAE,MAAM,CAAC,UAAU,KAAK,SAAS;QAC3C,UAAU,EAAE,MAAM,CAAC,UAAU,KAAK,SAAS;QAC3C,QAAQ,EAAE,MAAM,CAAC,YAAY,CAAC,OAAO;KACtC,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import type {\n CmsDocument,\n CollectionDef,\n DocumentPatch,\n DocumentRef,\n ValuePath,\n DocumentUrl,\n FieldDef,\n Locale,\n LocaleConfig,\n LocalizedString,\n RestoreScope,\n} from '@elytracms/core/cms-core'\nimport type { CmsValidationIssue } from '@elytracms/core/cms-core'\nimport type { OperationError } from '@elytracms/core/operations'\nimport type { CmsDocumentRecord, PresenceEntry, ReferenceTarget } from '@elytracms/core/persistence'\nimport type { CommitOutcome, DocumentConflict } from '../operations/cms-persistence'\nimport type { TableViewState } from '../cms-views/view-model'\nimport { pseudoColumns } from '../cms-views/view-model'\nimport type { ReadState } from '../query'\n// The concrete inline-edit + bulk vocabulary (Stream A relocates it into `lib` as it settles).\nimport type { CellCommitInput, CellCommitOutcome } from '../../components/cms/cell-edit'\nimport type { BulkAction, BulkReport, BulkSetField } from '../../components/cms/bulk-actions'\nimport type { DocumentVersionsView, ImportReport } from '../../components/cms/cms-store'\n\n/**\n * `CollectionViewSource` — the studio's UNIVERSAL interaction seam (EC-240,\n * vision AD-19).\n *\n * The list surface (`CollectionTable`) and the edit-shell (`DetailEditor`) are\n * the studio's universal interaction surfaces. Historically they read the\n * concrete `CmsStore` through `useCms()`, so they only served `cmsDocuments`.\n * This interface is the data-agnostic seam they render over instead, so the SAME\n * surfaces drive the users rail (EC-240), redirects (EC-303), the Components app\n * edit-shell, and any future rail — no hand-rolled UI per rail.\n *\n * **Shape:** the source is scoped to ONE collection (it carries `schema` +\n * `collectionId`), and its methods mirror the subset of `CmsStore` the surfaces\n * actually use — dropping the `collectionId` argument (the source already knows\n * it). Keeping the names/signatures aligned makes the CMS adapter a\n * behaviour-preserving pass-through and the migration near-mechanical.\n *\n * **Referential stability:** the surfaces put `documents`, `isPublished`,\n * `localeConfig`, etc. in `useMemo` dependency arrays. An implementation MUST\n * return stable references across renders (delegate to the underlying store's\n * already-stable methods; never wrap in a fresh closure each render) or memos\n * bust and the inline-edit order snapshot / perf regress.\n *\n * **Capabilities:** flags gate whole shell regions AND method presence — a source\n * with `capabilities.versions === false` (the users rail) never mounts the\n * Versions pane, so no `listVersions` read fires on credential records. Optional\n * method groups are present iff the flag is set.\n */\nexport interface CollectionViewSource {\n /** The rail/collection this source serves. */\n readonly collectionId: string\n /** The schema lens: fields, form tabs/groups, `titleField`, display nouns. */\n readonly schema: CollectionDef\n /** Which regions/methods this source supports. */\n readonly capabilities: CollectionViewCapabilities\n\n /** The project scope (asset picker, published-pool queries). Absent in fixture sessions. */\n readonly projectId?: string\n /**\n * The collection id assets live in (for a cell/field's `titleOf` label fallback\n * when {@link resolveAsset} finds no record). Present only for sources that\n * reference assets — a rail without assets (users) omits it.\n */\n readonly assetCollectionId?: string\n /** The ambient locale lens (only meaningful when `capabilities.locales`). */\n readonly localeConfig: LocaleConfig\n readonly activeLocale: Locale\n\n // --- Reads -----------------------------------------------------------------\n /** The full row set (view mechanics filter/sort this client-side). */\n documents(): CmsDocument[]\n get(ref: DocumentRef): CmsDocument | undefined\n /** A relation/asset target's display title (hides the string-vs-function `titleField`). */\n titleOf(ref: DocumentRef): string\n /**\n * List the candidate records of a relation field's declared `target` collection\n * — the pick-list for a relation picker (EC-072). References are first-class and\n * collection-based: the field's schema declares which collection(s) it targets,\n * and this resolves those rows for ANY target (cross-collection), not just this\n * source's own collection. The caller passes `field.target`; a multi-target field\n * (future) merges one call per declared target. An unknown/empty target yields `[]`.\n */\n referenceCandidates(collectionId: string): CmsDocument[]\n /**\n * The workspace schema a reference/asset can point at — the schema counterpart to\n * {@link referenceCandidates}. Needed for the cases that read a TARGET collection's\n * fields (an asset field editing the asset collection's own metadata; the reuse\n * capability correlating embed manifests against their target collection). A\n * reference is collection-based, so resolving one sometimes needs the target's\n * schema, not just its rows.\n */\n readonly collections: readonly CollectionDef[]\n isFieldReadOnly(field: FieldDef): boolean\n /**\n * Principal-based field access (EC-240, vision AD-18): `edit` (writable), `read`\n * (visible but read-only), or `hidden` (not rendered for this principal). This is\n * IDENTITY-driven and distinct from a field's content-conditional `hidden(values)`\n * — e.g. an editor's own `role` resolves to `read`, a viewer never SEES a\n * sensitive field (`hidden`). A single tri-state (never two independent flags) so\n * the nonsensical writable-but-invisible state can't be expressed. Optional:\n * absent ⇒ derived from {@link isFieldReadOnly} (`read`|`edit`), i.e. default-open\n * (permissions RESTRICT, never grant a baseline). Per-rail policy lives on the\n * source; the starter's policy flows into its construction (engine = mechanism).\n * Prefer {@link resolveFieldAccess} over calling this directly (it applies the\n * default-open fallback).\n */\n fieldAccess?(field: FieldDef, record?: CmsDocument): FieldAccess\n validate(doc: CmsDocument): CmsValidationIssue[]\n /**\n * Resolve an asset id to its display metadata for a cell/field, or `undefined`\n * when there is no asset backend (a source without assets degrades to a label\n * via {@link titleOf}, matching today's \"no AssetsProvider\" behaviour).\n */\n resolveAsset?(id: string): AssetDisplay | undefined\n /**\n * How this rail's records are addressed in the EC-155 reference index — the\n * target shape the delete/unpublish confirmation's inbound pre-flight queries\n * (EC-433). Absent ⇒ the document shape (`{ type: 'document', id: 'collection:id' }`),\n * which every `cmsDocuments` collection uses. The assets rail supplies\n * `{ type: 'asset' }` so its list/bulk delete confirmation enumerates the SAME\n * inbound references its detail surface does — a document-shaped lookup would\n * report a referenced asset as \"not referenced\" (the EC-319 objection that\n * originally kept `remove` off the assets rail).\n */\n referenceTarget?(ref: DocumentRef): ReferenceTarget\n /** The last failed/denied mutation, surfaced by the shell (never a silent failure). */\n readonly lastError: OperationError | null\n clearLastError(): void\n /**\n * Land any queued/optimistic writes so a subsequent pre-flight READ sees them\n * (the reference index is maintained on write). A source with no async\n * persistence omits it — its reads are already authoritative.\n */\n flush?(): Promise<void>\n\n /**\n * The studio route splat for a record's detail surface (the row link + the\n * post-create navigation target), e.g. `studioPath.cmsDocument(collectionId, id)`.\n * Rail-owned so the list never hard-codes the CMS route. `undefined` ⇒ the rail\n * has no detail surface, so rows render as plain text (no navigation).\n */\n detailPath?(id: string): string\n /**\n * The studio route splat for this rail's LIST surface — the edit shell's\n * \"back to list\" breadcrumb crumb and its post-delete navigation target, e.g.\n * `studioPath.cmsCollection(collectionId)`. Rail-owned so the edit shell never\n * hard-codes the CMS route (the counterpart to {@link detailPath}). `undefined`\n * ⇒ the crumb is unlinked and delete falls back to the project index.\n */\n readonly listPath?: string\n\n // --- View state (list) -----------------------------------------------------\n viewState(): TableViewState\n setViewState(update: TableViewState | ((prev: TableViewState) => TableViewState)): void\n\n // --- Writes ----------------------------------------------------------------\n create(): DocumentRef\n /**\n * EC-595 A2 — HOW this source's records are written: exactly one model per source,\n * so a surface can never write a revisioned document the record way or vice versa.\n * `record` — the explicit-save rails (users, redirects, assets, the account): the\n * editor's Save button hands the whole record over. `document` — the CMS: every\n * writer emits patches at its source, committed on quiescence and merged per path\n * at the store; the editor shows per-field conflicts and follows the subscription.\n */\n readonly writes: CollectionWrites\n remove(ref: DocumentRef): void\n /**\n * Bulk publish/unpublish/delete/set-field (only when `capabilities.bulk`): one\n * command PER item, per-item outcomes enumerated in the report (partial failure\n * is never silent).\n */\n bulk?(ids: readonly string[], action: BulkAction, setField?: BulkSetField): Promise<BulkReport>\n\n // --- Capability method groups (present iff the matching flag is set) --------\n /** `publish` — published-pool predicate + publish/unpublish. */\n readonly publishing?: PublishingCapability\n /**\n * Per-document timestamps (EC-445) — the data behind the `$updatedAt`\n * pseudo-column (and, once its data path lands, `$publishedAt`). Present iff the\n * source materializes them (the CMS source, sourced through session-hydration);\n * a rail that carries no timestamps omits it, so those pseudo-columns simply don't\n * apply (see {@link pseudoColumnsForSource}). The accessor shape mirrors\n * {@link PublishingCapability.isPublished} — a per-ref lookup, referentially stable.\n */\n readonly timestamps?: DocumentTimestampsCapability\n /** `versions` — version history + restore. */\n readonly versioning?: VersioningCapability\n /** `inlineEdit` — the list's single-command optimistic cell write. */\n readonly inlineEditing?: InlineEditCapability\n /**\n * Config-as-code named lenses (EC-246b) the header view selector switches\n * between. Optional: a source that declares no named views (a non-CMS rail\n * like users) omits it, and the selector renders nothing (single default).\n */\n readonly views?: NamedViewsCapability\n /**\n * `importExport` — JSON round-trip of the collection's records. CMS-specific;\n * a rail whose records are credential-bearing (users) omits it, so neither the\n * import dialog nor the export action mounts (identities aren't dumped to JSON).\n */\n readonly imports?: ImportCapability\n}\n\n/** EC-595 A2 — the write model of a source (see {@link CollectionViewSource.writes}). */\nexport type CollectionWrites = RecordWrites | DocumentWrites\n\n/**\n * An explicit-save rail: the editor's Save button writes the record. `base` (EC-616) is the\n * record as the editor OPENED it (or as of their last successful save) — a rail that can\n * write per column sends only what differs from it, so a column another writer changed\n * meanwhile is kept; absent for a record being created.\n */\nexport interface RecordWrites {\n readonly kind: 'record'\n save(doc: CmsDocument, base?: CmsDocument): void\n}\n\n/**\n * The revisioned-document write model (EC-595, ratified 2026-10-24): patches at the\n * source, the persister's queue, per-field conflicts, the document subscription.\n */\nexport interface DocumentWrites {\n readonly kind: 'document'\n /**\n * Commit the patches a writer made; resolves once acknowledged (`undefined` in a\n * fixture session). `bases` — REQUIRED, per patch: the revision of the view the writer\n * authored it against (EC-647 — never the session's current one by default; a write\n * that names none is refused).\n */\n commit(\n ref: DocumentRef,\n patches: readonly DocumentPatch[],\n bases: readonly (number | undefined)[],\n ): Promise<CommitOutcome | undefined>\n /** The revision the session's view of a document is at now. */\n revisionOf(ref: DocumentRef): number | undefined\n /**\n * EC-647 — the revision a document value the session SHOWED is at (`undefined` for a\n * value it never showed, or a provisional one): a writer holding the value it displayed\n * holds its revision.\n */\n revisionOfView(view: CmsDocument): number | undefined\n /** Create a document from a whole value (insert-if-absent): duplicate. */\n insert(doc: CmsDocument): void\n /** A fresh document id from the session's injected factory (EC-578). */\n newId(): string\n /** The open per-field conflicts on a document. */\n conflicts(ref: DocumentRef): readonly DocumentConflict[]\n resolveConflict(ref: DocumentRef, conflictId: string, choice: 'theirs' | 'mine'): void\n /** Adopt a row the document subscription pushed (local edits win on their paths until flushed). */\n adoptSnapshot(ref: DocumentRef, record: CmsDocumentRecord | undefined): void\n /** The forced flush's persistence half: await every queued write of the document. */\n flush(ref: DocumentRef): Promise<void>\n /**\n * Write the whole document at `revision` — the revision of the view the snapshot was\n * authored on (EC-647; a document-level key the value-position grammar has no position\n * for — the slot `alias`). A move since is refused, never overwritten.\n */\n replace(doc: CmsDocument, revision: number | undefined): void\n /**\n * EC-595 point 7 — follow ONE document while it is open: every pushed row is adopted\n * under the local edits (the editor rebases), and the presence on it is delivered to\n * `onPresence`. Returns the unsubscribe function.\n */\n subscribe(ref: DocumentRef, onPresence: (presence: readonly PresenceEntry[]) => void): () => void\n /** Thin presence (Stage A): this session is on the document, in `path`'s field (or on no field). */\n reportPresence(ref: DocumentRef, path: ValuePath | undefined): void\n /** This session left the document. */\n clearPresence(ref: DocumentRef): void\n /** This session's presence id (one per tab), so the editor can leave itself out. */\n readonly presenceSessionId: string\n}\n\n/**\n * The capability matrix. Each flag gates a shell region AND the presence of its\n * method group. The **users rail** (EC-240) sets `publish:false`, `versions:false`\n * (credential caution), `create:false` (identity minted by sign-up), and no\n * `liveUrl`/`hierarchy`.\n */\nexport interface CollectionViewCapabilities {\n readonly publish: boolean\n readonly versions: boolean\n readonly locales: boolean\n readonly inlineEdit: boolean\n readonly create: boolean\n /**\n * Duplicate-document action (EC-442, amended same day). Deliberately its own\n * flag, never derived from `create`: the owner call (2026-07-17) is CMS\n * collections ONLY — no primitive rail offers Duplicate for now (identities\n * and uploads aren't copyable content; redirects deferred until requested as\n * a product feature).\n */\n readonly duplicate: boolean\n readonly remove: boolean\n readonly bulk: boolean\n /** The derived live-URL column (published pool × deployment routes). */\n readonly liveUrl: boolean\n /** Parent-self-relation tree mode (hierarchical collections only). */\n readonly hierarchy: boolean\n /** JSON import/export of the collection's records (off for credential rails). */\n readonly importExport: boolean\n /**\n * The rail is administration-scoped (mirrors `ADMIN_ONLY_OPERATIONS`). The\n * nav-permission layer reads this to gate the rail item. Absent ⇒ not admin-only.\n */\n readonly adminOnly?: boolean\n}\n\n/**\n * Principal-based field access (EC-240, AD-18). One tri-state drives BOTH visibility\n * and writability: `edit` (writable), `read` (visible, read-only), `hidden` (not\n * rendered). Default `edit` (default-open). Distinct from content-`hidden(values)`;\n * they compose — a field shows iff *not content-hidden AND access ≠ hidden*, and is\n * editable iff *access = edit AND not otherwise read-only* (published view, `form.readOnly`).\n */\nexport type FieldAccess = 'edit' | 'read' | 'hidden'\n\n/**\n * Resolve a field's effective principal access for a source — its {@link\n * CollectionViewSource.fieldAccess} policy when present, else derived from the\n * boolean `isFieldReadOnly` (default-open). The one place the shell asks \"may this\n * principal edit / see this field?\", so the default-open fallback lives in one spot.\n */\nexport function resolveFieldAccess(\n source: CollectionViewSource,\n field: FieldDef,\n record?: CmsDocument,\n): FieldAccess {\n return source.fieldAccess?.(field, record) ?? (source.isFieldReadOnly(field) ? 'read' : 'edit')\n}\n\n/**\n * Asset display metadata a cell/field renders (thumbnail or labelled badge). EC-587:\n * named apart from core's `ResolvedAsset` (the delivery shape) — this is the studio's\n * label/thumbnail slice; its `alt` is the one reader's (`assetAlt`).\n */\nexport interface AssetDisplay {\n readonly url?: string\n readonly contentType?: string\n readonly title?: string\n readonly filename?: string\n readonly alt?: string\n}\n\n/** `publish` capability. */\nexport interface PublishingCapability {\n /** Published-ness from the version pointer (NOT a row field). */\n isPublished(ref: DocumentRef): boolean\n /**\n * Does this PUBLISHED document's working draft carry unpublished changes\n * (EC-449)? The cheap per-row signal behind the amber \"changed\" `$state` dot —\n * sourced from the MATERIALIZED `hasUnpublishedChanges` record flag (computed\n * once at the write chokepoint), never a per-row snapshot compare. Optional: a\n * source without the signal omits it, and the list derives only\n * published/draft (exactly the pre-EC-449 rendering). Same referentially-stable\n * per-ref accessor shape as {@link isPublished}.\n */\n hasUnpublishedChanges?(ref: DocumentRef): boolean\n publish(ref: DocumentRef): Promise<void>\n unpublish(ref: DocumentRef): Promise<void>\n /**\n * Live URLs for the routable documents, keyed by id — derived from the\n * PUBLISHED pool (never draft rows) so an unpublished slug/parent edit can't\n * leak into a live link (EC-205/EC-216). `null` when the collection is not\n * routable → the live-URL column is hidden. Only present when `capabilities.liveUrl`.\n *\n * EC-628: a {@link ReadState} — the URL map only when the published read answered; a\n * failed read is `error` with its sentence, which the URL column shows in every row\n * (never the \"not published\" dash an empty map would give).\n */\n liveUrls?(): ReadState<ReadonlyMap<string, DocumentUrl>> | null\n}\n\n/**\n * `timestamps` capability (EC-445/EC-447): per-document record timestamps as ISO\n * strings, or `undefined` when a document carries none. The `$updatedAt`/`$publishedAt`\n * pseudo-columns read + sort by these. `updatedAt` is the last-write instant;\n * `publishedAt` (EC-447) is the FIRST-publish instant — set-once on publish, cleared on\n * unpublish, so it is `undefined` for a draft / never-published / currently-unpublished\n * document (the column shows EmDash).\n */\nexport interface DocumentTimestampsCapability {\n updatedAt(ref: DocumentRef): string | undefined\n publishedAt(ref: DocumentRef): string | undefined\n}\n\n/**\n * The reserved pseudo-columns applicable to a source's list (EC-445), derived from\n * its capabilities: `$updatedAt` iff it materializes timestamps, `$state` iff it has a\n * publish lifecycle, `$url` iff it resolves delivery URLs. The one place the list\n * surface + the Display picker agree on which pseudo-columns exist for a source, so\n * the column universe never drifts between where columns render and where they toggle.\n */\nexport function pseudoColumnsForSource(source: CollectionViewSource): string[] {\n return pseudoColumns(source.schema, {\n timestamps: source.timestamps !== undefined,\n publishing: source.publishing !== undefined,\n routable: source.capabilities.liveUrl,\n })\n}\n\n/** `versions` capability. */\nexport interface VersioningCapability {\n restore(ref: DocumentRef, version: number, scope?: RestoreScope): Promise<CmsDocument | null>\n /**\n * The document's recorded version history + live pin. Folds the flush-before-read\n * ordering INTO the loader (the adapter's concern), so every fetch lists an\n * authoritative history regardless of which surface triggered the change (EC-224).\n */\n load(ref: DocumentRef): Promise<DocumentVersionsView>\n}\n\n/** `inlineEdit` capability: the list's single-command optimistic cell write (EC-168). */\nexport interface InlineEditCapability {\n commitField(input: CellCommitInput): Promise<CellCommitOutcome>\n}\n\n/** A named lens the header selector offers — id + display name only (the header never needs its state). */\nexport interface NamedView {\n readonly id: string\n /** Display name; a {@link LocalizedString} (EC-343), resolved at the render site like `form.label`. */\n readonly name: LocalizedString\n}\n\n/** `importExport` capability: validate + fold external JSON records into the collection (EC-079). */\nexport interface ImportCapability {\n importDocuments(items: unknown[]): ImportReport\n}\n\n/**\n * `views` capability: config-as-code named lenses (EC-246b). The header selector\n * only *picks* a lens — it never creates/edits/deletes one (shared views are code).\n */\nexport interface NamedViewsCapability {\n /** The built-in default + config-declared named views. */\n list(): readonly NamedView[]\n /** The currently-selected view id. */\n activeId(): string\n /** Switch views (resets the working table state to the view's baseline). */\n setActive(id: string): void\n}\n\nexport type { CellCommitInput, CellCommitOutcome, ImportReport }\n"]}
|
|
1
|
+
{"version":3,"file":"collection-view-source.js","sourceRoot":"","sources":["../../../src/lib/collection-view/collection-view-source.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAoTvD;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAA4B,EAC5B,KAAe,EACf,MAAoB;IAEpB,OAAO,MAAM,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;AACjG,CAAC;AAyDD;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAA4B;IACjE,OAAO,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE;QAClC,UAAU,EAAE,MAAM,CAAC,UAAU,KAAK,SAAS;QAC3C,UAAU,EAAE,MAAM,CAAC,UAAU,KAAK,SAAS;QAC3C,QAAQ,EAAE,MAAM,CAAC,YAAY,CAAC,OAAO;KACtC,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import type {\n CmsDocument,\n CollectionDef,\n DocumentPatch,\n DocumentRef,\n ValuePath,\n DocumentUrl,\n FieldDef,\n Locale,\n LocaleConfig,\n LocalizedString,\n RestoreScope,\n} from '@elytracms/core/cms-core'\nimport type { CmsValidationIssue } from '@elytracms/core/cms-core'\nimport type { OperationError } from '@elytracms/core/operations'\nimport type { CmsDocumentRecord, PresenceEntry, ReferenceTarget } from '@elytracms/core/persistence'\nimport type { CommitOutcome, DocumentConflict } from '../operations/cms-persistence'\nimport type { TableViewState } from '../cms-views/view-model'\nimport { pseudoColumns } from '../cms-views/view-model'\nimport type { ReadState } from '../query'\n// The concrete inline-edit + bulk vocabulary (Stream A relocates it into `lib` as it settles).\nimport type { CellCommitInput, CellCommitOutcome } from '../../components/cms/cell-edit'\nimport type { BulkAction, BulkReport, BulkSetField } from '../../components/cms/bulk-actions'\nimport type { DocumentVersionsView, ImportReport } from '../../components/cms/cms-store'\n\n/**\n * `CollectionViewSource` — the studio's UNIVERSAL interaction seam (EC-240,\n * vision AD-19).\n *\n * The list surface (`CollectionTable`) and the edit-shell (`DetailEditor`) are\n * the studio's universal interaction surfaces. Historically they read the\n * concrete `CmsStore` through `useCms()`, so they only served `cmsDocuments`.\n * This interface is the data-agnostic seam they render over instead, so the SAME\n * surfaces drive the users rail (EC-240), redirects (EC-303), the Components app\n * edit-shell, and any future rail — no hand-rolled UI per rail.\n *\n * **Shape:** the source is scoped to ONE collection (it carries `schema` +\n * `collectionId`), and its methods mirror the subset of `CmsStore` the surfaces\n * actually use — dropping the `collectionId` argument (the source already knows\n * it). Keeping the names/signatures aligned makes the CMS adapter a\n * behaviour-preserving pass-through and the migration near-mechanical.\n *\n * **Referential stability:** the surfaces put `documents`, `isPublished`,\n * `localeConfig`, etc. in `useMemo` dependency arrays. An implementation MUST\n * return stable references across renders (delegate to the underlying store's\n * already-stable methods; never wrap in a fresh closure each render) or memos\n * bust and the inline-edit order snapshot / perf regress.\n *\n * **Capabilities:** flags gate whole shell regions AND method presence — a source\n * with `capabilities.versions === false` (the users rail) never mounts the\n * Versions pane, so no `listVersions` read fires on credential records. Optional\n * method groups are present iff the flag is set.\n */\nexport interface CollectionViewSource {\n /** The rail/collection this source serves. */\n readonly collectionId: string\n /** The schema lens: fields, form tabs/groups, `titleField`, display nouns. */\n readonly schema: CollectionDef\n /** Which regions/methods this source supports. */\n readonly capabilities: CollectionViewCapabilities\n\n /** The project scope (asset picker, published-pool queries). Absent in fixture sessions. */\n readonly projectId?: string\n /**\n * The collection id assets live in (for a cell/field's `titleOf` label fallback\n * when {@link resolveAsset} finds no record). Present only for sources that\n * reference assets — a rail without assets (users) omits it.\n */\n readonly assetCollectionId?: string\n /** The ambient locale lens (only meaningful when `capabilities.locales`). */\n readonly localeConfig: LocaleConfig\n readonly activeLocale: Locale\n\n // --- Reads -----------------------------------------------------------------\n /** The full row set (view mechanics filter/sort this client-side). */\n documents(): CmsDocument[]\n get(ref: DocumentRef): CmsDocument | undefined\n /** A relation/asset target's display title (hides the string-vs-function `titleField`). */\n titleOf(ref: DocumentRef): string\n /**\n * List the candidate records of a relation field's declared `target` collection\n * — the pick-list for a relation picker (EC-072). References are first-class and\n * collection-based: the field's schema declares which collection(s) it targets,\n * and this resolves those rows for ANY target (cross-collection), not just this\n * source's own collection. The caller passes `field.target`; a multi-target field\n * (future) merges one call per declared target. An unknown/empty target yields `[]`.\n */\n referenceCandidates(collectionId: string): CmsDocument[]\n /**\n * The workspace schema a reference/asset can point at — the schema counterpart to\n * {@link referenceCandidates}. Needed for the cases that read a TARGET collection's\n * fields (an asset field editing the asset collection's own metadata; the reuse\n * capability correlating embed manifests against their target collection). A\n * reference is collection-based, so resolving one sometimes needs the target's\n * schema, not just its rows.\n */\n readonly collections: readonly CollectionDef[]\n isFieldReadOnly(field: FieldDef): boolean\n /**\n * Principal-based field access (EC-240, vision AD-18): `edit` (writable), `read`\n * (visible but read-only), or `hidden` (not rendered for this principal). This is\n * IDENTITY-driven and distinct from a field's content-conditional `hidden(values)`\n * — e.g. an editor's own `role` resolves to `read`, a viewer never SEES a\n * sensitive field (`hidden`). A single tri-state (never two independent flags) so\n * the nonsensical writable-but-invisible state can't be expressed. Optional:\n * absent ⇒ derived from {@link isFieldReadOnly} (`read`|`edit`), i.e. default-open\n * (permissions RESTRICT, never grant a baseline). Per-rail policy lives on the\n * source; the starter's policy flows into its construction (engine = mechanism).\n * Prefer {@link resolveFieldAccess} over calling this directly (it applies the\n * default-open fallback).\n */\n fieldAccess?(field: FieldDef, record?: CmsDocument): FieldAccess\n validate(doc: CmsDocument): CmsValidationIssue[]\n /**\n * Resolve an asset id to its display metadata for a cell/field, or `undefined`\n * when there is no asset backend (a source without assets degrades to a label\n * via {@link titleOf}, matching today's \"no AssetsProvider\" behaviour).\n */\n resolveAsset?(id: string): AssetDisplay | undefined\n /**\n * How this rail's records are addressed in the EC-155 reference index — the\n * target shape the delete/unpublish confirmation's inbound pre-flight queries\n * (EC-433). Absent ⇒ the document shape (`{ type: 'document', id: 'collection:id' }`),\n * which every `cmsDocuments` collection uses. The assets rail supplies\n * `{ type: 'asset' }` so its list/bulk delete confirmation enumerates the SAME\n * inbound references its detail surface does — a document-shaped lookup would\n * report a referenced asset as \"not referenced\" (the EC-319 objection that\n * originally kept `remove` off the assets rail).\n */\n referenceTarget?(ref: DocumentRef): ReferenceTarget\n /** The last failed/denied mutation, surfaced by the shell (never a silent failure). */\n readonly lastError: OperationError | null\n clearLastError(): void\n /**\n * Land any queued/optimistic writes so a subsequent pre-flight READ sees them\n * (the reference index is maintained on write). A source with no async\n * persistence omits it — its reads are already authoritative.\n */\n flush?(): Promise<void>\n\n /**\n * The studio route splat for a record's detail surface (the row link + the\n * post-create navigation target), e.g. `studioPath.cmsDocument(collectionId, id)`.\n * Rail-owned so the list never hard-codes the CMS route. `undefined` ⇒ the rail\n * has no detail surface, so rows render as plain text (no navigation).\n */\n detailPath?(id: string): string\n /**\n * The studio route splat for this rail's LIST surface — the edit shell's\n * \"back to list\" breadcrumb crumb and its post-delete navigation target, e.g.\n * `studioPath.cmsCollection(collectionId)`. Rail-owned so the edit shell never\n * hard-codes the CMS route (the counterpart to {@link detailPath}). `undefined`\n * ⇒ the crumb is unlinked and delete falls back to the project index.\n */\n readonly listPath?: string\n\n // --- View state (list) -----------------------------------------------------\n viewState(): TableViewState\n setViewState(update: TableViewState | ((prev: TableViewState) => TableViewState)): void\n\n // --- Writes ----------------------------------------------------------------\n create(): DocumentRef\n /**\n * EC-595 A2 — HOW this source's records are written: exactly one model per source,\n * so a surface can never write a revisioned document the record way or vice versa.\n * `record` — the explicit-save rails (users, redirects, assets, the account): the\n * editor's Save button hands the whole record over. `document` — the CMS: every\n * writer emits patches at its source, committed on quiescence and merged per path\n * at the store; the editor shows per-field conflicts and follows the subscription.\n */\n readonly writes: CollectionWrites\n remove(ref: DocumentRef): void\n /**\n * Bulk publish/unpublish/delete/set-field (only when `capabilities.bulk`): one\n * command PER item, per-item outcomes enumerated in the report (partial failure\n * is never silent).\n */\n bulk?(ids: readonly string[], action: BulkAction, setField?: BulkSetField): Promise<BulkReport>\n\n // --- Capability method groups (present iff the matching flag is set) --------\n /** `publish` — published-pool predicate + publish/unpublish. */\n readonly publishing?: PublishingCapability\n /**\n * Per-document timestamps (EC-445) — the data behind the `$updatedAt`\n * pseudo-column (and, once its data path lands, `$publishedAt`). Present iff the\n * source materializes them (the CMS source, sourced through session-hydration);\n * a rail that carries no timestamps omits it, so those pseudo-columns simply don't\n * apply (see {@link pseudoColumnsForSource}). The accessor shape mirrors\n * {@link PublishingCapability.isPublished} — a per-ref lookup, referentially stable.\n */\n readonly timestamps?: DocumentTimestampsCapability\n /** `versions` — version history + restore. */\n readonly versioning?: VersioningCapability\n /** `inlineEdit` — the list's single-command optimistic cell write. */\n readonly inlineEditing?: InlineEditCapability\n /**\n * Config-as-code named lenses (EC-246b) the header view selector switches\n * between. Optional: a source that declares no named views (a non-CMS rail\n * like users) omits it, and the selector renders nothing (single default).\n */\n readonly views?: NamedViewsCapability\n /**\n * `importExport` — JSON round-trip of the collection's records. CMS-specific;\n * a rail whose records are credential-bearing (users) omits it, so neither the\n * import dialog nor the export action mounts (identities aren't dumped to JSON).\n */\n readonly imports?: ImportCapability\n}\n\n/** EC-595 A2 — the write model of a source (see {@link CollectionViewSource.writes}). */\nexport type CollectionWrites = RecordWrites | DocumentWrites\n\n/**\n * An explicit-save rail: the editor's Save button writes the record. `base` (EC-616) is the\n * record as the editor OPENED it (or as of their last successful save) — a rail that can\n * write per column sends only what differs from it, so a column another writer changed\n * meanwhile is kept; absent for a record being created.\n */\nexport interface RecordWrites {\n readonly kind: 'record'\n save(doc: CmsDocument, base?: CmsDocument): void\n}\n\n/**\n * The revisioned-document write model (EC-595, ratified 2026-10-24): patches at the\n * source, the persister's queue, per-field conflicts, the document subscription.\n */\nexport interface DocumentWrites {\n readonly kind: 'document'\n /**\n * Commit the patches a writer made; resolves once acknowledged (`undefined` in a\n * fixture session). `bases` — REQUIRED, per patch: the revision of the view the writer\n * authored it against (EC-647 — never the session's current one by default; a write\n * that names none is refused).\n */\n commit(\n ref: DocumentRef,\n patches: readonly DocumentPatch[],\n bases: readonly (number | undefined)[],\n ): Promise<CommitOutcome | undefined>\n /** The revision the session's view of a document is at now. */\n revisionOf(ref: DocumentRef): number | undefined\n /**\n * EC-647 — the revision a document value the session SHOWED is at (`undefined` for a\n * value it never showed, or a provisional one): a writer holding the value it displayed\n * holds its revision. EC-667 — a view showing a pending write is a composite: with\n * `path`, the revision the content at that position was authored against (an editor's\n * first keystroke there); without it, the EARLIEST of any position the view shows — the\n * token of a whole-document writer.\n */\n revisionOfView(view: CmsDocument, path?: ValuePath): number | undefined\n /** Create a document from a whole value (insert-if-absent): duplicate. */\n insert(doc: CmsDocument): void\n /** A fresh document id from the session's injected factory (EC-578). */\n newId(): string\n /** The open per-field conflicts on a document. */\n conflicts(ref: DocumentRef): readonly DocumentConflict[]\n resolveConflict(ref: DocumentRef, conflictId: string, choice: 'theirs' | 'mine'): void\n /** Adopt a row the document subscription pushed (local edits win on their paths until flushed). */\n adoptSnapshot(ref: DocumentRef, record: CmsDocumentRecord | undefined): void\n /** The forced flush's persistence half: await every queued write of the document. */\n flush(ref: DocumentRef): Promise<void>\n /**\n * Write the whole document at `revision` — the revision of the view the snapshot was\n * authored on (EC-647; a document-level key the value-position grammar has no position\n * for — the slot `alias`). A move since is refused, never overwritten.\n */\n replace(doc: CmsDocument, revision: number | undefined): void\n /**\n * EC-595 point 7 — follow ONE document while it is open: every pushed row is adopted\n * under the local edits (the editor rebases), and the presence on it is delivered to\n * `onPresence`. Returns the unsubscribe function.\n */\n subscribe(ref: DocumentRef, onPresence: (presence: readonly PresenceEntry[]) => void): () => void\n /** Thin presence (Stage A): this session is on the document, in `path`'s field (or on no field). */\n reportPresence(ref: DocumentRef, path: ValuePath | undefined): void\n /** This session left the document. */\n clearPresence(ref: DocumentRef): void\n /** This session's presence id (one per tab), so the editor can leave itself out. */\n readonly presenceSessionId: string\n}\n\n/**\n * The capability matrix. Each flag gates a shell region AND the presence of its\n * method group. The **users rail** (EC-240) sets `publish:false`, `versions:false`\n * (credential caution), `create:false` (identity minted by sign-up), and no\n * `liveUrl`/`hierarchy`.\n */\nexport interface CollectionViewCapabilities {\n readonly publish: boolean\n readonly versions: boolean\n readonly locales: boolean\n readonly inlineEdit: boolean\n readonly create: boolean\n /**\n * Duplicate-document action (EC-442, amended same day). Deliberately its own\n * flag, never derived from `create`: the owner call (2026-07-17) is CMS\n * collections ONLY — no primitive rail offers Duplicate for now (identities\n * and uploads aren't copyable content; redirects deferred until requested as\n * a product feature).\n */\n readonly duplicate: boolean\n readonly remove: boolean\n readonly bulk: boolean\n /** The derived live-URL column (published pool × deployment routes). */\n readonly liveUrl: boolean\n /** Parent-self-relation tree mode (hierarchical collections only). */\n readonly hierarchy: boolean\n /** JSON import/export of the collection's records (off for credential rails). */\n readonly importExport: boolean\n /**\n * The rail is administration-scoped (mirrors `ADMIN_ONLY_OPERATIONS`). The\n * nav-permission layer reads this to gate the rail item. Absent ⇒ not admin-only.\n */\n readonly adminOnly?: boolean\n}\n\n/**\n * Principal-based field access (EC-240, AD-18). One tri-state drives BOTH visibility\n * and writability: `edit` (writable), `read` (visible, read-only), `hidden` (not\n * rendered). Default `edit` (default-open). Distinct from content-`hidden(values)`;\n * they compose — a field shows iff *not content-hidden AND access ≠ hidden*, and is\n * editable iff *access = edit AND not otherwise read-only* (published view, `form.readOnly`).\n */\nexport type FieldAccess = 'edit' | 'read' | 'hidden'\n\n/**\n * Resolve a field's effective principal access for a source — its {@link\n * CollectionViewSource.fieldAccess} policy when present, else derived from the\n * boolean `isFieldReadOnly` (default-open). The one place the shell asks \"may this\n * principal edit / see this field?\", so the default-open fallback lives in one spot.\n */\nexport function resolveFieldAccess(\n source: CollectionViewSource,\n field: FieldDef,\n record?: CmsDocument,\n): FieldAccess {\n return source.fieldAccess?.(field, record) ?? (source.isFieldReadOnly(field) ? 'read' : 'edit')\n}\n\n/**\n * Asset display metadata a cell/field renders (thumbnail or labelled badge). EC-587:\n * named apart from core's `ResolvedAsset` (the delivery shape) — this is the studio's\n * label/thumbnail slice; its `alt` is the one reader's (`assetAlt`).\n */\nexport interface AssetDisplay {\n readonly url?: string\n readonly contentType?: string\n readonly title?: string\n readonly filename?: string\n readonly alt?: string\n}\n\n/** `publish` capability. */\nexport interface PublishingCapability {\n /** Published-ness from the version pointer (NOT a row field). */\n isPublished(ref: DocumentRef): boolean\n /**\n * Does this PUBLISHED document's working draft carry unpublished changes\n * (EC-449)? The cheap per-row signal behind the amber \"changed\" `$state` dot —\n * sourced from the MATERIALIZED `hasUnpublishedChanges` record flag (computed\n * once at the write chokepoint), never a per-row snapshot compare. Optional: a\n * source without the signal omits it, and the list derives only\n * published/draft (exactly the pre-EC-449 rendering). Same referentially-stable\n * per-ref accessor shape as {@link isPublished}.\n */\n hasUnpublishedChanges?(ref: DocumentRef): boolean\n publish(ref: DocumentRef): Promise<void>\n unpublish(ref: DocumentRef): Promise<void>\n /**\n * Live URLs for the routable documents, keyed by id — derived from the\n * PUBLISHED pool (never draft rows) so an unpublished slug/parent edit can't\n * leak into a live link (EC-205/EC-216). `null` when the collection is not\n * routable → the live-URL column is hidden. Only present when `capabilities.liveUrl`.\n *\n * EC-628: a {@link ReadState} — the URL map only when the published read answered; a\n * failed read is `error` with its sentence, which the URL column shows in every row\n * (never the \"not published\" dash an empty map would give).\n */\n liveUrls?(): ReadState<ReadonlyMap<string, DocumentUrl>> | null\n}\n\n/**\n * `timestamps` capability (EC-445/EC-447): per-document record timestamps as ISO\n * strings, or `undefined` when a document carries none. The `$updatedAt`/`$publishedAt`\n * pseudo-columns read + sort by these. `updatedAt` is the last-write instant;\n * `publishedAt` (EC-447) is the FIRST-publish instant — set-once on publish, cleared on\n * unpublish, so it is `undefined` for a draft / never-published / currently-unpublished\n * document (the column shows EmDash).\n */\nexport interface DocumentTimestampsCapability {\n updatedAt(ref: DocumentRef): string | undefined\n publishedAt(ref: DocumentRef): string | undefined\n}\n\n/**\n * The reserved pseudo-columns applicable to a source's list (EC-445), derived from\n * its capabilities: `$updatedAt` iff it materializes timestamps, `$state` iff it has a\n * publish lifecycle, `$url` iff it resolves delivery URLs. The one place the list\n * surface + the Display picker agree on which pseudo-columns exist for a source, so\n * the column universe never drifts between where columns render and where they toggle.\n */\nexport function pseudoColumnsForSource(source: CollectionViewSource): string[] {\n return pseudoColumns(source.schema, {\n timestamps: source.timestamps !== undefined,\n publishing: source.publishing !== undefined,\n routable: source.capabilities.liveUrl,\n })\n}\n\n/** `versions` capability. */\nexport interface VersioningCapability {\n restore(ref: DocumentRef, version: number, scope?: RestoreScope): Promise<CmsDocument | null>\n /**\n * The document's recorded version history + live pin. Folds the flush-before-read\n * ordering INTO the loader (the adapter's concern), so every fetch lists an\n * authoritative history regardless of which surface triggered the change (EC-224).\n */\n load(ref: DocumentRef): Promise<DocumentVersionsView>\n}\n\n/** `inlineEdit` capability: the list's single-command optimistic cell write (EC-168). */\nexport interface InlineEditCapability {\n commitField(input: CellCommitInput): Promise<CellCommitOutcome>\n}\n\n/** A named lens the header selector offers — id + display name only (the header never needs its state). */\nexport interface NamedView {\n readonly id: string\n /** Display name; a {@link LocalizedString} (EC-343), resolved at the render site like `form.label`. */\n readonly name: LocalizedString\n}\n\n/** `importExport` capability: validate + fold external JSON records into the collection (EC-079). */\nexport interface ImportCapability {\n importDocuments(items: unknown[]): ImportReport\n}\n\n/**\n * `views` capability: config-as-code named lenses (EC-246b). The header selector\n * only *picks* a lens — it never creates/edits/deletes one (shared views are code).\n */\nexport interface NamedViewsCapability {\n /** The built-in default + config-declared named views. */\n list(): readonly NamedView[]\n /** The currently-selected view id. */\n activeId(): string\n /** Switch views (resets the working table state to the view's baseline). */\n setActive(id: string): void\n}\n\nexport type { CellCommitInput, CellCommitOutcome, ImportReport }\n"]}
|
|
@@ -13,4 +13,4 @@
|
|
|
13
13
|
* (The convex-side stamp does NOT read this mirror — `convex/delivery.ts` imports the
|
|
14
14
|
* package.json declaration directly; its bundler allows what tsc's rootDir forbids.)
|
|
15
15
|
*/
|
|
16
|
-
export declare const ENGINE_VERSION = "0.0.
|
|
16
|
+
export declare const ENGINE_VERSION = "0.0.97";
|
|
@@ -13,5 +13,5 @@
|
|
|
13
13
|
* (The convex-side stamp does NOT read this mirror — `convex/delivery.ts` imports the
|
|
14
14
|
* package.json declaration directly; its bundler allows what tsc's rootDir forbids.)
|
|
15
15
|
*/
|
|
16
|
-
export const ENGINE_VERSION = '0.0.
|
|
16
|
+
export const ENGINE_VERSION = '0.0.97';
|
|
17
17
|
//# sourceMappingURL=engine-version.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine-version.js","sourceRoot":"","sources":["../../src/lib/engine-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAA","sourcesContent":["/**\n * The engine version this studio build was compiled from (EC-552) — the client-side\n * half of the deployed-functions version handshake: the boot gate compares it against\n * what `delivery:engineVersion` answers from the deployment.\n *\n * A MIRROR of `package.json`'s `version`, not the declaration itself: client source\n * cannot import `../../package.json` (outside the tsc build's `rootDir`; moving the\n * root would reshape `dist` and every export path). The parity test beside this file\n * fs-reads the real declaration and fails `pnpm verify` the moment the two diverge,\n * so a release cannot ship with a stale mirror. `pnpm release:bump` writes this\n * alongside the five `package.json` versions (EC-608).\n *\n * (The convex-side stamp does NOT read this mirror — `convex/delivery.ts` imports the\n * package.json declaration directly; its bundler allows what tsc's rootDir forbids.)\n */\nexport const ENGINE_VERSION = '0.0.
|
|
1
|
+
{"version":3,"file":"engine-version.js","sourceRoot":"","sources":["../../src/lib/engine-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAA","sourcesContent":["/**\n * The engine version this studio build was compiled from (EC-552) — the client-side\n * half of the deployed-functions version handshake: the boot gate compares it against\n * what `delivery:engineVersion` answers from the deployment.\n *\n * A MIRROR of `package.json`'s `version`, not the declaration itself: client source\n * cannot import `../../package.json` (outside the tsc build's `rootDir`; moving the\n * root would reshape `dist` and every export path). The parity test beside this file\n * fs-reads the real declaration and fails `pnpm verify` the moment the two diverge,\n * so a release cannot ship with a stale mirror. `pnpm release:bump` writes this\n * alongside the five `package.json` versions (EC-608).\n *\n * (The convex-side stamp does NOT read this mirror — `convex/delivery.ts` imports the\n * package.json declaration directly; its bundler allows what tsc's rootDir forbids.)\n */\nexport const ENGINE_VERSION = '0.0.97'\n"]}
|