@tearleads/client-sdk 0.1.82 → 0.1.84

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.
Files changed (157) hide show
  1. package/data/documents/documentSync.js.map +1 -1
  2. package/data/documents/shared/crypto.js.map +1 -1
  3. package/data/documents/shared/types.d.ts +1 -1
  4. package/data/documents/shared/types.js.map +1 -1
  5. package/data/persistence/container-contents/containerContentsPersistence.js +2 -2
  6. package/data/persistence/container-contents/containerContentsPersistence.js.map +1 -1
  7. package/data/persistence/container-contents/containerContentsPersistenceTypes.d.ts +1 -1
  8. package/data/persistence/container-contents/containerContentsPersistenceTypes.js.map +1 -1
  9. package/data/persistence/container-contents/containerDeletionPersistence.js +1 -1
  10. package/data/persistence/container-contents/containerDeletionPersistence.js.map +1 -1
  11. package/data/persistence/container-contents/containerMetadataMutationPersistence.js +3 -3
  12. package/data/persistence/container-contents/containerMetadataMutationPersistence.js.map +1 -1
  13. package/data/persistence/container-contents/containerMetadataPullContinuationPersistence.js +1 -1
  14. package/data/persistence/container-contents/containerMetadataPullContinuationPersistence.js.map +1 -1
  15. package/data/persistence/container-contents/containerMetadataRows.js +1 -1
  16. package/data/persistence/container-contents/containerMetadataRows.js.map +1 -1
  17. package/data/persistence/container-contents/containerPendingUpdatePersistence.js +2 -2
  18. package/data/persistence/container-contents/containerPendingUpdatePersistence.js.map +1 -1
  19. package/data/persistence/container-contents/containerReconcilePersistence.js +1 -1
  20. package/data/persistence/container-contents/containerReconcilePersistence.js.map +1 -1
  21. package/data/persistence/container-contents/containerRemovalWork.js +1 -1
  22. package/data/persistence/container-contents/containerRemovalWork.js.map +1 -1
  23. package/data/persistence/documents/documentDiscoveryReset.d.ts +6 -0
  24. package/data/persistence/documents/documentsPersistence.js +2 -2
  25. package/data/persistence/documents/documentsPersistence.js.map +1 -1
  26. package/data/persistence/documents/internal/createDocumentWithHistoryCheckpoint.js +1 -1
  27. package/data/persistence/documents/internal/createDocumentWithHistoryCheckpoint.js.map +1 -1
  28. package/data/persistence/documents/internal/discardDocument.js +2 -2
  29. package/data/persistence/documents/internal/discardDocument.js.map +1 -1
  30. package/data/persistence/documents/internal/documentDeletionPersistence.js +2 -2
  31. package/data/persistence/documents/internal/documentDeletionPersistence.js.map +1 -1
  32. package/data/persistence/documents/internal/documentMutationPersistence.js +3 -3
  33. package/data/persistence/documents/internal/documentMutationPersistence.js.map +1 -1
  34. package/data/persistence/documents/internal/documentPullContinuationPersistence.js +1 -1
  35. package/data/persistence/documents/internal/documentPullContinuationPersistence.js.map +1 -1
  36. package/data/persistence/documents/internal/documentRecordIdentity.js.map +1 -1
  37. package/data/persistence/documents/internal/documentRows.js.map +1 -1
  38. package/data/persistence/documents/internal/ensureDocumentsSchema.js +1 -1
  39. package/data/persistence/documents/internal/ensureDocumentsSchema.js.map +1 -1
  40. package/data/persistence/documents/internal/orphanSideRows.js +1 -1
  41. package/data/persistence/documents/internal/orphanSideRows.js.map +1 -1
  42. package/data/persistence/documents/internal/rowQueryPersistence.js +1 -1
  43. package/data/persistence/documents/internal/rowQueryPersistence.js.map +1 -1
  44. package/data/persistence/documents/internal/syncQueuePersistence.js +2 -2
  45. package/data/persistence/documents/internal/syncQueuePersistence.js.map +1 -1
  46. package/data/{sqlite/documentPersistence → persistence/documents/records}/documentHistoryPersistence.d.ts +1 -1
  47. package/data/{sqlite/documentPersistence → persistence/documents/records}/documentHistoryPersistence.js +5 -5
  48. package/data/persistence/documents/records/documentHistoryPersistence.js.map +1 -0
  49. package/data/{sqlite/documentPersistence → persistence/documents/records}/documentPendingUpdatePersistence.d.ts +2 -2
  50. package/data/{sqlite/documentPersistence → persistence/documents/records}/documentPendingUpdatePersistence.js +2 -2
  51. package/data/persistence/documents/records/documentPendingUpdatePersistence.js.map +1 -0
  52. package/data/{sqlite/documentPersistence → persistence/documents/records}/documentPersistenceTypes.d.ts +2 -2
  53. package/data/persistence/documents/records/documentPersistenceTypes.js.map +1 -0
  54. package/data/{sqlite/documentPersistence → persistence/documents/records}/documentRecordPersistence.d.ts +1 -1
  55. package/data/{sqlite/documentPersistence → persistence/documents/records}/documentRecordPersistence.js +4 -4
  56. package/data/persistence/documents/records/documentRecordPersistence.js.map +1 -0
  57. package/data/{sqlite/documentPersistence → persistence/documents/records}/documentSyncFailurePersistence.d.ts +2 -2
  58. package/data/{sqlite/documentPersistence → persistence/documents/records}/documentSyncFailurePersistence.js +3 -3
  59. package/data/persistence/documents/records/documentSyncFailurePersistence.js.map +1 -0
  60. package/data/{sqlite/documentPersistence → persistence/documents/records}/index.d.ts +2 -2
  61. package/data/{sqlite/documentPersistence → persistence/documents/records}/index.js +4 -4
  62. package/data/persistence/documents/records/index.js.map +1 -0
  63. package/data/persistence/documents/types.d.ts +2 -2
  64. package/data/persistence/documents/types.js.map +1 -1
  65. package/{workflows/sync/remoteResetPrincipalScope.d.ts → data/persistence/principalPolicy/principalPolicyRemoteReset.d.ts} +1 -1
  66. package/{workflows/sync/remoteResetPrincipalScope.js → data/persistence/principalPolicy/principalPolicyRemoteReset.js} +7 -7
  67. package/data/persistence/principalPolicy/principalPolicyRemoteReset.js.map +1 -0
  68. package/data/persistence/verifiedManifestBundlePersistence.d.ts +2 -0
  69. package/data/persistence/verifiedManifestBundlePersistence.js +7 -0
  70. package/data/persistence/verifiedManifestBundlePersistence.js.map +1 -1
  71. package/data/sqlite/{documentPersistence/documentOrphanBlobReclaims.js → schema/documentOrphanBlobReclaimSchema.js} +1 -1
  72. package/data/sqlite/schema/documentOrphanBlobReclaimSchema.js.map +1 -0
  73. package/data/sqlite/schema/index.js +1 -1
  74. package/data/sqlite/schema/index.js.map +1 -1
  75. package/data/sqlite/sqliteBindBudget.js +12 -0
  76. package/data/sqlite/sqliteBindBudget.js.map +1 -1
  77. package/package.json +1 -1
  78. package/workflows/container-contents/container-state/create/createIntentSeed.js.map +1 -1
  79. package/workflows/container-contents/container-state/create/createWithMetadataPlan.js.map +1 -1
  80. package/workflows/container-contents/container-state/types.js.map +1 -1
  81. package/workflows/container-contents/metadata/index.js.map +1 -1
  82. package/workflows/container-contents/metadata/metadataEvents.d.ts +1 -1
  83. package/workflows/container-contents/metadata/metadataEvents.js.map +1 -1
  84. package/workflows/container-contents/metadata/metadataIncomingUpdateIsolation.js +1 -1
  85. package/workflows/container-contents/metadata/metadataIncomingUpdateIsolation.js.map +1 -1
  86. package/workflows/container-contents/metadata/metadataRemoteSync.d.ts +1 -1
  87. package/workflows/container-contents/metadata/metadataRemoteSync.js.map +1 -1
  88. package/workflows/container-contents/pendingWrites/index.js +1 -1
  89. package/workflows/container-contents/pendingWrites/index.js.map +1 -1
  90. package/workflows/container-contents/recovery/accessRestoration.js +1 -1
  91. package/workflows/container-contents/recovery/accessRestoration.js.map +1 -1
  92. package/workflows/container-contents/remoteReset/containerRemoteReset.d.ts +185 -0
  93. package/workflows/container-contents/remoteReset/containerRemoteReset.js +174 -0
  94. package/workflows/container-contents/remoteReset/containerRemoteReset.js.map +1 -0
  95. package/workflows/container-contents/remoteReset/remoteResetCursorScope.d.ts +14 -0
  96. package/workflows/{sync → container-contents/remoteReset}/remoteResetCursorScope.js +2 -2
  97. package/workflows/container-contents/remoteReset/remoteResetCursorScope.js.map +1 -0
  98. package/workflows/documents/create/documentCreationPersistence.js.map +1 -1
  99. package/workflows/documents/create/index.d.ts +1 -1
  100. package/workflows/documents/create/index.js.map +1 -1
  101. package/workflows/documents/documentMutationCommit.d.ts +1 -1
  102. package/workflows/documents/documentMutationCommit.js.map +1 -1
  103. package/workflows/documents/index.d.ts +2 -2
  104. package/workflows/documents/index.js +2 -2
  105. package/workflows/documents/index.js.map +1 -1
  106. package/workflows/documents/remoteReset/documentRemoteReset.d.ts +56 -0
  107. package/workflows/documents/remoteReset/documentRemoteReset.js +135 -0
  108. package/workflows/documents/remoteReset/documentRemoteReset.js.map +1 -0
  109. package/workflows/{sync → documents/remoteReset}/remoteResetAttachments.js +3 -3
  110. package/workflows/documents/remoteReset/remoteResetAttachments.js.map +1 -0
  111. package/workflows/{sync → documents/remoteReset}/remoteResetPlans.d.ts +1 -1
  112. package/workflows/{sync → documents/remoteReset}/remoteResetPlans.js +7 -7
  113. package/workflows/documents/remoteReset/remoteResetPlans.js.map +1 -0
  114. package/workflows/documents/sync/index.js.map +1 -1
  115. package/workflows/documents/sync/initialSync.d.ts +1 -1
  116. package/workflows/documents/sync/initialSync.js.map +1 -1
  117. package/workflows/documents/sync/readOnlySync.d.ts +1 -1
  118. package/workflows/documents/sync/readOnlySync.js.map +1 -1
  119. package/workflows/documents/sync/syncAttemptPlanning.js.map +1 -1
  120. package/workflows/documents/sync/syncAttemptState.d.ts +1 -1
  121. package/workflows/documents/sync/syncAttemptState.js.map +1 -1
  122. package/workflows/documents/sync/syncResponseResult.js.map +1 -1
  123. package/workflows/documents/sync/syncSubmittedResult.js.map +1 -1
  124. package/workflows/documents/syncFailures/index.js.map +1 -1
  125. package/workflows/documents/syncPlan/syncContentKeys.d.ts +1 -1
  126. package/workflows/documents/syncPlan/syncContentKeys.js.map +1 -1
  127. package/workflows/documents/syncPlan/syncPlanMaterial.js.map +1 -1
  128. package/workflows/documents/syncPlan/syncPlanRequestBounds.js.map +1 -1
  129. package/workflows/documents/syncRekeys/syncContainerRekeys.js.map +1 -1
  130. package/workflows/documents/syncRekeys/syncRecoveryRekey.d.ts +1 -1
  131. package/workflows/documents/syncRekeys/syncRecoveryRekey.js +1 -1
  132. package/workflows/documents/syncRekeys/syncRecoveryRekey.js.map +1 -1
  133. package/workflows/organizations/readModel/listLocalOrganizations.js +1 -1
  134. package/workflows/organizations/readModel/listLocalOrganizations.js.map +1 -1
  135. package/workflows/organizations/readModel/organizationRemoteReset.js +25 -0
  136. package/workflows/organizations/readModel/organizationRemoteReset.js.map +1 -0
  137. package/workflows/registration/persistRegistrationBootstrap.d.ts +1 -1
  138. package/workflows/registration/persistRegistrationBootstrap.js.map +1 -1
  139. package/workflows/sync/remoteReset.js +60 -229
  140. package/workflows/sync/remoteReset.js.map +1 -1
  141. package/workflows/sync/remoteResetScope.d.ts +7 -10
  142. package/workflows/sync/remoteResetScope.js +26 -108
  143. package/workflows/sync/remoteResetScope.js.map +1 -1
  144. package/data/sqlite/documentPersistence/documentHistoryPersistence.js.map +0 -1
  145. package/data/sqlite/documentPersistence/documentOrphanBlobReclaims.js.map +0 -1
  146. package/data/sqlite/documentPersistence/documentPendingUpdatePersistence.js.map +0 -1
  147. package/data/sqlite/documentPersistence/documentPersistenceTypes.js.map +0 -1
  148. package/data/sqlite/documentPersistence/documentRecordPersistence.js.map +0 -1
  149. package/data/sqlite/documentPersistence/documentSyncFailurePersistence.js.map +0 -1
  150. package/data/sqlite/documentPersistence/index.js.map +0 -1
  151. package/workflows/sync/remoteResetAttachments.js.map +0 -1
  152. package/workflows/sync/remoteResetBatches.js +0 -13
  153. package/workflows/sync/remoteResetBatches.js.map +0 -1
  154. package/workflows/sync/remoteResetCursorScope.js.map +0 -1
  155. package/workflows/sync/remoteResetPlans.js.map +0 -1
  156. package/workflows/sync/remoteResetPrincipalScope.js.map +0 -1
  157. /package/data/{sqlite/documentPersistence → persistence/documents/records}/documentPersistenceTypes.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"discardDocument.js","sourceRoot":"","sources":["discardDocument.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, isNotNull } from \"drizzle-orm\";\nimport { DEFAULT_DOCUMENT_KIND } from \"../../../documents/documentConstants\";\nimport {\n type DocumentProjectorRegistry,\n type DocumentProjectorRegistryInput,\n resolveDocumentProjectorRegistry,\n} from \"../../../documents/documentKinds\";\nimport { clearDocumentSyncFailure } from \"../../../sqlite/documentPersistence\";\nimport { deleteDocumentHistory } from \"../../../sqlite/documentPersistence/documentHistoryPersistence\";\nimport {\n documentAttachmentBlobProjection,\n documentPendingAttachments,\n documentPlacementIntentTables,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../../sqlite/sqlSchema\";\nimport type {\n DiscardDocumentToShellResult,\n DocumentsPersistence,\n StoredDocumentRecord,\n} from \"../types\";\nimport {\n getDocumentScope,\n resolveDocumentSaveTimestamp,\n saveDocumentRows,\n} from \"./documentRows\";\nimport { queueDocumentAttachmentStorageKeys } from \"./orphanSideRows\";\n\nfunction buildDiscardShellDocument(\n localId: string,\n existingDocument: StoredDocumentRecord,\n): StoredDocumentRecord {\n return {\n id: localId,\n accessEpoch: existingDocument.accessEpoch,\n accessStateHash: existingDocument.accessStateHash ?? null,\n containerId: existingDocument.containerId,\n contentKeyBundle: null,\n documentId: existingDocument.documentId,\n documentKekTargets: null,\n documentManifestBundle: null,\n effectiveAccessLevel: existingDocument.effectiveAccessLevel ?? null,\n lastCommitLsn: null,\n pendingBaseVersion: null,\n pullContinuation: null,\n recoveryGeneration: existingDocument.recoveryGeneration ?? 0,\n snapshotEndVersion: \"\",\n text: \"\",\n ...(existingDocument.documentKind === undefined\n ? {}\n : { documentKind: existingDocument.documentKind }),\n ...(existingDocument.title === undefined\n ? {}\n : { title: existingDocument.title }),\n };\n}\n\ninterface DiscardCandidate {\n detachedStorageKeys: string[];\n existingDocument: StoredDocumentRecord;\n pendingAttachments: ReadonlyArray<{ slotId: string; storageKey: string }>;\n}\n\nasync function loadDiscardCandidate(input: {\n expectedDocumentId: string;\n localId: string;\n lockedExecSql: ExecSql;\n persistence: Pick<\n DocumentsPersistence,\n \"listPendingAttachments\" | \"loadDocument\"\n >;\n tx: ClientSQLiteTransactionScope;\n}): Promise<DiscardCandidate | null> {\n const { expectedDocumentId, localId, lockedExecSql, persistence, tx } = input;\n const existingDocument = await persistence.loadDocument(\n lockedExecSql,\n localId,\n );\n if (\n !existingDocument?.documentId ||\n existingDocument.documentId !== expectedDocumentId ||\n !existingDocument.containerId\n ) {\n return null;\n }\n const moveIntentRows = await tx\n .select({ id: localIntents.id })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n eq(localIntents.localId, localId),\n ),\n )\n .limit(1);\n if (moveIntentRows.length > 0) return null;\n\n const pendingAttachments = await persistence.listPendingAttachments(\n lockedExecSql,\n localId,\n );\n const detachedRows = await tx\n .select({ storageKey: documentAttachmentBlobProjection.storageKey })\n .from(documentAttachmentBlobProjection)\n .where(\n and(\n eq(documentAttachmentBlobProjection.localId, localId),\n isNotNull(documentAttachmentBlobProjection.detachedAt),\n ),\n );\n return {\n detachedStorageKeys: detachedRows.map((row) => row.storageKey),\n existingDocument,\n pendingAttachments,\n };\n}\n\n// Staged-upload rows AND detached local-attachment markers both go: a marker\n// for a locally-discarded detach would otherwise keep filtering the slot out\n// after a re-pull restores it, leaving the attachment permanently invisible.\nasync function clearDiscardedDocumentRows(input: {\n candidate: DiscardCandidate;\n localId: string;\n lockedExecSql: ExecSql;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const { candidate, localId, lockedExecSql, tx } = input;\n await tx\n .delete(documentPendingAttachments)\n .where(eq(documentPendingAttachments.localId, localId))\n .run();\n for (const pendingAttachment of candidate.pendingAttachments) {\n await tx\n .delete(documentAttachmentBlobProjection)\n .where(\n and(\n eq(documentAttachmentBlobProjection.localId, localId),\n eq(documentAttachmentBlobProjection.slotId, pendingAttachment.slotId),\n eq(\n documentAttachmentBlobProjection.storageKey,\n pendingAttachment.storageKey,\n ),\n ),\n )\n .run();\n }\n await tx\n .delete(documentAttachmentBlobProjection)\n .where(\n and(\n eq(documentAttachmentBlobProjection.localId, localId),\n isNotNull(documentAttachmentBlobProjection.detachedAt),\n ),\n )\n .run();\n await deleteDocumentHistory(lockedExecSql, getDocumentScope(localId));\n await clearDocumentSyncFailure(lockedExecSql, getDocumentScope(localId));\n}\n\nasync function saveDiscardShell(input: {\n candidate: DiscardCandidate;\n documentProjectors: DocumentProjectorRegistry;\n localId: string;\n lockedExecSql: ExecSql;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const { candidate, documentProjectors, localId, lockedExecSql, tx } = input;\n const shellDocument = buildDiscardShellDocument(\n localId,\n candidate.existingDocument,\n );\n // The document-kind projection derives from discarded content, so clear it\n // inside the transaction and let the next pull rebuild it.\n await documentProjectors.deleteStoredDocumentClientProjection({\n documentKind: shellDocument.documentKind ?? DEFAULT_DOCUMENT_KIND,\n execSql: lockedExecSql,\n localId,\n });\n const updatedAt = await resolveDocumentSaveTimestamp({\n document: shellDocument,\n tx,\n });\n if (!(await saveDocumentRows({ document: shellDocument, tx, updatedAt }))) {\n throw new Error(\"Document recovery generation changed during discard\");\n }\n}\n\n// Eligibility, teardown, client-projection clearing, and shell replacement\n// share one immediate transaction, so a second connection cannot relink the\n// local id between identity validation and destructive writes.\nasync function discardDocumentRowsToShell(input: {\n documentProjectors: DocumentProjectorRegistry;\n expectedDocumentId: string;\n localId: string;\n lockedExecSql: ExecSql;\n persistence: Pick<\n DocumentsPersistence,\n \"listPendingAttachments\" | \"loadDocument\"\n >;\n}): Promise<DiscardDocumentToShellResult> {\n const clientProjectionTables =\n input.documentProjectors.getClientProjectionTables();\n if (clientProjectionTables.length > 0) {\n await ensureSqlTables(input.lockedExecSql, clientProjectionTables);\n }\n return getClientSQLitePersistenceRuntime(input.lockedExecSql).transaction(\n async (tx) => {\n const candidate = await loadDiscardCandidate({ ...input, tx });\n if (!candidate) return { discarded: false };\n await clearDiscardedDocumentRows({\n candidate,\n localId: input.localId,\n lockedExecSql: input.lockedExecSql,\n tx,\n });\n await saveDiscardShell({ ...input, candidate, tx });\n // The deleted rows were these bytes' only pointers from this document,\n // but a hydrated copy is shared by every slot holding the same blob.\n // Queue them for the reference-checked reclaim instead of deleting.\n await queueDocumentAttachmentStorageKeys(tx, [\n ...new Set([\n ...candidate.pendingAttachments.map((row) => row.storageKey),\n ...candidate.detachedStorageKeys,\n ]),\n ]);\n return {\n discarded: true,\n documentKind:\n candidate.existingDocument.documentKind ?? DEFAULT_DOCUMENT_KIND,\n };\n },\n { behavior: \"immediate\" },\n );\n}\n\n/**\n * Atomically convert a stuck document's local state to the\n * freshly-discovered-share shell: drop its queued updates, staged attachment\n * rows (and their settled local-attachment halves, which share the staged\n * storage keys the caller reclaims), detached attachment markers, durable\n * history, recorded sync failure, and document-kind client projection, then\n * overwrite the record with an empty snapshot that keeps its identity,\n * placement, title, and kind. Everything commits in ONE transaction.\n *\n * Refused under the serialized mutation when the document is local-only or\n * unlinked (its rows are the only copy), when its documentId no longer\n * matches the caller's expectation (a stale caller or racing relink must not\n * discard a different identity's edits), or when any move intent references\n * it (the local containerId is then the move's optimistic placement, and\n * reseeding it as server truth would silently commit the move locally while\n * discarding the intent that was meant to perform it).\n */\nexport async function discardStoredDocumentToShell(input: {\n documentProjectors: DocumentProjectorRegistryInput;\n execSql: ExecSql;\n expectedDocumentId: string;\n localId: string;\n persistence: Pick<\n DocumentsPersistence,\n \"listPendingAttachments\" | \"loadDocument\"\n >;\n}): Promise<DiscardDocumentToShellResult> {\n const { expectedDocumentId, localId, persistence } = input;\n return runSerializedSqlMutation(input.execSql, async (lockedExecSql) => {\n await ensureSqlTables(lockedExecSql, documentPlacementIntentTables);\n return discardDocumentRowsToShell({\n documentProjectors: resolveDocumentProjectorRegistry(\n input.documentProjectors,\n ),\n expectedDocumentId,\n localId,\n lockedExecSql,\n persistence,\n });\n });\n}\n"]}
