@tearleads/client-sdk 0.1.43 → 0.1.45

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 (189) hide show
  1. package/client/containerDocumentDiscovery.js +71 -29
  2. package/client/containerDocumentDiscovery.js.map +1 -1
  3. package/client/deviceFirst.js +4 -5
  4. package/client/deviceFirst.js.map +1 -1
  5. package/data/persistence/container-contents/containerContentsPersistence.js +2 -3
  6. package/data/persistence/container-contents/containerContentsPersistence.js.map +1 -1
  7. package/data/persistence/container-contents/containerCreateIntentSettlement.js +16 -16
  8. package/data/persistence/container-contents/containerCreateIntentSettlement.js.map +1 -1
  9. package/data/persistence/container-contents/containerDeletionPersistence.js +5 -11
  10. package/data/persistence/container-contents/containerDeletionPersistence.js.map +1 -1
  11. package/data/persistence/container-contents/containerDocumentReassignment.js +13 -13
  12. package/data/persistence/container-contents/containerDocumentReassignment.js.map +1 -1
  13. package/data/persistence/container-contents/containerHydrationPersistence.js +7 -4
  14. package/data/persistence/container-contents/containerHydrationPersistence.js.map +1 -1
  15. package/data/persistence/container-contents/containerIntentPersistence.js +43 -42
  16. package/data/persistence/container-contents/containerIntentPersistence.js.map +1 -1
  17. package/data/persistence/container-contents/containerMetadataMutationPersistence.js +15 -20
  18. package/data/persistence/container-contents/containerMetadataMutationPersistence.js.map +1 -1
  19. package/data/persistence/container-contents/containerMoveAbandonPersistence.js +1 -6
  20. package/data/persistence/container-contents/containerMoveAbandonPersistence.js.map +1 -1
  21. package/data/persistence/container-contents/containerMoveIntentRows.js +9 -8
  22. package/data/persistence/container-contents/containerMoveIntentRows.js.map +1 -1
  23. package/data/persistence/container-contents/containerReconcilePersistence.js +4 -8
  24. package/data/persistence/container-contents/containerReconcilePersistence.js.map +1 -1
  25. package/data/persistence/container-contents/containerRecoveryPersistence.js +20 -19
  26. package/data/persistence/container-contents/containerRecoveryPersistence.js.map +1 -1
  27. package/data/persistence/container-contents/containerRemovalWork.js +10 -10
  28. package/data/persistence/container-contents/containerRemovalWork.js.map +1 -1
  29. package/data/persistence/container-contents/containerStructuralRepair.js +21 -44
  30. package/data/persistence/container-contents/containerStructuralRepair.js.map +1 -1
  31. package/data/persistence/container-contents/documentLinkRemovalRepair.js +12 -11
  32. package/data/persistence/container-contents/documentLinkRemovalRepair.js.map +1 -1
  33. package/data/persistence/container-contents/documentMoveIntentPersistence.js +53 -28
  34. package/data/persistence/container-contents/documentMoveIntentPersistence.js.map +1 -1
  35. package/data/persistence/container-contents/documentPlacementIntentEnqueue.js +9 -8
  36. package/data/persistence/container-contents/documentPlacementIntentEnqueue.js.map +1 -1
  37. package/data/persistence/container-contents/documentPurgePlacement.js +6 -6
  38. package/data/persistence/container-contents/documentPurgePlacement.js.map +1 -1
  39. package/data/persistence/container-contents/dormantContainerMetadata.js +1 -5
  40. package/data/persistence/container-contents/dormantContainerMetadata.js.map +1 -1
  41. package/data/persistence/container-contents/pendingContainerCreate.js +5 -5
  42. package/data/persistence/container-contents/pendingContainerCreate.js.map +1 -1
  43. package/data/persistence/containers/containerPersistence.js +10 -27
  44. package/data/persistence/containers/containerPersistence.js.map +1 -1
  45. package/data/persistence/containers/containerSyncWatermarkPersistence.d.ts +11 -3
  46. package/data/persistence/containers/containerSyncWatermarkPersistence.js +46 -58
  47. package/data/persistence/containers/containerSyncWatermarkPersistence.js.map +1 -1
  48. package/data/persistence/containers/documentContainerProjectionPersistence.js +2 -2
  49. package/data/persistence/containers/documentContainerProjectionPersistence.js.map +1 -1
  50. package/data/persistence/containers/documentPlacement.js +10 -10
  51. package/data/persistence/containers/documentPlacement.js.map +1 -1
  52. package/data/persistence/documents/internal/discardDocument.js +5 -5
  53. package/data/persistence/documents/internal/discardDocument.js.map +1 -1
  54. package/data/persistence/documents/internal/documentDeletionPersistence.js +7 -9
  55. package/data/persistence/documents/internal/documentDeletionPersistence.js.map +1 -1
  56. package/data/persistence/documents/internal/documentMutationPersistence.js +6 -8
  57. package/data/persistence/documents/internal/documentMutationPersistence.js.map +1 -1
  58. package/data/persistence/documents/internal/ensureDocumentsSchema.js +2 -2
  59. package/data/persistence/documents/internal/ensureDocumentsSchema.js.map +1 -1
  60. package/data/persistence/documents/internal/orphanSideRows.js +3 -22
  61. package/data/persistence/documents/internal/orphanSideRows.js.map +1 -1
  62. package/data/persistence/organizations/organizationDataUsagePersistence.js +31 -64
  63. package/data/persistence/organizations/organizationDataUsagePersistence.js.map +1 -1
  64. package/data/persistence/organizations/organizationReadModelPurge.js +1 -5
  65. package/data/persistence/organizations/organizationReadModelPurge.js.map +1 -1
  66. package/data/persistence/principalCurrentAcknowledgementPersistence.js +24 -17
  67. package/data/persistence/principalCurrentAcknowledgementPersistence.js.map +1 -1
  68. package/data/persistence/principalHistoryIncompleteRetention.js +3 -9
  69. package/data/persistence/principalHistoryIncompleteRetention.js.map +1 -1
  70. package/data/persistence/principalHistoryStagePersistence.d.ts +3 -9
  71. package/data/persistence/principalHistoryStagePersistence.js +15 -25
  72. package/data/persistence/principalHistoryStagePersistence.js.map +1 -1
  73. package/data/persistence/principalHistoryStageRetention.js +16 -15
  74. package/data/persistence/principalHistoryStageRetention.js.map +1 -1
  75. package/data/persistence/principalHistoryUnpublishedRetention.js +6 -13
  76. package/data/persistence/principalHistoryUnpublishedRetention.js.map +1 -1
  77. package/data/sqlite/containerSchema.d.ts +84 -180
  78. package/data/sqlite/containerSchema.js +14 -23
  79. package/data/sqlite/containerSchema.js.map +1 -1
  80. package/data/sqlite/documentPlacementIntentSchema.js +3 -50
  81. package/data/sqlite/documentPlacementIntentSchema.js.map +1 -1
  82. package/data/sqlite/documentSyncFailurePersistence.d.ts +16 -6
  83. package/data/sqlite/documentSyncFailurePersistence.js +44 -54
  84. package/data/sqlite/documentSyncFailurePersistence.js.map +1 -1
  85. package/data/sqlite/{documentPlacementIntentSchema.d.ts → localIntentSchema.d.ts} +162 -88
  86. package/data/sqlite/localIntentSchema.js +90 -0
  87. package/data/sqlite/localIntentSchema.js.map +1 -0
  88. package/data/sqlite/organizationDataUsageSchema.js +11 -17
  89. package/data/sqlite/organizationDataUsageSchema.js.map +1 -1
  90. package/data/sqlite/principalHistoryRetentionSchema.js +0 -15
  91. package/data/sqlite/principalHistoryRetentionSchema.js.map +1 -1
  92. package/data/sqlite/principalHistoryStageSchema.d.ts +44 -2
  93. package/data/sqlite/principalHistoryStageSchema.js +14 -2
  94. package/data/sqlite/principalHistoryStageSchema.js.map +1 -1
  95. package/data/sqlite/schema.d.ts +1019 -2284
  96. package/data/sqlite/schema.js +48 -169
  97. package/data/sqlite/schema.js.map +1 -1
  98. package/internal/api-client/ApiClient.d.ts +6 -34
  99. package/internal/api-client/ApiClient.js +8 -10
  100. package/internal/api-client/ApiClient.js.map +1 -1
  101. package/internal/api-client/apiRequestRuntime.d.ts +2 -0
  102. package/internal/api-client/apiRequestRuntime.js +4 -0
  103. package/internal/api-client/apiRequestRuntime.js.map +1 -1
  104. package/internal/api-client/requestInternals.js +7 -7
  105. package/internal/api-client/requestInternals.js.map +1 -1
  106. package/internal/api-client/routes/containers/reads.d.ts +13 -7
  107. package/internal/api-client/routes/containers/reads.js +36 -15
  108. package/internal/api-client/routes/containers/reads.js.map +1 -1
  109. package/internal/api-client/types.d.ts +1 -5
  110. package/internal/api-client/types.js.map +1 -1
  111. package/internal/validators/containerReadRefinements.js +15 -5
  112. package/internal/validators/containerReadRefinements.js.map +1 -1
  113. package/internal/validators/containerReadSchemas.d.ts +36 -0
  114. package/internal/validators/containerReadSchemas.js +64 -1
  115. package/internal/validators/containerReadSchemas.js.map +1 -1
  116. package/internal/validators/operation/containerReads.d.ts +118 -182
  117. package/internal/validators/operation/containerReads.js +20 -54
  118. package/internal/validators/operation/containerReads.js.map +1 -1
  119. package/internal/validators/operation/index.d.ts +1 -1
  120. package/internal/validators/operation/index.js +1 -1
  121. package/internal/validators/operation/index.js.map +1 -1
  122. package/internal/validators/operation/registry.d.ts +2 -2
  123. package/internal/validators/operation/registry.js +2 -2
  124. package/internal/validators/operation/registry.js.map +1 -1
  125. package/internal/validators/request/containerDocumentLanes.d.ts +23 -0
  126. package/internal/validators/request/containerDocumentLanes.js +23 -0
  127. package/internal/validators/request/containerDocumentLanes.js.map +1 -0
  128. package/internal/validators/request/containerParentLanes.js +5 -35
  129. package/internal/validators/request/containerParentLanes.js.map +1 -1
  130. package/internal/validators/request/index.d.ts +1 -0
  131. package/internal/validators/request/index.js +1 -0
  132. package/internal/validators/request/index.js.map +1 -1
  133. package/internal/validators/response/containerDocumentLanes.d.ts +82 -0
  134. package/internal/validators/response/containerDocumentLanes.js +35 -0
  135. package/internal/validators/response/containerDocumentLanes.js.map +1 -0
  136. package/internal/validators/response/containerParentLanes.js +3 -15
  137. package/internal/validators/response/containerParentLanes.js.map +1 -1
  138. package/internal/validators/response/index.d.ts +2 -1
  139. package/internal/validators/response/index.js +1 -1
  140. package/internal/validators/response/index.js.map +1 -1
  141. package/package.json +1 -1
  142. package/sync/reconciliation/knownContainerSweep.js +57 -54
  143. package/sync/reconciliation/knownContainerSweep.js.map +1 -1
  144. package/sync/reconciliation/reconcileContainer.js +49 -17
  145. package/sync/reconciliation/reconcileContainer.js.map +1 -1
  146. package/sync/reconciliation/service.js +44 -16
  147. package/sync/reconciliation/service.js.map +1 -1
  148. package/sync/reconciliation/serviceTypes.d.ts +23 -2
  149. package/sync/reconciliation/serviceTypes.js.map +1 -1
  150. package/workflows/container-contents/containerDocumentListing.js +92 -38
  151. package/workflows/container-contents/containerDocumentListing.js.map +1 -1
  152. package/workflows/container-contents/containerPersistence.d.ts +8 -2
  153. package/workflows/container-contents/containerPersistence.js +6 -7
  154. package/workflows/container-contents/containerPersistence.js.map +1 -1
  155. package/workflows/container-contents/documentDiscovery.d.ts +3 -3
  156. package/workflows/container-contents/documentDiscovery.js +24 -96
  157. package/workflows/container-contents/documentDiscovery.js.map +1 -1
  158. package/workflows/container-contents/documentDiscoveryApply.js +65 -0
  159. package/workflows/container-contents/documentDiscoveryApply.js.map +1 -0
  160. package/workflows/container-contents/documentDiscoveryLanes.js +53 -0
  161. package/workflows/container-contents/documentDiscoveryLanes.js.map +1 -0
  162. package/workflows/container-contents/documentDiscoveryTypes.d.ts +25 -19
  163. package/workflows/container-contents/documentDiscoveryTypes.js.map +1 -1
  164. package/workflows/container-contents/documentPriming.js +6 -10
  165. package/workflows/container-contents/documentPriming.js.map +1 -1
  166. package/workflows/container-contents/documentQueries/sql.js +11 -10
  167. package/workflows/container-contents/documentQueries/sql.js.map +1 -1
  168. package/workflows/container-contents/documentQueries.js +2 -2
  169. package/workflows/container-contents/documentQueries.js.map +1 -1
  170. package/workflows/container-contents/pendingDownloads.js +2 -5
  171. package/workflows/container-contents/pendingDownloads.js.map +1 -1
  172. package/workflows/container-contents/pendingWrites/deferredTails.js +3 -11
  173. package/workflows/container-contents/pendingWrites/deferredTails.js.map +1 -1
  174. package/workflows/container-contents/pendingWrites/sources.js +56 -80
  175. package/workflows/container-contents/pendingWrites/sources.js.map +1 -1
  176. package/workflows/container-contents/pendingWrites.js +6 -4
  177. package/workflows/container-contents/pendingWrites.js.map +1 -1
  178. package/workflows/container-contents/remoteContainerState.js +2 -1
  179. package/workflows/container-contents/remoteContainerState.js.map +1 -1
  180. package/workflows/container-contents/remoteHydration/laneFetchMarkers.js +11 -17
  181. package/workflows/container-contents/remoteHydration/laneFetchMarkers.js.map +1 -1
  182. package/workflows/sync/remoteReset.js +16 -23
  183. package/workflows/sync/remoteReset.js.map +1 -1
  184. package/workflows/sync/remoteResetCursorScope.js +10 -18
  185. package/workflows/sync/remoteResetCursorScope.js.map +1 -1
  186. package/workflows/sync/remoteResetPrincipalScope.js +1 -2
  187. package/workflows/sync/remoteResetPrincipalScope.js.map +1 -1
  188. package/workflows/sync/remoteResetScope.js +18 -18
  189. package/workflows/sync/remoteResetScope.js.map +1 -1
