@tearleads/client-sdk 0.1.34 → 0.1.36

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.
@@ -1,20 +1,18 @@
1
1
  import { encodeVersionVector, exportFullHistorySnapshot, } from "../../../internal/loro/index.js";
2
- import { addDocumentAttachments, getDocumentAttachments, removeDocumentAttachment, } from "../../../data/documents/documentContent.js";
3
- import { createPendingUpdateFields } from "../../../data/documents/documentSync.js";
2
+ import { addDocumentAttachments } from "../../../data/documents/documentContent.js";
4
3
  import { requestDocumentStoreSync } from "../registry.js";
5
4
  import { buildAttachmentIntent, buildPendingAttachments, } from "./attachmentIntents.js";
6
5
  import { persistStagedAttachmentMutation, restoreFailedAttachmentMutation, } from "./attachmentMutationPersistence.js";
7
- import { assignLocalAttachmentSlots, removeLocalAttachmentSlot, } from "./attachmentPersistence.js";
8
6
  import { deleteUnreferencedStagedAttachmentBytes, installPendingAttachmentRows, stagePendingAttachments, } from "./attachmentStaging.js";
9
7
  import { captureAttachmentWriteGeneration, isAttachmentWriteGenerationCurrent, shouldAbortAttachmentPersistFollowup, } from "./attachmentWriteGeneration.js";
8
+ import { settleDeferredInitialUpdate } from "./deferredInitialUpdate.js";
10
9
  import { ensureDocumentStoreReady } from "./initialization.js";
11
- import { runDocumentOrphanMaintenance } from "./orphanMaintenance.js";
12
- import { advancePendingBaseVersion, pendingDeltaSinceBase, persistDocument, } from "./persistence.js";
10
+ import { advancePendingBaseVersion, pendingDeltaSinceBase, } from "./persistence.js";
13
11
  import { reportDocumentStoreWriteFailure } from "./reportWriteFailure.js";
14
- import { canAttachFiles, canWriteDocument, setReadySnapshot, } from "./state.js";
12
+ import { canAttachFiles, canWriteDocument, } from "./state.js";
15
13
  import { captureDocumentStoreSyncGeneration } from "./syncGeneration.js";
16
14
  // Fixed literal: a reported message must never carry file names or slot ids.