1
+ {"version":3,"file":"discardDocument.js","sourceRoot":"","sources":["discardDocument.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, isNotNull } from \"drizzle-orm\";\nimport { DEFAULT_DOCUMENT_KIND } from \"../../../documents/documentConstants\";\nimport {\n type DocumentProjectorRegistry,\n type DocumentProjectorRegistryInput,\n resolveDocumentProjectorRegistry,\n} from \"../../../documents/documentKinds\";\nimport {\n documentAttachmentBlobProjection,\n documentPendingAttachments,\n documentPlacementIntentTables,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../../sqlite/sqlSchema\";\nimport { clearDocumentSyncFailure } from \"../records\";\nimport { deleteDocumentHistory } from \"../records/documentHistoryPersistence\";\nimport type {\n DiscardDocumentToShellResult,\n DocumentsPersistence,\n StoredDocumentRecord,\n} from \"../types\";\nimport {\n getDocumentScope,\n resolveDocumentSaveTimestamp,\n saveDocumentRows,\n} from \"./documentRows\";\nimport { queueDocumentAttachmentStorageKeys } from \"./orphanSideRows\";\n\nfunction buildDiscardShellDocument(\n localId: string,\n existingDocument: StoredDocumentRecord,\n): StoredDocumentRecord {\n return {\n id: localId,\n accessEpoch: existingDocument.accessEpoch,\n accessStateHash: existingDocument.accessStateHash ?? null,\n containerId: existingDocument.containerId,\n contentKeyBundle: null,\n documentId: existingDocument.documentId,\n documentKekTargets: null,\n documentManifestBundle: null,\n effectiveAccessLevel: existingDocument.effectiveAccessLevel ?? null,\n lastCommitLsn: null,\n pendingBaseVersion: null,\n pullContinuation: null,\n recoveryGeneration: existingDocument.recoveryGeneration ?? 0,\n snapshotEndVersion: \"\",\n text: \"\",\n ...(existingDocument.documentKind === undefined\n ? {}\n : { documentKind: existingDocument.documentKind }),\n ...(existingDocument.title === undefined\n ? {}\n : { title: existingDocument.title }),\n };\n}\n\ninterface DiscardCandidate {\n detachedStorageKeys: string[];\n existingDocument: StoredDocumentRecord;\n pendingAttachments: ReadonlyArray<{ slotId: string; storageKey: string }>;\n}\n\nasync function loadDiscardCandidate(input: {\n expectedDocumentId: string;\n localId: string;\n lockedExecSql: ExecSql;\n persistence: Pick<\n DocumentsPersistence,\n \"listPendingAttachments\" | \"loadDocument\"\n >;\n tx: ClientSQLiteTransactionScope;\n}): Promise<DiscardCandidate | null> {\n const { expectedDocumentId, localId, lockedExecSql, persistence, tx } = input;\n const existingDocument = await persistence.loadDocument(\n lockedExecSql,\n localId,\n );\n if (\n !existingDocument?.documentId ||\n existingDocument.documentId !== expectedDocumentId ||\n !existingDocument.containerId\n ) {\n return null;\n }\n const moveIntentRows = await tx\n .select({ id: localIntents.id })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n eq(localIntents.localId, localId),\n ),\n )\n .limit(1);\n if (moveIntentRows.length > 0) return null;\n\n const pendingAttachments = await persistence.listPendingAttachments(\n lockedExecSql,\n localId,\n );\n const detachedRows = await tx\n .select({ storageKey: documentAttachmentBlobProjection.storageKey })\n .from(documentAttachmentBlobProjection)\n .where(\n and(\n eq(documentAttachmentBlobProjection.localId, localId),\n isNotNull(documentAttachmentBlobProjection.detachedAt),\n ),\n );\n return {\n detachedStorageKeys: detachedRows.map((row) => row.storageKey),\n existingDocument,\n pendingAttachments,\n };\n}\n\n// Staged-upload rows AND detached local-attachment markers both go: a marker\n// for a locally-discarded detach would otherwise keep filtering the slot out\n// after a re-pull restores it, leaving the attachment permanently invisible.\nasync function clearDiscardedDocumentRows(input: {\n candidate: DiscardCandidate;\n localId: string;\n lockedExecSql: ExecSql;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const { candidate, localId, lockedExecSql, tx } = input;\n await tx\n .delete(documentPendingAttachments)\n .where(eq(documentPendingAttachments.localId, localId))\n .run();\n for (const pendingAttachment of candidate.pendingAttachments) {\n await tx\n .delete(documentAttachmentBlobProjection)\n .where(\n and(\n eq(documentAttachmentBlobProjection.localId, localId),\n eq(documentAttachmentBlobProjection.slotId, pendingAttachment.slotId),\n eq(\n documentAttachmentBlobProjection.storageKey,\n pendingAttachment.storageKey,\n ),\n ),\n )\n .run();\n }\n await tx\n .delete(documentAttachmentBlobProjection)\n .where(\n and(\n eq(documentAttachmentBlobProjection.localId, localId),\n isNotNull(documentAttachmentBlobProjection.detachedAt),\n ),\n )\n .run();\n await deleteDocumentHistory(lockedExecSql, getDocumentScope(localId));\n await clearDocumentSyncFailure(lockedExecSql, getDocumentScope(localId));\n}\n\nasync function saveDiscardShell(input: {\n candidate: DiscardCandidate;\n documentProjectors: DocumentProjectorRegistry;\n localId: string;\n lockedExecSql: ExecSql;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const { candidate, documentProjectors, localId, lockedExecSql, tx } = input;\n const shellDocument = buildDiscardShellDocument(\n localId,\n candidate.existingDocument,\n );\n // The document-kind projection derives from discarded content, so clear it\n // inside the transaction and let the next pull rebuild it.\n await documentProjectors.deleteStoredDocumentClientProjection({\n documentKind: shellDocument.documentKind ?? DEFAULT_DOCUMENT_KIND,\n execSql: lockedExecSql,\n localId,\n });\n const updatedAt = await resolveDocumentSaveTimestamp({\n document: shellDocument,\n tx,\n });\n if (!(await saveDocumentRows({ document: shellDocument, tx, updatedAt }))) {\n throw new Error(\"Document recovery generation changed during discard\");\n }\n}\n\n// Eligibility, teardown, client-projection clearing, and shell replacement\n// share one immediate transaction, so a second connection cannot relink the\n// local id between identity validation and destructive writes.\nasync function discardDocumentRowsToShell(input: {\n documentProjectors: DocumentProjectorRegistry;\n expectedDocumentId: string;\n localId: string;\n lockedExecSql: ExecSql;\n persistence: Pick<\n DocumentsPersistence,\n \"listPendingAttachments\" | \"loadDocument\"\n >;\n}): Promise<DiscardDocumentToShellResult> {\n const clientProjectionTables =\n input.documentProjectors.getClientProjectionTables();\n if (clientProjectionTables.length > 0) {\n await ensureSqlTables(input.lockedExecSql, clientProjectionTables);\n }\n return getClientSQLitePersistenceRuntime(input.lockedExecSql).transaction(\n async (tx) => {\n const candidate = await loadDiscardCandidate({ ...input, tx });\n if (!candidate) return { discarded: false };\n await clearDiscardedDocumentRows({\n candidate,\n localId: input.localId,\n lockedExecSql: input.lockedExecSql,\n tx,\n });\n await saveDiscardShell({ ...input, candidate, tx });\n // The deleted rows were these bytes' only pointers from this document,\n // but a hydrated copy is shared by every slot holding the same blob.\n // Queue them for the reference-checked reclaim instead of deleting.\n await queueDocumentAttachmentStorageKeys(tx, [\n ...new Set([\n ...candidate.pendingAttachments.map((row) => row.storageKey),\n ...candidate.detachedStorageKeys,\n ]),\n ]);\n return {\n discarded: true,\n documentKind:\n candidate.existingDocument.documentKind ?? DEFAULT_DOCUMENT_KIND,\n };\n },\n { behavior: \"immediate\" },\n );\n}\n\n/**\n * Atomically convert a stuck document's local state to the\n * freshly-discovered-share shell: drop its queued updates, staged attachment\n * rows (and their settled local-attachment halves, which share the staged\n * storage keys the caller reclaims), detached attachment markers, durable\n * history, recorded sync failure, and document-kind client projection, then\n * overwrite the record with an empty snapshot that keeps its identity,\n * placement, title, and kind. Everything commits in ONE transaction.\n *\n * Refused under the serialized mutation when the document is local-only or\n * unlinked (its rows are the only copy), when its documentId no longer\n * matches the caller's expectation (a stale caller or racing relink must not\n * discard a different identity's edits), or when any move intent references\n * it (the local containerId is then the move's optimistic placement, and\n * reseeding it as server truth would silently commit the move locally while\n * discarding the intent that was meant to perform it).\n */\nexport async function discardStoredDocumentToShell(input: {\n documentProjectors: DocumentProjectorRegistryInput;\n execSql: ExecSql;\n expectedDocumentId: string;\n localId: string;\n persistence: Pick<\n DocumentsPersistence,\n \"listPendingAttachments\" | \"loadDocument\"\n >;\n}): Promise<DiscardDocumentToShellResult> {\n const { expectedDocumentId, localId, persistence } = input;\n return runSerializedSqlMutation(input.execSql, async (lockedExecSql) => {\n await ensureSqlTables(lockedExecSql, documentPlacementIntentTables);\n return discardDocumentRowsToShell({\n documentProjectors: resolveDocumentProjectorRegistry(\n input.documentProjectors,\n ),\n expectedDocumentId,\n localId,\n lockedExecSql,\n persistence,\n });\n });\n}\n"]}
@@ -1,11 +1,11 @@
1
1
  import { and, eq, inArray, ne } from "drizzle-orm";
2
2
  import { DOCUMENTS_APP_KIND } from "../../../documents/documentConstants.js";
3
- import { clearDocumentSyncFailure, deleteDocumentRecord, loadDocumentRecord, } from "../../../sqlite/documentPersistence/index.js";
4
- import { deleteDocumentHistory } from "../../../sqlite/documentPersistence/documentHistoryPersistence.js";
5
3
  import { documentAttachmentBlobProjection, documentContainerProjection, documentPendingAttachments, documentPlacementIntentTables, documentProjection, documentProjectionText, documents, inLocalIntentLane, LOCAL_INTENT_LANES, localIntents, } from "../../../sqlite/schema/index.js";
6
4
  import { documentIntentLinkTargets } from "../../../sqlite/schema/documentPlacementIntentSchema.js";
7
5
  import { getClientSQLitePersistenceRuntime, } from "../../../sqlite/sqlitePersistenceRuntime.js";
8
6
  import { ensureSqlTables, runSerializedSqlMutation, } from "../../../sqlite/sqlSchema.js";
7
+ import { clearDocumentSyncFailure, deleteDocumentRecord, loadDocumentRecord, } from "../records/index.js";
8
+ import { deleteDocumentHistory } from "../records/documentHistoryPersistence.js";
9
9
  import { deleteContainerDocumentTombstoneHoldsForDocuments } from "./containerDocumentTombstoneHolds.js";
10
10
  import { sameCanonicalDocumentSecurityIdentity } from "./documentRecordIdentity.js";
11
11
  import { getDocumentScope } from "./documentRows.js";