@@ -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 { deleteDocumentHistory } from \"../../../sqlite/documentHistoryPersistence\";\nimport {\n clearDocumentSyncFailure,\n deleteDocumentRecord,\n loadDocumentRecord,\n} from \"../../../sqlite/documentPersistence\";\nimport { documentIntentLinkTargets } from \"../../../sqlite/documentPlacementIntentSchema\";\nimport {\n documentAttachmentBlobProjection,\n documentContainerProjection,\n documentMoveIntents,\n documentMoveIntentTables,\n documentPendingAttachments,\n documentProjection,\n documentProjectionText,\n documents,\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 { StoredDocumentRecord } from \"../types\";\nimport { DOCUMENTS_APP_KIND } from \"./constants\";\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 await tx\n .delete(documentIntentLinkTargets)\n .where(\n inArray(\n documentIntentLinkTargets.intentId,\n tx\n .select({ id: documentMoveIntents.id })\n .from(documentMoveIntents)\n .where(eq(documentMoveIntents.documentId, existingDocumentId)),\n ),\n )\n .run();\n await tx\n .delete(documentMoveIntents)\n .where(eq(documentMoveIntents.documentId, existingDocumentId))\n .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, documentMoveIntentTables);\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 { deleteDocumentHistory } from \"../../../sqlite/documentHistoryPersistence\";\nimport {\n clearDocumentSyncFailure,\n deleteDocumentRecord,\n loadDocumentRecord,\n} from \"../../../sqlite/documentPersistence\";\nimport { documentIntentLinkTargets } from \"../../../sqlite/documentPlacementIntentSchema\";\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 {\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 { DOCUMENTS_APP_KIND } from \"./constants\";\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"]}
@@ -4,7 +4,8 @@ import { and, eq } from "drizzle-orm";
4
4
  import { documentSyncPullContinuationsEqual } from "../../../documents/shared/pullContinuation.js";