17
- const ATTACHMENT_FAILURE_MESSAGE = "Documents: attachment mutation failed";
15
+ export const ATTACHMENT_FAILURE_MESSAGE = "Documents: attachment mutation failed";
18
16
  function logAttachedFiles(state, count) {
19
17
  state.runtime.util.log(state.runtime.state.online && state.runtime.auth.isAuthenticated
20
18
  ? `Attached ${count} file${count === 1 ? "" : "s"} to document ${state.localId}.`
@@ -172,148 +170,46 @@ async function persistSlotAttachmentFile(state, slotId, file, writeGeneration) {
172
170
  state.runtime.util.log(`Queued attachment ${file.name} for slot ${slotId}.`);
173
171
  requestDocumentStoreSync(state);
174
172
  }
175
- function installCommittedAttachmentRemoval(input) {
176
- const { slotId, state, storageKey } = input;
177
- if (!storageKey)
178
- return;
179
- const pending = state.pendingAttachments.find((attachment) => attachment.slotId === slotId && attachment.storageKey === storageKey);
180
- if (input.syncedAttachment && pending?.upload) {
181
- assignLocalAttachmentSlots(state, [
182
- {
183
- slotId,
184
- storageKey,
185
- blobId: pending.upload.blobId,
186
- contentSha256: pending.contentSha256,
187
- },
188
- ]);
189
- }
190
- state.pendingAttachments = state.pendingAttachments.filter((attachment) => attachment.slotId !== slotId || attachment.storageKey !== storageKey);
191
- // The committed removal queued this key. A reset can leave another document
192
- // uploading from the same copy; a detached marker keeps it until sync.
193
- void runDocumentOrphanMaintenance(state);
194
- if (input.syncedAttachment ||
195
- state.attachmentStorageKeyBySlotId[slotId] !== storageKey) {
196
- return;
197
- }
198
- removeLocalAttachmentSlot(state, slotId);
199
- setReadySnapshot(state, input.currentDoc, state.snapshot.syncing, state.snapshot.text, state.snapshot.structuredFields);
200
- }
201
- async function persistRemovedAttachment(state, slotId, writeGeneration) {
202
- if (!isAttachmentWriteGenerationCurrent(state, writeGeneration))
203
- return;
204
- const currentDoc = state.doc;
205
- if (!currentDoc) {
206
- return;
207
- }
208
- const generation = captureDocumentStoreSyncGeneration(state, currentDoc);
209
- if (!generation) {
210
- return;
211
- }
212
- const existingAttachment = getDocumentAttachments(currentDoc).find((attachment) => attachment.slotId === slotId);
213
- if (!existingAttachment) {
214
- return;
215
- }
216
- // After a reset, a store opened cold holds the slot only as a pending upload.
217
- const storageKey = state.attachmentStorageKeyBySlotId[slotId] ??
218
- state.pendingAttachments.find((attachment) => attachment.slotId === slotId)
219
- ?.storageKey;
220
- if (!isAttachmentWriteGenerationCurrent(state, writeGeneration) ||
221
- state.doc !== currentDoc)
222
- return;
223
- const rollbackSnapshot = exportFullHistorySnapshot(currentDoc).slice();
224
- removeDocumentAttachment(currentDoc, slotId);
225
- const attachmentUpdate = pendingDeltaSinceBase(state, currentDoc);
226
- const coveredVersion = encodeVersionVector(currentDoc);
227
- const pendingUpdate = createPendingUpdateFields(attachmentUpdate);
228
- const syncedAttachment = Boolean(state.record?.documentId);
229
- // Preserve the optimistic text/structured fields (see persistAttachedFiles):
230
- // removing an attachment while typing must not regress the editor.
231
- let persisted;
232
- try {
233
- persisted = await persistDocument(state, currentDoc, { snapshotEndVersion: coveredVersion }, {
234
- ...(storageKey
235
- ? {
236
- attachmentRemoval: {
237
- mode: syncedAttachment
238
- ? "detach"
239
- : "delete",
240
- slotId,
241
- storageKey,
242
- },
243
- }
244
- : {}),
245
- ...(pendingUpdate ? { pendingUpdate } : {}),
246
- preserveSnapshotStructuredFields: true,
247
- preserveSnapshotText: true,
248
- }, generation);
249
- }
250
- catch (error) {
251
- await restoreFailedAttachmentMutation({
252
- generation,
253
- rollbackSnapshot,
254
- state,
255
- });
256
- throw error;
257
- }
258
- if (shouldAbortAttachmentPersistFollowup(state, writeGeneration, generation, persisted)) {
259
- // See persistAttachedFiles: only durable same-identity work re-arms sync.
260
- return;
261
- }
262
- installCommittedAttachmentRemoval({
263
- currentDoc,
264
- slotId,
265
- state,
266
- storageKey,
267
- syncedAttachment,
268
- });
269
- advancePendingBaseVersion(state, currentDoc);
270
- state.runtime.util.log(`Removed attachment ${existingAttachment.name} from document ${state.localId}.`);
271
- requestDocumentStoreSync(state);
272
- }
273
- export async function attachFilesToDocumentStore(state, scheduleSync, files) {
274
- if (files.length === 0) {
275
- return;
276
- }
173
+ async function captureReadyAttachWrite(state, scheduleSync, callGeneration) {
277
174
  let ready;
278
175
  try {
279
176
  ready = await ensureDocumentStoreReady(state, scheduleSync);
280
177
  }
281
178
  catch (error) {
282
179
  reportDocumentStoreWriteFailure(state, ATTACHMENT_FAILURE_MESSAGE, error);
283
- return;
180
+ return null;
284
181
  }
285
- if (!ready || !state.doc || !canWriteDocument(state)) {
286
- return;
182
+ if (!ready || !state.doc || !canWriteDocument(state))
183
+ return null;
184
+ // A reset or relink while this call waited owns the replacement document.
185
+ if (callGeneration &&
186
+ !isAttachmentWriteGenerationCurrent(state, callGeneration)) {
187
+ return null;
287
188
  }
288
- const writeGeneration = captureAttachmentWriteGeneration(state);
289
- if (!writeGeneration)
290
- return;
291
- state.writeChain = state.writeChain
292
- .catch(() => undefined)
293
- .then(async () => persistAttachedFiles(state, files, writeGeneration))
294
- .catch((error) => {
295
- reportDocumentStoreWriteFailure(state, ATTACHMENT_FAILURE_MESSAGE, error);
296
- });
297
- return state.writeChain;
189
+ return captureAttachmentWriteGeneration(state);
298
190
  }
299
- export async function removeAttachmentFromDocumentStore(state, scheduleSync, slotId) {
300
- let ready;
191
+ async function attachThenSettle(state, scheduleSync, files, callGeneration) {
192
+ const writeGeneration = files.length === 0
193
+ ? null
194
+ : await captureReadyAttachWrite(state, scheduleSync, callGeneration);
301
195
  try {
302
- ready = await ensureDocumentStoreReady(state, scheduleSync);
303
- }
304
- catch (error) {
305
- reportDocumentStoreWriteFailure(state, ATTACHMENT_FAILURE_MESSAGE, error);
306
- return;
196
+ if (writeGeneration) {
197
+ await persistAttachedFiles(state, files, writeGeneration);
198
+ }
307
199
  }
308
- if (!ready || !state.doc || !canWriteDocument(state)) {
309
- return;
200
+ finally {
201
+ await settleDeferredInitialUpdate(state, writeGeneration);
310
202
  }
311
- const writeGeneration = captureAttachmentWriteGeneration(state);
312
- if (!writeGeneration)
313
- return;
203
+ }
204
+ export async function attachFilesToDocumentStore(state, scheduleSync, files) {
205
+ // Join the chain before awaiting readiness so settlements keep call order,
206
+ // and settle even when nothing attaches: a deferred birth's sync hold lifts
207
+ // only behind every earlier attach, never while one is still staging. The
208
+ // caller's document is captured now (a cold store captures once ready).
209
+ const callGeneration = captureAttachmentWriteGeneration(state);
314
210
  state.writeChain = state.writeChain
315
211
  .catch(() => undefined)
316
- .then(async () => persistRemovedAttachment(state, slotId, writeGeneration))
212
+ .then(async () => attachThenSettle(state, scheduleSync, files, callGeneration))
317
213
  .catch((error) => {
318
214
  reportDocumentStoreWriteFailure(state, ATTACHMENT_FAILURE_MESSAGE, error);
319
215
  });
@@ -1 +1 @@
1
- {"version":3,"file":"attachments.js","sourceRoot":"","sources":["attachments.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n encodeVersionVector,\n exportFullHistorySnapshot,\n} from \"@tearleads/loro\";\nimport {\n addDocumentAttachments,\n getDocumentAttachments,\n removeDocumentAttachment,\n} from \"../../../data/documents/documentContent\";\nimport { createPendingUpdateFields } from \"../../../data/documents/documentSync\";\nimport type { PendingAttachmentRecord } from \"../../../workflows/documents\";\nimport { requestDocumentStoreSync } from \"../registry\";\nimport type { DocumentAttachmentUpload } from \"../types\";\nimport {\n buildAttachmentIntent,\n buildPendingAttachments,\n} from \"./attachmentIntents\";\nimport {\n persistStagedAttachmentMutation,\n restoreFailedAttachmentMutation,\n} from \"./attachmentMutationPersistence\";\nimport {\n assignLocalAttachmentSlots,\n removeLocalAttachmentSlot,\n} from \"./attachmentPersistence\";\nimport {\n deleteUnreferencedStagedAttachmentBytes,\n installPendingAttachmentRows,\n stagePendingAttachments,\n} from \"./attachmentStaging\";\nimport {\n type AttachmentWriteGeneration,\n captureAttachmentWriteGeneration,\n isAttachmentWriteGenerationCurrent,\n shouldAbortAttachmentPersistFollowup,\n} from \"./attachmentWriteGeneration\";\nimport { ensureDocumentStoreReady } from \"./initialization\";\nimport { runDocumentOrphanMaintenance } from \"./orphanMaintenance\";\nimport {\n advancePendingBaseVersion,\n pendingDeltaSinceBase,\n persistDocument,\n} from \"./persistence\";\nimport { reportDocumentStoreWriteFailure } from \"./reportWriteFailure\";\nimport {\n canAttachFiles,\n canWriteDocument,\n type DocumentStoreState,\n setReadySnapshot,\n} from \"./state\";\nimport { captureDocumentStoreSyncGeneration } from \"./syncGeneration\";\n\n// Fixed literal: a reported message must never carry file names or slot ids.\nconst ATTACHMENT_FAILURE_MESSAGE = \"Documents: attachment mutation failed\";\n\nfunction logAttachedFiles(state: DocumentStoreState, count: number) {\n state.runtime.util.log(\n state.runtime.state.online && state.runtime.auth.isAuthenticated\n ? `Attached ${count} file${count === 1 ? \"\" : \"s\"} to document ${state.localId}.`\n : `Stored ${count} attachment${count === 1 ? \"\" : \"s\"} locally for document ${state.localId}.`,\n );\n}\n\nasync function persistAttachedFiles(\n state: DocumentStoreState,\n files: ReadonlyArray<DocumentAttachmentUpload>,\n writeGeneration: AttachmentWriteGeneration,\n) {\n if (!isAttachmentWriteGenerationCurrent(state, writeGeneration)) return;\n const currentDoc = state.doc;\n\n if (!currentDoc || !canAttachFiles(state)) {\n state.runtime.util.log(\n \"Documents: attachments require a local key package.\",\n );\n return;\n }\n const generation = captureDocumentStoreSyncGeneration(state, currentDoc);\n if (!generation) {\n return;\n }\n\n const { nextAttachments, nextPendingAttachments } =\n await buildPendingAttachments(state.localId, files);\n if (\n !isAttachmentWriteGenerationCurrent(state, writeGeneration) ||\n state.doc !== currentDoc\n )\n return;\n const rollbackSnapshot = exportFullHistorySnapshot(currentDoc).slice();\n addDocumentAttachments(currentDoc, nextAttachments);\n const attachmentUpdate = pendingDeltaSinceBase(state, currentDoc);\n // Captured at delta time: the enqueued row proves coverage up to exactly\n // this version, so it is the frontier the persist below may publish.\n const coveredVersion = encodeVersionVector(currentDoc);\n\n let staged: Awaited<ReturnType<typeof stagePendingAttachments>>;\n try {\n staged = await stagePendingAttachments({\n files,\n generation,\n pendingAttachments: nextPendingAttachments,\n state,\n });\n } catch (error) {\n await restoreFailedAttachmentMutation({\n generation,\n rollbackSnapshot,\n state,\n });\n throw error;\n }\n if (!staged) {\n await restoreFailedAttachmentMutation({\n generation,\n rollbackSnapshot,\n state,\n });\n return;\n }\n // This persist changes attachments, not prose. setReadySnapshot always\n // re-derives attachments from the doc, but preserve the text/structured\n // fields so an attach that overlaps an in-flight keystroke does not republish\n // a stale doc read over the live optimistic editor value.\n const persisted = await persistStagedAttachmentMutation({\n attachmentUpdate,\n coveredVersion,\n currentDoc,\n generation,\n rollbackSnapshot,\n staged,\n state,\n });\n if (\n shouldAbortAttachmentPersistFollowup(\n state,\n writeGeneration,\n generation,\n persisted,\n )\n ) {\n await deleteUnreferencedStagedAttachmentBytes({\n generation,\n pendingAttachments: staged.pendingAttachments,\n state,\n });\n return;\n }\n installPendingAttachmentRows({ ...staged, state });\n advancePendingBaseVersion(state, currentDoc);\n logAttachedFiles(state, files.length);\n requestDocumentStoreSync(state);\n}\n\nasync function persistSlotAttachmentFile(\n state: DocumentStoreState,\n slotId: string,\n file: DocumentAttachmentUpload,\n writeGeneration: AttachmentWriteGeneration,\n) {\n if (!isAttachmentWriteGenerationCurrent(state, writeGeneration)) return;\n const currentDoc = state.doc;\n\n if (!currentDoc || !canAttachFiles(state)) {\n state.runtime.util.log(\n \"Documents: slot attachments require a local key package.\",\n );\n return;\n }\n const generation = captureDocumentStoreSyncGeneration(state, currentDoc);\n if (!generation) {\n return;\n }\n\n const replacementAttachment = await buildAttachmentIntent(slotId, file);\n if (\n !isAttachmentWriteGenerationCurrent(state, writeGeneration) ||\n state.doc !== currentDoc\n )\n return;\n const rollbackSnapshot = exportFullHistorySnapshot(currentDoc).slice();\n addDocumentAttachments(currentDoc, [replacementAttachment]);\n const attachmentUpdate = pendingDeltaSinceBase(state, currentDoc);\n const coveredVersion = encodeVersionVector(currentDoc);\n\n const storageKey = `${state.localId}-${slotId}-${crypto.randomUUID()}`;\n const pendingAttachment: PendingAttachmentRecord = {\n byteLength: replacementAttachment.byteLength,\n contentSha256: replacementAttachment.contentSha256,\n localId: state.localId,\n mimeType: replacementAttachment.mimeType,\n name: replacementAttachment.name,\n slotId,\n storageKey,\n };\n let staged: Awaited<ReturnType<typeof stagePendingAttachments>>;\n try {\n staged = await stagePendingAttachments({\n files: [file],\n generation,\n pendingAttachments: [pendingAttachment],\n state,\n });\n } catch (error) {\n await restoreFailedAttachmentMutation({\n generation,\n rollbackSnapshot,\n state,\n });\n throw error;\n }\n if (!staged) {\n await restoreFailedAttachmentMutation({\n generation,\n rollbackSnapshot,\n state,\n });\n return;\n }\n // Preserve the optimistic text/structured fields (see persistAttachedFiles):\n // a slot replacement overlapping a keystroke must not regress the editor.\n const persisted = await persistStagedAttachmentMutation({\n attachmentUpdate,\n coveredVersion,\n currentDoc,\n generation,\n rollbackSnapshot,\n staged,\n state,\n });\n if (\n shouldAbortAttachmentPersistFollowup(\n state,\n writeGeneration,\n generation,\n persisted,\n )\n ) {\n await deleteUnreferencedStagedAttachmentBytes({\n generation,\n pendingAttachments: staged.pendingAttachments,\n state,\n });\n return;\n }\n installPendingAttachmentRows({ ...staged, state });\n advancePendingBaseVersion(state, currentDoc);\n state.runtime.util.log(`Queued attachment ${file.name} for slot ${slotId}.`);\n requestDocumentStoreSync(state);\n}\n\nfunction installCommittedAttachmentRemoval(input: {\n currentDoc: NonNullable<DocumentStoreState[\"doc\"]>;\n slotId: string;\n state: DocumentStoreState;\n storageKey: string | undefined;\n syncedAttachment: boolean;\n}): void {\n const { slotId, state, storageKey } = input;\n if (!storageKey) return;\n const pending = state.pendingAttachments.find(\n (attachment) =>\n attachment.slotId === slotId && attachment.storageKey === storageKey,\n );\n if (input.syncedAttachment && pending?.upload) {\n assignLocalAttachmentSlots(state, [\n {\n slotId,\n storageKey,\n blobId: pending.upload.blobId,\n contentSha256: pending.contentSha256,\n },\n ]);\n }\n state.pendingAttachments = state.pendingAttachments.filter(\n (attachment) =>\n attachment.slotId !== slotId || attachment.storageKey !== storageKey,\n );\n // The committed removal queued this key. A reset can leave another document\n // uploading from the same copy; a detached marker keeps it until sync.\n void runDocumentOrphanMaintenance(state);\n if (\n input.syncedAttachment ||\n state.attachmentStorageKeyBySlotId[slotId] !== storageKey\n ) {\n return;\n }\n removeLocalAttachmentSlot(state, slotId);\n setReadySnapshot(\n state,\n input.currentDoc,\n state.snapshot.syncing,\n state.snapshot.text,\n state.snapshot.structuredFields,\n );\n}\n\nasync function persistRemovedAttachment(\n state: DocumentStoreState,\n slotId: string,\n writeGeneration: AttachmentWriteGeneration,\n) {\n if (!isAttachmentWriteGenerationCurrent(state, writeGeneration)) return;\n const currentDoc = state.doc;\n if (!currentDoc) {\n return;\n }\n const generation = captureDocumentStoreSyncGeneration(state, currentDoc);\n if (!generation) {\n return;\n }\n\n const existingAttachment = getDocumentAttachments(currentDoc).find(\n (attachment) => attachment.slotId === slotId,\n );\n if (!existingAttachment) {\n return;\n }\n\n // After a reset, a store opened cold holds the slot only as a pending upload.\n const storageKey =\n state.attachmentStorageKeyBySlotId[slotId] ??\n state.pendingAttachments.find((attachment) => attachment.slotId === slotId)\n ?.storageKey;\n if (\n !isAttachmentWriteGenerationCurrent(state, writeGeneration) ||\n state.doc !== currentDoc\n )\n return;\n const rollbackSnapshot = exportFullHistorySnapshot(currentDoc).slice();\n removeDocumentAttachment(currentDoc, slotId);\n const attachmentUpdate = pendingDeltaSinceBase(state, currentDoc);\n const coveredVersion = encodeVersionVector(currentDoc);\n const pendingUpdate = createPendingUpdateFields(attachmentUpdate);\n const syncedAttachment = Boolean(state.record?.documentId);\n\n // Preserve the optimistic text/structured fields (see persistAttachedFiles):\n // removing an attachment while typing must not regress the editor.\n let persisted: Awaited<ReturnType<typeof persistDocument>>;\n try {\n persisted = await persistDocument(\n state,\n currentDoc,\n { snapshotEndVersion: coveredVersion },\n {\n ...(storageKey\n ? {\n attachmentRemoval: {\n mode: syncedAttachment\n ? (\"detach\" as const)\n : (\"delete\" as const),\n slotId,\n storageKey,\n },\n }\n : {}),\n ...(pendingUpdate ? { pendingUpdate } : {}),\n preserveSnapshotStructuredFields: true,\n preserveSnapshotText: true,\n },\n generation,\n );\n } catch (error) {\n await restoreFailedAttachmentMutation({\n generation,\n rollbackSnapshot,\n state,\n });\n throw error;\n }\n if (\n shouldAbortAttachmentPersistFollowup(\n state,\n writeGeneration,\n generation,\n persisted,\n )\n ) {\n // See persistAttachedFiles: only durable same-identity work re-arms sync.\n return;\n }\n installCommittedAttachmentRemoval({\n currentDoc,\n slotId,\n state,\n storageKey,\n syncedAttachment,\n });\n advancePendingBaseVersion(state, currentDoc);\n state.runtime.util.log(\n `Removed attachment ${existingAttachment.name} from document ${state.localId}.`,\n );\n requestDocumentStoreSync(state);\n}\n\nexport async function attachFilesToDocumentStore(\n state: DocumentStoreState,\n scheduleSync: () => void,\n files: ReadonlyArray<DocumentAttachmentUpload>,\n) {\n if (files.length === 0) {\n return;\n }\n\n let ready: boolean;\n try {\n ready = await ensureDocumentStoreReady(state, scheduleSync);\n } catch (error) {\n reportDocumentStoreWriteFailure(state, ATTACHMENT_FAILURE_MESSAGE, error);\n return;\n }\n\n if (!ready || !state.doc || !canWriteDocument(state)) {\n return;\n }\n\n const writeGeneration = captureAttachmentWriteGeneration(state);\n if (!writeGeneration) return;\n state.writeChain = state.writeChain\n .catch(() => undefined)\n .then(async () => persistAttachedFiles(state, files, writeGeneration))\n .catch((error: unknown) => {\n reportDocumentStoreWriteFailure(state, ATTACHMENT_FAILURE_MESSAGE, error);\n });\n return state.writeChain;\n}\n\nexport async function removeAttachmentFromDocumentStore(\n state: DocumentStoreState,\n scheduleSync: () => void,\n slotId: string,\n) {\n let ready: boolean;\n try {\n ready = await ensureDocumentStoreReady(state, scheduleSync);\n } catch (error) {\n reportDocumentStoreWriteFailure(state, ATTACHMENT_FAILURE_MESSAGE, error);\n return;\n }\n\n if (!ready || !state.doc || !canWriteDocument(state)) {\n return;\n }\n\n const writeGeneration = captureAttachmentWriteGeneration(state);\n if (!writeGeneration) return;\n state.writeChain = state.writeChain\n .catch(() => undefined)\n .then(async () => persistRemovedAttachment(state, slotId, writeGeneration))\n .catch((error: unknown) => {\n reportDocumentStoreWriteFailure(state, ATTACHMENT_FAILURE_MESSAGE, error);\n });\n return state.writeChain;\n}\n\nexport async function replaceAttachmentInDocumentStore(\n state: DocumentStoreState,\n scheduleSync: () => void,\n slotId: string,\n file: DocumentAttachmentUpload,\n) {\n let ready: boolean;\n try {\n ready = await ensureDocumentStoreReady(state, scheduleSync);\n } catch (error) {\n reportDocumentStoreWriteFailure(state, ATTACHMENT_FAILURE_MESSAGE, error);\n return;\n }\n\n if (!ready || !state.doc || !canWriteDocument(state)) {\n return;\n }\n\n const writeGeneration = captureAttachmentWriteGeneration(state);\n if (!writeGeneration) return;\n state.writeChain = state.writeChain\n .catch(() => undefined)\n .then(async () =>\n persistSlotAttachmentFile(state, slotId, file, writeGeneration),\n )\n .catch((error: unknown) => {\n reportDocumentStoreWriteFailure(state, ATTACHMENT_FAILURE_MESSAGE, error);\n });\n return state.writeChain;\n}\n"]}
1
+ {"version":3,"file":"attachments.js","sourceRoot":"","sources":["attachments.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n encodeVersionVector,\n exportFullHistorySnapshot,\n} from \"@tearleads/loro\";\nimport { addDocumentAttachments } from \"../../../data/documents/documentContent\";\nimport type { PendingAttachmentRecord } from \"../../../workflows/documents\";\nimport { requestDocumentStoreSync } from \"../registry\";\nimport type { DocumentAttachmentUpload } from \"../types\";\nimport {\n buildAttachmentIntent,\n buildPendingAttachments,\n} from \"./attachmentIntents\";\nimport {\n persistStagedAttachmentMutation,\n restoreFailedAttachmentMutation,\n} from \"./attachmentMutationPersistence\";\nimport {\n deleteUnreferencedStagedAttachmentBytes,\n installPendingAttachmentRows,\n stagePendingAttachments,\n} from \"./attachmentStaging\";\nimport {\n type AttachmentWriteGeneration,\n captureAttachmentWriteGeneration,\n isAttachmentWriteGenerationCurrent,\n shouldAbortAttachmentPersistFollowup,\n} from \"./attachmentWriteGeneration\";\nimport { settleDeferredInitialUpdate } from \"./deferredInitialUpdate\";\nimport { ensureDocumentStoreReady } from \"./initialization\";\nimport {\n advancePendingBaseVersion,\n pendingDeltaSinceBase,\n} from \"./persistence\";\nimport { reportDocumentStoreWriteFailure } from \"./reportWriteFailure\";\nimport {\n canAttachFiles,\n canWriteDocument,\n type DocumentStoreState,\n} from \"./state\";\nimport { captureDocumentStoreSyncGeneration } from \"./syncGeneration\";\n\n// Fixed literal: a reported message must never carry file names or slot ids.\nexport const ATTACHMENT_FAILURE_MESSAGE =\n \"Documents: attachment mutation failed\";\n\nfunction logAttachedFiles(state: DocumentStoreState, count: number) {\n state.runtime.util.log(\n state.runtime.state.online && state.runtime.auth.isAuthenticated\n ? `Attached ${count} file${count === 1 ? \"\" : \"s\"} to document ${state.localId}.`\n : `Stored ${count} attachment${count === 1 ? \"\" : \"s\"} locally for document ${state.localId}.`,\n );\n}\n\nasync function persistAttachedFiles(\n state: DocumentStoreState,\n files: ReadonlyArray<DocumentAttachmentUpload>,\n writeGeneration: AttachmentWriteGeneration,\n) {\n if (!isAttachmentWriteGenerationCurrent(state, writeGeneration)) return;\n const currentDoc = state.doc;\n\n if (!currentDoc || !canAttachFiles(state)) {\n state.runtime.util.log(\n \"Documents: attachments require a local key package.\",\n );\n return;\n }\n const generation = captureDocumentStoreSyncGeneration(state, currentDoc);\n if (!generation) {\n return;\n }\n\n const { nextAttachments, nextPendingAttachments } =\n await buildPendingAttachments(state.localId, files);\n if (\n !isAttachmentWriteGenerationCurrent(state, writeGeneration) ||\n state.doc !== currentDoc\n )\n return;\n const rollbackSnapshot = exportFullHistorySnapshot(currentDoc).slice();\n addDocumentAttachments(currentDoc, nextAttachments);\n const attachmentUpdate = pendingDeltaSinceBase(state, currentDoc);\n // Captured at delta time: the enqueued row proves coverage up to exactly\n // this version, so it is the frontier the persist below may publish.\n const coveredVersion = encodeVersionVector(currentDoc);\n\n let staged: Awaited<ReturnType<typeof stagePendingAttachments>>;\n try {\n staged = await stagePendingAttachments({\n files,\n generation,\n pendingAttachments: nextPendingAttachments,\n state,\n });\n } catch (error) {\n await restoreFailedAttachmentMutation({\n generation,\n rollbackSnapshot,\n state,\n });\n throw error;\n }\n if (!staged) {\n await restoreFailedAttachmentMutation({\n generation,\n rollbackSnapshot,\n state,\n });\n return;\n }\n // This persist changes attachments, not prose. setReadySnapshot always\n // re-derives attachments from the doc, but preserve the text/structured\n // fields so an attach that overlaps an in-flight keystroke does not republish\n // a stale doc read over the live optimistic editor value.\n const persisted = await persistStagedAttachmentMutation({\n attachmentUpdate,\n coveredVersion,\n currentDoc,\n generation,\n rollbackSnapshot,\n staged,\n state,\n });\n if (\n shouldAbortAttachmentPersistFollowup(\n state,\n writeGeneration,\n generation,\n persisted,\n )\n ) {\n await deleteUnreferencedStagedAttachmentBytes({\n generation,\n pendingAttachments: staged.pendingAttachments,\n state,\n });\n return;\n }\n installPendingAttachmentRows({ ...staged, state });\n advancePendingBaseVersion(state, currentDoc);\n logAttachedFiles(state, files.length);\n requestDocumentStoreSync(state);\n}\n\nasync function persistSlotAttachmentFile(\n state: DocumentStoreState,\n slotId: string,\n file: DocumentAttachmentUpload,\n writeGeneration: AttachmentWriteGeneration,\n) {\n if (!isAttachmentWriteGenerationCurrent(state, writeGeneration)) return;\n const currentDoc = state.doc;\n\n if (!currentDoc || !canAttachFiles(state)) {\n state.runtime.util.log(\n \"Documents: slot attachments require a local key package.\",\n );\n return;\n }\n const generation = captureDocumentStoreSyncGeneration(state, currentDoc);\n if (!generation) {\n return;\n }\n\n const replacementAttachment = await buildAttachmentIntent(slotId, file);\n if (\n !isAttachmentWriteGenerationCurrent(state, writeGeneration) ||\n state.doc !== currentDoc\n )\n return;\n const rollbackSnapshot = exportFullHistorySnapshot(currentDoc).slice();\n addDocumentAttachments(currentDoc, [replacementAttachment]);\n const attachmentUpdate = pendingDeltaSinceBase(state, currentDoc);\n const coveredVersion = encodeVersionVector(currentDoc);\n\n const storageKey = `${state.localId}-${slotId}-${crypto.randomUUID()}`;\n const pendingAttachment: PendingAttachmentRecord = {\n byteLength: replacementAttachment.byteLength,\n contentSha256: replacementAttachment.contentSha256,\n localId: state.localId,\n mimeType: replacementAttachment.mimeType,\n name: replacementAttachment.name,\n slotId,\n storageKey,\n };\n let staged: Awaited<ReturnType<typeof stagePendingAttachments>>;\n try {\n staged = await stagePendingAttachments({\n files: [file],\n generation,\n pendingAttachments: [pendingAttachment],\n state,\n });\n } catch (error) {\n await restoreFailedAttachmentMutation({\n generation,\n rollbackSnapshot,\n state,\n });\n throw error;\n }\n if (!staged) {\n await restoreFailedAttachmentMutation({\n generation,\n rollbackSnapshot,\n state,\n });\n return;\n }\n // Preserve the optimistic text/structured fields (see persistAttachedFiles):\n // a slot replacement overlapping a keystroke must not regress the editor.\n const persisted = await persistStagedAttachmentMutation({\n attachmentUpdate,\n coveredVersion,\n currentDoc,\n generation,\n rollbackSnapshot,\n staged,\n state,\n });\n if (\n shouldAbortAttachmentPersistFollowup(\n state,\n writeGeneration,\n generation,\n persisted,\n )\n ) {\n await deleteUnreferencedStagedAttachmentBytes({\n generation,\n pendingAttachments: staged.pendingAttachments,\n state,\n });\n return;\n }\n installPendingAttachmentRows({ ...staged, state });\n advancePendingBaseVersion(state, currentDoc);\n state.runtime.util.log(`Queued attachment ${file.name} for slot ${slotId}.`);\n requestDocumentStoreSync(state);\n}\n\nasync function captureReadyAttachWrite(\n state: DocumentStoreState,\n scheduleSync: () => void,\n callGeneration: AttachmentWriteGeneration | null,\n) {\n let ready: boolean;\n try {\n ready = await ensureDocumentStoreReady(state, scheduleSync);\n } catch (error) {\n reportDocumentStoreWriteFailure(state, ATTACHMENT_FAILURE_MESSAGE, error);\n return null;\n }\n if (!ready || !state.doc || !canWriteDocument(state)) return null;\n // A reset or relink while this call waited owns the replacement document.\n if (\n callGeneration &&\n !isAttachmentWriteGenerationCurrent(state, callGeneration)\n ) {\n return null;\n }\n return captureAttachmentWriteGeneration(state);\n}\n\nasync function attachThenSettle(\n state: DocumentStoreState,\n scheduleSync: () => void,\n files: ReadonlyArray<DocumentAttachmentUpload>,\n callGeneration: AttachmentWriteGeneration | null,\n) {\n const writeGeneration =\n files.length === 0\n ? null\n : await captureReadyAttachWrite(state, scheduleSync, callGeneration);\n try {\n if (writeGeneration) {\n await persistAttachedFiles(state, files, writeGeneration);\n }\n } finally {\n await settleDeferredInitialUpdate(state, writeGeneration);\n }\n}\n\nexport async function attachFilesToDocumentStore(\n state: DocumentStoreState,\n scheduleSync: () => void,\n files: ReadonlyArray<DocumentAttachmentUpload>,\n) {\n // Join the chain before awaiting readiness so settlements keep call order,\n // and settle even when nothing attaches: a deferred birth's sync hold lifts\n // only behind every earlier attach, never while one is still staging. The\n // caller's document is captured now (a cold store captures once ready).\n const callGeneration = captureAttachmentWriteGeneration(state);\n state.writeChain = state.writeChain\n .catch(() => undefined)\n .then(async () =>\n attachThenSettle(state, scheduleSync, files, callGeneration),\n )\n .catch((error: unknown) => {\n reportDocumentStoreWriteFailure(state, ATTACHMENT_FAILURE_MESSAGE, error);\n });\n return state.writeChain;\n}\n\nexport async function replaceAttachmentInDocumentStore(\n state: DocumentStoreState,\n scheduleSync: () => void,\n slotId: string,\n file: DocumentAttachmentUpload,\n) {\n let ready: boolean;\n try {\n ready = await ensureDocumentStoreReady(state, scheduleSync);\n } catch (error) {\n reportDocumentStoreWriteFailure(state, ATTACHMENT_FAILURE_MESSAGE, error);\n return;\n }\n\n if (!ready || !state.doc || !canWriteDocument(state)) {\n return;\n }\n\n const writeGeneration = captureAttachmentWriteGeneration(state);\n if (!writeGeneration) return;\n state.writeChain = state.writeChain\n .catch(() => undefined)\n .then(async () =>\n persistSlotAttachmentFile(state, slotId, file, writeGeneration),\n )\n .catch((error: unknown) => {\n reportDocumentStoreWriteFailure(state, ATTACHMENT_FAILURE_MESSAGE, error);\n });\n return state.writeChain;\n}\n"]}
@@ -0,0 +1,116 @@
1
+ import { encodeVersionVector, satisfiesVersionVector } from "../../../internal/loro/index.js";
2
+ import { createPendingUpdateFields } from "../../../data/documents/documentSync.js";
3
+ import { requestDocumentStoreSync } from "../registry.js";
4
+ import { captureAttachmentWriteGeneration, isAttachmentWriteGenerationCurrent, } from "./attachmentWriteGeneration.js";
5
+ import { reloadDocumentFromDurableHistory } from "./durableDocumentReload.js";
6
+ import { ensureDocumentStoreInitialized } from "./initialization.js";
7
+ import { advancePendingBaseVersion, pendingDeltaSinceBase, persistDocument, } from "./persistence.js";
8
+ import { resetDocumentStore } from "./state.js";
9
+ import { captureDocumentStoreSyncGeneration, isDocumentStoreSyncGenerationCurrent, } from "./syncGeneration.js";
10
+ /**
11
+ * Runs after the first attach on a store opened with `deferInitialUpdate`.
12
+ * Creation committed the birth without a pending update, leaving the marker at
13
+ * the empty version so that attach's delta would carry it, and scheduled sync
14
+ * passes wait meanwhile (see `canRunScheduledSync`) so none ships the birth on
15
+ * its own. When the attach did not land (staging failed or returned null, its
16
+ * persist was refused, or the store could not attach), enqueue the deferred
17
+ * tail now so the document still syncs, without its attachment. Either way,
18
+ * lift the hold and request the pass it skipped.
19
+ */
20
+ export async function settleDeferredInitialUpdate(state, writeGeneration) {
21
+ if (!state.deferInitialUpdate)
22
+ return;
23
+ // Before initialization finishes no attach has touched a live doc, and a
24
+ // reload here would install one behind initialization's back (or mark a
25
+ // not-yet-created document removed). Lift the hold; initialization then
26
+ // restores the empty marker and schedules the pass that ships the birth.
27
+ if (!state.initialized) {
28
+ state.deferInitialUpdate = false;
29
+ return;
30
+ }
31
+ // A pass exports the live doc, so it must hold only durable content (see
32
+ // restoreDurableLiveDocument) before the hold lifts. When that cannot be
33
+ // shown, even after a transient failure, reinitialize from durable history
34
+ // rather than latch the hold: the reset lifts it and drops the live doc.
35
+ let durable = false;
36
+ try {
37
+ durable = await restoreDurableLiveDocument(state);
38
+ }
39
+ finally {
40
+ if (!durable && state.deferInitialUpdate && state.doc) {
41
+ resetDocumentStore(state);
42
+ ensureDocumentStoreInitialized(state, () => requestDocumentStoreSync(state));
43
+ }
44
+ else if (!durable) {
45
+ // No live doc remains to leak (a concurrent removal): just lift it.
46
+ state.deferInitialUpdate = false;
47
+ }
48
+ }
49
+ if (!durable)
50
+ return;
51
+ state.deferInitialUpdate = false;
52
+ try {
53
+ await enqueueDeferredTail(state, writeGeneration);
54
+ }
55
+ finally {
56
+ requestDocumentStoreSync(state);
57
+ }
58
+ }
59
+ function isLiveDocumentDurable(state) {
60
+ return (state.doc !== null &&
61
+ state.record !== null &&
62
+ satisfiesVersionVector(state.record.snapshotEndVersion, encodeVersionVector(state.doc)));
63
+ }
64
+ /**
65
+ * A failed attach whose rollback reload could not run (its generation went
66
+ * stale mid-stage) leaves the uncommitted slot in the live doc, and that slot
67
+ * must never sync without its staged bytes. Reload durable history under a
68
+ * fresh generation before trusting the live doc.
69
+ */
70
+ async function restoreDurableLiveDocument(state) {
71
+ if (isLiveDocumentDurable(state))
72
+ return true;
73
+ const generation = captureDocumentStoreSyncGeneration(state, state.doc);
74
+ if (generation) {
75
+ await reloadDocumentFromDurableHistory({
76
+ expectedGeneration: generation,
77
+ preserveQueuedWritesWhenIdentityMatches: true,
78
+ state,
79
+ });
80
+ }
81
+ return isLiveDocumentDurable(state);
82
+ }
83
+ async function enqueueDeferredTail(state, writeGeneration) {
84
+ // A reset or relink owns the replacement store; its deferred tail stays
85
+ // durable and the next write or sync pass materializes it. With no attach
86
+ // (nothing to attach, or the store was not writable) the live store owns it.
87
+ const generation = writeGeneration ?? captureAttachmentWriteGeneration(state);
88
+ if (!generation || !isAttachmentWriteGenerationCurrent(state, generation)) {
89
+ return;
90
+ }
91
+ const currentDoc = state.doc;
92
+ const baseVersion = state.pendingBaseVersion;
93
+ if (!currentDoc || baseVersion === null)
94
+ return;
95
+ const documentVersion = encodeVersionVector(currentDoc);
96
+ // Settled: the attach (or an earlier write) already enqueued the birth.
97
+ if (satisfiesVersionVector(baseVersion, documentVersion))
98
+ return;
99
+ const syncGeneration = captureDocumentStoreSyncGeneration(state, currentDoc);
100
+ if (!syncGeneration)
101
+ return;
102
+ const pendingUpdate = createPendingUpdateFields(pendingDeltaSinceBase(state, currentDoc));
103
+ if (!pendingUpdate)
104
+ return;
105
+ const persisted = await persistDocument(state, currentDoc, { snapshotEndVersion: documentVersion }, {
106
+ pendingUpdate,
107
+ preserveSnapshotStructuredFields: true,
108
+ preserveSnapshotText: true,
109
+ }, syncGeneration);
110
+ if (!persisted ||
111
+ !isDocumentStoreSyncGenerationCurrent(state, syncGeneration)) {
112
+ return;
113
+ }
114
+ advancePendingBaseVersion(state, currentDoc);
115
+ }
116
+ //# sourceMappingURL=deferredInitialUpdate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deferredInitialUpdate.js","sourceRoot":"","sources":["deferredInitialUpdate.ts"],"names":[],"mappings":"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","sourcesContent":["import { encodeVersionVector, satisfiesVersionVector } from \"@tearleads/loro\";\nimport { createPendingUpdateFields } from \"../../../data/documents/documentSync\";\nimport { requestDocumentStoreSync } from \"../registry\";\nimport {\n type AttachmentWriteGeneration,\n captureAttachmentWriteGeneration,\n isAttachmentWriteGenerationCurrent,\n} from \"./attachmentWriteGeneration\";\nimport { reloadDocumentFromDurableHistory } from \"./durableDocumentReload\";\nimport { ensureDocumentStoreInitialized } from \"./initialization\";\nimport {\n advancePendingBaseVersion,\n pendingDeltaSinceBase,\n persistDocument,\n} from \"./persistence\";\nimport { type DocumentStoreState, resetDocumentStore } from \"./state\";\nimport {\n captureDocumentStoreSyncGeneration,\n isDocumentStoreSyncGenerationCurrent,\n} from \"./syncGeneration\";\n\n/**\n * Runs after the first attach on a store opened with `deferInitialUpdate`.\n * Creation committed the birth without a pending update, leaving the marker at\n * the empty version so that attach's delta would carry it, and scheduled sync\n * passes wait meanwhile (see `canRunScheduledSync`) so none ships the birth on\n * its own. When the attach did not land (staging failed or returned null, its\n * persist was refused, or the store could not attach), enqueue the deferred\n * tail now so the document still syncs, without its attachment. Either way,\n * lift the hold and request the pass it skipped.\n */\nexport async function settleDeferredInitialUpdate(\n state: DocumentStoreState,\n writeGeneration: AttachmentWriteGeneration | null,\n): Promise<void> {\n if (!state.deferInitialUpdate) return;\n // Before initialization finishes no attach has touched a live doc, and a\n // reload here would install one behind initialization's back (or mark a\n // not-yet-created document removed). Lift the hold; initialization then\n // restores the empty marker and schedules the pass that ships the birth.\n if (!state.initialized) {\n state.deferInitialUpdate = false;\n return;\n }\n // A pass exports the live doc, so it must hold only durable content (see\n // restoreDurableLiveDocument) before the hold lifts. When that cannot be\n // shown, even after a transient failure, reinitialize from durable history\n // rather than latch the hold: the reset lifts it and drops the live doc.\n let durable = false;\n try {\n durable = await restoreDurableLiveDocument(state);\n } finally {\n if (!durable && state.deferInitialUpdate && state.doc) {\n resetDocumentStore(state);\n ensureDocumentStoreInitialized(state, () =>\n requestDocumentStoreSync(state),\n );\n } else if (!durable) {\n // No live doc remains to leak (a concurrent removal): just lift it.\n state.deferInitialUpdate = false;\n }\n }\n if (!durable) return;\n state.deferInitialUpdate = false;\n try {\n await enqueueDeferredTail(state, writeGeneration);\n } finally {\n requestDocumentStoreSync(state);\n }\n}\n\nfunction isLiveDocumentDurable(state: DocumentStoreState): boolean {\n return (\n state.doc !== null &&\n state.record !== null &&\n satisfiesVersionVector(\n state.record.snapshotEndVersion,\n encodeVersionVector(state.doc),\n )\n );\n}\n\n/**\n * A failed attach whose rollback reload could not run (its generation went\n * stale mid-stage) leaves the uncommitted slot in the live doc, and that slot\n * must never sync without its staged bytes. Reload durable history under a\n * fresh generation before trusting the live doc.\n */\nasync function restoreDurableLiveDocument(\n state: DocumentStoreState,\n): Promise<boolean> {\n if (isLiveDocumentDurable(state)) return true;\n const generation = captureDocumentStoreSyncGeneration(state, state.doc);\n if (generation) {\n await reloadDocumentFromDurableHistory({\n expectedGeneration: generation,\n preserveQueuedWritesWhenIdentityMatches: true,\n state,\n });\n }\n return isLiveDocumentDurable(state);\n}\n\nasync function enqueueDeferredTail(\n state: DocumentStoreState,\n writeGeneration: AttachmentWriteGeneration | null,\n): Promise<void> {\n // A reset or relink owns the replacement store; its deferred tail stays\n // durable and the next write or sync pass materializes it. With no attach\n // (nothing to attach, or the store was not writable) the live store owns it.\n const generation = writeGeneration ?? captureAttachmentWriteGeneration(state);\n if (!generation || !isAttachmentWriteGenerationCurrent(state, generation)) {\n return;\n }\n const currentDoc = state.doc;\n const baseVersion = state.pendingBaseVersion;\n if (!currentDoc || baseVersion === null) return;\n const documentVersion = encodeVersionVector(currentDoc);\n // Settled: the attach (or an earlier write) already enqueued the birth.\n if (satisfiesVersionVector(baseVersion, documentVersion)) return;\n const syncGeneration = captureDocumentStoreSyncGeneration(state, currentDoc);\n if (!syncGeneration) return;\n const pendingUpdate = createPendingUpdateFields(\n pendingDeltaSinceBase(state, currentDoc),\n );\n if (!pendingUpdate) return;\n const persisted = await persistDocument(\n state,\n currentDoc,\n { snapshotEndVersion: documentVersion },\n {\n pendingUpdate,\n preserveSnapshotStructuredFields: true,\n preserveSnapshotText: true,\n },\n syncGeneration,\n );\n if (\n !persisted ||\n !isDocumentStoreSyncGenerationCurrent(state, syncGeneration)\n ) {\n return;\n }\n advancePendingBaseVersion(state, currentDoc);\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { bytesToBase64 } from "../../../internal/encoding/index.js";
2
- import { encodeVersionVector, exportAllUpdates, exportFullHistorySnapshot, mergeVersionVectors, } from "../../../internal/loro/index.js";
2
+ import { emptyVersionVector, encodeVersionVector, exportAllUpdates, exportFullHistorySnapshot, mergeVersionVectors, } from "../../../internal/loro/index.js";
3
3
  import { DEFAULT_DOCUMENT_ACCESS_EPOCH, DEFAULT_DOCUMENT_KIND, } from "../../../data/documents/documentConstants.js";
4
4
  import { initializeStoredDocumentKind, readStoredDocumentState, } from "../../../data/documents/documentKinds.js";
5
5
  import { createPendingUpdateFields } from "../../../data/documents/documentSync.js";
@@ -25,6 +25,14 @@ async function createInitialDocumentRecord(state, nextDoc) {
25
25
  const initialUpdate = hasInitialContent
26
26
  ? createPendingUpdateFields(exportAllUpdates(nextDoc))
27
27
  : null;
28
+ // A deferred birth still commits its checkpoint, but leaves the marker at the
29
+ // empty version with nothing queued: the promised attach exports every op
30
+ // since that marker, so one update carries the birth and the attachment.
31
+ const birthDurability = !initialUpdate
32
+ ? {}
33
+ : state.deferInitialUpdate
34
+ ? { pendingBaseVersionOverride: emptyVersionVector() }
35
+ : { pendingUpdate: initialUpdate };
28
36
  const created = {
29
37
  id: state.localId,
30
38
  containerId: state.runtime.state.containerId ?? null,
@@ -54,7 +62,7 @@ async function createInitialDocumentRecord(state, nextDoc) {
54
62
  pruneCoveredLocalState: false,
55
63
  snapshot: bytesToBase64(exportFullHistorySnapshot(nextDoc)),
56
64
  },
57
- ...(initialUpdate ? { pendingUpdate: initialUpdate } : {}),
65
+ ...birthDurability,
58
66
  });
59
67
  return persisted;
60
68
  }
@@ -212,6 +220,10 @@ export function ensureDocumentStoreInitialized(state, scheduleSync) {
212
220
  }
213
221
  const initializePromise = initializeDocumentStore(state, scheduleSync).catch((error) => {
214
222
  state.initializePromise = null;
223
+ // A caller that deferred the birth for its attach may give up once
224
+ // opening fails; never leave its durable birth holding sync. No attach
225
+ // has reached a live doc yet, so lifting the hold ships nothing unsafe.
226
+ state.deferInitialUpdate = false;
215
227
  if (isDatabaseUnavailableError(error)) {
216
228
  return;
217
229
  }
@@ -1 +1 @@
1
- {"version":3,"file":"initialization.js","sourceRoot":"","sources":["initialization.ts"],"names":[],"mappings":"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","sourcesContent":["import { bytesToBase64 } from \"@tearleads/encoding\";\nimport {\n encodeVersionVector,\n exportAllUpdates,\n exportFullHistorySnapshot,\n mergeVersionVectors,\n} from \"@tearleads/loro\";\nimport {\n DEFAULT_DOCUMENT_ACCESS_EPOCH,\n DEFAULT_DOCUMENT_KIND,\n} from \"../../../data/documents/documentConstants\";\nimport {\n initializeStoredDocumentKind,\n readStoredDocumentState,\n} from \"../../../data/documents/documentKinds\";\nimport type { DocumentSummary } from \"../../../data/documents/documentSummary\";\nimport { createPendingUpdateFields } from \"../../../data/documents/documentSync\";\nimport {\n type DocumentRecord,\n isDatabaseUnavailableError,\n loadPersistedDocumentStoreState,\n} from \"../../../workflows/documents\";\nimport type { DocumentStoreRelinkInput } from \"../types\";\nimport { chainIdentityWrite } from \"./identityWriteChain\";\nimport {\n installPersistedAttachments,\n type LoadedDocumentStoreState,\n} from \"./initializationAttachments\";\nimport {\n type RestoredHistoryState,\n restoredRemoteTailCoverage,\n restorePersistedDocumentContent,\n} from \"./initializationHistory\";\nimport {\n healLocalAttachmentDetachState,\n recoverDroppedAttachmentSlots,\n} from \"./initializationRecovery\";\nimport type { DocumentRelinkCommitSideEffect } from \"./internalRelink\";\nimport { runDocumentOrphanMaintenance } from \"./orphanMaintenance\";\nimport {\n advancePendingBaseVersion,\n documentSummaryFromRecord,\n persistDocument,\n saveDocumentRecord,\n} from \"./persistence\";\nimport { logRevalidationScheduled } from \"./remoteRevalidationTelemetry\";\nimport {\n type DocumentState,\n type DocumentStoreState,\n setReadySnapshot,\n} from \"./state\";\nimport { createStoredDocument } from \"./storedDocument\";\nimport {\n allowDocumentStoreRemoteSync,\n captureDocumentStoreSyncGeneration,\n invalidateDocumentStoreRemoteSync,\n isDocumentStoreSyncGenerationCurrent,\n markDocumentStoreRemoteSyncPending,\n} from \"./syncGeneration\";\n\nasync function createInitialDocumentRecord(\n state: DocumentStoreState,\n nextDoc: DocumentState,\n): ReturnType<typeof saveDocumentRecord> {\n initializeStoredDocumentKind(\n nextDoc,\n state.initialDocumentKind,\n state.runtime.infra.documentProjectors,\n );\n if (state.initialText.length > 0) {\n nextDoc.getText(\"text\").update(state.initialText);\n }\n const initialDocumentState = readStoredDocumentState(\n nextDoc,\n state.runtime.infra.documentProjectors,\n );\n const hasInitialContent =\n state.initialText.length > 0 ||\n state.initialDocumentKind !== DEFAULT_DOCUMENT_KIND;\n const initialUpdate = hasInitialContent\n ? createPendingUpdateFields(exportAllUpdates(nextDoc))\n : null;\n\n const created: DocumentRecord = {\n id: state.localId,\n containerId: state.runtime.state.containerId ?? null,\n documentId: state.initialDocumentId,\n documentKind: initialDocumentState.documentKind,\n text: initialDocumentState.text,\n title: initialDocumentState.title,\n snapshotEndVersion: encodeVersionVector(nextDoc),\n accessEpoch: DEFAULT_DOCUMENT_ACCESS_EPOCH,\n accessStateHash: null,\n effectiveAccessLevel: \"admin\",\n lastCommitLsn: null,\n contentKeyBundle: null,\n documentKekTargets: null,\n documentManifestBundle: null,\n };\n // The create-only persistence primitive commits the canonical row and birth\n // checkpoint together. A concurrent initializer either wins both or adopts\n // both, so it cannot replace another pane's history or keying state.\n const persisted = await saveDocumentRecord(\n state,\n nextDoc,\n {\n ...created,\n snapshotEndVersion: encodeVersionVector(nextDoc),\n },\n {\n historyCheckpoint: {\n coveredTailIds: [],\n endVersionVector: encodeVersionVector(nextDoc),\n pruneCoveredLocalState: false,\n snapshot: bytesToBase64(exportFullHistorySnapshot(nextDoc)),\n },\n ...(initialUpdate ? { pendingUpdate: initialUpdate } : {}),\n },\n );\n return persisted;\n}\n\nfunction installPersistedDocumentRecord(\n state: DocumentStoreState,\n record: NonNullable<DocumentStoreState[\"record\"]>,\n): void {\n state.record = record;\n state.pullContinuation = record.pullContinuation ?? null;\n}\n\ninterface RestoredInitialDocumentState {\n activePersistedState: LoadedDocumentStoreState;\n activeRecord: NonNullable<DocumentStoreState[\"record\"]>;\n doc: DocumentState;\n history: RestoredHistoryState | null;\n}\n\nasync function restoreLoadedInitialDocument(input: {\n initializeGeneration: number;\n nextDoc: DocumentState;\n persistedState: LoadedDocumentStoreState;\n state: DocumentStoreState;\n}): Promise<RestoredInitialDocumentState | null> {\n const { initializeGeneration, persistedState, state } = input;\n const record = persistedState.document;\n if (!record) return null;\n const history = await restorePersistedDocumentContent(\n input.nextDoc,\n persistedState.historyRestoreState,\n );\n if (state.localWriteGeneration !== initializeGeneration) return null;\n installPersistedDocumentRecord(state, record);\n return {\n activePersistedState: persistedState,\n activeRecord: record,\n doc: input.nextDoc,\n history,\n };\n}\n\nasync function restoreOrCreateInitialDocument(input: {\n initializeGeneration: number;\n nextDoc: DocumentState;\n persistedState: LoadedDocumentStoreState;\n state: DocumentStoreState;\n}): Promise<RestoredInitialDocumentState | null> {\n const { initializeGeneration, persistedState, state } = input;\n if (persistedState.document) {\n return restoreLoadedInitialDocument(input);\n }\n\n const created = await createInitialDocumentRecord(state, input.nextDoc);\n if (!created || state.localWriteGeneration !== initializeGeneration) {\n return null;\n }\n const activePersistedState = await loadPersistedDocumentStoreState({\n execSql: state.runtime.infra.execSql,\n localId: state.localId,\n persistence: state.persistence,\n });\n if (state.localWriteGeneration !== initializeGeneration) {\n return null;\n }\n if (!activePersistedState.document) {\n throw new Error(\"Document creation completed without a durable record\");\n }\n const replacementDoc = await createStoredDocument(state);\n return restoreLoadedInitialDocument({\n initializeGeneration,\n nextDoc: replacementDoc,\n persistedState: activePersistedState,\n state,\n });\n}\n\nfunction restorePendingBaseVersion(input: {\n activeRecord: NonNullable<DocumentStoreState[\"record\"]>;\n doc: DocumentState;\n history: RestoredHistoryState | null;\n state: DocumentStoreState;\n}): void {\n const { activeRecord, doc, history, state } = input;\n const markerBase =\n activeRecord.pendingBaseVersion ??\n (activeRecord.snapshotEndVersion.length > 0\n ? activeRecord.snapshotEndVersion\n : null);\n if (markerBase === null) {\n advancePendingBaseVersion(state, doc);\n return;\n }\n\n const remoteCoverage = restoredRemoteTailCoverage(history);\n state.pendingBaseVersion =\n remoteCoverage.length > 0\n ? mergeVersionVectors([markerBase, ...remoteCoverage])\n : markerBase;\n}\n\nasync function initializeDocumentStore(\n state: DocumentStoreState,\n scheduleSync: () => void,\n) {\n if (state.runtime.infra.dbStatus !== \"ready\") {\n return;\n }\n\n // A store reset while this fire-and-forget initialization is loading (a\n // runtime swap, or a local-edits discard that re-seeds the persisted rows)\n // must win: assigning the state loaded BEFORE the reset would bring the\n // replaced record and doc back to life. Every reset bumps the local write\n // generation, so a stale capture here aborts before each assignment batch.\n const initializeGeneration = state.localWriteGeneration;\n\n const nextDoc = await createStoredDocument(state);\n const persistedState = await loadPersistedDocumentStoreState({\n execSql: state.runtime.infra.execSql,\n localId: state.localId,\n persistence: state.persistence,\n });\n if (state.localWriteGeneration !== initializeGeneration) {\n return;\n }\n const restored = await restoreOrCreateInitialDocument({\n initializeGeneration,\n nextDoc,\n persistedState,\n state,\n });\n if (!restored) {\n return;\n }\n const activePersistedState = restored.activePersistedState;\n installPersistedAttachments(state, activePersistedState);\n\n state.doc = restored.doc;\n // Restore the durable outgoing-delta marker. Re-seeding it to the restored\n // document's version would move it PAST any device-first `deferRemoteSync`\n // op (persisted into the durable history but deliberately left un-enqueued\n // and behind the marker), permanently dropping that op from sync across a\n // restart. When a marker was persisted, restore it so the next edit still\n // re-derives the deferred delta. Only seed for rows that never persisted\n // one, from the RECORD's persisted frontier rather than the restored\n // document: a crash between the tail append and the queue/marker writes can\n // resurrect ops the outgoing queue never durably received, and seeding from\n // the restored document would classify those as covered and orphan them\n // from sync forever.\n restorePendingBaseVersion({\n activeRecord: restored.activeRecord,\n doc: restored.doc,\n history: restored.history,\n state,\n });\n // Heal attachment slots lost to an interrupted attach write before marking\n // ready, so the recovered slots are in the snapshot the editor first renders\n // and are queued for sync. recoverDroppedAttachmentSlots advances the marker\n // again for whatever it re-derives. Both helpers gate every durable write\n // on this generation (validated inside the writes' serialized mutations),\n // so a reset mid-helper cannot repopulate discarded state.\n const writeGeneration = captureDocumentStoreSyncGeneration(\n state,\n restored.doc,\n );\n if (!writeGeneration) {\n return;\n }\n await recoverDroppedAttachmentSlots(state, restored.doc, writeGeneration);\n if (state.localWriteGeneration !== initializeGeneration) {\n return;\n }\n await healLocalAttachmentDetachState(\n state,\n restored.doc,\n activePersistedState,\n writeGeneration,\n );\n if (state.localWriteGeneration !== initializeGeneration) {\n return;\n }\n state.initialized = true;\n state.initializePromise = null;\n setReadySnapshot(state, restored.doc, false);\n\n if (restored.activeRecord.documentId && state.scheduleStartupRemoteSync) {\n // Websocket invalidations are intentionally session-local. A process can\n // stop before receiving a peer device's update event, and no event survives\n // the restart to distinguish a clean local snapshot from a stale one. Probe\n // each opened remote document once after loading it; unopened documents stay\n // lazy, while body and attachment state for a restored window converges.\n allowDocumentStoreRemoteSync(state);\n markDocumentStoreRemoteSyncPending(state, \"independent\");\n logRevalidationScheduled(state.runtime, \"startup\");\n }\n scheduleSync();\n void runDocumentOrphanMaintenance(state);\n}\n\nexport function ensureDocumentStoreInitialized(\n state: DocumentStoreState,\n scheduleSync: () => void,\n) {\n if (\n state.initialized ||\n state.initializePromise ||\n state.runtime.infra.dbStatus !== \"ready\"\n ) {\n return;\n }\n\n const initializePromise = initializeDocumentStore(state, scheduleSync).catch(\n (error: unknown) => {\n state.initializePromise = null;\n\n if (isDatabaseUnavailableError(error)) {\n return;\n }\n\n throw error;\n },\n );\n state.initializePromise = initializePromise;\n void initializePromise.catch(() => undefined);\n}\n\nexport async function awaitInitializationForSync(state: DocumentStoreState) {\n if (!state.initializePromise) {\n return true;\n }\n\n try {\n await state.initializePromise;\n return true;\n } catch (error) {\n if (isDatabaseUnavailableError(error)) {\n return false;\n }\n\n throw error;\n }\n}\n\nexport async function ensureDocumentStoreReady(\n state: DocumentStoreState,\n scheduleSync: () => void,\n): Promise<boolean> {\n ensureDocumentStoreInitialized(state, scheduleSync);\n\n if (state.initialized) {\n return true;\n }\n\n if (!state.initializePromise) {\n return false;\n }\n\n return awaitInitializationForSync(state);\n}\n\nexport async function relinkDocumentStore(\n state: DocumentStoreState,\n input: DocumentStoreRelinkInput,\n scheduleSync: () => void,\n commitSideEffect?: DocumentRelinkCommitSideEffect,\n): Promise<DocumentSummary | null> {\n if (\n input.stillCurrent?.() === false ||\n !(await ensureDocumentStoreReady(state, scheduleSync)) ||\n input.stillCurrent?.() === false ||\n !state.doc\n ) {\n return null;\n }\n const currentDoc = state.doc;\n const generation = captureDocumentStoreSyncGeneration(state, currentDoc);\n if (!generation) return null;\n // Read the record and persist on the identity-write chain so an in-flight\n // eager create cannot interleave: a create that captured a null identity\n // before its network round trip would otherwise write the derived documentId\n // over the one this relink assigns.\n const persisted = await chainIdentityWrite(state, async () => {\n if (!isDocumentStoreSyncGenerationCurrent(state, generation)) return null;\n const currentDocumentId = state.record?.documentId ?? null;\n const currentAccessEpoch =\n state.record?.accessEpoch ?? DEFAULT_DOCUMENT_ACCESS_EPOCH;\n const patch: Partial<DocumentRecord> = {\n accessEpoch: Math.max(currentAccessEpoch, input.accessEpoch),\n accessStateHash:\n input.accessStateHash === undefined\n ? (state.record?.accessStateHash ?? null)\n : input.accessStateHash,\n containerId: input.containerId,\n documentId: input.documentId,\n };\n if (input.contentKeyBundle !== undefined) {\n patch.contentKeyBundle = input.contentKeyBundle;\n }\n if (input.documentKekTargets !== undefined) {\n patch.documentKekTargets = input.documentKekTargets;\n }\n if (input.documentManifestBundle !== undefined) {\n patch.documentManifestBundle = input.documentManifestBundle;\n }\n\n // Relink rewrites access/identity metadata, not content. If it lands while\n // the user is mid-edit, re-deriving text/structured fields from the\n // (possibly lagging) doc would republish a stale read over the live\n // optimistic snapshot and drop in-flight keystrokes; preserve the snapshot\n // like the keystroke and sync persists do.\n // A prior chained write may have cleared this store (resurrect-guard\n // refusal): persisting the captured doc against a null record would\n // resurrect the document as a create. The identity check runs inside\n // the chain, after every earlier write settled.\n if (!isDocumentStoreSyncGenerationCurrent(state, generation)) {\n return null;\n }\n const result = await persistDocument(\n state,\n currentDoc,\n patch,\n {\n preserveSnapshotStructuredFields: true,\n preserveSnapshotText: true,\n stillCurrent: input.stillCurrent,\n },\n generation,\n commitSideEffect,\n );\n if (result && currentDocumentId !== input.documentId) {\n state.writerProjection = null;\n invalidateDocumentStoreRemoteSync(state);\n }\n return result;\n });\n // A refused persist means the durable row vanished mid-relink (resurrect\n // guard); there is no summary to report.\n if (!persisted) {\n return null;\n }\n if (persisted.updatedAt === undefined) {\n return null;\n }\n return documentSummaryFromRecord(\n persisted.record,\n persisted.updatedAt,\n state.runtime.infra.documentProjectors,\n );\n}\n"]}
1
+ {"version":3,"file":"initialization.js","sourceRoot":"","sources":["initialization.ts"],"names":[],"mappings":"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","sourcesContent":["import { bytesToBase64 } from \"@tearleads/encoding\";\nimport {\n emptyVersionVector,\n encodeVersionVector,\n exportAllUpdates,\n exportFullHistorySnapshot,\n mergeVersionVectors,\n} from \"@tearleads/loro\";\nimport {\n DEFAULT_DOCUMENT_ACCESS_EPOCH,\n DEFAULT_DOCUMENT_KIND,\n} from \"../../../data/documents/documentConstants\";\nimport {\n initializeStoredDocumentKind,\n readStoredDocumentState,\n} from \"../../../data/documents/documentKinds\";\nimport type { DocumentSummary } from \"../../../data/documents/documentSummary\";\nimport { createPendingUpdateFields } from \"../../../data/documents/documentSync\";\nimport {\n type DocumentRecord,\n isDatabaseUnavailableError,\n loadPersistedDocumentStoreState,\n} from \"../../../workflows/documents\";\nimport type { DocumentStoreRelinkInput } from \"../types\";\nimport { chainIdentityWrite } from \"./identityWriteChain\";\nimport {\n installPersistedAttachments,\n type LoadedDocumentStoreState,\n} from \"./initializationAttachments\";\nimport {\n type RestoredHistoryState,\n restoredRemoteTailCoverage,\n restorePersistedDocumentContent,\n} from \"./initializationHistory\";\nimport {\n healLocalAttachmentDetachState,\n recoverDroppedAttachmentSlots,\n} from \"./initializationRecovery\";\nimport type { DocumentRelinkCommitSideEffect } from \"./internalRelink\";\nimport { runDocumentOrphanMaintenance } from \"./orphanMaintenance\";\nimport {\n advancePendingBaseVersion,\n documentSummaryFromRecord,\n persistDocument,\n saveDocumentRecord,\n} from \"./persistence\";\nimport { logRevalidationScheduled } from \"./remoteRevalidationTelemetry\";\nimport {\n type DocumentState,\n type DocumentStoreState,\n setReadySnapshot,\n} from \"./state\";\nimport { createStoredDocument } from \"./storedDocument\";\nimport {\n allowDocumentStoreRemoteSync,\n captureDocumentStoreSyncGeneration,\n invalidateDocumentStoreRemoteSync,\n isDocumentStoreSyncGenerationCurrent,\n markDocumentStoreRemoteSyncPending,\n} from \"./syncGeneration\";\n\nasync function createInitialDocumentRecord(\n state: DocumentStoreState,\n nextDoc: DocumentState,\n): ReturnType<typeof saveDocumentRecord> {\n initializeStoredDocumentKind(\n nextDoc,\n state.initialDocumentKind,\n state.runtime.infra.documentProjectors,\n );\n if (state.initialText.length > 0) {\n nextDoc.getText(\"text\").update(state.initialText);\n }\n const initialDocumentState = readStoredDocumentState(\n nextDoc,\n state.runtime.infra.documentProjectors,\n );\n const hasInitialContent =\n state.initialText.length > 0 ||\n state.initialDocumentKind !== DEFAULT_DOCUMENT_KIND;\n const initialUpdate = hasInitialContent\n ? createPendingUpdateFields(exportAllUpdates(nextDoc))\n : null;\n // A deferred birth still commits its checkpoint, but leaves the marker at the\n // empty version with nothing queued: the promised attach exports every op\n // since that marker, so one update carries the birth and the attachment.\n const birthDurability = !initialUpdate\n ? {}\n : state.deferInitialUpdate\n ? { pendingBaseVersionOverride: emptyVersionVector() }\n : { pendingUpdate: initialUpdate };\n\n const created: DocumentRecord = {\n id: state.localId,\n containerId: state.runtime.state.containerId ?? null,\n documentId: state.initialDocumentId,\n documentKind: initialDocumentState.documentKind,\n text: initialDocumentState.text,\n title: initialDocumentState.title,\n snapshotEndVersion: encodeVersionVector(nextDoc),\n accessEpoch: DEFAULT_DOCUMENT_ACCESS_EPOCH,\n accessStateHash: null,\n effectiveAccessLevel: \"admin\",\n lastCommitLsn: null,\n contentKeyBundle: null,\n documentKekTargets: null,\n documentManifestBundle: null,\n };\n // The create-only persistence primitive commits the canonical row and birth\n // checkpoint together. A concurrent initializer either wins both or adopts\n // both, so it cannot replace another pane's history or keying state.\n const persisted = await saveDocumentRecord(\n state,\n nextDoc,\n {\n ...created,\n snapshotEndVersion: encodeVersionVector(nextDoc),\n },\n {\n historyCheckpoint: {\n coveredTailIds: [],\n endVersionVector: encodeVersionVector(nextDoc),\n pruneCoveredLocalState: false,\n snapshot: bytesToBase64(exportFullHistorySnapshot(nextDoc)),\n },\n ...birthDurability,\n },\n );\n return persisted;\n}\n\nfunction installPersistedDocumentRecord(\n state: DocumentStoreState,\n record: NonNullable<DocumentStoreState[\"record\"]>,\n): void {\n state.record = record;\n state.pullContinuation = record.pullContinuation ?? null;\n}\n\ninterface RestoredInitialDocumentState {\n activePersistedState: LoadedDocumentStoreState;\n activeRecord: NonNullable<DocumentStoreState[\"record\"]>;\n doc: DocumentState;\n history: RestoredHistoryState | null;\n}\n\nasync function restoreLoadedInitialDocument(input: {\n initializeGeneration: number;\n nextDoc: DocumentState;\n persistedState: LoadedDocumentStoreState;\n state: DocumentStoreState;\n}): Promise<RestoredInitialDocumentState | null> {\n const { initializeGeneration, persistedState, state } = input;\n const record = persistedState.document;\n if (!record) return null;\n const history = await restorePersistedDocumentContent(\n input.nextDoc,\n persistedState.historyRestoreState,\n );\n if (state.localWriteGeneration !== initializeGeneration) return null;\n installPersistedDocumentRecord(state, record);\n return {\n activePersistedState: persistedState,\n activeRecord: record,\n doc: input.nextDoc,\n history,\n };\n}\n\nasync function restoreOrCreateInitialDocument(input: {\n initializeGeneration: number;\n nextDoc: DocumentState;\n persistedState: LoadedDocumentStoreState;\n state: DocumentStoreState;\n}): Promise<RestoredInitialDocumentState | null> {\n const { initializeGeneration, persistedState, state } = input;\n if (persistedState.document) {\n return restoreLoadedInitialDocument(input);\n }\n\n const created = await createInitialDocumentRecord(state, input.nextDoc);\n if (!created || state.localWriteGeneration !== initializeGeneration) {\n return null;\n }\n const activePersistedState = await loadPersistedDocumentStoreState({\n execSql: state.runtime.infra.execSql,\n localId: state.localId,\n persistence: state.persistence,\n });\n if (state.localWriteGeneration !== initializeGeneration) {\n return null;\n }\n if (!activePersistedState.document) {\n throw new Error(\"Document creation completed without a durable record\");\n }\n const replacementDoc = await createStoredDocument(state);\n return restoreLoadedInitialDocument({\n initializeGeneration,\n nextDoc: replacementDoc,\n persistedState: activePersistedState,\n state,\n });\n}\n\nfunction restorePendingBaseVersion(input: {\n activeRecord: NonNullable<DocumentStoreState[\"record\"]>;\n doc: DocumentState;\n history: RestoredHistoryState | null;\n state: DocumentStoreState;\n}): void {\n const { activeRecord, doc, history, state } = input;\n const markerBase =\n activeRecord.pendingBaseVersion ??\n (activeRecord.snapshotEndVersion.length > 0\n ? activeRecord.snapshotEndVersion\n : null);\n if (markerBase === null) {\n advancePendingBaseVersion(state, doc);\n return;\n }\n\n const remoteCoverage = restoredRemoteTailCoverage(history);\n state.pendingBaseVersion =\n remoteCoverage.length > 0\n ? mergeVersionVectors([markerBase, ...remoteCoverage])\n : markerBase;\n}\n\nasync function initializeDocumentStore(\n state: DocumentStoreState,\n scheduleSync: () => void,\n) {\n if (state.runtime.infra.dbStatus !== \"ready\") {\n return;\n }\n\n // A store reset while this fire-and-forget initialization is loading (a\n // runtime swap, or a local-edits discard that re-seeds the persisted rows)\n // must win: assigning the state loaded BEFORE the reset would bring the\n // replaced record and doc back to life. Every reset bumps the local write\n // generation, so a stale capture here aborts before each assignment batch.\n const initializeGeneration = state.localWriteGeneration;\n\n const nextDoc = await createStoredDocument(state);\n const persistedState = await loadPersistedDocumentStoreState({\n execSql: state.runtime.infra.execSql,\n localId: state.localId,\n persistence: state.persistence,\n });\n if (state.localWriteGeneration !== initializeGeneration) {\n return;\n }\n const restored = await restoreOrCreateInitialDocument({\n initializeGeneration,\n nextDoc,\n persistedState,\n state,\n });\n if (!restored) {\n return;\n }\n const activePersistedState = restored.activePersistedState;\n installPersistedAttachments(state, activePersistedState);\n\n state.doc = restored.doc;\n // Restore the durable outgoing-delta marker. Re-seeding it to the restored\n // document's version would move it PAST any device-first `deferRemoteSync`\n // op (persisted into the durable history but deliberately left un-enqueued\n // and behind the marker), permanently dropping that op from sync across a\n // restart. When a marker was persisted, restore it so the next edit still\n // re-derives the deferred delta. Only seed for rows that never persisted\n // one, from the RECORD's persisted frontier rather than the restored\n // document: a crash between the tail append and the queue/marker writes can\n // resurrect ops the outgoing queue never durably received, and seeding from\n // the restored document would classify those as covered and orphan them\n // from sync forever.\n restorePendingBaseVersion({\n activeRecord: restored.activeRecord,\n doc: restored.doc,\n history: restored.history,\n state,\n });\n // Heal attachment slots lost to an interrupted attach write before marking\n // ready, so the recovered slots are in the snapshot the editor first renders\n // and are queued for sync. recoverDroppedAttachmentSlots advances the marker\n // again for whatever it re-derives. Both helpers gate every durable write\n // on this generation (validated inside the writes' serialized mutations),\n // so a reset mid-helper cannot repopulate discarded state.\n const writeGeneration = captureDocumentStoreSyncGeneration(\n state,\n restored.doc,\n );\n if (!writeGeneration) {\n return;\n }\n await recoverDroppedAttachmentSlots(state, restored.doc, writeGeneration);\n if (state.localWriteGeneration !== initializeGeneration) {\n return;\n }\n await healLocalAttachmentDetachState(\n state,\n restored.doc,\n activePersistedState,\n writeGeneration,\n );\n if (state.localWriteGeneration !== initializeGeneration) {\n return;\n }\n state.initialized = true;\n state.initializePromise = null;\n setReadySnapshot(state, restored.doc, false);\n\n if (restored.activeRecord.documentId && state.scheduleStartupRemoteSync) {\n // Websocket invalidations are intentionally session-local. A process can\n // stop before receiving a peer device's update event, and no event survives\n // the restart to distinguish a clean local snapshot from a stale one. Probe\n // each opened remote document once after loading it; unopened documents stay\n // lazy, while body and attachment state for a restored window converges.\n allowDocumentStoreRemoteSync(state);\n markDocumentStoreRemoteSyncPending(state, \"independent\");\n logRevalidationScheduled(state.runtime, \"startup\");\n }\n scheduleSync();\n void runDocumentOrphanMaintenance(state);\n}\n\nexport function ensureDocumentStoreInitialized(\n state: DocumentStoreState,\n scheduleSync: () => void,\n) {\n if (\n state.initialized ||\n state.initializePromise ||\n state.runtime.infra.dbStatus !== \"ready\"\n ) {\n return;\n }\n\n const initializePromise = initializeDocumentStore(state, scheduleSync).catch(\n (error: unknown) => {\n state.initializePromise = null;\n // A caller that deferred the birth for its attach may give up once\n // opening fails; never leave its durable birth holding sync. No attach\n // has reached a live doc yet, so lifting the hold ships nothing unsafe.\n state.deferInitialUpdate = false;\n\n if (isDatabaseUnavailableError(error)) {\n return;\n }\n\n throw error;\n },\n );\n state.initializePromise = initializePromise;\n void initializePromise.catch(() => undefined);\n}\n\nexport async function awaitInitializationForSync(state: DocumentStoreState) {\n if (!state.initializePromise) {\n return true;\n }\n\n try {\n await state.initializePromise;\n return true;\n } catch (error) {\n if (isDatabaseUnavailableError(error)) {\n return false;\n }\n\n throw error;\n }\n}\n\nexport async function ensureDocumentStoreReady(\n state: DocumentStoreState,\n scheduleSync: () => void,\n): Promise<boolean> {\n ensureDocumentStoreInitialized(state, scheduleSync);\n\n if (state.initialized) {\n return true;\n }\n\n if (!state.initializePromise) {\n return false;\n }\n\n return awaitInitializationForSync(state);\n}\n\nexport async function relinkDocumentStore(\n state: DocumentStoreState,\n input: DocumentStoreRelinkInput,\n scheduleSync: () => void,\n commitSideEffect?: DocumentRelinkCommitSideEffect,\n): Promise<DocumentSummary | null> {\n if (\n input.stillCurrent?.() === false ||\n !(await ensureDocumentStoreReady(state, scheduleSync)) ||\n input.stillCurrent?.() === false ||\n !state.doc\n ) {\n return null;\n }\n const currentDoc = state.doc;\n const generation = captureDocumentStoreSyncGeneration(state, currentDoc);\n if (!generation) return null;\n // Read the record and persist on the identity-write chain so an in-flight\n // eager create cannot interleave: a create that captured a null identity\n // before its network round trip would otherwise write the derived documentId\n // over the one this relink assigns.\n const persisted = await chainIdentityWrite(state, async () => {\n if (!isDocumentStoreSyncGenerationCurrent(state, generation)) return null;\n const currentDocumentId = state.record?.documentId ?? null;\n const currentAccessEpoch =\n state.record?.accessEpoch ?? DEFAULT_DOCUMENT_ACCESS_EPOCH;\n const patch: Partial<DocumentRecord> = {\n accessEpoch: Math.max(currentAccessEpoch, input.accessEpoch),\n accessStateHash:\n input.accessStateHash === undefined\n ? (state.record?.accessStateHash ?? null)\n : input.accessStateHash,\n containerId: input.containerId,\n documentId: input.documentId,\n };\n if (input.contentKeyBundle !== undefined) {\n patch.contentKeyBundle = input.contentKeyBundle;\n }\n if (input.documentKekTargets !== undefined) {\n patch.documentKekTargets = input.documentKekTargets;\n }\n if (input.documentManifestBundle !== undefined) {\n patch.documentManifestBundle = input.documentManifestBundle;\n }\n\n // Relink rewrites access/identity metadata, not content. If it lands while\n // the user is mid-edit, re-deriving text/structured fields from the\n // (possibly lagging) doc would republish a stale read over the live\n // optimistic snapshot and drop in-flight keystrokes; preserve the snapshot\n // like the keystroke and sync persists do.\n // A prior chained write may have cleared this store (resurrect-guard\n // refusal): persisting the captured doc against a null record would\n // resurrect the document as a create. The identity check runs inside\n // the chain, after every earlier write settled.\n if (!isDocumentStoreSyncGenerationCurrent(state, generation)) {\n return null;\n }\n const result = await persistDocument(\n state,\n currentDoc,\n patch,\n {\n preserveSnapshotStructuredFields: true,\n preserveSnapshotText: true,\n stillCurrent: input.stillCurrent,\n },\n generation,\n commitSideEffect,\n );\n if (result && currentDocumentId !== input.documentId) {\n state.writerProjection = null;\n invalidateDocumentStoreRemoteSync(state);\n }\n return result;\n });\n // A refused persist means the durable row vanished mid-relink (resurrect\n // guard); there is no summary to report.\n if (!persisted) {\n return null;\n }\n if (persisted.updatedAt === undefined) {\n return null;\n }\n return documentSummaryFromRecord(\n persisted.record,\n persisted.updatedAt,\n state.runtime.infra.documentProjectors,\n );\n}\n"]}