@@ -1 +1 @@
1
- {"version":3,"file":"documentDeletionPersistence.js","sourceRoot":"","sources":["documentDeletionPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, inArray, ne } from \"drizzle-orm\";\nimport { DOCUMENTS_APP_KIND } from \"../../../documents/documentConstants\";\nimport {\n clearDocumentSyncFailure,\n deleteDocumentRecord,\n loadDocumentRecord,\n} from \"../../../sqlite/documentPersistence\";\nimport { deleteDocumentHistory } from \"../../../sqlite/documentPersistence/documentHistoryPersistence\";\nimport {\n documentAttachmentBlobProjection,\n documentContainerProjection,\n documentPendingAttachments,\n documentPlacementIntentTables,\n documentProjection,\n documentProjectionText,\n documents,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../../sqlite/schema\";\nimport { documentIntentLinkTargets } from \"../../../sqlite/schema/documentPlacementIntentSchema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../../sqlite/sqlSchema\";\nimport type { StoredDocumentRecord } from \"../types\";\nimport { deleteContainerDocumentTombstoneHoldsForDocuments } from \"./containerDocumentTombstoneHolds\";\nimport { sameCanonicalDocumentSecurityIdentity } from \"./documentRecordIdentity\";\nimport { getDocumentScope } from \"./documentRows\";\nimport { queueDocumentAttachmentBlobReclaims } from \"./orphanSideRows\";\n\nasync function deleteStoredDocumentRows(input: {\n existingDocumentId: string | null;\n localId: string;\n lockedExecSql: ExecSql;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const { existingDocumentId, localId, lockedExecSql, tx } = input;\n await queueDocumentAttachmentBlobReclaims({\n localWhere: eq(documentAttachmentBlobProjection.localId, localId),\n pendingWhere: eq(documentPendingAttachments.localId, localId),\n tx,\n });\n await tx\n .delete(documentProjection)\n .where(eq(documentProjection.localId, localId))\n .run();\n await tx\n .delete(documentProjectionText)\n .where(eq(documentProjectionText.localId, localId))\n .run();\n await tx\n .delete(documentPendingAttachments)\n .where(eq(documentPendingAttachments.localId, localId))\n .run();\n await tx\n .delete(documentAttachmentBlobProjection)\n .where(eq(documentAttachmentBlobProjection.localId, localId))\n .run();\n if (existingDocumentId) {\n await tx\n .delete(documentContainerProjection)\n .where(eq(documentContainerProjection.documentId, existingDocumentId))\n .run();\n await deleteContainerDocumentTombstoneHoldsForDocuments(tx, [\n existingDocumentId,\n ]);\n const placementIntent = and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n eq(localIntents.subjectId, existingDocumentId),\n );\n await tx\n .delete(documentIntentLinkTargets)\n .where(\n inArray(\n documentIntentLinkTargets.intentId,\n tx\n .select({ id: localIntents.id })\n .from(localIntents)\n .where(placementIntent),\n ),\n )\n .run();\n await tx.delete(localIntents).where(placementIntent).run();\n }\n const scope = getDocumentScope(localId);\n await deleteDocumentHistory(lockedExecSql, scope);\n await clearDocumentSyncFailure(lockedExecSql, scope);\n await deleteDocumentRecord(lockedExecSql, scope);\n}\n\nasync function ensureDocumentDeletionTables(execSql: ExecSql): Promise<void> {\n // Move intents live in the container-contents schema, which document-only\n // callers may not have initialized yet.\n await ensureSqlTables(execSql, documentPlacementIntentTables);\n}\n\nasync function hasAnotherDocumentAlias(input: {\n documentId: string;\n localId: string;\n tx: ClientSQLiteTransactionScope;\n}): Promise<boolean> {\n const rows = await input.tx\n .select({ localId: documents.localId })\n .from(documents)\n .where(\n and(\n eq(documents.appKind, DOCUMENTS_APP_KIND),\n eq(documents.documentId, input.documentId),\n ne(documents.localId, input.localId),\n ),\n )\n .limit(1);\n return rows.length > 0;\n}\n\nexport async function deleteStoredDocument(\n execSql: ExecSql,\n localId: string,\n): Promise<void> {\n await runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureDocumentDeletionTables(lockedExecSql);\n await getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const existingDocument = await loadDocumentRecord(lockedExecSql, {\n appKind: DOCUMENTS_APP_KIND,\n localId,\n });\n await deleteStoredDocumentRows({\n existingDocumentId: existingDocument?.documentId ?? null,\n localId,\n lockedExecSql,\n tx,\n });\n },\n { behavior: \"immediate\" },\n );\n });\n}\n\nexport async function deleteStoredDocumentIfMatches(\n execSql: ExecSql,\n expectedRecord: StoredDocumentRecord,\n deleteClientProjection: (transactionExecSql: ExecSql) => Promise<void>,\n): Promise<boolean> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureDocumentDeletionTables(lockedExecSql);\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const existingDocument = await loadDocumentRecord(lockedExecSql, {\n appKind: DOCUMENTS_APP_KIND,\n localId: expectedRecord.id,\n });\n if (\n !existingDocument ||\n !sameCanonicalDocumentSecurityIdentity(\n existingDocument,\n expectedRecord,\n )\n ) {\n return false;\n }\n const [placement] = await tx\n .select({ containerId: documentProjection.containerId })\n .from(documentProjection)\n .where(eq(documentProjection.localId, expectedRecord.id))\n .limit(1);\n if ((placement?.containerId ?? null) !== expectedRecord.containerId)\n return false;\n if (\n existingDocument.documentId &&\n (await hasAnotherDocumentAlias({\n documentId: existingDocument.documentId,\n localId: expectedRecord.id,\n tx,\n }))\n ) {\n return false;\n }\n await deleteStoredDocumentRows({\n existingDocumentId: existingDocument.documentId ?? null,\n localId: expectedRecord.id,\n lockedExecSql,\n tx,\n });\n await deleteClientProjection(lockedExecSql);\n return true;\n },\n { behavior: \"immediate\" },\n );\n });\n}\n\nexport async function deleteStoredDocumentSideRowsIfAbsent(\n execSql: ExecSql,\n localId: string,\n expectedDocumentId: string | null,\n deleteClientProjection: (transactionExecSql: ExecSql) => Promise<void>,\n): Promise<boolean> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureDocumentDeletionTables(lockedExecSql);\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const existingDocument = await loadDocumentRecord(lockedExecSql, {\n appKind: DOCUMENTS_APP_KIND,\n localId,\n });\n if (existingDocument) return false;\n if (\n expectedDocumentId &&\n (await hasAnotherDocumentAlias({\n documentId: expectedDocumentId,\n localId,\n tx,\n }))\n ) {\n return false;\n }\n\n await deleteStoredDocumentRows({\n existingDocumentId: expectedDocumentId,\n localId,\n lockedExecSql,\n tx,\n });\n await deleteClientProjection(lockedExecSql);\n return true;\n },\n { behavior: \"immediate\" },\n );\n });\n}\n"]}
1
+ {"version":3,"file":"documentDeletionPersistence.js","sourceRoot":"","sources":["documentDeletionPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, inArray, ne } from \"drizzle-orm\";\nimport { DOCUMENTS_APP_KIND } from \"../../../documents/documentConstants\";\nimport {\n documentAttachmentBlobProjection,\n documentContainerProjection,\n documentPendingAttachments,\n documentPlacementIntentTables,\n documentProjection,\n documentProjectionText,\n documents,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../../sqlite/schema\";\nimport { documentIntentLinkTargets } from \"../../../sqlite/schema/documentPlacementIntentSchema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../../sqlite/sqlSchema\";\nimport {\n clearDocumentSyncFailure,\n deleteDocumentRecord,\n loadDocumentRecord,\n} from \"../records\";\nimport { deleteDocumentHistory } from \"../records/documentHistoryPersistence\";\nimport type { StoredDocumentRecord } from \"../types\";\nimport { deleteContainerDocumentTombstoneHoldsForDocuments } from \"./containerDocumentTombstoneHolds\";\nimport { sameCanonicalDocumentSecurityIdentity } from \"./documentRecordIdentity\";\nimport { getDocumentScope } from \"./documentRows\";\nimport { queueDocumentAttachmentBlobReclaims } from \"./orphanSideRows\";\n\nasync function deleteStoredDocumentRows(input: {\n existingDocumentId: string | null;\n localId: string;\n lockedExecSql: ExecSql;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const { existingDocumentId, localId, lockedExecSql, tx } = input;\n await queueDocumentAttachmentBlobReclaims({\n localWhere: eq(documentAttachmentBlobProjection.localId, localId),\n pendingWhere: eq(documentPendingAttachments.localId, localId),\n tx,\n });\n await tx\n .delete(documentProjection)\n .where(eq(documentProjection.localId, localId))\n .run();\n await tx\n .delete(documentProjectionText)\n .where(eq(documentProjectionText.localId, localId))\n .run();\n await tx\n .delete(documentPendingAttachments)\n .where(eq(documentPendingAttachments.localId, localId))\n .run();\n await tx\n .delete(documentAttachmentBlobProjection)\n .where(eq(documentAttachmentBlobProjection.localId, localId))\n .run();\n if (existingDocumentId) {\n await tx\n .delete(documentContainerProjection)\n .where(eq(documentContainerProjection.documentId, existingDocumentId))\n .run();\n await deleteContainerDocumentTombstoneHoldsForDocuments(tx, [\n existingDocumentId,\n ]);\n const placementIntent = and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n eq(localIntents.subjectId, existingDocumentId),\n );\n await tx\n .delete(documentIntentLinkTargets)\n .where(\n inArray(\n documentIntentLinkTargets.intentId,\n tx\n .select({ id: localIntents.id })\n .from(localIntents)\n .where(placementIntent),\n ),\n )\n .run();\n await tx.delete(localIntents).where(placementIntent).run();\n }\n const scope = getDocumentScope(localId);\n await deleteDocumentHistory(lockedExecSql, scope);\n await clearDocumentSyncFailure(lockedExecSql, scope);\n await deleteDocumentRecord(lockedExecSql, scope);\n}\n\nasync function ensureDocumentDeletionTables(execSql: ExecSql): Promise<void> {\n // Move intents live in the container-contents schema, which document-only\n // callers may not have initialized yet.\n await ensureSqlTables(execSql, documentPlacementIntentTables);\n}\n\nasync function hasAnotherDocumentAlias(input: {\n documentId: string;\n localId: string;\n tx: ClientSQLiteTransactionScope;\n}): Promise<boolean> {\n const rows = await input.tx\n .select({ localId: documents.localId })\n .from(documents)\n .where(\n and(\n eq(documents.appKind, DOCUMENTS_APP_KIND),\n eq(documents.documentId, input.documentId),\n ne(documents.localId, input.localId),\n ),\n )\n .limit(1);\n return rows.length > 0;\n}\n\nexport async function deleteStoredDocument(\n execSql: ExecSql,\n localId: string,\n): Promise<void> {\n await runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureDocumentDeletionTables(lockedExecSql);\n await getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const existingDocument = await loadDocumentRecord(lockedExecSql, {\n appKind: DOCUMENTS_APP_KIND,\n localId,\n });\n await deleteStoredDocumentRows({\n existingDocumentId: existingDocument?.documentId ?? null,\n localId,\n lockedExecSql,\n tx,\n });\n },\n { behavior: \"immediate\" },\n );\n });\n}\n\nexport async function deleteStoredDocumentIfMatches(\n execSql: ExecSql,\n expectedRecord: StoredDocumentRecord,\n deleteClientProjection: (transactionExecSql: ExecSql) => Promise<void>,\n): Promise<boolean> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureDocumentDeletionTables(lockedExecSql);\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const existingDocument = await loadDocumentRecord(lockedExecSql, {\n appKind: DOCUMENTS_APP_KIND,\n localId: expectedRecord.id,\n });\n if (\n !existingDocument ||\n !sameCanonicalDocumentSecurityIdentity(\n existingDocument,\n expectedRecord,\n )\n ) {\n return false;\n }\n const [placement] = await tx\n .select({ containerId: documentProjection.containerId })\n .from(documentProjection)\n .where(eq(documentProjection.localId, expectedRecord.id))\n .limit(1);\n if ((placement?.containerId ?? null) !== expectedRecord.containerId)\n return false;\n if (\n existingDocument.documentId &&\n (await hasAnotherDocumentAlias({\n documentId: existingDocument.documentId,\n localId: expectedRecord.id,\n tx,\n }))\n ) {\n return false;\n }\n await deleteStoredDocumentRows({\n existingDocumentId: existingDocument.documentId ?? null,\n localId: expectedRecord.id,\n lockedExecSql,\n tx,\n });\n await deleteClientProjection(lockedExecSql);\n return true;\n },\n { behavior: \"immediate\" },\n );\n });\n}\n\nexport async function deleteStoredDocumentSideRowsIfAbsent(\n execSql: ExecSql,\n localId: string,\n expectedDocumentId: string | null,\n deleteClientProjection: (transactionExecSql: ExecSql) => Promise<void>,\n): Promise<boolean> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureDocumentDeletionTables(lockedExecSql);\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const existingDocument = await loadDocumentRecord(lockedExecSql, {\n appKind: DOCUMENTS_APP_KIND,\n localId,\n });\n if (existingDocument) return false;\n if (\n expectedDocumentId &&\n (await hasAnotherDocumentAlias({\n documentId: expectedDocumentId,\n localId,\n tx,\n }))\n ) {\n return false;\n }\n\n await deleteStoredDocumentRows({\n existingDocumentId: expectedDocumentId,\n localId,\n lockedExecSql,\n tx,\n });\n await deleteClientProjection(lockedExecSql);\n return true;\n },\n { behavior: \"immediate\" },\n );\n });\n}\n"]}
@@ -3,12 +3,12 @@ import { createDocument, importSnapshot, updateMatchesDocumentHistory, } from ".
3
3
  import { and, eq } from "drizzle-orm";
4
4
  import { DOCUMENTS_APP_KIND } from "../../../documents/documentConstants.js";
5
5
  import { documentSyncPullContinuationsEqual } from "../../../documents/shared/sync/pullContinuation.js";
6
- import { appendDocumentHistoryUpdates, replaceDocumentHistoryCheckpoint, } from "../../../sqlite/documentPersistence/documentHistoryPersistence.js";
7
- import { insertDocumentPendingUpdateWithHistoryInTransaction, settleQueuedDocumentUpdatesInTransaction, } from "../../../sqlite/documentPersistence/documentPendingUpdatePersistence.js";
8
- import { clearDocumentSyncFailureInTransaction } from "../../../sqlite/documentPersistence/documentSyncFailurePersistence.js";
9
6
  import { documentHistoryUpdates } from "../../../sqlite/schema/index.js";
10
7
  import { getClientSQLitePersistenceRuntime, } from "../../../sqlite/sqlitePersistenceRuntime.js";
11
8
  import { runSerializedSqlMutation } from "../../../sqlite/sqlSchema.js";
9
+ import { appendDocumentHistoryUpdates, replaceDocumentHistoryCheckpoint, } from "../records/documentHistoryPersistence.js";
10
+ import { insertDocumentPendingUpdateWithHistoryInTransaction, settleQueuedDocumentUpdatesInTransaction, } from "../records/documentPendingUpdatePersistence.js";
11
+ import { clearDocumentSyncFailureInTransaction } from "../records/documentSyncFailurePersistence.js";
12
12
  import { applyStoredAttachmentRemoval, upsertStoredAttachmentStagingRows, } from "./attachmentStagingPersistence.js";
13
13
  import { sameDocumentSecurityIdentity, sameNullableDocumentValue, } from "./documentRecordIdentity.js";
14
14
  import { resolveDocumentSaveTimestamp, saveDocumentRows } from "./documentRows.js";