5
5
  import { appendDocumentHistoryUpdates, replaceDocumentHistoryCheckpoint, } from "../../../sqlite/documentHistoryPersistence.js";
6
6
  import { insertDocumentPendingUpdateWithHistoryInTransaction, settleQueuedDocumentUpdatesInTransaction, } from "../../../sqlite/documentPendingUpdatePersistence.js";
7
- import { documentHistoryUpdates, documentSyncFailures, } from "../../../sqlite/schema.js";
7
+ import { clearDocumentSyncFailureInTransaction } from "../../../sqlite/documentSyncFailurePersistence.js";
8
+ import { documentHistoryUpdates } from "../../../sqlite/schema.js";
8
9
  import { getClientSQLitePersistenceRuntime, } from "../../../sqlite/sqlitePersistenceRuntime.js";
9
10
  import { runSerializedSqlMutation } from "../../../sqlite/sqlSchema.js";
10
11
  import { applyStoredAttachmentRemoval, upsertStoredAttachmentStagingRows, } from "./attachmentStagingPersistence.js";
@@ -30,12 +31,6 @@ function sameExpectedDocumentRecord(current, expected) {
30
31
  async function deleteAcceptedPendingUpdates(tx, localId, ids) {
31
32
  await settleQueuedDocumentUpdatesInTransaction(tx, { appKind: DOCUMENTS_APP_KIND, localId }, ids);
32
33
  }
33
- async function clearSyncFailure(tx, localId) {
34
- await tx
35
- .delete(documentSyncFailures)
36
- .where(and(eq(documentSyncFailures.appKind, DOCUMENTS_APP_KIND), eq(documentSyncFailures.localId, localId)))
37
- .run();
38
- }
39
34
  async function insertPendingUpdate(tx, documentId, pendingUpdate) {
40
35
  if (!pendingUpdate)
41
36
  return;
@@ -173,7 +168,10 @@ async function applyStoredDocumentMutation(input) {
173
168
  await insertPendingUpdate(tx, mutation.document.id, mutation.pendingUpdate);
174
169
  await deleteAcceptedPendingUpdates(tx, mutation.document.id, mutation.acceptedPendingUpdateIds);
175
170
  if (mutation.clearSyncFailure) {
176
- await clearSyncFailure(tx, mutation.document.id);
171
+ await clearDocumentSyncFailureInTransaction(tx, {
172
+ appKind: DOCUMENTS_APP_KIND,
173
+ localId: mutation.document.id,
174
+ });
177
175
  }
178
176
  const updatedAt = await resolveDocumentSaveTimestamp({
179
177
  document: mutation.document,
@@ -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 { documentSyncPullContinuationsEqual } from \"../../../documents/shared/pullContinuation\";\nimport {\n appendDocumentHistoryUpdates,\n replaceDocumentHistoryCheckpoint,\n} from \"../../../sqlite/documentHistoryPersistence\";\nimport {\n insertDocumentPendingUpdateWithHistoryInTransaction,\n settleQueuedDocumentUpdatesInTransaction,\n} from \"../../../sqlite/documentPendingUpdatePersistence\";\nimport {\n documentHistoryUpdates,\n documentSyncFailures,\n} 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 { DOCUMENTS_APP_KIND } from \"./constants\";\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 clearSyncFailure(\n tx: ClientSQLiteTransactionScope,\n localId: string,\n): Promise<void> {\n await tx\n .delete(documentSyncFailures)\n .where(\n and(\n eq(documentSyncFailures.appKind, DOCUMENTS_APP_KIND),\n eq(documentSyncFailures.localId, localId),\n ),\n )\n .run();\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 clearSyncFailure(tx, mutation.document.id);\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,kCAAkC,EAAE,MAAM,4CAA4C,CAAC;AAChG,OAAO,EACL,4BAA4B,EAC5B,gCAAgC,GACjC,MAAM,4CAA4C,CAAC;AACpD,OAAO,EACL,mDAAmD,EACnD,wCAAwC,GACzC,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EAAE,qCAAqC,EAAE,MAAM,gDAAgD,CAAC;AACvG,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAEL,iCAAiC,GAClC,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAErE,OAAO,EACL,4BAA4B,EAC5B,iCAAiC,GAClC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,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 { documentSyncPullContinuationsEqual } from \"../../../documents/shared/pullContinuation\";\nimport {\n appendDocumentHistoryUpdates,\n replaceDocumentHistoryCheckpoint,\n} from \"../../../sqlite/documentHistoryPersistence\";\nimport {\n insertDocumentPendingUpdateWithHistoryInTransaction,\n settleQueuedDocumentUpdatesInTransaction,\n} from \"../../../sqlite/documentPendingUpdatePersistence\";\nimport { clearDocumentSyncFailureInTransaction } from \"../../../sqlite/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 { DOCUMENTS_APP_KIND } from \"./constants\";\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,5 +1,5 @@
1
1
  import { ensureDocumentProjectionTables, ensureDocumentTables, } from "../../../sqlite/documentPersistence.js";
2
- import { documentContainerProjectionTables, documentMoveIntentTables, keyingCheckpointTables, } from "../../../sqlite/schema.js";
2
+ import { documentContainerProjectionTables, documentPlacementIntentTables, keyingCheckpointTables, } from "../../../sqlite/schema.js";
3
3
  import { ensureSqlTables, runOncePerConnection, runSerializedSqlMutation, } from "../../../sqlite/sqlSchema.js";
4
4
  export async function ensureDocumentsSchema(execSql) {
5
5
  await runOncePerConnection(execSql, "ensure:documents", () => runSerializedSqlMutation(execSql, async (lockedExecSql) => {
@@ -7,7 +7,7 @@ export async function ensureDocumentsSchema(execSql) {
7
7
  await ensureDocumentProjectionTables(lockedExecSql);
8
8
  await ensureSqlTables(lockedExecSql, [
9
9
  ...documentContainerProjectionTables,
10
- ...documentMoveIntentTables,
10
+ ...documentPlacementIntentTables,
11
11
  ...keyingCheckpointTables,
12
12
  ]);
13
13
  }));
@@ -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,wBAAwB,EACxB,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,wBAAwB;YAC3B,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 documentMoveIntentTables,\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 ...documentMoveIntentTables,\n ...keyingCheckpointTables,\n ]);\n }),\n );\n}\n"]}
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,6 +1,6 @@
1
1
  import { and, asc, eq, inArray, isNotNull, lt, notExists, or, } from "drizzle-orm";
2
2
  import { documentOrphanBlobReclaims } from "../../../sqlite/documentOrphanBlobReclaims.js";
3
- import { documentAttachmentBlobProjection, documentHistoryCheckpoints, documentHistoryUpdates, documentPendingAttachments, documentSyncFailures, documents, } from "../../../sqlite/schema.js";
3
+ import { documentAttachmentBlobProjection, documentHistoryCheckpoints, documentHistoryUpdates, documentPendingAttachments, documents, } from "../../../sqlite/schema.js";
4
4
  import { getClientSQLitePersistenceRuntime, } from "../../../sqlite/sqlitePersistenceRuntime.js";
5
5
  import { runSerializedSqlMutation, } from "../../../sqlite/sqlSchema.js";
6
6
  import { DOCUMENTS_APP_KIND } from "./constants.js";
@@ -67,14 +67,6 @@ async function selectHistorySweepCandidates(tx, olderThan) {
67
67
  .limit(DOCUMENT_ORPHAN_SWEEP_BATCH_SIZE);
68
68
  return { checkpoints, updates };
69
69
  }
70
- async function selectWriteSweepCandidates(tx, olderThan) {
71
- const syncFailures = await tx
72
- .select({ localId: documentSyncFailures.localId })
73
- .from(documentSyncFailures)
74
- .where(and(eq(documentSyncFailures.appKind, DOCUMENTS_APP_KIND), lt(documentSyncFailures.attemptedAt, olderThan), hasNoDocument(tx, documentSyncFailures.localId)))
75
- .limit(DOCUMENT_ORPHAN_SWEEP_BATCH_SIZE);
76
- return { syncFailures };
77
- }
78
70
  async function selectAttachmentSweepCandidates(tx, olderThan) {
79
71
  const pending = await tx
80
72
  .select({
@@ -110,14 +102,6 @@ async function deleteHistorySweepCandidates(tx, candidates) {
110
102
  .run();
111
103
  }
112
104
  }
113
- async function deleteWriteSweepCandidates(tx, candidates) {
114
- if (candidates.syncFailures.length > 0) {
115
- await tx
116
- .delete(documentSyncFailures)
117
- .where(and(eq(documentSyncFailures.appKind, DOCUMENTS_APP_KIND), inArray(documentSyncFailures.localId, candidates.syncFailures.map((row) => row.localId))))
118
- .run();
119
- }
120
- }
121
105
  async function deleteAttachmentSweepCandidates(tx, candidates) {
122
106
  await queueDocumentAttachmentStorageKeys(tx, [
123
107
  ...new Set([...candidates.pending, ...candidates.local].map((row) => row.storageKey)),
@@ -135,11 +119,10 @@ async function deleteAttachmentSweepCandidates(tx, candidates) {
135
119
  .run();
136
120
  }
137
121
  }
138
- function hasFullSweepBatch(history, writes, attachments) {
122
+ function hasFullSweepBatch(history, attachments) {
139
123
  return [
140
124
  history.checkpoints,
141
125
  history.updates,
142
- writes.syncFailures,
143
126
  attachments.pending,
144
127
  attachments.local,
145
128
  ].some((rows) => rows.length === DOCUMENT_ORPHAN_SWEEP_BATCH_SIZE);
@@ -149,12 +132,10 @@ export async function deleteOrphanedDocumentSideRows(execSql, options = {}) {
149
132
  const olderThan = new Date((options.now?.getTime() ?? Date.now()) - DOCUMENT_ORPHAN_SWEEP_MIN_AGE_MS).toISOString();
150
133
  return runSerializedSqlMutation(execSql, async (lockedExecSql) => getClientSQLitePersistenceRuntime(lockedExecSql).transaction(async (tx) => {
151
134
  const history = await selectHistorySweepCandidates(tx, olderThan);
152
- const writes = await selectWriteSweepCandidates(tx, olderThan);
153
135
  const attachments = await selectAttachmentSweepCandidates(tx, olderThan);
154
136
  await deleteHistorySweepCandidates(tx, history);
155
- await deleteWriteSweepCandidates(tx, writes);
156
137
  await deleteAttachmentSweepCandidates(tx, attachments);
157
- return hasFullSweepBatch(history, writes, attachments);
138
+ return hasFullSweepBatch(history, attachments);
158
139
  }));
159
140
  }
160
141
  export async function listDocumentOrphanBlobReclaims(execSql, limit) {
@@ -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 { documentOrphanBlobReclaims } from \"../../../sqlite/documentOrphanBlobReclaims\";\nimport {\n documentAttachmentBlobProjection,\n documentHistoryCheckpoints,\n documentHistoryUpdates,\n documentPendingAttachments,\n documentSyncFailures,\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\";\nimport { DOCUMENTS_APP_KIND } from \"./constants\";\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 | typeof documentSyncFailures.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 WriteSweepCandidates {\n syncFailures: ReadonlyArray<{ localId: string }>;\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 selectWriteSweepCandidates(\n tx: ClientSQLiteTransactionScope,\n olderThan: string,\n): Promise<WriteSweepCandidates> {\n const syncFailures = await tx\n .select({ localId: documentSyncFailures.localId })\n .from(documentSyncFailures)\n .where(\n and(\n eq(documentSyncFailures.appKind, DOCUMENTS_APP_KIND),\n lt(documentSyncFailures.attemptedAt, olderThan),\n hasNoDocument(tx, documentSyncFailures.localId),\n ),\n )\n .limit(DOCUMENT_ORPHAN_SWEEP_BATCH_SIZE);\n return { syncFailures };\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 deleteWriteSweepCandidates(\n tx: ClientSQLiteTransactionScope,\n candidates: WriteSweepCandidates,\n): Promise<void> {\n if (candidates.syncFailures.length > 0) {\n await tx\n .delete(documentSyncFailures)\n .where(\n and(\n eq(documentSyncFailures.appKind, DOCUMENTS_APP_KIND),\n inArray(\n documentSyncFailures.localId,\n candidates.syncFailures.map((row) => row.localId),\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 writes: WriteSweepCandidates,\n attachments: AttachmentSweepCandidates,\n): boolean {\n return [\n history.checkpoints,\n history.updates,\n writes.syncFailures,\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 writes = await selectWriteSweepCandidates(tx, olderThan);\n const attachments = await selectAttachmentSweepCandidates(tx, olderThan);\n await deleteHistorySweepCandidates(tx, history);\n await deleteWriteSweepCandidates(tx, writes);\n await deleteAttachmentSweepCandidates(tx, attachments);\n return hasFullSweepBatch(history, writes, 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 { documentOrphanBlobReclaims } from \"../../../sqlite/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\";\nimport { DOCUMENTS_APP_KIND } from \"./constants\";\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,6 +1,6 @@
1
1
  import { ORGANIZATION_DOCUMENT_USAGE_CATEGORIES, OrganizationDataUsageResponseSchema, } from "../../../internal/validators/response/index.js";
2
2
  import { and, eq } from "drizzle-orm";
3
- import { ORGANIZATION_DATA_USAGE_PROJECTION_VERSION, organizationDataUsageCategories, organizationDataUsageSnapshots, organizationDataUsageTables, } from "../../sqlite/organizationDataUsageSchema.js";
3
+ import { ORGANIZATION_DATA_USAGE_PROJECTION_VERSION, organizationDataUsageSnapshots, organizationDataUsageTables, } from "../../sqlite/organizationDataUsageSchema.js";
4
4
  import { organizationReadModelTables } from "../../sqlite/organizationReadModelTableRegistry.js";
5
5
  import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
6
6
  import { ensureSqlTables } from "../../sqlite/sqlTableSchema.js";
@@ -8,9 +8,6 @@ import { clearOrganizationPresentationDenialInTransaction } from "./organization
8
8
  function isNonNegativeSafeInteger(value) {
9
9
  return Number.isSafeInteger(value) && value >= 0;
10
10
  }
11
- function isDocumentUsageCategory(value) {
12
- return ORGANIZATION_DOCUMENT_USAGE_CATEGORIES.some((category) => category === value);
13
- }
14
11
  function isIsoTimestamp(value) {
15
12
  try {
16
13
  return value.length > 0 && new Date(value).toISOString() === value;
@@ -34,10 +31,6 @@ function parseResponse(response) {
34
31
  }
35
32
  export async function purgeOrganizationDataUsageProjectionInTransaction(input) {
36
33
  requireScope(input.organizationId, input.requesterUserId);
37
- const condition = input.requesterUserId
38
- ? and(eq(organizationDataUsageCategories.organizationId, input.organizationId), eq(organizationDataUsageCategories.requesterUserId, input.requesterUserId))
39
- : eq(organizationDataUsageCategories.organizationId, input.organizationId);
40
- await input.tx.delete(organizationDataUsageCategories).where(condition).run();
41
34
  const snapshotCondition = input.requesterUserId
42
35
  ? and(eq(organizationDataUsageSnapshots.organizationId, input.organizationId), eq(organizationDataUsageSnapshots.requesterUserId, input.requesterUserId))
43
36
  : eq(organizationDataUsageSnapshots.organizationId, input.organizationId);
@@ -58,53 +51,49 @@ function isStoredSnapshotValid(snapshot, organizationId, requesterUserId) {
58
51
  isNonNegativeSafeInteger(snapshot.totalByteLength) &&
59
52
  isIsoTimestamp(snapshot.refreshedAt));
60
53
  }
61
- function storedResponse(snapshot, categories) {
62
- if (categories.length !== ORGANIZATION_DOCUMENT_USAGE_CATEGORIES.length) {
63
- return null;
64
- }
65
- const byCategory = new Map();
66
- for (const row of categories) {
67
- const category = row.category;
68
- if (row.organizationId !== snapshot.organizationId ||
69
- row.requesterUserId !== snapshot.requesterUserId ||
70
- !isDocumentUsageCategory(category) ||
71
- byCategory.has(category) ||
72
- !isNonNegativeSafeInteger(row.byteLength) ||
73
- !isNonNegativeSafeInteger(row.documentCount) ||
74
- !isNonNegativeSafeInteger(row.updateCount)) {
75
- return null;
76
- }
77
- byCategory.set(category, {
78
- byteLength: row.byteLength,
79
- category,
80
- documentCount: row.documentCount,
81
- updateCount: row.updateCount,
82
- });
54
+ function parseStoredBreakdown(json) {
55
+ try {
56
+ return JSON.parse(json);
83
57
  }
84
- const breakdown = ORGANIZATION_DOCUMENT_USAGE_CATEGORIES.flatMap((category) => {
85
- const entry = byCategory.get(category);
86
- return entry ? [entry] : [];
87
- });
88
- if (breakdown.length !== ORGANIZATION_DOCUMENT_USAGE_CATEGORIES.length) {
58
+ catch {
89
59
  return null;
90
60
  }
91
- const response = {
61
+ }
62
+ // The stored breakdown is untrusted local JSON: the strict response schema
63
+ // re-checks every entry (known category, each exactly once, safe non-negative
64
+ // counts that sum to the stored totals) before it is served.
65
+ function storedResponse(snapshot) {
66
+ const parsed = OrganizationDataUsageResponseSchema.safeParse({
92
67
  organizationId: snapshot.organizationId,
93
68
  blobs: {
94
69
  blobCount: snapshot.blobCount,
95
70
  byteLength: snapshot.blobByteLength,
96
71
  },
97
72
  documents: {
98
- breakdown,
73
+ breakdown: parseStoredBreakdown(snapshot.categoryBreakdownJson),
99
74
  byteLength: snapshot.documentByteLength,
100
75
  documentCount: snapshot.documentCount,
101
76
  updateCount: snapshot.documentUpdateCount,
102
77
  },
103
78
  totalByteLength: snapshot.totalByteLength,
104
- };
105
- const parsed = OrganizationDataUsageResponseSchema.safeParse(response);
79
+ });
106
80
  return parsed.success ? parsed.data : null;
107
81
  }
82
+ function canonicalBreakdownJson(response) {
83
+ const order = new Map(ORGANIZATION_DOCUMENT_USAGE_CATEGORIES.map((category, index) => [
84
+ category,
85
+ index,
86
+ ]));
87
+ const breakdown = [...response.documents.breakdown]
88
+ .sort((left, right) => (order.get(left.category) ?? 0) - (order.get(right.category) ?? 0))
89
+ .map((entry) => ({
90
+ byteLength: entry.byteLength,
91
+ category: entry.category,
92
+ documentCount: entry.documentCount,
93
+ updateCount: entry.updateCount,
94
+ }));
95
+ return JSON.stringify(breakdown);
96
+ }
108
97
  export async function loadOrganizationDataUsageProjection(execSql, organizationId, requesterUserId) {
109
98
  requireScope(organizationId, requesterUserId);
110
99
  await ensureSqlTables(execSql, organizationDataUsageTables);
@@ -115,22 +104,11 @@ export async function loadOrganizationDataUsageProjection(execSql, organizationI
115
104
  .from(organizationDataUsageSnapshots)
116
105
  .where(scope)
117
106
  .limit(1);
118
- const categories = await tx
119
- .select()
120
- .from(organizationDataUsageCategories)
121
- .where(and(eq(organizationDataUsageCategories.organizationId, organizationId), eq(organizationDataUsageCategories.requesterUserId, requesterUserId)));
122
107
  if (!snapshot) {
123
- if (categories.length > 0) {
124
- await purgeOrganizationDataUsageProjectionInTransaction({
125
- organizationId,
126
- requesterUserId,
127
- tx,
128
- });
129
- }
130
108
  return null;
131
109
  }
132
110
  const response = isStoredSnapshotValid(snapshot, organizationId, requesterUserId)
133
- ? storedResponse(snapshot, categories)
111
+ ? storedResponse(snapshot)
134
112
  : null;
135
113
  if (!response) {
136
114
  await purgeOrganizationDataUsageProjectionInTransaction({
@@ -158,8 +136,6 @@ export async function saveOrganizationDataUsageProjection(input) {
158
136
  requesterUserId: input.requesterUserId,
159
137
  tx,
160
138
  });
161
- const scope = and(eq(organizationDataUsageCategories.organizationId, response.organizationId), eq(organizationDataUsageCategories.requesterUserId, input.requesterUserId));
162
- await tx.delete(organizationDataUsageCategories).where(scope).run();
163
139
  const snapshot = {
164
140
  organizationId: response.organizationId,
165
141
  requesterUserId: input.requesterUserId,
@@ -170,6 +146,7 @@ export async function saveOrganizationDataUsageProjection(input) {
170
146
  documentCount: response.documents.documentCount,
171
147
  documentUpdateCount: response.documents.updateCount,
172
148
  totalByteLength: response.totalByteLength,
149
+ categoryBreakdownJson: canonicalBreakdownJson(response),
173
150
  refreshedAt: new Date().toISOString(),
174
151
  };
175
152
  await tx
@@ -188,21 +165,11 @@ export async function saveOrganizationDataUsageProjection(input) {
188
165
  documentCount: snapshot.documentCount,
189
166
  documentUpdateCount: snapshot.documentUpdateCount,
190
167
  totalByteLength: snapshot.totalByteLength,
168
+ categoryBreakdownJson: snapshot.categoryBreakdownJson,
191
169
  refreshedAt: snapshot.refreshedAt,
192
170
  },
193
171
  })
194
172
  .run();
195
- await tx
196
- .insert(organizationDataUsageCategories)
197
- .values(response.documents.breakdown.map((entry) => ({
198
- organizationId: response.organizationId,
199
- requesterUserId: input.requesterUserId,
200
- category: entry.category,
201
- byteLength: entry.byteLength,
202
- documentCount: entry.documentCount,
203
- updateCount: entry.updateCount,
204
- })))
205
- .run();
206
173
  });
207
174
  }
208
175
  export async function purgeOrganizationDataUsageProjection(execSql, organizationId, requesterUserId) {