@@ -1 +1 @@
1
- {"version":3,"file":"documentMutationPersistence.js","sourceRoot":"","sources":["documentMutationPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { base64ToBytes } from \"@tearleads/encoding\";\nimport {\n createDocument,\n importSnapshot,\n updateMatchesDocumentHistory,\n} from \"@tearleads/loro\";\nimport { and, eq } from \"drizzle-orm\";\nimport { DOCUMENTS_APP_KIND } from \"../../../documents/documentConstants\";\nimport { documentSyncPullContinuationsEqual } from \"../../../documents/shared/sync/pullContinuation\";\nimport {\n appendDocumentHistoryUpdates,\n replaceDocumentHistoryCheckpoint,\n} from \"../../../sqlite/documentPersistence/documentHistoryPersistence\";\nimport {\n insertDocumentPendingUpdateWithHistoryInTransaction,\n settleQueuedDocumentUpdatesInTransaction,\n} from \"../../../sqlite/documentPersistence/documentPendingUpdatePersistence\";\nimport { clearDocumentSyncFailureInTransaction } from \"../../../sqlite/documentPersistence/documentSyncFailurePersistence\";\nimport { documentHistoryUpdates } from \"../../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport { runSerializedSqlMutation } from \"../../../sqlite/sqlSchema\";\nimport type { DocumentsPersistence, StoredDocumentRecord } from \"../types\";\nimport {\n applyStoredAttachmentRemoval,\n upsertStoredAttachmentStagingRows,\n} from \"./attachmentStagingPersistence\";\nimport {\n sameDocumentSecurityIdentity,\n sameNullableDocumentValue,\n} from \"./documentRecordIdentity\";\nimport { resolveDocumentSaveTimestamp, saveDocumentRows } from \"./documentRows\";\n\nfunction sameRecoveryGeneration(\n current: StoredDocumentRecord,\n expected: StoredDocumentRecord,\n): boolean {\n return (\n (current.recoveryGeneration ?? 0) === (expected.recoveryGeneration ?? 0)\n );\n}\n\nfunction sameExpectedDocumentRecord(\n current: StoredDocumentRecord,\n expected: StoredDocumentRecord,\n): boolean {\n return (\n sameDocumentSecurityIdentity(current, expected) &&\n sameRecoveryGeneration(current, expected) &&\n sameNullableDocumentValue(current.lastCommitLsn, expected.lastCommitLsn) &&\n current.snapshotEndVersion === expected.snapshotEndVersion &&\n sameNullableDocumentValue(\n current.pendingBaseVersion,\n expected.pendingBaseVersion,\n ) &&\n documentSyncPullContinuationsEqual(\n current.pullContinuation,\n expected.pullContinuation,\n ) &&\n Boolean(current.pullContinuationRecoveryRequired) ===\n Boolean(expected.pullContinuationRecoveryRequired) &&\n (current.documentKind ?? null) === (expected.documentKind ?? null) &&\n current.text === expected.text &&\n (current.title ?? null) === (expected.title ?? null)\n );\n}\n\nasync function deleteAcceptedPendingUpdates(\n tx: ClientSQLiteTransactionScope,\n localId: string,\n ids: readonly string[],\n): Promise<void> {\n await settleQueuedDocumentUpdatesInTransaction(\n tx,\n { appKind: DOCUMENTS_APP_KIND, localId },\n ids,\n );\n}\n\nasync function insertPendingUpdate(\n tx: ClientSQLiteTransactionScope,\n documentId: string,\n pendingUpdate: Parameters<\n DocumentsPersistence[\"commitDocumentMutation\"]\n >[1][\"pendingUpdate\"],\n): Promise<void> {\n if (!pendingUpdate) return;\n await insertDocumentPendingUpdateWithHistoryInTransaction({\n createdAt: new Date().toISOString(),\n pendingUpdate,\n scope: { appKind: DOCUMENTS_APP_KIND, localId: documentId },\n tx,\n });\n}\n\nasync function settleMutationConflict(input: {\n currentRecord: StoredDocumentRecord | null;\n mutation: Parameters<DocumentsPersistence[\"commitDocumentMutation\"]>[1];\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n if (\n input.currentRecord &&\n input.mutation.settleAcceptedPendingOnConflict &&\n sameDocumentSecurityIdentity(\n input.currentRecord,\n input.mutation.expectedRecord,\n ) &&\n sameRecoveryGeneration(input.currentRecord, input.mutation.expectedRecord)\n ) {\n await deleteAcceptedPendingUpdates(\n input.tx,\n input.mutation.document.id,\n input.mutation.acceptedPendingUpdateIds,\n );\n }\n}\n\nasync function appendMutationHistory(\n execSql: Parameters<DocumentsPersistence[\"commitDocumentMutation\"]>[0],\n input: Parameters<DocumentsPersistence[\"commitDocumentMutation\"]>[1],\n tx: ClientSQLiteTransactionScope,\n): Promise<void> {\n if (input.historyCheckpoint) {\n const coveredLocalState = input.historyCheckpoint.pruneCoveredLocalState\n ? await findCoveredRecoveryLocalState(\n tx,\n input.document.id,\n input.historyCheckpoint.snapshot,\n )\n : {\n checkpointIds: [],\n hasOrdinaryPendingUpdates: false,\n hasUnprunablePendingUpdates: false,\n hasUnverifiedHistoryTail: false,\n tailIds: [],\n };\n if (\n input.historyCheckpoint.pruneCoveredLocalState &&\n (coveredLocalState.hasOrdinaryPendingUpdates ||\n coveredLocalState.hasUnprunablePendingUpdates)\n ) {\n throw new Error(\n \"Document recovery found unproven pending updates before installation\",\n );\n }\n if (\n input.historyCheckpoint.pruneCoveredLocalState &&\n coveredLocalState.hasUnverifiedHistoryTail\n ) {\n throw new Error(\n \"Document recovery found unverified history tail before installation\",\n );\n }\n // A queued rotation baseline the recovery snapshot covers is unqueued\n // first so the replacement below can delete it with the covered tail.\n await deleteAcceptedPendingUpdates(\n tx,\n input.document.id,\n coveredLocalState.checkpointIds,\n );\n const checkpointReplaced = await replaceDocumentHistoryCheckpoint(\n execSql,\n { appKind: DOCUMENTS_APP_KIND, localId: input.document.id },\n {\n ...input.historyCheckpoint,\n coveredTailIds: input.historyCheckpoint.pruneCoveredLocalState\n ? coveredLocalState.tailIds\n : input.historyCheckpoint.coveredTailIds,\n },\n );\n if (input.historyCheckpoint.pruneCoveredLocalState && !checkpointReplaced) {\n throw new Error(\n \"Document recovery checkpoint was superseded before installation\",\n );\n }\n }\n if (input.historyUpdates && input.historyUpdates.length > 0) {\n await appendDocumentHistoryUpdates(\n execSql,\n { appKind: DOCUMENTS_APP_KIND, localId: input.document.id },\n input.historyUpdates,\n input.historyUpdateOrigin ?? \"local\",\n );\n }\n}\n\nasync function findCoveredRecoveryLocalState(\n tx: ClientSQLiteTransactionScope,\n localId: string,\n recoverySnapshot: string,\n): Promise<{\n checkpointIds: string[];\n hasOrdinaryPendingUpdates: boolean;\n hasUnprunablePendingUpdates: boolean;\n hasUnverifiedHistoryTail: boolean;\n tailIds: string[];\n}> {\n const recoveredDocument = await createDocument(\n `recovery-tail-verification:${localId}`,\n );\n try {\n importSnapshot(recoveredDocument, base64ToBytes(recoverySnapshot));\n const rows = await tx\n .select({\n id: documentHistoryUpdates.id,\n origin: documentHistoryUpdates.origin,\n sourceVersionVector: documentHistoryUpdates.sourceVersionVector,\n updateData: documentHistoryUpdates.updateData,\n updateId: documentHistoryUpdates.updateId,\n })\n .from(documentHistoryUpdates)\n .where(\n and(\n eq(documentHistoryUpdates.appKind, DOCUMENTS_APP_KIND),\n eq(documentHistoryUpdates.localId, localId),\n ),\n );\n const pendingUpdates = rows.filter((row) => row.updateId !== null);\n const tail = rows.filter((row) => row.origin !== \"outbox\");\n const checkpointUpdateData = new Set(\n pendingUpdates.flatMap((row) =>\n row.sourceVersionVector === null ? [] : [row.updateData],\n ),\n );\n let hasUnverifiedHistoryTail = false;\n const tailIds = tail.flatMap((row) => {\n if (row.id === null) {\n // An unverifiable row identity cannot participate in the guarded\n // delete, so accepting it would leave a stale recovery redirect.\n hasUnverifiedHistoryTail = true;\n return [];\n }\n if (checkpointUpdateData.has(row.updateData)) return [row.id];\n try {\n if (\n updateMatchesDocumentHistory(\n recoveredDocument,\n base64ToBytes(row.updateData),\n )\n ) {\n return [row.id];\n }\n } catch {\n // Fall through: malformed history is not proven by the rebuild.\n }\n hasUnverifiedHistoryTail = true;\n return [];\n });\n return {\n checkpointIds: pendingUpdates.flatMap((row) =>\n row.updateId !== null && row.sourceVersionVector !== null\n ? [row.updateId]\n : [],\n ),\n hasOrdinaryPendingUpdates: pendingUpdates.some(\n (row) => row.sourceVersionVector === null,\n ),\n hasUnprunablePendingUpdates: pendingUpdates.some(\n (row) => row.id === null,\n ),\n hasUnverifiedHistoryTail,\n tailIds,\n };\n } finally {\n recoveredDocument.free();\n }\n}\n\ntype CommitDocumentMutationInput = Parameters<\n DocumentsPersistence[\"commitDocumentMutation\"]\n>[1];\ntype SaveClientProjection = Parameters<\n DocumentsPersistence[\"commitDocumentMutation\"]\n>[2];\n\nasync function applyStoredDocumentMutation(input: {\n lockedExecSql: Parameters<DocumentsPersistence[\"commitDocumentMutation\"]>[0];\n mutation: CommitDocumentMutationInput;\n saveClientProjection: SaveClientProjection;\n tx: ClientSQLiteTransactionScope;\n loadDocument: DocumentsPersistence[\"loadDocument\"];\n}) {\n const { lockedExecSql, mutation, tx } = input;\n const currentRecord = await input.loadDocument(\n lockedExecSql,\n mutation.document.id,\n );\n if (mutation.stillCurrent && !mutation.stillCurrent()) {\n return { committed: false as const, currentRecord };\n }\n if (\n !currentRecord ||\n !sameExpectedDocumentRecord(currentRecord, mutation.expectedRecord)\n ) {\n await settleMutationConflict({ currentRecord, mutation, tx });\n return { committed: false as const, currentRecord };\n }\n\n if (mutation.attachmentRemoval) {\n await applyStoredAttachmentRemoval({\n localId: mutation.document.id,\n removal: mutation.attachmentRemoval,\n tx,\n });\n }\n if (mutation.attachmentStaging) {\n await upsertStoredAttachmentStagingRows({\n createdAt: new Date().toISOString(),\n localId: mutation.document.id,\n staging: mutation.attachmentStaging,\n tx,\n });\n }\n await appendMutationHistory(lockedExecSql, mutation, tx);\n await insertPendingUpdate(tx, mutation.document.id, mutation.pendingUpdate);\n await deleteAcceptedPendingUpdates(\n tx,\n mutation.document.id,\n mutation.acceptedPendingUpdateIds,\n );\n if (mutation.clearSyncFailure) {\n await clearDocumentSyncFailureInTransaction(tx, {\n appKind: DOCUMENTS_APP_KIND,\n localId: mutation.document.id,\n });\n }\n const updatedAt = await resolveDocumentSaveTimestamp({\n document: mutation.document,\n options:\n mutation.updatedAt === undefined\n ? undefined\n : { updatedAt: mutation.updatedAt },\n tx,\n });\n if (\n !(await saveDocumentRows({ document: mutation.document, tx, updatedAt }))\n ) {\n throw new Error(\n \"Document recovery generation changed before mutation save\",\n );\n }\n await input.saveClientProjection(lockedExecSql, updatedAt);\n return { committed: true as const, updatedAt };\n}\n\nasync function runStoredDocumentMutationTransaction(input: {\n lockedExecSql: Parameters<DocumentsPersistence[\"commitDocumentMutation\"]>[0];\n mutation: CommitDocumentMutationInput;\n saveClientProjection: SaveClientProjection;\n loadDocument: DocumentsPersistence[\"loadDocument\"];\n}) {\n const outcome = await getClientSQLitePersistenceRuntime(\n input.lockedExecSql,\n ).guardedTransaction(\n (tx) => applyStoredDocumentMutation({ ...input, tx }),\n () => !input.mutation.stillCurrent || input.mutation.stillCurrent(),\n { behavior: \"immediate\" },\n );\n if (outcome.committed && outcome.result) {\n return outcome.result;\n }\n return {\n committed: false as const,\n currentRecord: await input.loadDocument(\n input.lockedExecSql,\n input.mutation.document.id,\n ),\n };\n}\n\nexport async function commitStoredDocumentMutation(\n execSql: Parameters<DocumentsPersistence[\"commitDocumentMutation\"]>[0],\n input: CommitDocumentMutationInput,\n saveClientProjection: SaveClientProjection,\n loadDocument: DocumentsPersistence[\"loadDocument\"],\n) {\n return runSerializedSqlMutation(execSql, (lockedExecSql) =>\n runStoredDocumentMutationTransaction({\n lockedExecSql,\n loadDocument,\n mutation: input,\n saveClientProjection,\n }),\n );\n}\n\nexport async function settleStoredDocumentPendingUpdates(\n execSql: Parameters<DocumentsPersistence[\"settleAcceptedPendingUpdates\"]>[0],\n input: Parameters<DocumentsPersistence[\"settleAcceptedPendingUpdates\"]>[1],\n loadDocument: DocumentsPersistence[\"loadDocument\"],\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) =>\n getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const currentRecord = await loadDocument(\n lockedExecSql,\n input.expectedRecord.id,\n );\n if (\n currentRecord &&\n sameDocumentSecurityIdentity(currentRecord, input.expectedRecord) &&\n sameRecoveryGeneration(currentRecord, input.expectedRecord)\n ) {\n await deleteAcceptedPendingUpdates(\n tx,\n input.expectedRecord.id,\n input.pendingUpdateIds,\n );\n }\n return currentRecord;\n },\n { behavior: \"immediate\" },\n ),\n );\n}\n"]}
1
+ {"version":3,"file":"documentMutationPersistence.js","sourceRoot":"","sources":["documentMutationPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EACL,cAAc,EACd,cAAc,EACd,4BAA4B,GAC7B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,EAAE,kCAAkC,EAAE,MAAM,iDAAiD,CAAC;AACrG,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAEL,iCAAiC,GAClC,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EACL,4BAA4B,EAC5B,gCAAgC,GACjC,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,mDAAmD,EACnD,wCAAwC,GACzC,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAAE,qCAAqC,EAAE,MAAM,2CAA2C,CAAC;AAElG,OAAO,EACL,4BAA4B,EAC5B,iCAAiC,GAClC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,4BAA4B,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEhF,SAAS,sBAAsB,CAC7B,OAA6B,EAC7B,QAA8B;IAE9B,OAAO,CACL,CAAC,OAAO,CAAC,kBAAkB,IAAI,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,kBAAkB,IAAI,CAAC,CAAC,CACzE,CAAC;AACJ,CAAC;AAED,SAAS,0BAA0B,CACjC,OAA6B,EAC7B,QAA8B;IAE9B,OAAO,CACL,4BAA4B,CAAC,OAAO,EAAE,QAAQ,CAAC;QAC/C,sBAAsB,CAAC,OAAO,EAAE,QAAQ,CAAC;QACzC,yBAAyB,CAAC,OAAO,CAAC,aAAa,EAAE,QAAQ,CAAC,aAAa,CAAC;QACxE,OAAO,CAAC,kBAAkB,KAAK,QAAQ,CAAC,kBAAkB;QAC1D,yBAAyB,CACvB,OAAO,CAAC,kBAAkB,EAC1B,QAAQ,CAAC,kBAAkB,CAC5B;QACD,kCAAkC,CAChC,OAAO,CAAC,gBAAgB,EACxB,QAAQ,CAAC,gBAAgB,CAC1B;QACD,OAAO,CAAC,OAAO,CAAC,gCAAgC,CAAC;YAC/C,OAAO,CAAC,QAAQ,CAAC,gCAAgC,CAAC;QACpD,CAAC,OAAO,CAAC,YAAY,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,IAAI,IAAI,CAAC;QAClE,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI;QAC9B,CAAC,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,IAAI,IAAI,CAAC,CACrD,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,4BAA4B,CACzC,EAAgC,EAChC,OAAe,EACf,GAAsB;IAEtB,MAAM,wCAAwC,CAC5C,EAAE,EACF,EAAE,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,EACxC,GAAG,CACJ,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,mBAAmB,CAChC,EAAgC,EAChC,UAAkB,EAClB,aAEqB;IAErB,IAAI,CAAC,aAAa;QAAE,OAAO;IAC3B,MAAM,mDAAmD,CAAC;QACxD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,aAAa;QACb,KAAK,EAAE,EAAE,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,UAAU,EAAE;QAC3D,EAAE;KACH,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,sBAAsB,CAAC,KAIrC;IACC,IACE,KAAK,CAAC,aAAa;QACnB,KAAK,CAAC,QAAQ,CAAC,+BAA+B;QAC9C,4BAA4B,CAC1B,KAAK,CAAC,aAAa,EACnB,KAAK,CAAC,QAAQ,CAAC,cAAc,CAC9B;QACD,sBAAsB,CAAC,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,EAC1E,CAAC;QACD,MAAM,4BAA4B,CAChC,KAAK,CAAC,EAAE,EACR,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAC1B,KAAK,CAAC,QAAQ,CAAC,wBAAwB,CACxC,CAAC;IACJ,CAAC;AACH,CAAC;AAED,KAAK,UAAU,qBAAqB,CAClC,OAAsE,EACtE,KAAoE,EACpE,EAAgC;IAEhC,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAC5B,MAAM,iBAAiB,GAAG,KAAK,CAAC,iBAAiB,CAAC,sBAAsB;YACtE,CAAC,CAAC,MAAM,6BAA6B,CACjC,EAAE,EACF,KAAK,CAAC,QAAQ,CAAC,EAAE,EACjB,KAAK,CAAC,iBAAiB,CAAC,QAAQ,CACjC;YACH,CAAC,CAAC;gBACE,aAAa,EAAE,EAAE;gBACjB,yBAAyB,EAAE,KAAK;gBAChC,2BAA2B,EAAE,KAAK;gBAClC,wBAAwB,EAAE,KAAK;gBAC/B,OAAO,EAAE,EAAE;aACZ,CAAC;QACN,IACE,KAAK,CAAC,iBAAiB,CAAC,sBAAsB;YAC9C,CAAC,iBAAiB,CAAC,yBAAyB;gBAC1C,iBAAiB,CAAC,2BAA2B,CAAC,EAChD,CAAC;YACD,MAAM,IAAI,KAAK,CACb,sEAAsE,CACvE,CAAC;QACJ,CAAC;QACD,IACE,KAAK,CAAC,iBAAiB,CAAC,sBAAsB;YAC9C,iBAAiB,CAAC,wBAAwB,EAC1C,CAAC;YACD,MAAM,IAAI,KAAK,CACb,qEAAqE,CACtE,CAAC;QACJ,CAAC;QACD,sEAAsE;QACtE,sEAAsE;QACtE,MAAM,4BAA4B,CAChC,EAAE,EACF,KAAK,CAAC,QAAQ,CAAC,EAAE,EACjB,iBAAiB,CAAC,aAAa,CAChC,CAAC;QACF,MAAM,kBAAkB,GAAG,MAAM,gCAAgC,CAC/D,OAAO,EACP,EAAE,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,EAC3D;YACE,GAAG,KAAK,CAAC,iBAAiB;YAC1B,cAAc,EAAE,KAAK,CAAC,iBAAiB,CAAC,sBAAsB;gBAC5D,CAAC,CAAC,iBAAiB,CAAC,OAAO;gBAC3B,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,cAAc;SAC3C,CACF,CAAC;QACF,IAAI,KAAK,CAAC,iBAAiB,CAAC,sBAAsB,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1E,MAAM,IAAI,KAAK,CACb,iEAAiE,CAClE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,KAAK,CAAC,cAAc,IAAI,KAAK,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5D,MAAM,4BAA4B,CAChC,OAAO,EACP,EAAE,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,EAC3D,KAAK,CAAC,cAAc,EACpB,KAAK,CAAC,mBAAmB,IAAI,OAAO,CACrC,CAAC;IACJ,CAAC;AACH,CAAC;AAED,KAAK,UAAU,6BAA6B,CAC1C,EAAgC,EAChC,OAAe,EACf,gBAAwB;IAQxB,MAAM,iBAAiB,GAAG,MAAM,cAAc,CAC5C,8BAA8B,OAAO,EAAE,CACxC,CAAC;IACF,IAAI,CAAC;QACH,cAAc,CAAC,iBAAiB,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,MAAM,EAAE;aAClB,MAAM,CAAC;YACN,EAAE,EAAE,sBAAsB,CAAC,EAAE;YAC7B,MAAM,EAAE,sBAAsB,CAAC,MAAM;YACrC,mBAAmB,EAAE,sBAAsB,CAAC,mBAAmB;YAC/D,UAAU,EAAE,sBAAsB,CAAC,UAAU;YAC7C,QAAQ,EAAE,sBAAsB,CAAC,QAAQ;SAC1C,CAAC;aACD,IAAI,CAAC,sBAAsB,CAAC;aAC5B,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,sBAAsB,CAAC,OAAO,EAAE,kBAAkB,CAAC,EACtD,EAAE,CAAC,sBAAsB,CAAC,OAAO,EAAE,OAAO,CAAC,CAC5C,CACF,CAAC;QACJ,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC;QAC3D,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAClC,cAAc,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAC7B,GAAG,CAAC,mBAAmB,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CACzD,CACF,CAAC;QACF,IAAI,wBAAwB,GAAG,KAAK,CAAC;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;YACnC,IAAI,GAAG,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;gBACpB,iEAAiE;gBACjE,iEAAiE;gBACjE,wBAAwB,GAAG,IAAI,CAAC;gBAChC,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,IAAI,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC9D,IAAI,CAAC;gBACH,IACE,4BAA4B,CAC1B,iBAAiB,EACjB,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAC9B,EACD,CAAC;oBACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAClB,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,gEAAgE;YAClE,CAAC;YACD,wBAAwB,GAAG,IAAI,CAAC;YAChC,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;QACH,OAAO;YACL,aAAa,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAC5C,GAAG,CAAC,QAAQ,KAAK,IAAI,IAAI,GAAG,CAAC,mBAAmB,KAAK,IAAI;gBACvD,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAChB,CAAC,CAAC,EAAE,CACP;YACD,yBAAyB,EAAE,cAAc,CAAC,IAAI,CAC5C,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,mBAAmB,KAAK,IAAI,CAC1C;YACD,2BAA2B,EAAE,cAAc,CAAC,IAAI,CAC9C,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,IAAI,CACzB;YACD,wBAAwB;YACxB,OAAO;SACR,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,iBAAiB,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;AACH,CAAC;AASD,KAAK,UAAU,2BAA2B,CAAC,KAM1C;IACC,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC;IAC9C,MAAM,aAAa,GAAG,MAAM,KAAK,CAAC,YAAY,CAC5C,aAAa,EACb,QAAQ,CAAC,QAAQ,CAAC,EAAE,CACrB,CAAC;IACF,IAAI,QAAQ,CAAC,YAAY,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,CAAC;QACtD,OAAO,EAAE,SAAS,EAAE,KAAc,EAAE,aAAa,EAAE,CAAC;IACtD,CAAC;IACD,IACE,CAAC,aAAa;QACd,CAAC,0BAA0B,CAAC,aAAa,EAAE,QAAQ,CAAC,cAAc,CAAC,EACnE,CAAC;QACD,MAAM,sBAAsB,CAAC,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAC9D,OAAO,EAAE,SAAS,EAAE,KAAc,EAAE,aAAa,EAAE,CAAC;IACtD,CAAC;IAED,IAAI,QAAQ,CAAC,iBAAiB,EAAE,CAAC;QAC/B,MAAM,4BAA4B,CAAC;YACjC,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE;YAC7B,OAAO,EAAE,QAAQ,CAAC,iBAAiB;YACnC,EAAE;SACH,CAAC,CAAC;IACL,CAAC;IACD,IAAI,QAAQ,CAAC,iBAAiB,EAAE,CAAC;QAC/B,MAAM,iCAAiC,CAAC;YACtC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE;YAC7B,OAAO,EAAE,QAAQ,CAAC,iBAAiB;YACnC,EAAE;SACH,CAAC,CAAC;IACL,CAAC;IACD,MAAM,qBAAqB,CAAC,aAAa,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;IACzD,MAAM,mBAAmB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC;IAC5E,MAAM,4BAA4B,CAChC,EAAE,EACF,QAAQ,CAAC,QAAQ,CAAC,EAAE,EACpB,QAAQ,CAAC,wBAAwB,CAClC,CAAC;IACF,IAAI,QAAQ,CAAC,gBAAgB,EAAE,CAAC;QAC9B,MAAM,qCAAqC,CAAC,EAAE,EAAE;YAC9C,OAAO,EAAE,kBAAkB;YAC3B,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE;SAC9B,CAAC,CAAC;IACL,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,4BAA4B,CAAC;QACnD,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,OAAO,EACL,QAAQ,CAAC,SAAS,KAAK,SAAS;YAC9B,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE;QACvC,EAAE;KACH,CAAC,CAAC;IACH,IACE,CAAC,CAAC,MAAM,gBAAgB,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,EACzE,CAAC;QACD,MAAM,IAAI,KAAK,CACb,2DAA2D,CAC5D,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,CAAC,oBAAoB,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;IAC3D,OAAO,EAAE,SAAS,EAAE,IAAa,EAAE,SAAS,EAAE,CAAC;AACjD,CAAC;AAED,KAAK,UAAU,oCAAoC,CAAC,KAKnD;IACC,MAAM,OAAO,GAAG,MAAM,iCAAiC,CACrD,KAAK,CAAC,aAAa,CACpB,CAAC,kBAAkB,CAClB,CAAC,EAAE,EAAE,EAAE,CAAC,2BAA2B,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE,CAAC,EACrD,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,IAAI,KAAK,CAAC,QAAQ,CAAC,YAAY,EAAE,EACnE,EAAE,QAAQ,EAAE,WAAW,EAAE,CAC1B,CAAC;IACF,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACxC,OAAO,OAAO,CAAC,MAAM,CAAC;IACxB,CAAC;IACD,OAAO;QACL,SAAS,EAAE,KAAc;QACzB,aAAa,EAAE,MAAM,KAAK,CAAC,YAAY,CACrC,KAAK,CAAC,aAAa,EACnB,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAC3B;KACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,OAAsE,EACtE,KAAkC,EAClC,oBAA0C,EAC1C,YAAkD;IAElD,OAAO,wBAAwB,CAAC,OAAO,EAAE,CAAC,aAAa,EAAE,EAAE,CACzD,oCAAoC,CAAC;QACnC,aAAa;QACb,YAAY;QACZ,QAAQ,EAAE,KAAK;QACf,oBAAoB;KACrB,CAAC,CACH,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kCAAkC,CACtD,OAA4E,EAC5E,KAA0E,EAC1E,YAAkD;IAElD,OAAO,wBAAwB,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,CAC/D,iCAAiC,CAAC,aAAa,CAAC,CAAC,WAAW,CAC1D,KAAK,EAAE,EAAE,EAAE,EAAE;QACX,MAAM,aAAa,GAAG,MAAM,YAAY,CACtC,aAAa,EACb,KAAK,CAAC,cAAc,CAAC,EAAE,CACxB,CAAC;QACF,IACE,aAAa;YACb,4BAA4B,CAAC,aAAa,EAAE,KAAK,CAAC,cAAc,CAAC;YACjE,sBAAsB,CAAC,aAAa,EAAE,KAAK,CAAC,cAAc,CAAC,EAC3D,CAAC;YACD,MAAM,4BAA4B,CAChC,EAAE,EACF,KAAK,CAAC,cAAc,CAAC,EAAE,EACvB,KAAK,CAAC,gBAAgB,CACvB,CAAC;QACJ,CAAC;QACD,OAAO,aAAa,CAAC;IACvB,CAAC,EACD,EAAE,QAAQ,EAAE,WAAW,EAAE,CAC1B,CACF,CAAC;AACJ,CAAC","sourcesContent":["import { base64ToBytes } from \"@tearleads/encoding\";\nimport {\n createDocument,\n importSnapshot,\n updateMatchesDocumentHistory,\n} from \"@tearleads/loro\";\nimport { and, eq } from \"drizzle-orm\";\nimport { DOCUMENTS_APP_KIND } from \"../../../documents/documentConstants\";\nimport { documentSyncPullContinuationsEqual } from \"../../../documents/shared/sync/pullContinuation\";\nimport { documentHistoryUpdates } from \"../../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport { runSerializedSqlMutation } from \"../../../sqlite/sqlSchema\";\nimport {\n appendDocumentHistoryUpdates,\n replaceDocumentHistoryCheckpoint,\n} from \"../records/documentHistoryPersistence\";\nimport {\n insertDocumentPendingUpdateWithHistoryInTransaction,\n settleQueuedDocumentUpdatesInTransaction,\n} from \"../records/documentPendingUpdatePersistence\";\nimport { clearDocumentSyncFailureInTransaction } from \"../records/documentSyncFailurePersistence\";\nimport type { DocumentsPersistence, StoredDocumentRecord } from \"../types\";\nimport {\n applyStoredAttachmentRemoval,\n upsertStoredAttachmentStagingRows,\n} from \"./attachmentStagingPersistence\";\nimport {\n sameDocumentSecurityIdentity,\n sameNullableDocumentValue,\n} from \"./documentRecordIdentity\";\nimport { resolveDocumentSaveTimestamp, saveDocumentRows } from \"./documentRows\";\n\nfunction sameRecoveryGeneration(\n current: StoredDocumentRecord,\n expected: StoredDocumentRecord,\n): boolean {\n return (\n (current.recoveryGeneration ?? 0) === (expected.recoveryGeneration ?? 0)\n );\n}\n\nfunction sameExpectedDocumentRecord(\n current: StoredDocumentRecord,\n expected: StoredDocumentRecord,\n): boolean {\n return (\n sameDocumentSecurityIdentity(current, expected) &&\n sameRecoveryGeneration(current, expected) &&\n sameNullableDocumentValue(current.lastCommitLsn, expected.lastCommitLsn) &&\n current.snapshotEndVersion === expected.snapshotEndVersion &&\n sameNullableDocumentValue(\n current.pendingBaseVersion,\n expected.pendingBaseVersion,\n ) &&\n documentSyncPullContinuationsEqual(\n current.pullContinuation,\n expected.pullContinuation,\n ) &&\n Boolean(current.pullContinuationRecoveryRequired) ===\n Boolean(expected.pullContinuationRecoveryRequired) &&\n (current.documentKind ?? null) === (expected.documentKind ?? null) &&\n current.text === expected.text &&\n (current.title ?? null) === (expected.title ?? null)\n );\n}\n\nasync function deleteAcceptedPendingUpdates(\n tx: ClientSQLiteTransactionScope,\n localId: string,\n ids: readonly string[],\n): Promise<void> {\n await settleQueuedDocumentUpdatesInTransaction(\n tx,\n { appKind: DOCUMENTS_APP_KIND, localId },\n ids,\n );\n}\n\nasync function insertPendingUpdate(\n tx: ClientSQLiteTransactionScope,\n documentId: string,\n pendingUpdate: Parameters<\n DocumentsPersistence[\"commitDocumentMutation\"]\n >[1][\"pendingUpdate\"],\n): Promise<void> {\n if (!pendingUpdate) return;\n await insertDocumentPendingUpdateWithHistoryInTransaction({\n createdAt: new Date().toISOString(),\n pendingUpdate,\n scope: { appKind: DOCUMENTS_APP_KIND, localId: documentId },\n tx,\n });\n}\n\nasync function settleMutationConflict(input: {\n currentRecord: StoredDocumentRecord | null;\n mutation: Parameters<DocumentsPersistence[\"commitDocumentMutation\"]>[1];\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n if (\n input.currentRecord &&\n input.mutation.settleAcceptedPendingOnConflict &&\n sameDocumentSecurityIdentity(\n input.currentRecord,\n input.mutation.expectedRecord,\n ) &&\n sameRecoveryGeneration(input.currentRecord, input.mutation.expectedRecord)\n ) {\n await deleteAcceptedPendingUpdates(\n input.tx,\n input.mutation.document.id,\n input.mutation.acceptedPendingUpdateIds,\n );\n }\n}\n\nasync function appendMutationHistory(\n execSql: Parameters<DocumentsPersistence[\"commitDocumentMutation\"]>[0],\n input: Parameters<DocumentsPersistence[\"commitDocumentMutation\"]>[1],\n tx: ClientSQLiteTransactionScope,\n): Promise<void> {\n if (input.historyCheckpoint) {\n const coveredLocalState = input.historyCheckpoint.pruneCoveredLocalState\n ? await findCoveredRecoveryLocalState(\n tx,\n input.document.id,\n input.historyCheckpoint.snapshot,\n )\n : {\n checkpointIds: [],\n hasOrdinaryPendingUpdates: false,\n hasUnprunablePendingUpdates: false,\n hasUnverifiedHistoryTail: false,\n tailIds: [],\n };\n if (\n input.historyCheckpoint.pruneCoveredLocalState &&\n (coveredLocalState.hasOrdinaryPendingUpdates ||\n coveredLocalState.hasUnprunablePendingUpdates)\n ) {\n throw new Error(\n \"Document recovery found unproven pending updates before installation\",\n );\n }\n if (\n input.historyCheckpoint.pruneCoveredLocalState &&\n coveredLocalState.hasUnverifiedHistoryTail\n ) {\n throw new Error(\n \"Document recovery found unverified history tail before installation\",\n );\n }\n // A queued rotation baseline the recovery snapshot covers is unqueued\n // first so the replacement below can delete it with the covered tail.\n await deleteAcceptedPendingUpdates(\n tx,\n input.document.id,\n coveredLocalState.checkpointIds,\n );\n const checkpointReplaced = await replaceDocumentHistoryCheckpoint(\n execSql,\n { appKind: DOCUMENTS_APP_KIND, localId: input.document.id },\n {\n ...input.historyCheckpoint,\n coveredTailIds: input.historyCheckpoint.pruneCoveredLocalState\n ? coveredLocalState.tailIds\n : input.historyCheckpoint.coveredTailIds,\n },\n );\n if (input.historyCheckpoint.pruneCoveredLocalState && !checkpointReplaced) {\n throw new Error(\n \"Document recovery checkpoint was superseded before installation\",\n );\n }\n }\n if (input.historyUpdates && input.historyUpdates.length > 0) {\n await appendDocumentHistoryUpdates(\n execSql,\n { appKind: DOCUMENTS_APP_KIND, localId: input.document.id },\n input.historyUpdates,\n input.historyUpdateOrigin ?? \"local\",\n );\n }\n}\n\nasync function findCoveredRecoveryLocalState(\n tx: ClientSQLiteTransactionScope,\n localId: string,\n recoverySnapshot: string,\n): Promise<{\n checkpointIds: string[];\n hasOrdinaryPendingUpdates: boolean;\n hasUnprunablePendingUpdates: boolean;\n hasUnverifiedHistoryTail: boolean;\n tailIds: string[];\n}> {\n const recoveredDocument = await createDocument(\n `recovery-tail-verification:${localId}`,\n );\n try {\n importSnapshot(recoveredDocument, base64ToBytes(recoverySnapshot));\n const rows = await tx\n .select({\n id: documentHistoryUpdates.id,\n origin: documentHistoryUpdates.origin,\n sourceVersionVector: documentHistoryUpdates.sourceVersionVector,\n updateData: documentHistoryUpdates.updateData,\n updateId: documentHistoryUpdates.updateId,\n })\n .from(documentHistoryUpdates)\n .where(\n and(\n eq(documentHistoryUpdates.appKind, DOCUMENTS_APP_KIND),\n eq(documentHistoryUpdates.localId, localId),\n ),\n );\n const pendingUpdates = rows.filter((row) => row.updateId !== null);\n const tail = rows.filter((row) => row.origin !== \"outbox\");\n const checkpointUpdateData = new Set(\n pendingUpdates.flatMap((row) =>\n row.sourceVersionVector === null ? [] : [row.updateData],\n ),\n );\n let hasUnverifiedHistoryTail = false;\n const tailIds = tail.flatMap((row) => {\n if (row.id === null) {\n // An unverifiable row identity cannot participate in the guarded\n // delete, so accepting it would leave a stale recovery redirect.\n hasUnverifiedHistoryTail = true;\n return [];\n }\n if (checkpointUpdateData.has(row.updateData)) return [row.id];\n try {\n if (\n updateMatchesDocumentHistory(\n recoveredDocument,\n base64ToBytes(row.updateData),\n )\n ) {\n return [row.id];\n }\n } catch {\n // Fall through: malformed history is not proven by the rebuild.\n }\n hasUnverifiedHistoryTail = true;\n return [];\n });\n return {\n checkpointIds: pendingUpdates.flatMap((row) =>\n row.updateId !== null && row.sourceVersionVector !== null\n ? [row.updateId]\n : [],\n ),\n hasOrdinaryPendingUpdates: pendingUpdates.some(\n (row) => row.sourceVersionVector === null,\n ),\n hasUnprunablePendingUpdates: pendingUpdates.some(\n (row) => row.id === null,\n ),\n hasUnverifiedHistoryTail,\n tailIds,\n };\n } finally {\n recoveredDocument.free();\n }\n}\n\ntype CommitDocumentMutationInput = Parameters<\n DocumentsPersistence[\"commitDocumentMutation\"]\n>[1];\ntype SaveClientProjection = Parameters<\n DocumentsPersistence[\"commitDocumentMutation\"]\n>[2];\n\nasync function applyStoredDocumentMutation(input: {\n lockedExecSql: Parameters<DocumentsPersistence[\"commitDocumentMutation\"]>[0];\n mutation: CommitDocumentMutationInput;\n saveClientProjection: SaveClientProjection;\n tx: ClientSQLiteTransactionScope;\n loadDocument: DocumentsPersistence[\"loadDocument\"];\n}) {\n const { lockedExecSql, mutation, tx } = input;\n const currentRecord = await input.loadDocument(\n lockedExecSql,\n mutation.document.id,\n );\n if (mutation.stillCurrent && !mutation.stillCurrent()) {\n return { committed: false as const, currentRecord };\n }\n if (\n !currentRecord ||\n !sameExpectedDocumentRecord(currentRecord, mutation.expectedRecord)\n ) {\n await settleMutationConflict({ currentRecord, mutation, tx });\n return { committed: false as const, currentRecord };\n }\n\n if (mutation.attachmentRemoval) {\n await applyStoredAttachmentRemoval({\n localId: mutation.document.id,\n removal: mutation.attachmentRemoval,\n tx,\n });\n }\n if (mutation.attachmentStaging) {\n await upsertStoredAttachmentStagingRows({\n createdAt: new Date().toISOString(),\n localId: mutation.document.id,\n staging: mutation.attachmentStaging,\n tx,\n });\n }\n await appendMutationHistory(lockedExecSql, mutation, tx);\n await insertPendingUpdate(tx, mutation.document.id, mutation.pendingUpdate);\n await deleteAcceptedPendingUpdates(\n tx,\n mutation.document.id,\n mutation.acceptedPendingUpdateIds,\n );\n if (mutation.clearSyncFailure) {\n await clearDocumentSyncFailureInTransaction(tx, {\n appKind: DOCUMENTS_APP_KIND,\n localId: mutation.document.id,\n });\n }\n const updatedAt = await resolveDocumentSaveTimestamp({\n document: mutation.document,\n options:\n mutation.updatedAt === undefined\n ? undefined\n : { updatedAt: mutation.updatedAt },\n tx,\n });\n if (\n !(await saveDocumentRows({ document: mutation.document, tx, updatedAt }))\n ) {\n throw new Error(\n \"Document recovery generation changed before mutation save\",\n );\n }\n await input.saveClientProjection(lockedExecSql, updatedAt);\n return { committed: true as const, updatedAt };\n}\n\nasync function runStoredDocumentMutationTransaction(input: {\n lockedExecSql: Parameters<DocumentsPersistence[\"commitDocumentMutation\"]>[0];\n mutation: CommitDocumentMutationInput;\n saveClientProjection: SaveClientProjection;\n loadDocument: DocumentsPersistence[\"loadDocument\"];\n}) {\n const outcome = await getClientSQLitePersistenceRuntime(\n input.lockedExecSql,\n ).guardedTransaction(\n (tx) => applyStoredDocumentMutation({ ...input, tx }),\n () => !input.mutation.stillCurrent || input.mutation.stillCurrent(),\n { behavior: \"immediate\" },\n );\n if (outcome.committed && outcome.result) {\n return outcome.result;\n }\n return {\n committed: false as const,\n currentRecord: await input.loadDocument(\n input.lockedExecSql,\n input.mutation.document.id,\n ),\n };\n}\n\nexport async function commitStoredDocumentMutation(\n execSql: Parameters<DocumentsPersistence[\"commitDocumentMutation\"]>[0],\n input: CommitDocumentMutationInput,\n saveClientProjection: SaveClientProjection,\n loadDocument: DocumentsPersistence[\"loadDocument\"],\n) {\n return runSerializedSqlMutation(execSql, (lockedExecSql) =>\n runStoredDocumentMutationTransaction({\n lockedExecSql,\n loadDocument,\n mutation: input,\n saveClientProjection,\n }),\n );\n}\n\nexport async function settleStoredDocumentPendingUpdates(\n execSql: Parameters<DocumentsPersistence[\"settleAcceptedPendingUpdates\"]>[0],\n input: Parameters<DocumentsPersistence[\"settleAcceptedPendingUpdates\"]>[1],\n loadDocument: DocumentsPersistence[\"loadDocument\"],\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) =>\n getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const currentRecord = await loadDocument(\n lockedExecSql,\n input.expectedRecord.id,\n );\n if (\n currentRecord &&\n sameDocumentSecurityIdentity(currentRecord, input.expectedRecord) &&\n sameRecoveryGeneration(currentRecord, input.expectedRecord)\n ) {\n await deleteAcceptedPendingUpdates(\n tx,\n input.expectedRecord.id,\n input.pendingUpdateIds,\n );\n }\n return currentRecord;\n },\n { behavior: \"immediate\" },\n ),\n );\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import { DOCUMENTS_APP_KIND } from "../../../documents/documentConstants.js";
2
- import { invalidateDocumentSyncPullContinuation } from "../../../sqlite/documentPersistence/index.js";
3
2
  import { getClientSQLitePersistenceRuntime } from "../../../sqlite/sqlitePersistenceRuntime.js";
4
3
  import { runSerializedSqlMutation } from "../../../sqlite/sqlSchema.js";
4
+ import { invalidateDocumentSyncPullContinuation } from "../records/index.js";
5
5
  export async function invalidateStoredDocumentPullContinuation(execSql, input, loadDocument, loadHistoryRestoreState) {
6
6
  return runSerializedSqlMutation(execSql, async (lockedExecSql) => getClientSQLitePersistenceRuntime(lockedExecSql).transaction(async () => {
7
7
  const invalidated = await invalidateDocumentSyncPullContinuation(lockedExecSql, {
@@ -1 +1 @@
1
- {"version":3,"file":"documentPullContinuationPersistence.js","sourceRoot":"","sources":["documentPullContinuationPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,EAAE,sCAAsC,EAAE,MAAM,qCAAqC,CAAC;AAC7F,OAAO,EAAE,iCAAiC,EAAE,MAAM,0CAA0C,CAAC;AAC7F,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAGrE,MAAM,CAAC,KAAK,UAAU,wCAAwC,CAC5D,OAA0E,EAC1E,KAAwE,EACxE,YAAkD,EAClD,uBAAwE;IAExE,OAAO,wBAAwB,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,CAC/D,iCAAiC,CAAC,aAAa,CAAC,CAAC,WAAW,CAC1D,KAAK,IAAI,EAAE;QACT,MAAM,WAAW,GAAG,MAAM,sCAAsC,CAC9D,aAAa,EACb;YACE,GAAG,KAAK;YACR,OAAO,EAAE,kBAAkB;SAC5B,CACF,CAAC;QACF,IAAI,CAAC,WAAW;YAAE,OAAO,IAAI,CAAC;QAC9B,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,aAAa,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAChE,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,MAAM,mBAAmB,GAAG,MAAM,uBAAuB,CACvD,aAAa,EACb,KAAK,CAAC,OAAO,CACd,CAAC;QACF,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,CAAC;IACzC,CAAC,EACD,EAAE,QAAQ,EAAE,WAAW,EAAE,CAC1B,CACF,CAAC;AACJ,CAAC","sourcesContent":["import { DOCUMENTS_APP_KIND } from \"../../../documents/documentConstants\";\nimport { invalidateDocumentSyncPullContinuation } from \"../../../sqlite/documentPersistence\";\nimport { getClientSQLitePersistenceRuntime } from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport { runSerializedSqlMutation } from \"../../../sqlite/sqlSchema\";\nimport type { DocumentsPersistence } from \"../types\";\n\nexport async function invalidateStoredDocumentPullContinuation(\n execSql: Parameters<DocumentsPersistence[\"invalidatePullContinuation\"]>[0],\n input: Parameters<DocumentsPersistence[\"invalidatePullContinuation\"]>[1],\n loadDocument: DocumentsPersistence[\"loadDocument\"],\n loadHistoryRestoreState: DocumentsPersistence[\"loadHistoryRestoreState\"],\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) =>\n getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async () => {\n const invalidated = await invalidateDocumentSyncPullContinuation(\n lockedExecSql,\n {\n ...input,\n appKind: DOCUMENTS_APP_KIND,\n },\n );\n if (!invalidated) return null;\n const record = await loadDocument(lockedExecSql, input.localId);\n if (!record) return null;\n const historyRestoreState = await loadHistoryRestoreState(\n lockedExecSql,\n input.localId,\n );\n return { historyRestoreState, record };\n },\n { behavior: \"immediate\" },\n ),\n );\n}\n"]}
1
+ {"version":3,"file":"documentPullContinuationPersistence.js","sourceRoot":"","sources":["documentPullContinuationPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,EAAE,iCAAiC,EAAE,MAAM,0CAA0C,CAAC;AAC7F,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,sCAAsC,EAAE,MAAM,YAAY,CAAC;AAGpE,MAAM,CAAC,KAAK,UAAU,wCAAwC,CAC5D,OAA0E,EAC1E,KAAwE,EACxE,YAAkD,EAClD,uBAAwE;IAExE,OAAO,wBAAwB,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,CAC/D,iCAAiC,CAAC,aAAa,CAAC,CAAC,WAAW,CAC1D,KAAK,IAAI,EAAE;QACT,MAAM,WAAW,GAAG,MAAM,sCAAsC,CAC9D,aAAa,EACb;YACE,GAAG,KAAK;YACR,OAAO,EAAE,kBAAkB;SAC5B,CACF,CAAC;QACF,IAAI,CAAC,WAAW;YAAE,OAAO,IAAI,CAAC;QAC9B,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,aAAa,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAChE,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,MAAM,mBAAmB,GAAG,MAAM,uBAAuB,CACvD,aAAa,EACb,KAAK,CAAC,OAAO,CACd,CAAC;QACF,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,CAAC;IACzC,CAAC,EACD,EAAE,QAAQ,EAAE,WAAW,EAAE,CAC1B,CACF,CAAC;AACJ,CAAC","sourcesContent":["import { DOCUMENTS_APP_KIND } from \"../../../documents/documentConstants\";\nimport { getClientSQLitePersistenceRuntime } from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport { runSerializedSqlMutation } from \"../../../sqlite/sqlSchema\";\nimport { invalidateDocumentSyncPullContinuation } from \"../records\";\nimport type { DocumentsPersistence } from \"../types\";\n\nexport async function invalidateStoredDocumentPullContinuation(\n execSql: Parameters<DocumentsPersistence[\"invalidatePullContinuation\"]>[0],\n input: Parameters<DocumentsPersistence[\"invalidatePullContinuation\"]>[1],\n loadDocument: DocumentsPersistence[\"loadDocument\"],\n loadHistoryRestoreState: DocumentsPersistence[\"loadHistoryRestoreState\"],\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) =>\n getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async () => {\n const invalidated = await invalidateDocumentSyncPullContinuation(\n lockedExecSql,\n {\n ...input,\n appKind: DOCUMENTS_APP_KIND,\n },\n );\n if (!invalidated) return null;\n const record = await loadDocument(lockedExecSql, input.localId);\n if (!record) return null;\n const historyRestoreState = await loadHistoryRestoreState(\n lockedExecSql,\n input.localId,\n );\n return { historyRestoreState, record };\n },\n { behavior: \"immediate\" },\n ),\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"documentRecordIdentity.js","sourceRoot":"","sources":["documentRecordIdentity.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,yBAAyB,CACvC,IAA+B,EAC/B,KAAgC;IAEhC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,qCAAqC,CACnD,OAAuB,EACvB,QAAwB;IAExB,OAAO,CACL,OAAO,CAAC,EAAE,KAAK,QAAQ,CAAC,EAAE;QAC1B,OAAO,CAAC,UAAU,KAAK,QAAQ,CAAC,UAAU;QAC1C,yBAAyB,CACvB,OAAO,CAAC,kBAAkB,EAC1B,QAAQ,CAAC,kBAAkB,CAC5B;QACD,OAAO,CAAC,WAAW,KAAK,QAAQ,CAAC,WAAW;QAC5C,yBAAyB,CACvB,OAAO,CAAC,eAAe,EACvB,QAAQ,CAAC,eAAe,CACzB;QACD,CAAC,OAAO,CAAC,oBAAoB,IAAI,IAAI,CAAC;YACpC,CAAC,QAAQ,CAAC,oBAAoB,IAAI,IAAI,CAAC;QACzC,yBAAyB,CACvB,OAAO,CAAC,gBAAgB,EACxB,QAAQ,CAAC,gBAAgB,CAC1B;QACD,yBAAyB,CACvB,OAAO,CAAC,kBAAkB,EAC1B,QAAQ,CAAC,kBAAkB,CAC5B;QACD,yBAAyB,CACvB,OAAO,CAAC,sBAAsB,EAC9B,QAAQ,CAAC,sBAAsB,CAChC,CACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC1C,OAA6B,EAC7B,QAA8B;IAE9B,OAAO,CACL,qCAAqC,CAAC,OAAO,EAAE,QAAQ,CAAC;QACxD,OAAO,CAAC,WAAW,KAAK,QAAQ,CAAC,WAAW,CAC7C,CAAC;AACJ,CAAC","sourcesContent":["import type { DocumentRecord } from \"../../../sqlite/documentPersistence\";\nimport type { StoredDocumentRecord } from \"../types\";\n\nexport function sameNullableDocumentValue(\n left: string | null | undefined,\n right: string | null | undefined,\n): boolean {\n return (left ?? null) === (right ?? null);\n}\n\nexport function sameCanonicalDocumentSecurityIdentity(\n current: DocumentRecord,\n expected: DocumentRecord,\n): boolean {\n return (\n current.id === expected.id &&\n current.documentId === expected.documentId &&\n sameNullableDocumentValue(\n current.recoveryDocumentId,\n expected.recoveryDocumentId,\n ) &&\n current.accessEpoch === expected.accessEpoch &&\n sameNullableDocumentValue(\n current.accessStateHash,\n expected.accessStateHash,\n ) &&\n (current.effectiveAccessLevel ?? null) ===\n (expected.effectiveAccessLevel ?? null) &&\n sameNullableDocumentValue(\n current.contentKeyBundle,\n expected.contentKeyBundle,\n ) &&\n sameNullableDocumentValue(\n current.documentKekTargets,\n expected.documentKekTargets,\n ) &&\n sameNullableDocumentValue(\n current.documentManifestBundle,\n expected.documentManifestBundle,\n )\n );\n}\n\nexport function sameDocumentSecurityIdentity(\n current: StoredDocumentRecord,\n expected: StoredDocumentRecord,\n): boolean {\n return (\n sameCanonicalDocumentSecurityIdentity(current, expected) &&\n current.containerId === expected.containerId\n );\n}\n"]}
1
+ {"version":3,"file":"documentRecordIdentity.js","sourceRoot":"","sources":["documentRecordIdentity.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,yBAAyB,CACvC,IAA+B,EAC/B,KAAgC;IAEhC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,qCAAqC,CACnD,OAAuB,EACvB,QAAwB;IAExB,OAAO,CACL,OAAO,CAAC,EAAE,KAAK,QAAQ,CAAC,EAAE;QAC1B,OAAO,CAAC,UAAU,KAAK,QAAQ,CAAC,UAAU;QAC1C,yBAAyB,CACvB,OAAO,CAAC,kBAAkB,EAC1B,QAAQ,CAAC,kBAAkB,CAC5B;QACD,OAAO,CAAC,WAAW,KAAK,QAAQ,CAAC,WAAW;QAC5C,yBAAyB,CACvB,OAAO,CAAC,eAAe,EACvB,QAAQ,CAAC,eAAe,CACzB;QACD,CAAC,OAAO,CAAC,oBAAoB,IAAI,IAAI,CAAC;YACpC,CAAC,QAAQ,CAAC,oBAAoB,IAAI,IAAI,CAAC;QACzC,yBAAyB,CACvB,OAAO,CAAC,gBAAgB,EACxB,QAAQ,CAAC,gBAAgB,CAC1B;QACD,yBAAyB,CACvB,OAAO,CAAC,kBAAkB,EAC1B,QAAQ,CAAC,kBAAkB,CAC5B;QACD,yBAAyB,CACvB,OAAO,CAAC,sBAAsB,EAC9B,QAAQ,CAAC,sBAAsB,CAChC,CACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC1C,OAA6B,EAC7B,QAA8B;IAE9B,OAAO,CACL,qCAAqC,CAAC,OAAO,EAAE,QAAQ,CAAC;QACxD,OAAO,CAAC,WAAW,KAAK,QAAQ,CAAC,WAAW,CAC7C,CAAC;AACJ,CAAC","sourcesContent":["import type { DocumentRecord } from \"../records\";\nimport type { StoredDocumentRecord } from \"../types\";\n\nexport function sameNullableDocumentValue(\n left: string | null | undefined,\n right: string | null | undefined,\n): boolean {\n return (left ?? null) === (right ?? null);\n}\n\nexport function sameCanonicalDocumentSecurityIdentity(\n current: DocumentRecord,\n expected: DocumentRecord,\n): boolean {\n return (\n current.id === expected.id &&\n current.documentId === expected.documentId &&\n sameNullableDocumentValue(\n current.recoveryDocumentId,\n expected.recoveryDocumentId,\n ) &&\n current.accessEpoch === expected.accessEpoch &&\n sameNullableDocumentValue(\n current.accessStateHash,\n expected.accessStateHash,\n ) &&\n (current.effectiveAccessLevel ?? null) ===\n (expected.effectiveAccessLevel ?? null) &&\n sameNullableDocumentValue(\n current.contentKeyBundle,\n expected.contentKeyBundle,\n ) &&\n sameNullableDocumentValue(\n current.documentKekTargets,\n expected.documentKekTargets,\n ) &&\n sameNullableDocumentValue(\n current.documentManifestBundle,\n expected.documentManifestBundle,\n )\n );\n}\n\nexport function sameDocumentSecurityIdentity(\n current: StoredDocumentRecord,\n expected: StoredDocumentRecord,\n): boolean {\n return (\n sameCanonicalDocumentSecurityIdentity(current, expected) &&\n current.containerId === expected.containerId\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"documentRows.js","sourceRoot":"","sources":["documentRows.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, lte } from \"drizzle-orm\";\nimport {\n DEFAULT_DOCUMENT_KIND,\n DOCUMENTS_APP_KIND,\n} from \"../../../documents/documentConstants\";\nimport { deriveStoredDocumentTitle } from \"../../../documents/documentKinds\";\nimport { serializeDocumentSyncPullContinuation } from \"../../../documents/shared/sync/pullContinuation\";\nimport { normalizeEffectiveAccessLevel } from \"../../../keying/accessLevel\";\nimport type { DocumentScope } from \"../../../sqlite/documentPersistence\";\nimport {\n documentProjection,\n documentProjectionText,\n documents,\n} from \"../../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../../sqlite/sqlSchema\";\nimport type { StoredDocumentRecord } from \"../types\";\nimport {\n getProjectionDocumentKind,\n getProjectionText,\n getProjectionTitle,\n getProjectionUpdatedAt,\n} from \"./documentProjectionRows\";\n\nexport function getDocumentScope(localId: string): DocumentScope {\n return {\n appKind: DOCUMENTS_APP_KIND,\n localId,\n };\n}\n\nexport async function hasDocumentRow(\n execSql: ExecSql,\n localId: string,\n): Promise<boolean> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({ localId: documents.localId })\n .from(documents)\n .where(\n and(\n eq(documents.appKind, DOCUMENTS_APP_KIND),\n eq(documents.localId, localId),\n ),\n )\n .limit(1);\n return rows.length > 0;\n}\n\nfunction toDocumentRecordRow(input: {\n document: StoredDocumentRecord;\n updatedAt: string;\n}) {\n const { document, updatedAt } = input;\n return {\n appKind: DOCUMENTS_APP_KIND,\n localId: document.id,\n documentId: document.documentId,\n recoveryGeneration: document.recoveryGeneration ?? 0,\n recoveryDocumentId: document.documentId\n ? null\n : (document.recoveryDocumentId ?? null),\n snapshotEndVersion: document.snapshotEndVersion,\n accessEpoch: document.accessEpoch,\n accessStateHash: document.accessStateHash ?? null,\n effectiveAccessLevel: normalizeEffectiveAccessLevel(\n document.effectiveAccessLevel,\n ),\n lastCommitLsn: document.lastCommitLsn ?? null,\n documentManifestBundle: document.documentManifestBundle ?? null,\n contentKeyBundle: document.contentKeyBundle ?? null,\n documentKekTargets: document.documentKekTargets ?? null,\n // Only touch the outgoing-delta marker when the caller manages it. Callers\n // that leave it undefined (e.g. discovery upserts, registration bootstrap)\n // must not clobber a marker a device-first deferRemoteSync write persisted.\n ...(document.pendingBaseVersion === undefined\n ? {}\n : { pendingBaseVersion: document.pendingBaseVersion }),\n ...(document.pullContinuation === undefined\n ? {}\n : {\n pullContinuation: serializeDocumentSyncPullContinuation(\n document.pullContinuation,\n ),\n }),\n updatedAt,\n };\n}\n\nasync function saveDocumentProjectionRows(input: {\n document: StoredDocumentRecord;\n tx: ClientSQLiteTransactionScope;\n updatedAt: string;\n}): Promise<void> {\n const { document, tx, updatedAt } = input;\n const projectionRow = {\n localId: document.id,\n documentId: document.documentId,\n containerId: document.containerId,\n documentKind: document.documentKind ?? DEFAULT_DOCUMENT_KIND,\n title: document.title ?? deriveStoredDocumentTitle(document.text),\n updatedAt,\n };\n await tx\n .insert(documentProjection)\n .values(projectionRow)\n .onConflictDoUpdate({\n target: documentProjection.localId,\n set: projectionRow,\n })\n .run();\n\n const projectionTextRow = {\n localId: document.id,\n text: document.text,\n };\n await tx\n .insert(documentProjectionText)\n .values(projectionTextRow)\n .onConflictDoUpdate({\n target: documentProjectionText.localId,\n set: projectionTextRow,\n })\n .run();\n}\n\nexport async function createDocumentRowsIfAbsent(input: {\n document: StoredDocumentRecord;\n tx: ClientSQLiteTransactionScope;\n updatedAt: string;\n}): Promise<boolean> {\n const { document, tx, updatedAt } = input;\n const documentRow = toDocumentRecordRow({ document, updatedAt });\n const inserted = await tx\n .insert(documents)\n .values(documentRow)\n .onConflictDoNothing({\n target: [documents.appKind, documents.localId],\n })\n .returning({ localId: documents.localId });\n if (inserted.length === 0) {\n return false;\n }\n\n await saveDocumentProjectionRows(input);\n return true;\n}\n\nexport async function saveDocumentRows(input: {\n document: StoredDocumentRecord;\n tx: ClientSQLiteTransactionScope;\n updatedAt: string;\n}): Promise<boolean> {\n const { document, tx, updatedAt } = input;\n const documentRow = toDocumentRecordRow({ document, updatedAt });\n // The recovery-generation fence rides on the upsert: a stored row from a\n // newer recovery generation is left untouched and returns no row, so a\n // stale writer skips the projection writes without a separate read.\n const saved = await tx\n .insert(documents)\n .values(documentRow)\n .onConflictDoUpdate({\n target: [documents.appKind, documents.localId],\n set: documentRow,\n setWhere: lte(\n documents.recoveryGeneration,\n documentRow.recoveryGeneration,\n ),\n })\n .returning({ localId: documents.localId });\n if (saved.length === 0) {\n return false;\n }\n await saveDocumentProjectionRows(input);\n return true;\n}\n\nfunction didStoredDocumentContentChange(\n existing: Pick<\n StoredDocumentRecord,\n \"documentKind\" | \"snapshotEndVersion\" | \"text\" | \"title\"\n > | null,\n next: Pick<\n StoredDocumentRecord,\n \"documentKind\" | \"snapshotEndVersion\" | \"text\" | \"title\"\n >,\n): boolean {\n return (\n existing === null ||\n existing.documentKind !== (next.documentKind ?? DEFAULT_DOCUMENT_KIND) ||\n existing.snapshotEndVersion !== next.snapshotEndVersion ||\n existing.text !== next.text ||\n existing.title !== (next.title ?? deriveStoredDocumentTitle(next.text))\n );\n}\n\nexport async function resolveDocumentSaveTimestamp(input: {\n document: StoredDocumentRecord;\n options?: { updatedAt?: string } | undefined;\n tx: ClientSQLiteTransactionScope;\n}): Promise<string> {\n const { document, options, tx } = input;\n if (options?.updatedAt) {\n return options.updatedAt;\n }\n\n // One read for the stored row and its projection; the projection columns\n // are not nullable, so they are null exactly when the projection is absent.\n const [existing] = await tx\n .select({\n snapshotEndVersion: documents.snapshotEndVersion,\n documentKind: documentProjection.documentKind,\n text: documentProjectionText.text,\n title: documentProjection.title,\n updatedAt: documentProjection.updatedAt,\n })\n .from(documents)\n .leftJoin(\n documentProjection,\n eq(documentProjection.localId, documents.localId),\n )\n .leftJoin(\n documentProjectionText,\n eq(documentProjectionText.localId, documentProjection.localId),\n )\n .where(\n and(\n eq(documents.appKind, DOCUMENTS_APP_KIND),\n eq(documents.localId, document.id),\n ),\n )\n .limit(1);\n const existingProjection =\n existing?.documentKind != null &&\n existing.title != null &&\n existing.updatedAt != null\n ? {\n documentKind: existing.documentKind,\n text: existing.text,\n title: existing.title,\n updatedAt: existing.updatedAt,\n }\n : undefined;\n const now = new Date().toISOString();\n return didStoredDocumentContentChange(\n existing\n ? {\n documentKind: getProjectionDocumentKind(existingProjection),\n snapshotEndVersion: existing.snapshotEndVersion,\n text: getProjectionText(existingProjection),\n title: getProjectionTitle(existingProjection),\n }\n : null,\n document,\n )\n ? now\n : getProjectionUpdatedAt(existingProjection) || now;\n}\n"]}
1
+ {"version":3,"file":"documentRows.js","sourceRoot":"","sources":["documentRows.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, lte } from \"drizzle-orm\";\nimport {\n DEFAULT_DOCUMENT_KIND,\n DOCUMENTS_APP_KIND,\n} from \"../../../documents/documentConstants\";\nimport { deriveStoredDocumentTitle } from \"../../../documents/documentKinds\";\nimport { serializeDocumentSyncPullContinuation } from \"../../../documents/shared/sync/pullContinuation\";\nimport { normalizeEffectiveAccessLevel } from \"../../../keying/accessLevel\";\nimport {\n documentProjection,\n documentProjectionText,\n documents,\n} from \"../../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../../sqlite/sqlSchema\";\nimport type { DocumentScope } from \"../records\";\nimport type { StoredDocumentRecord } from \"../types\";\nimport {\n getProjectionDocumentKind,\n getProjectionText,\n getProjectionTitle,\n getProjectionUpdatedAt,\n} from \"./documentProjectionRows\";\n\nexport function getDocumentScope(localId: string): DocumentScope {\n return {\n appKind: DOCUMENTS_APP_KIND,\n localId,\n };\n}\n\nexport async function hasDocumentRow(\n execSql: ExecSql,\n localId: string,\n): Promise<boolean> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({ localId: documents.localId })\n .from(documents)\n .where(\n and(\n eq(documents.appKind, DOCUMENTS_APP_KIND),\n eq(documents.localId, localId),\n ),\n )\n .limit(1);\n return rows.length > 0;\n}\n\nfunction toDocumentRecordRow(input: {\n document: StoredDocumentRecord;\n updatedAt: string;\n}) {\n const { document, updatedAt } = input;\n return {\n appKind: DOCUMENTS_APP_KIND,\n localId: document.id,\n documentId: document.documentId,\n recoveryGeneration: document.recoveryGeneration ?? 0,\n recoveryDocumentId: document.documentId\n ? null\n : (document.recoveryDocumentId ?? null),\n snapshotEndVersion: document.snapshotEndVersion,\n accessEpoch: document.accessEpoch,\n accessStateHash: document.accessStateHash ?? null,\n effectiveAccessLevel: normalizeEffectiveAccessLevel(\n document.effectiveAccessLevel,\n ),\n lastCommitLsn: document.lastCommitLsn ?? null,\n documentManifestBundle: document.documentManifestBundle ?? null,\n contentKeyBundle: document.contentKeyBundle ?? null,\n documentKekTargets: document.documentKekTargets ?? null,\n // Only touch the outgoing-delta marker when the caller manages it. Callers\n // that leave it undefined (e.g. discovery upserts, registration bootstrap)\n // must not clobber a marker a device-first deferRemoteSync write persisted.\n ...(document.pendingBaseVersion === undefined\n ? {}\n : { pendingBaseVersion: document.pendingBaseVersion }),\n ...(document.pullContinuation === undefined\n ? {}\n : {\n pullContinuation: serializeDocumentSyncPullContinuation(\n document.pullContinuation,\n ),\n }),\n updatedAt,\n };\n}\n\nasync function saveDocumentProjectionRows(input: {\n document: StoredDocumentRecord;\n tx: ClientSQLiteTransactionScope;\n updatedAt: string;\n}): Promise<void> {\n const { document, tx, updatedAt } = input;\n const projectionRow = {\n localId: document.id,\n documentId: document.documentId,\n containerId: document.containerId,\n documentKind: document.documentKind ?? DEFAULT_DOCUMENT_KIND,\n title: document.title ?? deriveStoredDocumentTitle(document.text),\n updatedAt,\n };\n await tx\n .insert(documentProjection)\n .values(projectionRow)\n .onConflictDoUpdate({\n target: documentProjection.localId,\n set: projectionRow,\n })\n .run();\n\n const projectionTextRow = {\n localId: document.id,\n text: document.text,\n };\n await tx\n .insert(documentProjectionText)\n .values(projectionTextRow)\n .onConflictDoUpdate({\n target: documentProjectionText.localId,\n set: projectionTextRow,\n })\n .run();\n}\n\nexport async function createDocumentRowsIfAbsent(input: {\n document: StoredDocumentRecord;\n tx: ClientSQLiteTransactionScope;\n updatedAt: string;\n}): Promise<boolean> {\n const { document, tx, updatedAt } = input;\n const documentRow = toDocumentRecordRow({ document, updatedAt });\n const inserted = await tx\n .insert(documents)\n .values(documentRow)\n .onConflictDoNothing({\n target: [documents.appKind, documents.localId],\n })\n .returning({ localId: documents.localId });\n if (inserted.length === 0) {\n return false;\n }\n\n await saveDocumentProjectionRows(input);\n return true;\n}\n\nexport async function saveDocumentRows(input: {\n document: StoredDocumentRecord;\n tx: ClientSQLiteTransactionScope;\n updatedAt: string;\n}): Promise<boolean> {\n const { document, tx, updatedAt } = input;\n const documentRow = toDocumentRecordRow({ document, updatedAt });\n // The recovery-generation fence rides on the upsert: a stored row from a\n // newer recovery generation is left untouched and returns no row, so a\n // stale writer skips the projection writes without a separate read.\n const saved = await tx\n .insert(documents)\n .values(documentRow)\n .onConflictDoUpdate({\n target: [documents.appKind, documents.localId],\n set: documentRow,\n setWhere: lte(\n documents.recoveryGeneration,\n documentRow.recoveryGeneration,\n ),\n })\n .returning({ localId: documents.localId });\n if (saved.length === 0) {\n return false;\n }\n await saveDocumentProjectionRows(input);\n return true;\n}\n\nfunction didStoredDocumentContentChange(\n existing: Pick<\n StoredDocumentRecord,\n \"documentKind\" | \"snapshotEndVersion\" | \"text\" | \"title\"\n > | null,\n next: Pick<\n StoredDocumentRecord,\n \"documentKind\" | \"snapshotEndVersion\" | \"text\" | \"title\"\n >,\n): boolean {\n return (\n existing === null ||\n existing.documentKind !== (next.documentKind ?? DEFAULT_DOCUMENT_KIND) ||\n existing.snapshotEndVersion !== next.snapshotEndVersion ||\n existing.text !== next.text ||\n existing.title !== (next.title ?? deriveStoredDocumentTitle(next.text))\n );\n}\n\nexport async function resolveDocumentSaveTimestamp(input: {\n document: StoredDocumentRecord;\n options?: { updatedAt?: string } | undefined;\n tx: ClientSQLiteTransactionScope;\n}): Promise<string> {\n const { document, options, tx } = input;\n if (options?.updatedAt) {\n return options.updatedAt;\n }\n\n // One read for the stored row and its projection; the projection columns\n // are not nullable, so they are null exactly when the projection is absent.\n const [existing] = await tx\n .select({\n snapshotEndVersion: documents.snapshotEndVersion,\n documentKind: documentProjection.documentKind,\n text: documentProjectionText.text,\n title: documentProjection.title,\n updatedAt: documentProjection.updatedAt,\n })\n .from(documents)\n .leftJoin(\n documentProjection,\n eq(documentProjection.localId, documents.localId),\n )\n .leftJoin(\n documentProjectionText,\n eq(documentProjectionText.localId, documentProjection.localId),\n )\n .where(\n and(\n eq(documents.appKind, DOCUMENTS_APP_KIND),\n eq(documents.localId, document.id),\n ),\n )\n .limit(1);\n const existingProjection =\n existing?.documentKind != null &&\n existing.title != null &&\n existing.updatedAt != null\n ? {\n documentKind: existing.documentKind,\n text: existing.text,\n title: existing.title,\n updatedAt: existing.updatedAt,\n }\n : undefined;\n const now = new Date().toISOString();\n return didStoredDocumentContentChange(\n existing\n ? {\n documentKind: getProjectionDocumentKind(existingProjection),\n snapshotEndVersion: existing.snapshotEndVersion,\n text: getProjectionText(existingProjection),\n title: getProjectionTitle(existingProjection),\n }\n : null,\n document,\n )\n ? now\n : getProjectionUpdatedAt(existingProjection) || now;\n}\n"]}
@@ -1,6 +1,6 @@
1
- import { ensureDocumentProjectionTables, ensureDocumentTables, } from "../../../sqlite/documentPersistence/index.js";
2
1
  import { documentContainerProjectionTables, documentPlacementIntentTables, keyingCheckpointTables, } from "../../../sqlite/schema/index.js";
3
2
  import { ensureSqlTables, runOncePerConnection, runSerializedSqlMutation, } from "../../../sqlite/sqlSchema.js";
3
+ import { ensureDocumentProjectionTables, ensureDocumentTables, } from "../records/index.js";
4
4
  export async function ensureDocumentsSchema(execSql) {
5
5
  await runOncePerConnection(execSql, "ensure:documents", () => runSerializedSqlMutation(execSql, async (lockedExecSql) => {
6
6
  await ensureDocumentTables(lockedExecSql);
@@ -1 +1 @@
1
- {"version":3,"file":"ensureDocumentsSchema.js","sourceRoot":"","sources":["ensureDocumentsSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAC9B,oBAAoB,GACrB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,iCAAiC,EACjC,6BAA6B,EAC7B,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAEL,eAAe,EACf,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,OAAgB;IAC1D,MAAM,oBAAoB,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,EAAE,CAC3D,wBAAwB,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE;QACxD,MAAM,oBAAoB,CAAC,aAAa,CAAC,CAAC;QAC1C,MAAM,8BAA8B,CAAC,aAAa,CAAC,CAAC;QACpD,MAAM,eAAe,CAAC,aAAa,EAAE;YACnC,GAAG,iCAAiC;YACpC,GAAG,6BAA6B;YAChC,GAAG,sBAAsB;SAC1B,CAAC,CAAC;IACL,CAAC,CAAC,CACH,CAAC;AACJ,CAAC","sourcesContent":["import {\n ensureDocumentProjectionTables,\n ensureDocumentTables,\n} from \"../../../sqlite/documentPersistence\";\nimport {\n documentContainerProjectionTables,\n documentPlacementIntentTables,\n keyingCheckpointTables,\n} from \"../../../sqlite/schema\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runOncePerConnection,\n runSerializedSqlMutation,\n} from \"../../../sqlite/sqlSchema\";\n\nexport async function ensureDocumentsSchema(execSql: ExecSql): Promise<void> {\n await runOncePerConnection(execSql, \"ensure:documents\", () =>\n runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureDocumentTables(lockedExecSql);\n await ensureDocumentProjectionTables(lockedExecSql);\n await ensureSqlTables(lockedExecSql, [\n ...documentContainerProjectionTables,\n ...documentPlacementIntentTables,\n ...keyingCheckpointTables,\n ]);\n }),\n );\n}\n"]}
1
+ {"version":3,"file":"ensureDocumentsSchema.js","sourceRoot":"","sources":["ensureDocumentsSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iCAAiC,EACjC,6BAA6B,EAC7B,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAEL,eAAe,EACf,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,8BAA8B,EAC9B,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAEpB,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,OAAgB;IAC1D,MAAM,oBAAoB,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,EAAE,CAC3D,wBAAwB,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE;QACxD,MAAM,oBAAoB,CAAC,aAAa,CAAC,CAAC;QAC1C,MAAM,8BAA8B,CAAC,aAAa,CAAC,CAAC;QACpD,MAAM,eAAe,CAAC,aAAa,EAAE;YACnC,GAAG,iCAAiC;YACpC,GAAG,6BAA6B;YAChC,GAAG,sBAAsB;SAC1B,CAAC,CAAC;IACL,CAAC,CAAC,CACH,CAAC;AACJ,CAAC","sourcesContent":["import {\n documentContainerProjectionTables,\n documentPlacementIntentTables,\n keyingCheckpointTables,\n} from \"../../../sqlite/schema\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runOncePerConnection,\n runSerializedSqlMutation,\n} from \"../../../sqlite/sqlSchema\";\nimport {\n ensureDocumentProjectionTables,\n ensureDocumentTables,\n} from \"../records\";\n\nexport async function ensureDocumentsSchema(execSql: ExecSql): Promise<void> {\n await runOncePerConnection(execSql, \"ensure:documents\", () =>\n runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureDocumentTables(lockedExecSql);\n await ensureDocumentProjectionTables(lockedExecSql);\n await ensureSqlTables(lockedExecSql, [\n ...documentContainerProjectionTables,\n ...documentPlacementIntentTables,\n ...keyingCheckpointTables,\n ]);\n }),\n );\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import { and, asc, eq, inArray, isNotNull, lt, notExists, or, } from "drizzle-orm";
2
2
  import { DOCUMENTS_APP_KIND } from "../../../documents/documentConstants.js";
3
- import { documentOrphanBlobReclaims } from "../../../sqlite/documentPersistence/documentOrphanBlobReclaims.js";
4
3
  import { documentAttachmentBlobProjection, documentHistoryCheckpoints, documentHistoryUpdates, documentPendingAttachments, documents, } from "../../../sqlite/schema/index.js";
4
+ import { documentOrphanBlobReclaims } from "../../../sqlite/schema/documentOrphanBlobReclaimSchema.js";
5
5
  import { getClientSQLitePersistenceRuntime, } from "../../../sqlite/sqlitePersistenceRuntime.js";
6
6
  import { runSerializedSqlMutation, } from "../../../sqlite/sqlSchema.js";
7
7
  // Crash residue gets a full day to outlive slow devices and wall-clock skew;
@@ -1 +1 @@
1
- {"version":3,"file":"orphanSideRows.js","sourceRoot":"","sources":["orphanSideRows.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n and,\n asc,\n eq,\n inArray,\n isNotNull,\n lt,\n notExists,\n or,\n type SQL,\n} from \"drizzle-orm\";\nimport { DOCUMENTS_APP_KIND } from \"../../../documents/documentConstants\";\nimport { documentOrphanBlobReclaims } from \"../../../sqlite/documentPersistence/documentOrphanBlobReclaims\";\nimport {\n documentAttachmentBlobProjection,\n documentHistoryCheckpoints,\n documentHistoryUpdates,\n documentPendingAttachments,\n documents,\n} from \"../../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n runSerializedSqlMutation,\n} from \"../../../sqlite/sqlSchema\";\n\n// Crash residue gets a full day to outlive slow devices and wall-clock skew;\n// canonical-row absence is necessary but never sufficient for fresh writes.\nconst DOCUMENT_ORPHAN_SWEEP_MIN_AGE_MS = 24 * 60 * 60 * 1000;\nconst DOCUMENT_ORPHAN_SWEEP_BATCH_SIZE = 32;\n\ntype DocumentSideLocalIdColumn =\n | typeof documentAttachmentBlobProjection.localId\n | typeof documentHistoryCheckpoints.localId\n | typeof documentHistoryUpdates.localId\n | typeof documentPendingAttachments.localId;\n\nfunction requirePredicate(predicate: SQL | undefined): SQL {\n if (!predicate) {\n throw new Error(\"Document orphan cleanup requires a SQL predicate\");\n }\n return predicate;\n}\n\nfunction hasNoDocument(\n tx: ClientSQLiteTransactionScope,\n localId: DocumentSideLocalIdColumn,\n appKind: string | null = DOCUMENTS_APP_KIND,\n): SQL {\n return notExists(\n tx\n .select({ localId: documents.localId })\n .from(documents)\n .where(\n appKind === null\n ? eq(documents.localId, localId)\n : and(eq(documents.appKind, appKind), eq(documents.localId, localId)),\n ),\n );\n}\n\nexport async function queueDocumentAttachmentBlobReclaims(input: {\n localWhere: SQL;\n pendingWhere: SQL;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const { localWhere, pendingWhere, tx } = input;\n const [pendingAttachmentRows, localAttachmentRows] = await Promise.all([\n tx\n .select({ storageKey: documentPendingAttachments.storageKey })\n .from(documentPendingAttachments)\n .where(pendingWhere),\n tx\n .select({ storageKey: documentAttachmentBlobProjection.storageKey })\n .from(documentAttachmentBlobProjection)\n .where(localWhere),\n ]);\n const storageKeys = [\n ...new Set(\n [...pendingAttachmentRows, ...localAttachmentRows].map(\n (row) => row.storageKey,\n ),\n ),\n ];\n await queueDocumentAttachmentStorageKeys(tx, storageKeys);\n}\n\nexport async function queueDocumentAttachmentStorageKeys(\n tx: ClientSQLiteTransactionScope,\n storageKeys: ReadonlyArray<string>,\n): Promise<void> {\n if (storageKeys.length > 0) {\n await tx\n .insert(documentOrphanBlobReclaims)\n .values(storageKeys.map((storageKey) => ({ storageKey })))\n .onConflictDoNothing()\n .run();\n }\n}\n\ninterface AttachmentSweepCandidate {\n localId: string;\n slotId: string;\n storageKey: string;\n}\n\nfunction pendingAttachmentCandidatesWhere(\n candidates: ReadonlyArray<AttachmentSweepCandidate>,\n): SQL {\n return requirePredicate(\n or(\n ...candidates.map((candidate) =>\n and(\n eq(documentPendingAttachments.localId, candidate.localId),\n eq(documentPendingAttachments.slotId, candidate.slotId),\n ),\n ),\n ),\n );\n}\n\nfunction localAttachmentCandidatesWhere(\n candidates: ReadonlyArray<AttachmentSweepCandidate>,\n): SQL {\n return requirePredicate(\n or(\n ...candidates.map((candidate) =>\n and(\n eq(documentAttachmentBlobProjection.localId, candidate.localId),\n eq(documentAttachmentBlobProjection.slotId, candidate.slotId),\n ),\n ),\n ),\n );\n}\n\ninterface HistorySweepCandidates {\n checkpoints: ReadonlyArray<{ localId: string }>;\n updates: ReadonlyArray<{ id: string | null }>;\n}\n\ninterface AttachmentSweepCandidates {\n local: ReadonlyArray<AttachmentSweepCandidate>;\n pending: ReadonlyArray<AttachmentSweepCandidate>;\n}\n\nasync function selectHistorySweepCandidates(\n tx: ClientSQLiteTransactionScope,\n olderThan: string,\n): Promise<HistorySweepCandidates> {\n const checkpoints = await tx\n .select({ localId: documentHistoryCheckpoints.localId })\n .from(documentHistoryCheckpoints)\n .where(\n and(\n eq(documentHistoryCheckpoints.appKind, DOCUMENTS_APP_KIND),\n lt(documentHistoryCheckpoints.updatedAt, olderThan),\n hasNoDocument(tx, documentHistoryCheckpoints.localId),\n ),\n )\n .limit(DOCUMENT_ORPHAN_SWEEP_BATCH_SIZE);\n const updates = await tx\n .select({ id: documentHistoryUpdates.id })\n .from(documentHistoryUpdates)\n .where(\n and(\n eq(documentHistoryUpdates.appKind, DOCUMENTS_APP_KIND),\n isNotNull(documentHistoryUpdates.id),\n lt(documentHistoryUpdates.createdAt, olderThan),\n hasNoDocument(tx, documentHistoryUpdates.localId),\n ),\n )\n .limit(DOCUMENT_ORPHAN_SWEEP_BATCH_SIZE);\n return { checkpoints, updates };\n}\n\nasync function selectAttachmentSweepCandidates(\n tx: ClientSQLiteTransactionScope,\n olderThan: string,\n): Promise<AttachmentSweepCandidates> {\n const pending = await tx\n .select({\n localId: documentPendingAttachments.localId,\n slotId: documentPendingAttachments.slotId,\n storageKey: documentPendingAttachments.storageKey,\n })\n .from(documentPendingAttachments)\n .where(\n and(\n lt(documentPendingAttachments.createdAt, olderThan),\n hasNoDocument(tx, documentPendingAttachments.localId, null),\n ),\n )\n .limit(DOCUMENT_ORPHAN_SWEEP_BATCH_SIZE);\n const local = await tx\n .select({\n localId: documentAttachmentBlobProjection.localId,\n slotId: documentAttachmentBlobProjection.slotId,\n storageKey: documentAttachmentBlobProjection.storageKey,\n })\n .from(documentAttachmentBlobProjection)\n .where(\n and(\n lt(documentAttachmentBlobProjection.updatedAt, olderThan),\n hasNoDocument(tx, documentAttachmentBlobProjection.localId, null),\n ),\n )\n .limit(DOCUMENT_ORPHAN_SWEEP_BATCH_SIZE);\n return { local, pending };\n}\n\nasync function deleteHistorySweepCandidates(\n tx: ClientSQLiteTransactionScope,\n candidates: HistorySweepCandidates,\n): Promise<void> {\n if (candidates.checkpoints.length > 0) {\n await tx\n .delete(documentHistoryCheckpoints)\n .where(\n and(\n eq(documentHistoryCheckpoints.appKind, DOCUMENTS_APP_KIND),\n inArray(\n documentHistoryCheckpoints.localId,\n candidates.checkpoints.map((row) => row.localId),\n ),\n ),\n )\n .run();\n }\n if (candidates.updates.length > 0) {\n await tx\n .delete(documentHistoryUpdates)\n .where(\n inArray(\n documentHistoryUpdates.id,\n candidates.updates.flatMap((row) =>\n row.id === null ? [] : [row.id],\n ),\n ),\n )\n .run();\n }\n}\n\nasync function deleteAttachmentSweepCandidates(\n tx: ClientSQLiteTransactionScope,\n candidates: AttachmentSweepCandidates,\n): Promise<void> {\n await queueDocumentAttachmentStorageKeys(tx, [\n ...new Set(\n [...candidates.pending, ...candidates.local].map((row) => row.storageKey),\n ),\n ]);\n if (candidates.pending.length > 0) {\n await tx\n .delete(documentPendingAttachments)\n .where(pendingAttachmentCandidatesWhere(candidates.pending))\n .run();\n }\n if (candidates.local.length > 0) {\n await tx\n .delete(documentAttachmentBlobProjection)\n .where(localAttachmentCandidatesWhere(candidates.local))\n .run();\n }\n}\n\nfunction hasFullSweepBatch(\n history: HistorySweepCandidates,\n attachments: AttachmentSweepCandidates,\n): boolean {\n return [\n history.checkpoints,\n history.updates,\n attachments.pending,\n attachments.local,\n ].some((rows) => rows.length === DOCUMENT_ORPHAN_SWEEP_BATCH_SIZE);\n}\n\n/** Remove one bounded batch of aged side writes without a canonical row. */\nexport async function deleteOrphanedDocumentSideRows(\n execSql: ExecSql,\n options: { now?: Date | undefined } = {},\n): Promise<boolean> {\n const olderThan = new Date(\n (options.now?.getTime() ?? Date.now()) - DOCUMENT_ORPHAN_SWEEP_MIN_AGE_MS,\n ).toISOString();\n return runSerializedSqlMutation(execSql, async (lockedExecSql) =>\n getClientSQLitePersistenceRuntime(lockedExecSql).transaction(async (tx) => {\n const history = await selectHistorySweepCandidates(tx, olderThan);\n const attachments = await selectAttachmentSweepCandidates(tx, olderThan);\n await deleteHistorySweepCandidates(tx, history);\n await deleteAttachmentSweepCandidates(tx, attachments);\n return hasFullSweepBatch(history, attachments);\n }),\n );\n}\n\nexport async function listDocumentOrphanBlobReclaims(\n execSql: ExecSql,\n limit: number,\n): Promise<ReadonlyArray<string>> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({ storageKey: documentOrphanBlobReclaims.storageKey })\n .from(documentOrphanBlobReclaims)\n .orderBy(asc(documentOrphanBlobReclaims.storageKey))\n .limit(limit);\n return rows.map((row) => row.storageKey);\n}\n\nexport async function isDocumentBlobStorageKeyReferenced(\n execSql: ExecSql,\n storageKey: string,\n): Promise<boolean> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const [pendingRows, localRows] = await Promise.all([\n db\n .select({ storageKey: documentPendingAttachments.storageKey })\n .from(documentPendingAttachments)\n .where(eq(documentPendingAttachments.storageKey, storageKey))\n .limit(1),\n db\n .select({ storageKey: documentAttachmentBlobProjection.storageKey })\n .from(documentAttachmentBlobProjection)\n .where(eq(documentAttachmentBlobProjection.storageKey, storageKey))\n .limit(1),\n ]);\n return pendingRows.length > 0 || localRows.length > 0;\n}\n\nexport async function acknowledgeDocumentOrphanBlobReclaim(\n execSql: ExecSql,\n storageKey: string,\n): Promise<void> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n await db\n .delete(documentOrphanBlobReclaims)\n .where(eq(documentOrphanBlobReclaims.storageKey, storageKey))\n .run();\n}\n"]}
1
+ {"version":3,"file":"orphanSideRows.js","sourceRoot":"","sources":["orphanSideRows.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n and,\n asc,\n eq,\n inArray,\n isNotNull,\n lt,\n notExists,\n or,\n type SQL,\n} from \"drizzle-orm\";\nimport { DOCUMENTS_APP_KIND } from \"../../../documents/documentConstants\";\nimport {\n documentAttachmentBlobProjection,\n documentHistoryCheckpoints,\n documentHistoryUpdates,\n documentPendingAttachments,\n documents,\n} from \"../../../sqlite/schema\";\nimport { documentOrphanBlobReclaims } from \"../../../sqlite/schema/documentOrphanBlobReclaimSchema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n runSerializedSqlMutation,\n} from \"../../../sqlite/sqlSchema\";\n\n// Crash residue gets a full day to outlive slow devices and wall-clock skew;\n// canonical-row absence is necessary but never sufficient for fresh writes.\nconst DOCUMENT_ORPHAN_SWEEP_MIN_AGE_MS = 24 * 60 * 60 * 1000;\nconst DOCUMENT_ORPHAN_SWEEP_BATCH_SIZE = 32;\n\ntype DocumentSideLocalIdColumn =\n | typeof documentAttachmentBlobProjection.localId\n | typeof documentHistoryCheckpoints.localId\n | typeof documentHistoryUpdates.localId\n | typeof documentPendingAttachments.localId;\n\nfunction requirePredicate(predicate: SQL | undefined): SQL {\n if (!predicate) {\n throw new Error(\"Document orphan cleanup requires a SQL predicate\");\n }\n return predicate;\n}\n\nfunction hasNoDocument(\n tx: ClientSQLiteTransactionScope,\n localId: DocumentSideLocalIdColumn,\n appKind: string | null = DOCUMENTS_APP_KIND,\n): SQL {\n return notExists(\n tx\n .select({ localId: documents.localId })\n .from(documents)\n .where(\n appKind === null\n ? eq(documents.localId, localId)\n : and(eq(documents.appKind, appKind), eq(documents.localId, localId)),\n ),\n );\n}\n\nexport async function queueDocumentAttachmentBlobReclaims(input: {\n localWhere: SQL;\n pendingWhere: SQL;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const { localWhere, pendingWhere, tx } = input;\n const [pendingAttachmentRows, localAttachmentRows] = await Promise.all([\n tx\n .select({ storageKey: documentPendingAttachments.storageKey })\n .from(documentPendingAttachments)\n .where(pendingWhere),\n tx\n .select({ storageKey: documentAttachmentBlobProjection.storageKey })\n .from(documentAttachmentBlobProjection)\n .where(localWhere),\n ]);\n const storageKeys = [\n ...new Set(\n [...pendingAttachmentRows, ...localAttachmentRows].map(\n (row) => row.storageKey,\n ),\n ),\n ];\n await queueDocumentAttachmentStorageKeys(tx, storageKeys);\n}\n\nexport async function queueDocumentAttachmentStorageKeys(\n tx: ClientSQLiteTransactionScope,\n storageKeys: ReadonlyArray<string>,\n): Promise<void> {\n if (storageKeys.length > 0) {\n await tx\n .insert(documentOrphanBlobReclaims)\n .values(storageKeys.map((storageKey) => ({ storageKey })))\n .onConflictDoNothing()\n .run();\n }\n}\n\ninterface AttachmentSweepCandidate {\n localId: string;\n slotId: string;\n storageKey: string;\n}\n\nfunction pendingAttachmentCandidatesWhere(\n candidates: ReadonlyArray<AttachmentSweepCandidate>,\n): SQL {\n return requirePredicate(\n or(\n ...candidates.map((candidate) =>\n and(\n eq(documentPendingAttachments.localId, candidate.localId),\n eq(documentPendingAttachments.slotId, candidate.slotId),\n ),\n ),\n ),\n );\n}\n\nfunction localAttachmentCandidatesWhere(\n candidates: ReadonlyArray<AttachmentSweepCandidate>,\n): SQL {\n return requirePredicate(\n or(\n ...candidates.map((candidate) =>\n and(\n eq(documentAttachmentBlobProjection.localId, candidate.localId),\n eq(documentAttachmentBlobProjection.slotId, candidate.slotId),\n ),\n ),\n ),\n );\n}\n\ninterface HistorySweepCandidates {\n checkpoints: ReadonlyArray<{ localId: string }>;\n updates: ReadonlyArray<{ id: string | null }>;\n}\n\ninterface AttachmentSweepCandidates {\n local: ReadonlyArray<AttachmentSweepCandidate>;\n pending: ReadonlyArray<AttachmentSweepCandidate>;\n}\n\nasync function selectHistorySweepCandidates(\n tx: ClientSQLiteTransactionScope,\n olderThan: string,\n): Promise<HistorySweepCandidates> {\n const checkpoints = await tx\n .select({ localId: documentHistoryCheckpoints.localId })\n .from(documentHistoryCheckpoints)\n .where(\n and(\n eq(documentHistoryCheckpoints.appKind, DOCUMENTS_APP_KIND),\n lt(documentHistoryCheckpoints.updatedAt, olderThan),\n hasNoDocument(tx, documentHistoryCheckpoints.localId),\n ),\n )\n .limit(DOCUMENT_ORPHAN_SWEEP_BATCH_SIZE);\n const updates = await tx\n .select({ id: documentHistoryUpdates.id })\n .from(documentHistoryUpdates)\n .where(\n and(\n eq(documentHistoryUpdates.appKind, DOCUMENTS_APP_KIND),\n isNotNull(documentHistoryUpdates.id),\n lt(documentHistoryUpdates.createdAt, olderThan),\n hasNoDocument(tx, documentHistoryUpdates.localId),\n ),\n )\n .limit(DOCUMENT_ORPHAN_SWEEP_BATCH_SIZE);\n return { checkpoints, updates };\n}\n\nasync function selectAttachmentSweepCandidates(\n tx: ClientSQLiteTransactionScope,\n olderThan: string,\n): Promise<AttachmentSweepCandidates> {\n const pending = await tx\n .select({\n localId: documentPendingAttachments.localId,\n slotId: documentPendingAttachments.slotId,\n storageKey: documentPendingAttachments.storageKey,\n })\n .from(documentPendingAttachments)\n .where(\n and(\n lt(documentPendingAttachments.createdAt, olderThan),\n hasNoDocument(tx, documentPendingAttachments.localId, null),\n ),\n )\n .limit(DOCUMENT_ORPHAN_SWEEP_BATCH_SIZE);\n const local = await tx\n .select({\n localId: documentAttachmentBlobProjection.localId,\n slotId: documentAttachmentBlobProjection.slotId,\n storageKey: documentAttachmentBlobProjection.storageKey,\n })\n .from(documentAttachmentBlobProjection)\n .where(\n and(\n lt(documentAttachmentBlobProjection.updatedAt, olderThan),\n hasNoDocument(tx, documentAttachmentBlobProjection.localId, null),\n ),\n )\n .limit(DOCUMENT_ORPHAN_SWEEP_BATCH_SIZE);\n return { local, pending };\n}\n\nasync function deleteHistorySweepCandidates(\n tx: ClientSQLiteTransactionScope,\n candidates: HistorySweepCandidates,\n): Promise<void> {\n if (candidates.checkpoints.length > 0) {\n await tx\n .delete(documentHistoryCheckpoints)\n .where(\n and(\n eq(documentHistoryCheckpoints.appKind, DOCUMENTS_APP_KIND),\n inArray(\n documentHistoryCheckpoints.localId,\n candidates.checkpoints.map((row) => row.localId),\n ),\n ),\n )\n .run();\n }\n if (candidates.updates.length > 0) {\n await tx\n .delete(documentHistoryUpdates)\n .where(\n inArray(\n documentHistoryUpdates.id,\n candidates.updates.flatMap((row) =>\n row.id === null ? [] : [row.id],\n ),\n ),\n )\n .run();\n }\n}\n\nasync function deleteAttachmentSweepCandidates(\n tx: ClientSQLiteTransactionScope,\n candidates: AttachmentSweepCandidates,\n): Promise<void> {\n await queueDocumentAttachmentStorageKeys(tx, [\n ...new Set(\n [...candidates.pending, ...candidates.local].map((row) => row.storageKey),\n ),\n ]);\n if (candidates.pending.length > 0) {\n await tx\n .delete(documentPendingAttachments)\n .where(pendingAttachmentCandidatesWhere(candidates.pending))\n .run();\n }\n if (candidates.local.length > 0) {\n await tx\n .delete(documentAttachmentBlobProjection)\n .where(localAttachmentCandidatesWhere(candidates.local))\n .run();\n }\n}\n\nfunction hasFullSweepBatch(\n history: HistorySweepCandidates,\n attachments: AttachmentSweepCandidates,\n): boolean {\n return [\n history.checkpoints,\n history.updates,\n attachments.pending,\n attachments.local,\n ].some((rows) => rows.length === DOCUMENT_ORPHAN_SWEEP_BATCH_SIZE);\n}\n\n/** Remove one bounded batch of aged side writes without a canonical row. */\nexport async function deleteOrphanedDocumentSideRows(\n execSql: ExecSql,\n options: { now?: Date | undefined } = {},\n): Promise<boolean> {\n const olderThan = new Date(\n (options.now?.getTime() ?? Date.now()) - DOCUMENT_ORPHAN_SWEEP_MIN_AGE_MS,\n ).toISOString();\n return runSerializedSqlMutation(execSql, async (lockedExecSql) =>\n getClientSQLitePersistenceRuntime(lockedExecSql).transaction(async (tx) => {\n const history = await selectHistorySweepCandidates(tx, olderThan);\n const attachments = await selectAttachmentSweepCandidates(tx, olderThan);\n await deleteHistorySweepCandidates(tx, history);\n await deleteAttachmentSweepCandidates(tx, attachments);\n return hasFullSweepBatch(history, attachments);\n }),\n );\n}\n\nexport async function listDocumentOrphanBlobReclaims(\n execSql: ExecSql,\n limit: number,\n): Promise<ReadonlyArray<string>> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({ storageKey: documentOrphanBlobReclaims.storageKey })\n .from(documentOrphanBlobReclaims)\n .orderBy(asc(documentOrphanBlobReclaims.storageKey))\n .limit(limit);\n return rows.map((row) => row.storageKey);\n}\n\nexport async function isDocumentBlobStorageKeyReferenced(\n execSql: ExecSql,\n storageKey: string,\n): Promise<boolean> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const [pendingRows, localRows] = await Promise.all([\n db\n .select({ storageKey: documentPendingAttachments.storageKey })\n .from(documentPendingAttachments)\n .where(eq(documentPendingAttachments.storageKey, storageKey))\n .limit(1),\n db\n .select({ storageKey: documentAttachmentBlobProjection.storageKey })\n .from(documentAttachmentBlobProjection)\n .where(eq(documentAttachmentBlobProjection.storageKey, storageKey))\n .limit(1),\n ]);\n return pendingRows.length > 0 || localRows.length > 0;\n}\n\nexport async function acknowledgeDocumentOrphanBlobReclaim(\n execSql: ExecSql,\n storageKey: string,\n): Promise<void> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n await db\n .delete(documentOrphanBlobReclaims)\n .where(eq(documentOrphanBlobReclaims.storageKey, storageKey))\n .run();\n}\n"]}
@@ -1,9 +1,9 @@
1
1
  import { desc, eq, notInArray } from "drizzle-orm";
2
2
  import { DOCUMENTS_APP_KIND } from "../../../documents/documentConstants.js";
3
3
  import { HIDDEN_DOCUMENT_SUMMARY_KINDS } from "../../../documents/documentSummary.js";
4
- import { findLocalIdByDocumentId, loadDocumentRecord, } from "../../../sqlite/documentPersistence/index.js";
5
4
  import { documentProjection, documentProjectionText, documents, } from "../../../sqlite/schema/index.js";
6
5
  import { getClientSQLitePersistenceRuntime } from "../../../sqlite/sqlitePersistenceRuntime.js";
6
+ import { findLocalIdByDocumentId, loadDocumentRecord } from "../records/index.js";
7
7
  import { documentSummaryJoin, documentSummarySelection, getProjectionContainerId, getProjectionDocumentKind, getProjectionText, getProjectionTitle, mapDocumentSummary, } from "./documentProjectionRows.js";
8
8
  import { getDocumentScope, hasDocumentRow } from "./documentRows.js";
9
9
  export const documentRowQueryPersistence = {