@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":"documentMoveIntentPersistence.js","sourceRoot":"","sources":["documentMoveIntentPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, asc, eq, inArray } from \"drizzle-orm\";\nimport { documentIntentLinkTargets } from \"../../sqlite/documentPlacementIntentSchema\";\nimport {\n containerTables,\n documentMoveIntents,\n documentMoveIntentTables,\n documentProjectionTables,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport {\n enqueueDocumentLinkIntent,\n enqueueDocumentMoveIntent,\n enqueueDocumentUnlinkIntent,\n loadDocumentIntentLinkTargets,\n} from \"./documentPlacementIntentEnqueue\";\n\nexport const DOCUMENT_MOVE_INTENT_TYPE = \"document.move\";\nexport const DOCUMENT_LINK_INTENT_TYPE = \"document.link\";\nconst DOCUMENT_PLACEMENT_INTENT_TYPES = [\n DOCUMENT_MOVE_INTENT_TYPE,\n DOCUMENT_LINK_INTENT_TYPE,\n];\n\n// Resolves the organization a parked move belongs to, in confidence order:\n// the move's target container, the document's preserved projection\n// attribution, then the container the document currently sits in. Normal\n// saves routinely leave projection attribution empty (deferred-tail\n// reporting resolves through the same container join), so the container rows\n// carry the common case; rows with no resolvable organization stay in every\n// scope (device-first shapes, matching orphan priming).\nconst DENIED_INTENT_ORGANIZATION_SQL = `COALESCE(\n NULLIF(target_container.organization_id, ''),\n NULLIF(projection.organization_id, ''),\n NULLIF(projection_container.organization_id, '')\n)`;\n\nconst DENIED_INTENT_ORGANIZATION_JOINS_SQL = `\n LEFT JOIN containers target_container\n ON target_container.id = intent.target_container_id\n LEFT JOIN document_projection projection\n ON projection.local_id = intent.local_id\n LEFT JOIN containers projection_container\n ON projection_container.id = projection.container_id`;\n\n/**\n * - `pending`: replays on every structural pass.\n * - `blocked`: the last pass could not proceed locally (missing local\n * document / destination); replays, since hydration can heal it.\n * - `denied`: parked on a 403 until the access-restored replay (row 7).\n * - `unavailable`: terminal — after a projection refresh the destination\n * itself is proven deleted (coded 404 on its own projection probe). No\n * mutation 409 is ever terminal: with the destination live it can only\n * name an ancestor or source, and the next pass's fresh paths outrun it.\n * Container ids never return, so no replay can commit the intent as\n * written; it leaves the replay set until the local container tombstone\n * cascade retargets it or the user re-enqueues the move.\n */\nexport type DocumentMoveIntentSyncStatus =\n | \"pending\"\n | \"blocked\"\n | \"denied\"\n | \"unavailable\";\n\nexport interface DocumentMoveIntentRecord {\n additionalLinkContainerIds?: readonly string[] | undefined;\n removedLinkContainerIds?: readonly string[] | undefined;\n id: string;\n documentId: string;\n intentType:\n | typeof DOCUMENT_MOVE_INTENT_TYPE\n | typeof DOCUMENT_LINK_INTENT_TYPE;\n lastAttemptedAt: string | null;\n lastError: string | null;\n localId: string;\n replaceLinkedContainers: boolean;\n sourceContainerId: string | null;\n syncStatus: DocumentMoveIntentSyncStatus;\n targetContainerId: string;\n createdAt: string;\n updatedAt: string;\n}\n\ninterface SelectedDocumentMoveIntentRecord {\n id: string | null;\n documentId: string;\n intentType: string;\n lastAttemptedAt: string | null;\n lastError: string | null;\n localId: string;\n replaceLinkedContainers: boolean;\n sourceContainerId: string | null;\n syncStatus: string;\n targetContainerId: string;\n createdAt: string;\n updatedAt: string;\n}\n\nfunction parseDocumentMoveIntentSyncStatus(\n value: unknown,\n): DocumentMoveIntentSyncStatus {\n if (value === \"blocked\" || value === \"denied\" || value === \"unavailable\") {\n return value;\n }\n return \"pending\";\n}\n\nfunction resolveRecordedSyncStatus(input: {\n blocked?: boolean | undefined;\n denied?: boolean | undefined;\n unavailable?: boolean | undefined;\n}): DocumentMoveIntentSyncStatus {\n if (input.unavailable) return \"unavailable\";\n if (input.denied) return \"denied\";\n return input.blocked ? \"blocked\" : \"pending\";\n}\n\nfunction mapDocumentMoveIntentRecord(\n row: SelectedDocumentMoveIntentRecord,\n): DocumentMoveIntentRecord {\n return {\n id: String(row.id ?? \"\"),\n documentId: row.documentId,\n intentType:\n row.intentType === DOCUMENT_LINK_INTENT_TYPE\n ? DOCUMENT_LINK_INTENT_TYPE\n : DOCUMENT_MOVE_INTENT_TYPE,\n lastAttemptedAt: row.lastAttemptedAt,\n lastError: row.lastError,\n localId: row.localId,\n replaceLinkedContainers: Boolean(row.replaceLinkedContainers),\n sourceContainerId: row.sourceContainerId,\n syncStatus: parseDocumentMoveIntentSyncStatus(row.syncStatus),\n targetContainerId: row.targetContainerId,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\nexport const sqlDocumentMoveIntentPersistence = {\n async ensureSchema(execSql: ExecSql): Promise<void> {\n await ensureSqlTables(execSql, documentMoveIntentTables);\n },\n\n enqueueMoveIntent: enqueueDocumentMoveIntent,\n enqueueLinkIntent: enqueueDocumentLinkIntent,\n enqueueUnlinkIntent: enqueueDocumentUnlinkIntent,\n\n async listPendingMoveIntents(\n execSql: ExecSql,\n ): Promise<DocumentMoveIntentRecord[]> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureSqlTables(lockedExecSql, documentMoveIntentTables);\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const rows = await tx\n .select()\n .from(documentMoveIntents)\n // Blocked intents replay too: \"blocked\" names the reason the last\n // attempt could not proceed (missing local doc / destination), not a\n // terminal verdict. The blocking condition can heal after hydration or\n // recovery, and re-checking is cheap — a still-blocked intent simply\n // re-records its reason without counting as lane progress.\n // Unavailable intents never replay: the server proved a cited container\n // is gone, so every replay would re-issue the same doomed requests\n // (#2278 #4). Only the tombstone cascade or a re-enqueue revives them.\n .where(\n and(\n inArray(documentMoveIntents.syncStatus, [\"pending\", \"blocked\"]),\n inArray(\n documentMoveIntents.intentType,\n DOCUMENT_PLACEMENT_INTENT_TYPES,\n ),\n ),\n )\n .orderBy(asc(documentMoveIntents.createdAt));\n\n return Promise.all(\n rows.map(async (row) => {\n const targets = await loadDocumentIntentLinkTargets(\n lockedExecSql,\n row.id ?? \"\",\n );\n const added = targets\n .filter((target) => target.operation === \"link\")\n .map((target) => target.containerId)\n .sort();\n const removed = targets\n .filter((target) => target.operation === \"unlink\")\n .map((target) => target.containerId)\n .sort();\n return {\n ...mapDocumentMoveIntentRecord(row),\n ...(added.length ? { additionalLinkContainerIds: added } : {}),\n ...(removed.length ? { removedLinkContainerIds: removed } : {}),\n };\n }),\n );\n },\n );\n });\n },\n\n async markMoveIntentSynced(\n execSql: ExecSql,\n input: {\n documentId: string;\n expectedIntentId?: string | undefined;\n expectedUpdatedAt: string;\n },\n ): Promise<boolean> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const deleted = await lockedExecSql(\n `DELETE FROM document_move_intents\n WHERE document_id = ?\n AND intent_type IN (?, ?)\n AND updated_at = ?\n ${input.expectedIntentId ? \"AND id = ?\" : \"\"}\n RETURNING id`,\n [\n input.documentId,\n ...DOCUMENT_PLACEMENT_INTENT_TYPES,\n input.expectedUpdatedAt,\n ...(input.expectedIntentId ? [input.expectedIntentId] : []),\n ],\n );\n for (const { id } of deleted) {\n await getClientSQLitePersistenceRuntime(lockedExecSql)\n .db.delete(documentIntentLinkTargets)\n .where(eq(documentIntentLinkTargets.intentId, String(id)))\n .run();\n }\n return deleted.length > 0;\n });\n },\n\n async recordMoveIntentError(\n execSql: ExecSql,\n input: {\n blocked?: boolean | undefined;\n /**\n * A permission denial (403): the intent parks as `denied` — excluded\n * from routine structural replays — until the org-access-restored\n * signal or a manual retry flips it back to pending (edge-case row 7).\n */\n denied?: boolean | undefined;\n documentId: string;\n /** Exact intent revision read by the pass; preferred over wall-clock CAS. */\n expectedIntentId?: string | undefined;\n /**\n * Optimistic concurrency: when given, the error only records against\n * the intent revision the pass read — a re-enqueued move (new\n * updatedAt) is never parked by a stale in-flight failure.\n */\n expectedUpdatedAt?: string | undefined;\n message: string;\n stillCurrent?: (() => boolean) | undefined;\n /**\n * Server proof that a cited container was deleted (coded 404 or\n * `container_unavailable` 409): the intent parks terminally as\n * `unavailable`, outside every replay, until the tombstone cascade\n * retargets it or a re-enqueue replaces it. Outranks `denied`: a\n * restored permission cannot revive a deleted container.\n */\n unavailable?: boolean | undefined;\n },\n ): Promise<void> {\n await getClientSQLitePersistenceRuntime(execSql).guardedTransaction(\n async (db) => {\n const updatedAt = new Date().toISOString();\n await db\n .update(documentMoveIntents)\n .set({\n lastAttemptedAt: updatedAt,\n lastError: input.message,\n syncStatus: resolveRecordedSyncStatus(input),\n updatedAt,\n })\n .where(\n and(\n eq(documentMoveIntents.documentId, input.documentId),\n // Blocked/denied rows must stay updatable: a retried intent\n // records its fresh outcome, and a transient failure flips it\n // back to pending instead of freezing it forever.\n inArray(documentMoveIntents.syncStatus, [\n \"pending\",\n \"blocked\",\n \"denied\",\n ]),\n inArray(\n documentMoveIntents.intentType,\n DOCUMENT_PLACEMENT_INTENT_TYPES,\n ),\n ...(input.expectedIntentId\n ? [eq(documentMoveIntents.id, input.expectedIntentId)]\n : []),\n ...(input.expectedUpdatedAt\n ? [eq(documentMoveIntents.updatedAt, input.expectedUpdatedAt)]\n : []),\n ),\n )\n .run();\n },\n input.stillCurrent ?? (() => true),\n { behavior: \"immediate\" },\n );\n },\n /**\n * Evidence for the org-access-restored re-arm gate: a parked\n * permission-denied move proves a queued write was refused.\n */\n async hasDeniedMoveIntents(\n execSql: ExecSql,\n input?: { organizationId?: string },\n ): Promise<boolean> {\n await sqlDocumentMoveIntentPersistence.ensureSchema(execSql);\n await ensureSqlTables(execSql, [\n ...documentProjectionTables,\n ...containerTables,\n ]);\n const rows = await execSql(\n `SELECT intent.document_id AS document_id\n FROM document_move_intents intent\n ${input?.organizationId ? DENIED_INTENT_ORGANIZATION_JOINS_SQL : \"\"}\n WHERE intent.sync_status = 'denied'\n AND intent.intent_type IN (?, ?)\n ${input?.organizationId ? `AND (${DENIED_INTENT_ORGANIZATION_SQL} = ? OR ${DENIED_INTENT_ORGANIZATION_SQL} IS NULL)` : \"\"}\n LIMIT 1`,\n input?.organizationId\n ? [...DOCUMENT_PLACEMENT_INTENT_TYPES, input.organizationId]\n : DOCUMENT_PLACEMENT_INTENT_TYPES,\n );\n return rows.length > 0;\n },\n /**\n * Flip parked permission-denied moves back to pending so the re-armed\n * structural pass replays them (access restored, or a manual retry scoped\n * to one document).\n */\n async resetDeniedMoveIntents(\n execSql: ExecSql,\n input?: { localId?: string; organizationId?: string },\n ): Promise<void> {\n await sqlDocumentMoveIntentPersistence.ensureSchema(execSql);\n await ensureSqlTables(execSql, [\n ...documentProjectionTables,\n ...containerTables,\n ]);\n await runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n // Organization scoping resolves each intent's organization through the\n // container joins above: restoring one organization's access must not\n // un-park moves that another organization still denies. Rows with no\n // resolvable organization are included (device-first shapes).\n await lockedExecSql(\n `UPDATE document_move_intents\n SET sync_status = 'pending', updated_at = ?\n WHERE sync_status = 'denied'\n AND intent_type IN (?, ?)\n ${input?.localId ? \"AND local_id = ?\" : \"\"}\n ${\n input?.organizationId\n ? `AND local_id IN (\n SELECT intent.local_id FROM document_move_intents intent\n ${DENIED_INTENT_ORGANIZATION_JOINS_SQL}\n WHERE ${DENIED_INTENT_ORGANIZATION_SQL} = ?\n OR ${DENIED_INTENT_ORGANIZATION_SQL} IS NULL\n )`\n : \"\"\n }`,\n [\n new Date().toISOString(),\n ...DOCUMENT_PLACEMENT_INTENT_TYPES,\n ...(input?.localId ? [input.localId] : []),\n ...(input?.organizationId ? [input.organizationId] : []),\n ],\n );\n });\n },\n};\n"]}
1
+ {"version":3,"file":"documentMoveIntentPersistence.js","sourceRoot":"","sources":["documentMoveIntentPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, asc, eq, inArray } from \"drizzle-orm\";\nimport { documentIntentLinkTargets } from \"../../sqlite/documentPlacementIntentSchema\";\nimport {\n containerTables,\n documentPlacementIntentTables,\n documentProjectionTables,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport {\n enqueueDocumentLinkIntent,\n enqueueDocumentMoveIntent,\n enqueueDocumentUnlinkIntent,\n loadDocumentIntentLinkTargets,\n} from \"./documentPlacementIntentEnqueue\";\n\nexport const DOCUMENT_MOVE_INTENT_TYPE = \"document.move\";\nconst placementLane = inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement);\nexport const DOCUMENT_LINK_INTENT_TYPE = \"document.link\";\nconst DOCUMENT_PLACEMENT_INTENT_TYPES = [\n DOCUMENT_MOVE_INTENT_TYPE,\n DOCUMENT_LINK_INTENT_TYPE,\n];\n\n// Resolves the organization a parked move belongs to, in confidence order:\n// the move's target container, the document's preserved projection\n// attribution, then the container the document currently sits in. Normal\n// saves routinely leave projection attribution empty (deferred-tail\n// reporting resolves through the same container join), so the container rows\n// carry the common case; rows with no resolvable organization stay in every\n// scope (device-first shapes, matching orphan priming).\nconst DENIED_INTENT_ORGANIZATION_SQL = `COALESCE(\n NULLIF(target_container.organization_id, ''),\n NULLIF(projection.organization_id, ''),\n NULLIF(projection_container.organization_id, '')\n)`;\n\nconst DENIED_INTENT_ORGANIZATION_JOINS_SQL = `\n LEFT JOIN containers target_container\n ON target_container.id = intent.target_container_id\n LEFT JOIN document_projection projection\n ON projection.local_id = intent.local_id\n LEFT JOIN containers projection_container\n ON projection_container.id = projection.container_id`;\n\n/**\n * - `pending`: replays on every structural pass.\n * - `blocked`: the last pass could not proceed locally (missing local\n * document / destination); replays, since hydration can heal it.\n * - `denied`: parked on a 403 until the access-restored replay (row 7).\n * - `unavailable`: terminal — after a projection refresh the destination\n * itself is proven deleted (coded 404 on its own projection probe). No\n * mutation 409 is ever terminal: with the destination live it can only\n * name an ancestor or source, and the next pass's fresh paths outrun it.\n * Container ids never return, so no replay can commit the intent as\n * written; it leaves the replay set until the local container tombstone\n * cascade retargets it or the user re-enqueues the move.\n */\nexport type DocumentMoveIntentSyncStatus =\n | \"pending\"\n | \"blocked\"\n | \"denied\"\n | \"unavailable\";\n\nexport interface DocumentMoveIntentRecord {\n additionalLinkContainerIds?: readonly string[] | undefined;\n removedLinkContainerIds?: readonly string[] | undefined;\n id: string;\n documentId: string;\n intentType:\n | typeof DOCUMENT_MOVE_INTENT_TYPE\n | typeof DOCUMENT_LINK_INTENT_TYPE;\n lastAttemptedAt: string | null;\n lastError: string | null;\n localId: string;\n replaceLinkedContainers: boolean;\n sourceContainerId: string | null;\n syncStatus: DocumentMoveIntentSyncStatus;\n targetContainerId: string;\n createdAt: string;\n updatedAt: string;\n}\n\ninterface SelectedDocumentMoveIntentRecord {\n id: string | null;\n documentId: string;\n intentType: string;\n lastAttemptedAt: string | null;\n lastError: string | null;\n localId: string | null;\n replaceLinkedContainers: boolean;\n sourceContainerId: string | null;\n syncStatus: string;\n targetContainerId: string;\n createdAt: string;\n updatedAt: string;\n}\n\nfunction parseDocumentMoveIntentSyncStatus(\n value: unknown,\n): DocumentMoveIntentSyncStatus {\n if (value === \"blocked\" || value === \"denied\" || value === \"unavailable\") {\n return value;\n }\n return \"pending\";\n}\n\nfunction resolveRecordedSyncStatus(input: {\n blocked?: boolean | undefined;\n denied?: boolean | undefined;\n unavailable?: boolean | undefined;\n}): DocumentMoveIntentSyncStatus {\n if (input.unavailable) return \"unavailable\";\n if (input.denied) return \"denied\";\n return input.blocked ? \"blocked\" : \"pending\";\n}\n\nfunction mapDocumentMoveIntentRecord(\n row: SelectedDocumentMoveIntentRecord,\n): DocumentMoveIntentRecord {\n return {\n id: String(row.id ?? \"\"),\n documentId: row.documentId,\n intentType:\n row.intentType === DOCUMENT_LINK_INTENT_TYPE\n ? DOCUMENT_LINK_INTENT_TYPE\n : DOCUMENT_MOVE_INTENT_TYPE,\n lastAttemptedAt: row.lastAttemptedAt,\n lastError: row.lastError,\n // Placement intents always carry their local document row.\n localId: row.localId ?? \"\",\n replaceLinkedContainers: Boolean(row.replaceLinkedContainers),\n sourceContainerId: row.sourceContainerId,\n syncStatus: parseDocumentMoveIntentSyncStatus(row.syncStatus),\n targetContainerId: row.targetContainerId,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\nexport const sqlDocumentMoveIntentPersistence = {\n async ensureSchema(execSql: ExecSql): Promise<void> {\n await ensureSqlTables(execSql, documentPlacementIntentTables);\n },\n\n enqueueMoveIntent: enqueueDocumentMoveIntent,\n enqueueLinkIntent: enqueueDocumentLinkIntent,\n enqueueUnlinkIntent: enqueueDocumentUnlinkIntent,\n\n async listPendingMoveIntents(\n execSql: ExecSql,\n ): Promise<DocumentMoveIntentRecord[]> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureSqlTables(lockedExecSql, documentPlacementIntentTables);\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const rows = await tx\n .select({\n id: localIntents.id,\n documentId: localIntents.subjectId,\n intentType: localIntents.intentType,\n lastAttemptedAt: localIntents.lastAttemptedAt,\n lastError: localIntents.lastError,\n localId: localIntents.localId,\n replaceLinkedContainers: localIntents.replaceLinkedContainers,\n sourceContainerId: localIntents.sourceContainerId,\n syncStatus: localIntents.syncStatus,\n targetContainerId: localIntents.targetContainerId,\n createdAt: localIntents.createdAt,\n updatedAt: localIntents.updatedAt,\n })\n .from(localIntents)\n // Blocked intents replay too: \"blocked\" names the reason the last\n // attempt could not proceed (missing local doc / destination), not a\n // terminal verdict. The blocking condition can heal after hydration or\n // recovery, and re-checking is cheap — a still-blocked intent simply\n // re-records its reason without counting as lane progress.\n // Unavailable intents never replay: the server proved a cited container\n // is gone, so every replay would re-issue the same doomed requests\n // (#2278 #4). Only the tombstone cascade or a re-enqueue revives them.\n .where(\n and(\n placementLane,\n inArray(localIntents.syncStatus, [\"pending\", \"blocked\"]),\n inArray(\n localIntents.intentType,\n DOCUMENT_PLACEMENT_INTENT_TYPES,\n ),\n ),\n )\n .orderBy(asc(localIntents.createdAt));\n\n return Promise.all(\n rows.map(async (row) => {\n const targets = await loadDocumentIntentLinkTargets(\n lockedExecSql,\n row.id ?? \"\",\n );\n const added = targets\n .filter((target) => target.operation === \"link\")\n .map((target) => target.containerId)\n .sort();\n const removed = targets\n .filter((target) => target.operation === \"unlink\")\n .map((target) => target.containerId)\n .sort();\n return {\n ...mapDocumentMoveIntentRecord(row),\n ...(added.length ? { additionalLinkContainerIds: added } : {}),\n ...(removed.length ? { removedLinkContainerIds: removed } : {}),\n };\n }),\n );\n },\n );\n });\n },\n\n async markMoveIntentSynced(\n execSql: ExecSql,\n input: {\n documentId: string;\n expectedIntentId?: string | undefined;\n expectedUpdatedAt: string;\n },\n ): Promise<boolean> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const deleted = await lockedExecSql(\n `DELETE FROM local_intents\n WHERE lane = ?\n AND subject_id = ?\n AND intent_type IN (?, ?)\n AND updated_at = ?\n ${input.expectedIntentId ? \"AND id = ?\" : \"\"}\n RETURNING id`,\n [\n LOCAL_INTENT_LANES.documentPlacement,\n input.documentId,\n ...DOCUMENT_PLACEMENT_INTENT_TYPES,\n input.expectedUpdatedAt,\n ...(input.expectedIntentId ? [input.expectedIntentId] : []),\n ],\n );\n for (const { id } of deleted) {\n await getClientSQLitePersistenceRuntime(lockedExecSql)\n .db.delete(documentIntentLinkTargets)\n .where(eq(documentIntentLinkTargets.intentId, String(id)))\n .run();\n }\n return deleted.length > 0;\n });\n },\n\n async recordMoveIntentError(\n execSql: ExecSql,\n input: {\n blocked?: boolean | undefined;\n /**\n * A permission denial (403): the intent parks as `denied` — excluded\n * from routine structural replays — until the org-access-restored\n * signal or a manual retry flips it back to pending (edge-case row 7).\n */\n denied?: boolean | undefined;\n documentId: string;\n /** Exact intent revision read by the pass; preferred over wall-clock CAS. */\n expectedIntentId?: string | undefined;\n /**\n * Optimistic concurrency: when given, the error only records against\n * the intent revision the pass read — a re-enqueued move (new\n * updatedAt) is never parked by a stale in-flight failure.\n */\n expectedUpdatedAt?: string | undefined;\n message: string;\n stillCurrent?: (() => boolean) | undefined;\n /**\n * Server proof that a cited container was deleted (coded 404 or\n * `container_unavailable` 409): the intent parks terminally as\n * `unavailable`, outside every replay, until the tombstone cascade\n * retargets it or a re-enqueue replaces it. Outranks `denied`: a\n * restored permission cannot revive a deleted container.\n */\n unavailable?: boolean | undefined;\n },\n ): Promise<void> {\n await getClientSQLitePersistenceRuntime(execSql).guardedTransaction(\n async (db) => {\n const updatedAt = new Date().toISOString();\n await db\n .update(localIntents)\n .set({\n lastAttemptedAt: updatedAt,\n lastError: input.message,\n syncStatus: resolveRecordedSyncStatus(input),\n updatedAt,\n })\n .where(\n and(\n placementLane,\n eq(localIntents.subjectId, input.documentId),\n // Blocked/denied rows must stay updatable: a retried intent\n // records its fresh outcome, and a transient failure flips it\n // back to pending instead of freezing it forever.\n inArray(localIntents.syncStatus, [\n \"pending\",\n \"blocked\",\n \"denied\",\n ]),\n inArray(localIntents.intentType, DOCUMENT_PLACEMENT_INTENT_TYPES),\n ...(input.expectedIntentId\n ? [eq(localIntents.id, input.expectedIntentId)]\n : []),\n ...(input.expectedUpdatedAt\n ? [eq(localIntents.updatedAt, input.expectedUpdatedAt)]\n : []),\n ),\n )\n .run();\n },\n input.stillCurrent ?? (() => true),\n { behavior: \"immediate\" },\n );\n },\n /**\n * Evidence for the org-access-restored re-arm gate: a parked\n * permission-denied move proves a queued write was refused.\n */\n async hasDeniedMoveIntents(\n execSql: ExecSql,\n input?: { organizationId?: string },\n ): Promise<boolean> {\n await sqlDocumentMoveIntentPersistence.ensureSchema(execSql);\n await ensureSqlTables(execSql, [\n ...documentProjectionTables,\n ...containerTables,\n ]);\n const rows = await execSql(\n `SELECT intent.subject_id AS document_id\n FROM local_intents intent\n ${input?.organizationId ? DENIED_INTENT_ORGANIZATION_JOINS_SQL : \"\"}\n WHERE intent.lane = ?\n AND intent.sync_status = 'denied'\n AND intent.intent_type IN (?, ?)\n ${input?.organizationId ? `AND (${DENIED_INTENT_ORGANIZATION_SQL} = ? OR ${DENIED_INTENT_ORGANIZATION_SQL} IS NULL)` : \"\"}\n LIMIT 1`,\n [\n LOCAL_INTENT_LANES.documentPlacement,\n ...DOCUMENT_PLACEMENT_INTENT_TYPES,\n ...(input?.organizationId ? [input.organizationId] : []),\n ],\n );\n return rows.length > 0;\n },\n /**\n * Flip parked permission-denied moves back to pending so the re-armed\n * structural pass replays them (access restored, or a manual retry scoped\n * to one document).\n */\n async resetDeniedMoveIntents(\n execSql: ExecSql,\n input?: { localId?: string; organizationId?: string },\n ): Promise<void> {\n await sqlDocumentMoveIntentPersistence.ensureSchema(execSql);\n await ensureSqlTables(execSql, [\n ...documentProjectionTables,\n ...containerTables,\n ]);\n await runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n // Organization scoping resolves each intent's organization through the\n // container joins above: restoring one organization's access must not\n // un-park moves that another organization still denies. Rows with no\n // resolvable organization are included (device-first shapes).\n await lockedExecSql(\n `UPDATE local_intents\n SET sync_status = 'pending', updated_at = ?\n WHERE lane = ?\n AND sync_status = 'denied'\n AND intent_type IN (?, ?)\n ${input?.localId ? \"AND local_id = ?\" : \"\"}\n ${\n input?.organizationId\n ? `AND local_id IN (\n SELECT intent.local_id FROM local_intents intent\n ${DENIED_INTENT_ORGANIZATION_JOINS_SQL}\n WHERE intent.lane = ?\n AND (${DENIED_INTENT_ORGANIZATION_SQL} = ?\n OR ${DENIED_INTENT_ORGANIZATION_SQL} IS NULL)\n )`\n : \"\"\n }`,\n [\n new Date().toISOString(),\n LOCAL_INTENT_LANES.documentPlacement,\n ...DOCUMENT_PLACEMENT_INTENT_TYPES,\n ...(input?.localId ? [input.localId] : []),\n ...(input?.organizationId\n ? [LOCAL_INTENT_LANES.documentPlacement, input.organizationId]\n : []),\n ],\n );\n });\n },\n};\n"]}
@@ -1,7 +1,7 @@
1
1
  import { and, eq } from "drizzle-orm";
2
2
  import { uniqueSortedStrings } from "../../documents/shared/readers.js";
3
3
  import { documentIntentLinkTargets } from "../../sqlite/documentPlacementIntentSchema.js";
4
- import { documentContainerProjection, documentContainerProjectionTables, documentMoveIntents, documentMoveIntentTables, } from "../../sqlite/schema.js";
4
+ import { documentContainerProjection, documentContainerProjectionTables, documentPlacementIntentTables, inLocalIntentLane, LOCAL_INTENT_LANES, LOCAL_INTENT_SUBJECT, localIntents, } from "../../sqlite/schema.js";
5
5
  import { getClientSQLitePersistenceRuntime } from "../../sqlite/sqlitePersistenceRuntime.js";
6
6
  import { ensureSqlTables, runSerializedSqlMutation, } from "../../sqlite/sqlSchema.js";
7
7
  export async function loadDocumentIntentLinkTargets(execSql, intentId) {
@@ -19,7 +19,7 @@ async function loadSurvivingMoveTarget(execSql, previous) {
19
19
  const [link] = await getClientSQLitePersistenceRuntime(execSql)
20
20
  .db.select({ containerId: documentContainerProjection.containerId })
21
21
  .from(documentContainerProjection)
22
- .where(and(eq(documentContainerProjection.documentId, previous.documentId), eq(documentContainerProjection.containerId, previous.targetContainerId)))
22
+ .where(and(eq(documentContainerProjection.documentId, previous.subjectId), eq(documentContainerProjection.containerId, previous.targetContainerId)))
23
23
  .limit(1);
24
24
  return link?.containerId ?? null;
25
25
  }
@@ -83,14 +83,14 @@ function resolvePlacement(input, previous, operation) {
83
83
  }
84
84
  async function enqueuePlacementIntent(execSql, input, operation) {
85
85
  await runSerializedSqlMutation(execSql, async (lockedExecSql) => {
86
- await ensureSqlTables(lockedExecSql, documentMoveIntentTables);
86
+ await ensureSqlTables(lockedExecSql, documentPlacementIntentTables);
87
87
  await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);
88
88
  const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);
89
89
  await runtime.transaction(async (tx) => {
90
90
  const [previous] = await tx
91
91
  .select()
92
- .from(documentMoveIntents)
93
- .where(eq(documentMoveIntents.documentId, input.documentId))
92
+ .from(localIntents)
93
+ .where(and(inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement), eq(localIntents.subjectId, input.documentId)))
94
94
  .limit(1);
95
95
  const previousTargets = previous?.id
96
96
  ? await loadDocumentIntentLinkTargets(lockedExecSql, previous.id)
@@ -105,7 +105,8 @@ async function enqueuePlacementIntent(execSql, input, operation) {
105
105
  // user additions may be replayed, never stale server-discovered links.
106
106
  const next = {
107
107
  id,
108
- documentId: input.documentId,
108
+ lane: LOCAL_INTENT_LANES.documentPlacement,
109
+ subjectId: input.documentId,
109
110
  localId: input.localId,
110
111
  intentType: operation === "move" || previous?.intentType === "document.move"
111
112
  ? "document.move"
@@ -117,10 +118,10 @@ async function enqueuePlacementIntent(execSql, input, operation) {
117
118
  ...resolvePlacement(input, previous, operation),
118
119
  };
119
120
  await tx
120
- .insert(documentMoveIntents)
121
+ .insert(localIntents)
121
122
  .values({ ...next, createdAt: updatedAt })
122
123
  .onConflictDoUpdate({
123
- target: documentMoveIntents.documentId,
124
+ target: LOCAL_INTENT_SUBJECT,
124
125
  set: next,
125
126
  })
126
127
  .run();
@@ -1 +1 @@
1
- {"version":3,"file":"documentPlacementIntentEnqueue.js","sourceRoot":"","sources":["documentPlacementIntentEnqueue.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport { uniqueSortedStrings } from \"../../documents/shared/readers\";\nimport { documentIntentLinkTargets } from \"../../sqlite/documentPlacementIntentSchema\";\nimport {\n documentContainerProjection,\n documentContainerProjectionTables,\n documentMoveIntents,\n documentMoveIntentTables,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\n\ninterface PlacementIntentInput {\n id?: string | undefined;\n documentId: string;\n localId: string;\n replaceLinkedContainers?: boolean | undefined;\n removedContainerId?: string | undefined;\n sourceContainerId?: string | null | undefined;\n targetContainerId: string;\n}\n\nexport async function loadDocumentIntentLinkTargets(\n execSql: ExecSql,\n intentId: string,\n) {\n return getClientSQLitePersistenceRuntime(execSql)\n .db.select({\n containerId: documentIntentLinkTargets.containerId,\n operation: documentIntentLinkTargets.operation,\n })\n .from(documentIntentLinkTargets)\n .where(eq(documentIntentLinkTargets.intentId, intentId));\n}\n\ntype LinkTarget = { containerId: string; operation: \"link\" | \"unlink\" };\n\nasync function loadSurvivingMoveTarget(\n execSql: ExecSql,\n previous: typeof documentMoveIntents.$inferSelect | undefined,\n): Promise<string | null> {\n if (previous?.intentType !== \"document.move\") return null;\n const [link] = await getClientSQLitePersistenceRuntime(execSql)\n .db.select({ containerId: documentContainerProjection.containerId })\n .from(documentContainerProjection)\n .where(\n and(\n eq(documentContainerProjection.documentId, previous.documentId),\n eq(documentContainerProjection.containerId, previous.targetContainerId),\n ),\n )\n .limit(1);\n return link?.containerId ?? null;\n}\n\nfunction resolveTargets(\n input: PlacementIntentInput,\n previous: LinkTarget[],\n operation: \"move\" | \"link\" | \"unlink\",\n previousMoveTarget: string | null,\n): LinkTarget[] {\n const targets = new Map(\n previous.map((target) => [target.containerId, target.operation]),\n );\n if (operation === \"link\") targets.set(input.targetContainerId, \"link\");\n else if (operation === \"unlink\") {\n if (!input.removedContainerId)\n throw new Error(\"Unlink intent requires a removed container\");\n targets.set(input.removedContainerId, \"unlink\");\n } else if (input.replaceLinkedContainers) targets.clear();\n else {\n if (\n previousMoveTarget &&\n previousMoveTarget !== input.sourceContainerId &&\n previousMoveTarget !== input.targetContainerId &&\n targets.get(previousMoveTarget) !== \"unlink\"\n ) {\n targets.set(previousMoveTarget, \"link\");\n }\n targets.delete(input.targetContainerId);\n if (\n input.sourceContainerId &&\n input.sourceContainerId !== input.targetContainerId\n )\n targets.set(input.sourceContainerId, \"unlink\");\n }\n return uniqueSortedStrings([...targets.keys()]).map((containerId) => ({\n containerId,\n operation: targets.get(containerId) ?? \"link\",\n }));\n}\n\nfunction resolvePlacement(\n input: PlacementIntentInput,\n previous: typeof documentMoveIntents.$inferSelect | undefined,\n operation: \"move\" | \"link\" | \"unlink\",\n) {\n if (operation === \"unlink\") {\n return {\n sourceContainerId:\n previous?.intentType === \"document.move\"\n ? previous.sourceContainerId\n : input.targetContainerId,\n targetContainerId:\n previous?.intentType === \"document.move\" &&\n previous.targetContainerId !== input.removedContainerId\n ? previous.targetContainerId\n : input.targetContainerId,\n replaceLinkedContainers: previous?.replaceLinkedContainers ?? false,\n };\n }\n const linkOnly = operation === \"link\";\n const sourceContainerId =\n previous?.intentType === \"document.move\"\n ? (previous.sourceContainerId ?? input.sourceContainerId ?? null)\n : (input.sourceContainerId ?? null);\n return {\n sourceContainerId: linkOnly\n ? (previous?.sourceContainerId ?? input.sourceContainerId ?? null)\n : sourceContainerId,\n targetContainerId: linkOnly\n ? (previous?.targetContainerId ??\n input.sourceContainerId ??\n input.targetContainerId)\n : input.targetContainerId,\n replaceLinkedContainers:\n previous?.replaceLinkedContainers ||\n (input.replaceLinkedContainers ?? false),\n };\n}\n\nasync function enqueuePlacementIntent(\n execSql: ExecSql,\n input: PlacementIntentInput,\n operation: \"move\" | \"link\" | \"unlink\",\n): Promise<void> {\n await runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureSqlTables(lockedExecSql, documentMoveIntentTables);\n await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n await runtime.transaction(async (tx) => {\n const [previous] = await tx\n .select()\n .from(documentMoveIntents)\n .where(eq(documentMoveIntents.documentId, input.documentId))\n .limit(1);\n const previousTargets = previous?.id\n ? await loadDocumentIntentLinkTargets(lockedExecSql, previous.id)\n : [];\n const targets = resolveTargets(\n input,\n previousTargets,\n operation,\n operation === \"move\"\n ? await loadSurvivingMoveTarget(lockedExecSql, previous)\n : null,\n );\n const id = input.id ?? crypto.randomUUID();\n const updatedAt = new Date().toISOString();\n // An additive intent retains the active placement and any pending move.\n // Its targets are separate from the discovery projection: only explicit\n // user additions may be replayed, never stale server-discovered links.\n const next = {\n id,\n documentId: input.documentId,\n localId: input.localId,\n intentType:\n operation === \"move\" || previous?.intentType === \"document.move\"\n ? \"document.move\"\n : \"document.link\",\n lastAttemptedAt: null,\n lastError: null,\n syncStatus: \"pending\",\n updatedAt,\n ...resolvePlacement(input, previous, operation),\n };\n await tx\n .insert(documentMoveIntents)\n .values({ ...next, createdAt: updatedAt })\n .onConflictDoUpdate({\n target: documentMoveIntents.documentId,\n set: next,\n })\n .run();\n if (previous?.id)\n await tx\n .delete(documentIntentLinkTargets)\n .where(eq(documentIntentLinkTargets.intentId, previous.id))\n .run();\n if (targets.length)\n await tx\n .insert(documentIntentLinkTargets)\n .values(targets.map((target) => ({ intentId: id, ...target })))\n .run();\n });\n });\n}\n\nexport function enqueueDocumentMoveIntent(\n execSql: ExecSql,\n input: PlacementIntentInput,\n): Promise<void> {\n return enqueuePlacementIntent(execSql, input, \"move\");\n}\n\nexport function enqueueDocumentLinkIntent(\n execSql: ExecSql,\n input: PlacementIntentInput & { sourceContainerId: string },\n): Promise<void> {\n return enqueuePlacementIntent(execSql, input, \"link\");\n}\n\nexport function enqueueDocumentUnlinkIntent(\n execSql: ExecSql,\n input: PlacementIntentInput & { removedContainerId: string },\n): Promise<void> {\n return enqueuePlacementIntent(execSql, input, \"unlink\");\n}\n"]}
1
+ {"version":3,"file":"documentPlacementIntentEnqueue.js","sourceRoot":"","sources":["documentPlacementIntentEnqueue.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport { uniqueSortedStrings } from \"../../documents/shared/readers\";\nimport { documentIntentLinkTargets } from \"../../sqlite/documentPlacementIntentSchema\";\nimport {\n documentContainerProjection,\n documentContainerProjectionTables,\n documentPlacementIntentTables,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n LOCAL_INTENT_SUBJECT,\n localIntents,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\n\ninterface PlacementIntentInput {\n id?: string | undefined;\n documentId: string;\n localId: string;\n replaceLinkedContainers?: boolean | undefined;\n removedContainerId?: string | undefined;\n sourceContainerId?: string | null | undefined;\n targetContainerId: string;\n}\n\nexport async function loadDocumentIntentLinkTargets(\n execSql: ExecSql,\n intentId: string,\n) {\n return getClientSQLitePersistenceRuntime(execSql)\n .db.select({\n containerId: documentIntentLinkTargets.containerId,\n operation: documentIntentLinkTargets.operation,\n })\n .from(documentIntentLinkTargets)\n .where(eq(documentIntentLinkTargets.intentId, intentId));\n}\n\ntype LinkTarget = { containerId: string; operation: \"link\" | \"unlink\" };\n\nasync function loadSurvivingMoveTarget(\n execSql: ExecSql,\n previous: typeof localIntents.$inferSelect | undefined,\n): Promise<string | null> {\n if (previous?.intentType !== \"document.move\") return null;\n const [link] = await getClientSQLitePersistenceRuntime(execSql)\n .db.select({ containerId: documentContainerProjection.containerId })\n .from(documentContainerProjection)\n .where(\n and(\n eq(documentContainerProjection.documentId, previous.subjectId),\n eq(documentContainerProjection.containerId, previous.targetContainerId),\n ),\n )\n .limit(1);\n return link?.containerId ?? null;\n}\n\nfunction resolveTargets(\n input: PlacementIntentInput,\n previous: LinkTarget[],\n operation: \"move\" | \"link\" | \"unlink\",\n previousMoveTarget: string | null,\n): LinkTarget[] {\n const targets = new Map(\n previous.map((target) => [target.containerId, target.operation]),\n );\n if (operation === \"link\") targets.set(input.targetContainerId, \"link\");\n else if (operation === \"unlink\") {\n if (!input.removedContainerId)\n throw new Error(\"Unlink intent requires a removed container\");\n targets.set(input.removedContainerId, \"unlink\");\n } else if (input.replaceLinkedContainers) targets.clear();\n else {\n if (\n previousMoveTarget &&\n previousMoveTarget !== input.sourceContainerId &&\n previousMoveTarget !== input.targetContainerId &&\n targets.get(previousMoveTarget) !== \"unlink\"\n ) {\n targets.set(previousMoveTarget, \"link\");\n }\n targets.delete(input.targetContainerId);\n if (\n input.sourceContainerId &&\n input.sourceContainerId !== input.targetContainerId\n )\n targets.set(input.sourceContainerId, \"unlink\");\n }\n return uniqueSortedStrings([...targets.keys()]).map((containerId) => ({\n containerId,\n operation: targets.get(containerId) ?? \"link\",\n }));\n}\n\nfunction resolvePlacement(\n input: PlacementIntentInput,\n previous: typeof localIntents.$inferSelect | undefined,\n operation: \"move\" | \"link\" | \"unlink\",\n) {\n if (operation === \"unlink\") {\n return {\n sourceContainerId:\n previous?.intentType === \"document.move\"\n ? previous.sourceContainerId\n : input.targetContainerId,\n targetContainerId:\n previous?.intentType === \"document.move\" &&\n previous.targetContainerId !== input.removedContainerId\n ? previous.targetContainerId\n : input.targetContainerId,\n replaceLinkedContainers: previous?.replaceLinkedContainers ?? false,\n };\n }\n const linkOnly = operation === \"link\";\n const sourceContainerId =\n previous?.intentType === \"document.move\"\n ? (previous.sourceContainerId ?? input.sourceContainerId ?? null)\n : (input.sourceContainerId ?? null);\n return {\n sourceContainerId: linkOnly\n ? (previous?.sourceContainerId ?? input.sourceContainerId ?? null)\n : sourceContainerId,\n targetContainerId: linkOnly\n ? (previous?.targetContainerId ??\n input.sourceContainerId ??\n input.targetContainerId)\n : input.targetContainerId,\n replaceLinkedContainers:\n previous?.replaceLinkedContainers ||\n (input.replaceLinkedContainers ?? false),\n };\n}\n\nasync function enqueuePlacementIntent(\n execSql: ExecSql,\n input: PlacementIntentInput,\n operation: \"move\" | \"link\" | \"unlink\",\n): Promise<void> {\n await runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureSqlTables(lockedExecSql, documentPlacementIntentTables);\n await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n await runtime.transaction(async (tx) => {\n const [previous] = await tx\n .select()\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n eq(localIntents.subjectId, input.documentId),\n ),\n )\n .limit(1);\n const previousTargets = previous?.id\n ? await loadDocumentIntentLinkTargets(lockedExecSql, previous.id)\n : [];\n const targets = resolveTargets(\n input,\n previousTargets,\n operation,\n operation === \"move\"\n ? await loadSurvivingMoveTarget(lockedExecSql, previous)\n : null,\n );\n const id = input.id ?? crypto.randomUUID();\n const updatedAt = new Date().toISOString();\n // An additive intent retains the active placement and any pending move.\n // Its targets are separate from the discovery projection: only explicit\n // user additions may be replayed, never stale server-discovered links.\n const next = {\n id,\n lane: LOCAL_INTENT_LANES.documentPlacement,\n subjectId: input.documentId,\n localId: input.localId,\n intentType:\n operation === \"move\" || previous?.intentType === \"document.move\"\n ? \"document.move\"\n : \"document.link\",\n lastAttemptedAt: null,\n lastError: null,\n syncStatus: \"pending\",\n updatedAt,\n ...resolvePlacement(input, previous, operation),\n };\n await tx\n .insert(localIntents)\n .values({ ...next, createdAt: updatedAt })\n .onConflictDoUpdate({\n target: LOCAL_INTENT_SUBJECT,\n set: next,\n })\n .run();\n if (previous?.id)\n await tx\n .delete(documentIntentLinkTargets)\n .where(eq(documentIntentLinkTargets.intentId, previous.id))\n .run();\n if (targets.length)\n await tx\n .insert(documentIntentLinkTargets)\n .values(targets.map((target) => ({ intentId: id, ...target })))\n .run();\n });\n });\n}\n\nexport function enqueueDocumentMoveIntent(\n execSql: ExecSql,\n input: PlacementIntentInput,\n): Promise<void> {\n return enqueuePlacementIntent(execSql, input, \"move\");\n}\n\nexport function enqueueDocumentLinkIntent(\n execSql: ExecSql,\n input: PlacementIntentInput & { sourceContainerId: string },\n): Promise<void> {\n return enqueuePlacementIntent(execSql, input, \"link\");\n}\n\nexport function enqueueDocumentUnlinkIntent(\n execSql: ExecSql,\n input: PlacementIntentInput & { removedContainerId: string },\n): Promise<void> {\n return enqueuePlacementIntent(execSql, input, \"unlink\");\n}\n"]}
@@ -1,14 +1,14 @@
1
- import { eq } from "drizzle-orm";
2
- import { documentMoveIntents, documentMoveIntentTables, } from "../../sqlite/schema.js";
1
+ import { and, eq } from "drizzle-orm";
2
+ import { documentPlacementIntentTables, inLocalIntentLane, LOCAL_INTENT_LANES, localIntents, } from "../../sqlite/schema.js";
3
3
  import { getClientSQLitePersistenceRuntime } from "../../sqlite/sqlitePersistenceRuntime.js";
4
4
  import { ensureSqlTables } from "../../sqlite/sqlSchema.js";
5
5
  /** Even denied or unavailable placement intent belongs to the local user. */
6
6
  export async function hasUnsettledDocumentPlacement(execSql, localId) {
7
- await ensureSqlTables(execSql, documentMoveIntentTables);
7
+ await ensureSqlTables(execSql, documentPlacementIntentTables);
8
8
  const [intent] = await getClientSQLitePersistenceRuntime(execSql)
9
- .db.select({ id: documentMoveIntents.id })
10
- .from(documentMoveIntents)
11
- .where(eq(documentMoveIntents.localId, localId))
9
+ .db.select({ id: localIntents.id })
10
+ .from(localIntents)
11
+ .where(and(inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement), eq(localIntents.localId, localId)))
12
12
  .limit(1);
13
13
  return intent !== undefined;
14
14
  }
@@ -1 +1 @@
1
- {"version":3,"file":"documentPurgePlacement.js","sourceRoot":"","sources":["documentPurgePlacement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EACL,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iCAAiC,EAAE,MAAM,uCAAuC,CAAC;AAC1F,OAAO,EAAgB,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEvE,6EAA6E;AAC7E,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,OAAgB,EAChB,OAAe;IAEf,MAAM,eAAe,CAAC,OAAO,EAAE,wBAAwB,CAAC,CAAC;IACzD,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,iCAAiC,CAAC,OAAO,CAAC;SAC9D,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,mBAAmB,CAAC,EAAE,EAAE,CAAC;SACzC,IAAI,CAAC,mBAAmB,CAAC;SACzB,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;SAC/C,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,OAAO,MAAM,KAAK,SAAS,CAAC;AAC9B,CAAC","sourcesContent":["import { eq } from \"drizzle-orm\";\nimport {\n documentMoveIntents,\n documentMoveIntentTables,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\n\n/** Even denied or unavailable placement intent belongs to the local user. */\nexport async function hasUnsettledDocumentPlacement(\n execSql: ExecSql,\n localId: string,\n): Promise<boolean> {\n await ensureSqlTables(execSql, documentMoveIntentTables);\n const [intent] = await getClientSQLitePersistenceRuntime(execSql)\n .db.select({ id: documentMoveIntents.id })\n .from(documentMoveIntents)\n .where(eq(documentMoveIntents.localId, localId))\n .limit(1);\n return intent !== undefined;\n}\n"]}
1
+ {"version":3,"file":"documentPurgePlacement.js","sourceRoot":"","sources":["documentPurgePlacement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EACL,6BAA6B,EAC7B,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,GACb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iCAAiC,EAAE,MAAM,uCAAuC,CAAC;AAC1F,OAAO,EAAgB,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEvE,6EAA6E;AAC7E,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,OAAgB,EAChB,OAAe;IAEf,MAAM,eAAe,CAAC,OAAO,EAAE,6BAA6B,CAAC,CAAC;IAC9D,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,iCAAiC,CAAC,OAAO,CAAC;SAC9D,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,YAAY,CAAC,EAAE,EAAE,CAAC;SAClC,IAAI,CAAC,YAAY,CAAC;SAClB,KAAK,CACJ,GAAG,CACD,iBAAiB,CAAC,kBAAkB,CAAC,iBAAiB,CAAC,EACvD,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAClC,CACF;SACA,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,OAAO,MAAM,KAAK,SAAS,CAAC;AAC9B,CAAC","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport {\n documentPlacementIntentTables,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\n\n/** Even denied or unavailable placement intent belongs to the local user. */\nexport async function hasUnsettledDocumentPlacement(\n execSql: ExecSql,\n localId: string,\n): Promise<boolean> {\n await ensureSqlTables(execSql, documentPlacementIntentTables);\n const [intent] = await getClientSQLitePersistenceRuntime(execSql)\n .db.select({ id: localIntents.id })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n eq(localIntents.localId, localId),\n ),\n )\n .limit(1);\n return intent !== undefined;\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { and, eq, inArray, sql } from "drizzle-orm";
2
- import { documentHistoryCheckpoints, documentHistoryUpdates, documentSyncFailures, documents, dormantContainerMetadata, } from "../../sqlite/schema.js";
2
+ import { documentHistoryCheckpoints, documentHistoryUpdates, documents, dormantContainerMetadata, } from "../../sqlite/schema.js";
3
3
  export const CONTAINER_METADATA_APP_KIND = "container-metadata";
4
4
  export async function retainDormantContainerMetadataInTransaction(tx, retained) {
5
5
  if (retained.length === 0) {
@@ -44,10 +44,6 @@ export async function deleteContainerMetadataDocumentRowsInTransaction(tx, conta
44
44
  .delete(documentHistoryUpdates)
45
45
  .where(and(eq(documentHistoryUpdates.appKind, CONTAINER_METADATA_APP_KIND), inArray(documentHistoryUpdates.localId, ids)))
46
46
  .run();
47
- await tx
48
- .delete(documentSyncFailures)
49
- .where(and(eq(documentSyncFailures.appKind, CONTAINER_METADATA_APP_KIND), inArray(documentSyncFailures.localId, ids)))
50
- .run();
51
47
  await clearDormantContainerMetadataInTransaction(tx, ids);
52
48
  }
53
49
  //# sourceMappingURL=dormantContainerMetadata.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dormantContainerMetadata.js","sourceRoot":"","sources":["dormantContainerMetadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,0BAA0B,EAC1B,sBAAsB,EACtB,oBAAoB,EACpB,SAAS,EACT,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAG7B,MAAM,CAAC,MAAM,2BAA2B,GAAG,oBAAoB,CAAC;AAQhE,MAAM,CAAC,KAAK,UAAU,2CAA2C,CAC/D,EAAgC,EAChC,QAA0D;IAE1D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO;IACT,CAAC;IAED,MAAM,EAAE;SACL,MAAM,CAAC,wBAAwB,CAAC;SAChC,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;SACrB,kBAAkB,CAAC;QAClB,MAAM,EAAE,wBAAwB,CAAC,WAAW;QAC5C,GAAG,EAAE;YACH,cAAc,EAAE,GAAG,CAAA,0BAA0B;YAC7C,UAAU,EAAE,GAAG,CAAA,sBAAsB;SACtC;KACF,CAAC;SACD,GAAG,EAAE,CAAC;AACX,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0CAA0C,CAC9D,EAAgC,EAChC,YAAmC;IAEnC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO;IACT,CAAC;IACD,MAAM,EAAE;SACL,MAAM,CAAC,wBAAwB,CAAC;SAChC,KAAK,CAAC,OAAO,CAAC,wBAAwB,CAAC,WAAW,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC;SACvE,GAAG,EAAE,CAAC;AACX,CAAC;AAED,6EAA6E;AAC7E,MAAM,CAAC,KAAK,UAAU,gDAAgD,CACpE,EAAgC,EAChC,YAAmC;IAEnC,MAAM,GAAG,GAAG,CAAC,GAAG,YAAY,CAAC,CAAC;IAC9B,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO;IACT,CAAC;IACD,MAAM,EAAE;SACL,MAAM,CAAC,SAAS,CAAC;SACjB,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,2BAA2B,CAAC,EAClD,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAChC,CACF;SACA,GAAG,EAAE,CAAC;IACT,MAAM,EAAE;SACL,MAAM,CAAC,0BAA0B,CAAC;SAClC,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,0BAA0B,CAAC,OAAO,EAAE,2BAA2B,CAAC,EACnE,OAAO,CAAC,0BAA0B,CAAC,OAAO,EAAE,GAAG,CAAC,CACjD,CACF;SACA,GAAG,EAAE,CAAC;IACT,MAAM,EAAE;SACL,MAAM,CAAC,sBAAsB,CAAC;SAC9B,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,sBAAsB,CAAC,OAAO,EAAE,2BAA2B,CAAC,EAC/D,OAAO,CAAC,sBAAsB,CAAC,OAAO,EAAE,GAAG,CAAC,CAC7C,CACF;SACA,GAAG,EAAE,CAAC;IACT,MAAM,EAAE;SACL,MAAM,CAAC,oBAAoB,CAAC;SAC5B,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,CAAC,EAC7D,OAAO,CAAC,oBAAoB,CAAC,OAAO,EAAE,GAAG,CAAC,CAC3C,CACF;SACA,GAAG,EAAE,CAAC;IACT,MAAM,0CAA0C,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;AAC5D,CAAC","sourcesContent":["import { and, eq, inArray, sql } from \"drizzle-orm\";\nimport {\n documentHistoryCheckpoints,\n documentHistoryUpdates,\n documentSyncFailures,\n documents,\n dormantContainerMetadata,\n} from \"../../sqlite/schema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\n\nexport const CONTAINER_METADATA_APP_KIND = \"container-metadata\";\n\ninterface DormantContainerMetadataRetention {\n readonly containerId: string;\n readonly organizationId: string;\n readonly retainedAt: string;\n}\n\nexport async function retainDormantContainerMetadataInTransaction(\n tx: ClientSQLiteTransactionScope,\n retained: ReadonlyArray<DormantContainerMetadataRetention>,\n): Promise<void> {\n if (retained.length === 0) {\n return;\n }\n\n await tx\n .insert(dormantContainerMetadata)\n .values([...retained])\n .onConflictDoUpdate({\n target: dormantContainerMetadata.containerId,\n set: {\n organizationId: sql`excluded.organization_id`,\n retainedAt: sql`excluded.retained_at`,\n },\n })\n .run();\n}\n\nexport async function clearDormantContainerMetadataInTransaction(\n tx: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<void> {\n if (containerIds.length === 0) {\n return;\n }\n await tx\n .delete(dormantContainerMetadata)\n .where(inArray(dormantContainerMetadata.containerId, [...containerIds]))\n .run();\n}\n\n/** Delete every durable row owned by a container-metadata document scope. */\nexport async function deleteContainerMetadataDocumentRowsInTransaction(\n tx: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<void> {\n const ids = [...containerIds];\n if (ids.length === 0) {\n return;\n }\n await tx\n .delete(documents)\n .where(\n and(\n eq(documents.appKind, CONTAINER_METADATA_APP_KIND),\n inArray(documents.localId, ids),\n ),\n )\n .run();\n await tx\n .delete(documentHistoryCheckpoints)\n .where(\n and(\n eq(documentHistoryCheckpoints.appKind, CONTAINER_METADATA_APP_KIND),\n inArray(documentHistoryCheckpoints.localId, ids),\n ),\n )\n .run();\n await tx\n .delete(documentHistoryUpdates)\n .where(\n and(\n eq(documentHistoryUpdates.appKind, CONTAINER_METADATA_APP_KIND),\n inArray(documentHistoryUpdates.localId, ids),\n ),\n )\n .run();\n await tx\n .delete(documentSyncFailures)\n .where(\n and(\n eq(documentSyncFailures.appKind, CONTAINER_METADATA_APP_KIND),\n inArray(documentSyncFailures.localId, ids),\n ),\n )\n .run();\n await clearDormantContainerMetadataInTransaction(tx, ids);\n}\n"]}
1
+ {"version":3,"file":"dormantContainerMetadata.js","sourceRoot":"","sources":["dormantContainerMetadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,0BAA0B,EAC1B,sBAAsB,EACtB,SAAS,EACT,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAG7B,MAAM,CAAC,MAAM,2BAA2B,GAAG,oBAAoB,CAAC;AAQhE,MAAM,CAAC,KAAK,UAAU,2CAA2C,CAC/D,EAAgC,EAChC,QAA0D;IAE1D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO;IACT,CAAC;IAED,MAAM,EAAE;SACL,MAAM,CAAC,wBAAwB,CAAC;SAChC,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;SACrB,kBAAkB,CAAC;QAClB,MAAM,EAAE,wBAAwB,CAAC,WAAW;QAC5C,GAAG,EAAE;YACH,cAAc,EAAE,GAAG,CAAA,0BAA0B;YAC7C,UAAU,EAAE,GAAG,CAAA,sBAAsB;SACtC;KACF,CAAC;SACD,GAAG,EAAE,CAAC;AACX,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0CAA0C,CAC9D,EAAgC,EAChC,YAAmC;IAEnC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO;IACT,CAAC;IACD,MAAM,EAAE;SACL,MAAM,CAAC,wBAAwB,CAAC;SAChC,KAAK,CAAC,OAAO,CAAC,wBAAwB,CAAC,WAAW,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC;SACvE,GAAG,EAAE,CAAC;AACX,CAAC;AAED,6EAA6E;AAC7E,MAAM,CAAC,KAAK,UAAU,gDAAgD,CACpE,EAAgC,EAChC,YAAmC;IAEnC,MAAM,GAAG,GAAG,CAAC,GAAG,YAAY,CAAC,CAAC;IAC9B,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO;IACT,CAAC;IACD,MAAM,EAAE;SACL,MAAM,CAAC,SAAS,CAAC;SACjB,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,2BAA2B,CAAC,EAClD,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAChC,CACF;SACA,GAAG,EAAE,CAAC;IACT,MAAM,EAAE;SACL,MAAM,CAAC,0BAA0B,CAAC;SAClC,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,0BAA0B,CAAC,OAAO,EAAE,2BAA2B,CAAC,EACnE,OAAO,CAAC,0BAA0B,CAAC,OAAO,EAAE,GAAG,CAAC,CACjD,CACF;SACA,GAAG,EAAE,CAAC;IACT,MAAM,EAAE;SACL,MAAM,CAAC,sBAAsB,CAAC;SAC9B,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,sBAAsB,CAAC,OAAO,EAAE,2BAA2B,CAAC,EAC/D,OAAO,CAAC,sBAAsB,CAAC,OAAO,EAAE,GAAG,CAAC,CAC7C,CACF;SACA,GAAG,EAAE,CAAC;IACT,MAAM,0CAA0C,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;AAC5D,CAAC","sourcesContent":["import { and, eq, inArray, sql } from \"drizzle-orm\";\nimport {\n documentHistoryCheckpoints,\n documentHistoryUpdates,\n documents,\n dormantContainerMetadata,\n} from \"../../sqlite/schema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\n\nexport const CONTAINER_METADATA_APP_KIND = \"container-metadata\";\n\ninterface DormantContainerMetadataRetention {\n readonly containerId: string;\n readonly organizationId: string;\n readonly retainedAt: string;\n}\n\nexport async function retainDormantContainerMetadataInTransaction(\n tx: ClientSQLiteTransactionScope,\n retained: ReadonlyArray<DormantContainerMetadataRetention>,\n): Promise<void> {\n if (retained.length === 0) {\n return;\n }\n\n await tx\n .insert(dormantContainerMetadata)\n .values([...retained])\n .onConflictDoUpdate({\n target: dormantContainerMetadata.containerId,\n set: {\n organizationId: sql`excluded.organization_id`,\n retainedAt: sql`excluded.retained_at`,\n },\n })\n .run();\n}\n\nexport async function clearDormantContainerMetadataInTransaction(\n tx: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<void> {\n if (containerIds.length === 0) {\n return;\n }\n await tx\n .delete(dormantContainerMetadata)\n .where(inArray(dormantContainerMetadata.containerId, [...containerIds]))\n .run();\n}\n\n/** Delete every durable row owned by a container-metadata document scope. */\nexport async function deleteContainerMetadataDocumentRowsInTransaction(\n tx: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<void> {\n const ids = [...containerIds];\n if (ids.length === 0) {\n return;\n }\n await tx\n .delete(documents)\n .where(\n and(\n eq(documents.appKind, CONTAINER_METADATA_APP_KIND),\n inArray(documents.localId, ids),\n ),\n )\n .run();\n await tx\n .delete(documentHistoryCheckpoints)\n .where(\n and(\n eq(documentHistoryCheckpoints.appKind, CONTAINER_METADATA_APP_KIND),\n inArray(documentHistoryCheckpoints.localId, ids),\n ),\n )\n .run();\n await tx\n .delete(documentHistoryUpdates)\n .where(\n and(\n eq(documentHistoryUpdates.appKind, CONTAINER_METADATA_APP_KIND),\n inArray(documentHistoryUpdates.localId, ids),\n ),\n )\n .run();\n await clearDormantContainerMetadataInTransaction(tx, ids);\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { and, eq } from "drizzle-orm";
2
- import { containerCreateIntents, containerCreateIntentTables, } from "../../sqlite/schema.js";
2
+ import { inLocalIntentLane, LOCAL_INTENT_LANES, localIntents, localIntentTables, } from "../../sqlite/schema.js";
3
3
  import { getClientSQLitePersistenceRuntime } from "../../sqlite/sqlitePersistenceRuntime.js";
4
4
  import { ensureSqlTables } from "../../sqlite/sqlSchema.js";
5
5
  import { CONTAINER_CREATE_INTENT_TYPE } from "./containerContentsPersistenceTypes.js";
@@ -9,12 +9,12 @@ import { CONTAINER_CREATE_INTENT_TYPE } from "./containerContentsPersistenceType
9
9
  * until the create settles.
10
10
  */
11
11
  export async function isContainerCreatePending(execSql, containerId) {
12
- await ensureSqlTables(execSql, containerCreateIntentTables);
12
+ await ensureSqlTables(execSql, localIntentTables);
13
13
  const { db } = getClientSQLitePersistenceRuntime(execSql);
14
14
  const [pending] = await db
15
- .select({ containerId: containerCreateIntents.containerId })
16
- .from(containerCreateIntents)
17
- .where(and(eq(containerCreateIntents.containerId, containerId), eq(containerCreateIntents.intentType, CONTAINER_CREATE_INTENT_TYPE), eq(containerCreateIntents.syncStatus, "pending")))
15
+ .select({ containerId: localIntents.subjectId })
16
+ .from(localIntents)
17
+ .where(and(inLocalIntentLane(LOCAL_INTENT_LANES.containerCreate), eq(localIntents.subjectId, containerId), eq(localIntents.intentType, CONTAINER_CREATE_INTENT_TYPE), eq(localIntents.syncStatus, "pending")))
18
18
  .limit(1);
19
19
  return pending !== undefined;
20
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"pendingContainerCreate.js","sourceRoot":"","sources":["pendingContainerCreate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EACL,sBAAsB,EACtB,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iCAAiC,EAAE,MAAM,uCAAuC,CAAC;AAC1F,OAAO,EAAgB,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAC;AAEnF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,OAAgB,EAChB,WAAmB;IAEnB,MAAM,eAAe,CAAC,OAAO,EAAE,2BAA2B,CAAC,CAAC;IAC5D,MAAM,EAAE,EAAE,EAAE,GAAG,iCAAiC,CAAC,OAAO,CAAC,CAAC;IAC1D,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,EAAE;SACvB,MAAM,CAAC,EAAE,WAAW,EAAE,sBAAsB,CAAC,WAAW,EAAE,CAAC;SAC3D,IAAI,CAAC,sBAAsB,CAAC;SAC5B,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,sBAAsB,CAAC,WAAW,EAAE,WAAW,CAAC,EACnD,EAAE,CAAC,sBAAsB,CAAC,UAAU,EAAE,4BAA4B,CAAC,EACnE,EAAE,CAAC,sBAAsB,CAAC,UAAU,EAAE,SAAS,CAAC,CACjD,CACF;SACA,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,OAAO,OAAO,KAAK,SAAS,CAAC;AAC/B,CAAC","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport {\n containerCreateIntents,\n containerCreateIntentTables,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\nimport { CONTAINER_CREATE_INTENT_TYPE } from \"./containerContentsPersistenceTypes\";\n\n/**\n * A folder whose create has not settled can carry a listed identity that\n * adoption has not verified, or has refused; nothing is created inside it\n * until the create settles.\n */\nexport async function isContainerCreatePending(\n execSql: ExecSql,\n containerId: string,\n): Promise<boolean> {\n await ensureSqlTables(execSql, containerCreateIntentTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const [pending] = await db\n .select({ containerId: containerCreateIntents.containerId })\n .from(containerCreateIntents)\n .where(\n and(\n eq(containerCreateIntents.containerId, containerId),\n eq(containerCreateIntents.intentType, CONTAINER_CREATE_INTENT_TYPE),\n eq(containerCreateIntents.syncStatus, \"pending\"),\n ),\n )\n .limit(1);\n return pending !== undefined;\n}\n"]}
1
+ {"version":3,"file":"pendingContainerCreate.js","sourceRoot":"","sources":["pendingContainerCreate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iCAAiC,EAAE,MAAM,uCAAuC,CAAC;AAC1F,OAAO,EAAgB,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAC;AAEnF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,OAAgB,EAChB,WAAmB;IAEnB,MAAM,eAAe,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;IAClD,MAAM,EAAE,EAAE,EAAE,GAAG,iCAAiC,CAAC,OAAO,CAAC,CAAC;IAC1D,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,EAAE;SACvB,MAAM,CAAC,EAAE,WAAW,EAAE,YAAY,CAAC,SAAS,EAAE,CAAC;SAC/C,IAAI,CAAC,YAAY,CAAC;SAClB,KAAK,CACJ,GAAG,CACD,iBAAiB,CAAC,kBAAkB,CAAC,eAAe,CAAC,EACrD,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,WAAW,CAAC,EACvC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,4BAA4B,CAAC,EACzD,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,SAAS,CAAC,CACvC,CACF;SACA,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,OAAO,OAAO,KAAK,SAAS,CAAC;AAC/B,CAAC","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport {\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n localIntentTables,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\nimport { CONTAINER_CREATE_INTENT_TYPE } from \"./containerContentsPersistenceTypes\";\n\n/**\n * A folder whose create has not settled can carry a listed identity that\n * adoption has not verified, or has refused; nothing is created inside it\n * until the create settles.\n */\nexport async function isContainerCreatePending(\n execSql: ExecSql,\n containerId: string,\n): Promise<boolean> {\n await ensureSqlTables(execSql, localIntentTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const [pending] = await db\n .select({ containerId: localIntents.subjectId })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.containerCreate),\n eq(localIntents.subjectId, containerId),\n eq(localIntents.intentType, CONTAINER_CREATE_INTENT_TYPE),\n eq(localIntents.syncStatus, \"pending\"),\n ),\n )\n .limit(1);\n return pending !== undefined;\n}\n"]}
@@ -1,16 +1,16 @@
1
1
  import { isContainerSystemSlot, } from "../../../internal/validators/containerSystemSlot.js";
2
2
  import { asc, eq, inArray, sql } from "drizzle-orm";
3
3
  import { DEFAULT_EFFECTIVE_ACCESS_LEVEL, normalizeEffectiveAccessLevel, } from "../../accessLevel.js";
4
- import { containerProjection, containers, containerTables, } from "../../sqlite/schema.js";
4
+ import { containers, containerTables } from "../../sqlite/schema.js";
5
5
  import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
6
6
  import { ensureSqlTables } from "../../sqlite/sqlSchema.js";
7
7
  export function ensureContainerTables(execSql) {
8
8
  return ensureSqlTables(execSql, containerTables);
9
9
  }
10
- // The ONE display-name fallback: a projection row names the container; without
11
- // one the root reads '/' and everything else 'Untitled'.
10
+ // The ONE display-name fallback: a decrypted metadata name names the container;
11
+ // without one the root reads '/' and everything else 'Untitled'.
12
12
  const containerDisplayName = sql `COALESCE(
13
- ${containerProjection.displayName},
13
+ ${containers.displayName},
14
14
  CASE WHEN ${containers.parentId} IS NULL THEN '/' ELSE 'Untitled' END
15
15
  )`;
16
16
  function toDisplayContainerRecord(record) {
@@ -67,6 +67,8 @@ export async function saveContainerRows(input) {
67
67
  parentId: nextRecord.parentId,
68
68
  metadataDocumentId: nextRecord.metadataDocumentId,
69
69
  systemSlot: nextRecord.systemSlot ?? null,
70
+ displayName: nextRecord.name,
71
+ icon: nextRecord.icon,
70
72
  localCreatedAt: nextRecord.localCreatedAt,
71
73
  localUpdatedAt,
72
74
  serverCreatedAt: nextRecord.serverCreatedAt,
@@ -82,6 +84,8 @@ export async function saveContainerRows(input) {
82
84
  parentId: containerRow.parentId,
83
85
  metadataDocumentId: containerRow.metadataDocumentId,
84
86
  systemSlot: containerRow.systemSlot,
87
+ displayName: containerRow.displayName,
88
+ icon: containerRow.icon,
85
89
  effectiveAccessLevel: containerRow.effectiveAccessLevel,
86
90
  localUpdatedAt: containerRow.localUpdatedAt,
87
91
  serverCreatedAt: record.serverCreatedAt === undefined
@@ -93,20 +97,6 @@ export async function saveContainerRows(input) {
93
97
  },
94
98
  })
95
99
  .run();
96
- const projectionRow = {
97
- containerId: nextRecord.id,
98
- displayName: nextRecord.name,
99
- icon: nextRecord.icon,
100
- updatedAt: localUpdatedAt,
101
- };
102
- await tx
103
- .insert(containerProjection)
104
- .values(projectionRow)
105
- .onConflictDoUpdate({
106
- target: containerProjection.containerId,
107
- set: projectionRow,
108
- })
109
- .run();
110
100
  return nextRecord;
111
101
  }
112
102
  export async function loadContainers(execSql) {
@@ -120,14 +110,13 @@ export async function loadContainers(execSql) {
120
110
  metadataDocumentId: containers.metadataDocumentId,
121
111
  systemSlot: containers.systemSlot,
122
112
  name: containerDisplayName,
123
- icon: containerProjection.icon,
113
+ icon: containers.icon,
124
114
  localCreatedAt: containers.localCreatedAt,
125
115
  localUpdatedAt: containers.localUpdatedAt,
126
116
  serverCreatedAt: containers.serverCreatedAt,
127
117
  serverUpdatedAt: containers.serverUpdatedAt,
128
118
  })
129
119
  .from(containers)
130
- .leftJoin(containerProjection, eq(containerProjection.containerId, containers.id))
131
120
  .orderBy(asc(sql `${containerDisplayName} COLLATE NOCASE`));
132
121
  return rows.map((row) => mapSelectedContainerRecord(row));
133
122
  }
@@ -142,14 +131,13 @@ export async function loadContainerById(execSql, containerId) {
142
131
  metadataDocumentId: containers.metadataDocumentId,
143
132
  systemSlot: containers.systemSlot,
144
133
  name: containerDisplayName,
145
- icon: containerProjection.icon,
134
+ icon: containers.icon,
146
135
  localCreatedAt: containers.localCreatedAt,
147
136
  localUpdatedAt: containers.localUpdatedAt,
148
137
  serverCreatedAt: containers.serverCreatedAt,
149
138
  serverUpdatedAt: containers.serverUpdatedAt,
150
139
  })
151
140
  .from(containers)
152
- .leftJoin(containerProjection, eq(containerProjection.containerId, containers.id))
153
141
  .where(eq(containers.id, containerId))
154
142
  .limit(1);
155
143
  return rows[0] ? mapSelectedContainerRecord(rows[0]) : null;
@@ -166,7 +154,6 @@ export async function loadContainerDisplayNamesByIds(execSql, ids) {
166
154
  name: containerDisplayName,
167
155
  })
168
156
  .from(containers)
169
- .leftJoin(containerProjection, eq(containerProjection.containerId, containers.id))
170
157
  .where(inArray(containers.id, uniqueIds));
171
158
  return new Map(rows.flatMap((row) => {
172
159
  const displayName = row.name.trim();
@@ -195,10 +182,6 @@ export async function deleteContainerRowsInTransaction(tx, ids) {
195
182
  if (uniqueIds.length === 0) {
196
183
  return;
197
184
  }
198
- await tx
199
- .delete(containerProjection)
200
- .where(inArray(containerProjection.containerId, uniqueIds))
201
- .run();
202
185
  await tx.delete(containers).where(inArray(containers.id, uniqueIds)).run();
203
186
  }
204
187
  //# sourceMappingURL=containerPersistence.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"containerPersistence.js","sourceRoot":"","sources":["containerPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContainerAccessLevel } from \"@tearleads/crypto\";\nimport {\n type ContainerSystemSlot,\n isContainerSystemSlot,\n} from \"@tearleads/validators/containerSystemSlot\";\nimport { asc, eq, inArray, sql } from \"drizzle-orm\";\nimport {\n DEFAULT_EFFECTIVE_ACCESS_LEVEL,\n normalizeEffectiveAccessLevel,\n} from \"../../accessLevel\";\nimport {\n containerProjection,\n containers,\n containerTables,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\n\nexport interface ContainerRecord {\n createdAt?: string | null;\n effectiveAccessLevel?: ContainerAccessLevel | null;\n id: string;\n organizationId: string;\n parentId: string | null;\n metadataDocumentId: string | null;\n systemSlot?: ContainerSystemSlot | null;\n name: string;\n icon: string | null;\n localCreatedAt?: string | null;\n localUpdatedAt?: string | null;\n serverCreatedAt?: string | null;\n serverUpdatedAt?: string | null;\n updatedAt?: string | null;\n}\n\ninterface ContainerRecordWithTimestampDefaults extends ContainerRecord {\n createdAt: string | null;\n localCreatedAt: string;\n localUpdatedAt: string;\n serverCreatedAt: string | null;\n serverUpdatedAt: string | null;\n updatedAt: string | null;\n}\n\nexport function ensureContainerTables(execSql: ExecSql): Promise<void> {\n return ensureSqlTables(execSql, containerTables);\n}\n\n// The ONE display-name fallback: a projection row names the container; without\n// one the root reads '/' and everything else 'Untitled'.\nconst containerDisplayName = sql<string>`COALESCE(\n ${containerProjection.displayName},\n CASE WHEN ${containers.parentId} IS NULL THEN '/' ELSE 'Untitled' END\n )`;\n\ninterface SelectedContainerRecord {\n effectiveAccessLevel: string | null;\n id: string | null;\n organizationId: string;\n parentId: string | null;\n metadataDocumentId: string | null;\n systemSlot: string | null;\n name: string;\n icon: string | null;\n localCreatedAt: string;\n localUpdatedAt: string;\n serverCreatedAt: string | null;\n serverUpdatedAt: string | null;\n}\n\nfunction toDisplayContainerRecord(record: ContainerRecord): ContainerRecord {\n return {\n ...record,\n createdAt: record.serverCreatedAt ?? record.localCreatedAt ?? null,\n updatedAt: record.serverUpdatedAt ?? record.localUpdatedAt ?? null,\n };\n}\n\nfunction applyContainerTimestampDefaults(\n record: ContainerRecord,\n localUpdatedAt: string,\n): ContainerRecordWithTimestampDefaults {\n const hasServerTimestamps =\n record.serverCreatedAt !== undefined ||\n record.serverUpdatedAt !== undefined;\n const localCreatedAt =\n record.localCreatedAt ??\n (!hasServerTimestamps ? record.createdAt : null) ??\n localUpdatedAt;\n const serverCreatedAt = record.serverCreatedAt ?? null;\n const serverUpdatedAt = record.serverUpdatedAt ?? null;\n\n return {\n ...record,\n createdAt: serverCreatedAt ?? localCreatedAt,\n localCreatedAt,\n localUpdatedAt,\n serverCreatedAt,\n serverUpdatedAt,\n updatedAt: serverUpdatedAt ?? localUpdatedAt,\n };\n}\n\nfunction mapSelectedContainerRecord(\n row: SelectedContainerRecord,\n): ContainerRecord {\n const systemSlot = isContainerSystemSlot(row.systemSlot)\n ? row.systemSlot\n : null;\n\n return toDisplayContainerRecord({\n effectiveAccessLevel: normalizeEffectiveAccessLevel(\n row.effectiveAccessLevel,\n ),\n id: String(row.id ?? \"\"),\n organizationId: row.organizationId,\n parentId: row.parentId,\n metadataDocumentId: row.metadataDocumentId,\n systemSlot,\n name: row.name,\n icon: row.icon && row.icon.length > 0 ? row.icon : null,\n localCreatedAt: row.localCreatedAt,\n localUpdatedAt: row.localUpdatedAt,\n serverCreatedAt: row.serverCreatedAt,\n serverUpdatedAt: row.serverUpdatedAt,\n });\n}\n\nexport async function saveContainerRows(input: {\n tx: ClientSQLiteTransactionScope;\n record: ContainerRecord;\n localUpdatedAt: string;\n}): Promise<ContainerRecord> {\n const { record, tx, localUpdatedAt } = input;\n const nextRecord = applyContainerTimestampDefaults(record, localUpdatedAt);\n const containerRow = {\n effectiveAccessLevel:\n nextRecord.effectiveAccessLevel ?? DEFAULT_EFFECTIVE_ACCESS_LEVEL,\n id: nextRecord.id,\n organizationId: nextRecord.organizationId,\n parentId: nextRecord.parentId,\n metadataDocumentId: nextRecord.metadataDocumentId,\n systemSlot: nextRecord.systemSlot ?? null,\n localCreatedAt: nextRecord.localCreatedAt,\n localUpdatedAt,\n serverCreatedAt: nextRecord.serverCreatedAt,\n serverUpdatedAt: nextRecord.serverUpdatedAt,\n };\n await tx\n .insert(containers)\n .values(containerRow)\n .onConflictDoUpdate({\n target: containers.id,\n set: {\n organizationId: containerRow.organizationId,\n parentId: containerRow.parentId,\n metadataDocumentId: containerRow.metadataDocumentId,\n systemSlot: containerRow.systemSlot,\n effectiveAccessLevel: containerRow.effectiveAccessLevel,\n localUpdatedAt: containerRow.localUpdatedAt,\n serverCreatedAt:\n record.serverCreatedAt === undefined\n ? sql`${containers.serverCreatedAt}`\n : containerRow.serverCreatedAt,\n serverUpdatedAt:\n record.serverUpdatedAt === undefined\n ? sql`${containers.serverUpdatedAt}`\n : containerRow.serverUpdatedAt,\n },\n })\n .run();\n\n const projectionRow = {\n containerId: nextRecord.id,\n displayName: nextRecord.name,\n icon: nextRecord.icon,\n updatedAt: localUpdatedAt,\n };\n await tx\n .insert(containerProjection)\n .values(projectionRow)\n .onConflictDoUpdate({\n target: containerProjection.containerId,\n set: projectionRow,\n })\n .run();\n\n return nextRecord;\n}\n\nexport async function loadContainers(\n execSql: ExecSql,\n): Promise<ContainerRecord[]> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n effectiveAccessLevel: containers.effectiveAccessLevel,\n id: containers.id,\n organizationId: containers.organizationId,\n parentId: containers.parentId,\n metadataDocumentId: containers.metadataDocumentId,\n systemSlot: containers.systemSlot,\n name: containerDisplayName,\n icon: containerProjection.icon,\n localCreatedAt: containers.localCreatedAt,\n localUpdatedAt: containers.localUpdatedAt,\n serverCreatedAt: containers.serverCreatedAt,\n serverUpdatedAt: containers.serverUpdatedAt,\n })\n .from(containers)\n .leftJoin(\n containerProjection,\n eq(containerProjection.containerId, containers.id),\n )\n .orderBy(asc(sql`${containerDisplayName} COLLATE NOCASE`));\n\n return rows.map((row) => mapSelectedContainerRecord(row));\n}\n\nexport async function loadContainerById(\n execSql: ExecSql,\n containerId: string,\n): Promise<ContainerRecord | null> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n effectiveAccessLevel: containers.effectiveAccessLevel,\n id: containers.id,\n organizationId: containers.organizationId,\n parentId: containers.parentId,\n metadataDocumentId: containers.metadataDocumentId,\n systemSlot: containers.systemSlot,\n name: containerDisplayName,\n icon: containerProjection.icon,\n localCreatedAt: containers.localCreatedAt,\n localUpdatedAt: containers.localUpdatedAt,\n serverCreatedAt: containers.serverCreatedAt,\n serverUpdatedAt: containers.serverUpdatedAt,\n })\n .from(containers)\n .leftJoin(\n containerProjection,\n eq(containerProjection.containerId, containers.id),\n )\n .where(eq(containers.id, containerId))\n .limit(1);\n\n return rows[0] ? mapSelectedContainerRecord(rows[0]) : null;\n}\n\nexport async function loadContainerDisplayNamesByIds(\n execSql: ExecSql,\n ids: ReadonlyArray<string>,\n): Promise<Map<string, string>> {\n const uniqueIds = Array.from(new Set(ids));\n if (uniqueIds.length === 0) {\n return new Map();\n }\n\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n id: containers.id,\n name: containerDisplayName,\n })\n .from(containers)\n .leftJoin(\n containerProjection,\n eq(containerProjection.containerId, containers.id),\n )\n .where(inArray(containers.id, uniqueIds));\n\n return new Map(\n rows.flatMap((row) => {\n const displayName = row.name.trim();\n if (!row.id || displayName.length === 0) {\n return [];\n }\n\n return [[row.id, displayName]];\n }),\n );\n}\n\nexport async function saveContainer(\n execSql: ExecSql,\n record: ContainerRecord,\n): Promise<void> {\n const localUpdatedAt = new Date().toISOString();\n\n await getClientSQLitePersistenceRuntime(execSql).transaction(async (tx) => {\n await saveContainerRows({\n record,\n tx,\n localUpdatedAt,\n });\n });\n}\n\n/**\n * The container-row half of the delete cascade, statement-only so callers\n * can compose it inside one atomic transaction.\n */\nexport async function deleteContainerRowsInTransaction(\n tx: ClientSQLiteTransactionScope,\n ids: ReadonlyArray<string>,\n): Promise<void> {\n const uniqueIds = Array.from(new Set(ids));\n if (uniqueIds.length === 0) {\n return;\n }\n await tx\n .delete(containerProjection)\n .where(inArray(containerProjection.containerId, uniqueIds))\n .run();\n await tx.delete(containers).where(inArray(containers.id, uniqueIds)).run();\n}\n"]}
1
+ {"version":3,"file":"containerPersistence.js","sourceRoot":"","sources":["containerPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContainerAccessLevel } from \"@tearleads/crypto\";\nimport {\n type ContainerSystemSlot,\n isContainerSystemSlot,\n} from \"@tearleads/validators/containerSystemSlot\";\nimport { asc, eq, inArray, sql } from \"drizzle-orm\";\nimport {\n DEFAULT_EFFECTIVE_ACCESS_LEVEL,\n normalizeEffectiveAccessLevel,\n} from \"../../accessLevel\";\nimport { containers, containerTables } from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\n\nexport interface ContainerRecord {\n createdAt?: string | null;\n effectiveAccessLevel?: ContainerAccessLevel | null;\n id: string;\n organizationId: string;\n parentId: string | null;\n metadataDocumentId: string | null;\n systemSlot?: ContainerSystemSlot | null;\n name: string;\n icon: string | null;\n localCreatedAt?: string | null;\n localUpdatedAt?: string | null;\n serverCreatedAt?: string | null;\n serverUpdatedAt?: string | null;\n updatedAt?: string | null;\n}\n\ninterface ContainerRecordWithTimestampDefaults extends ContainerRecord {\n createdAt: string | null;\n localCreatedAt: string;\n localUpdatedAt: string;\n serverCreatedAt: string | null;\n serverUpdatedAt: string | null;\n updatedAt: string | null;\n}\n\nexport function ensureContainerTables(execSql: ExecSql): Promise<void> {\n return ensureSqlTables(execSql, containerTables);\n}\n\n// The ONE display-name fallback: a decrypted metadata name names the container;\n// without one the root reads '/' and everything else 'Untitled'.\nconst containerDisplayName = sql<string>`COALESCE(\n ${containers.displayName},\n CASE WHEN ${containers.parentId} IS NULL THEN '/' ELSE 'Untitled' END\n )`;\n\ninterface SelectedContainerRecord {\n effectiveAccessLevel: string | null;\n id: string | null;\n organizationId: string;\n parentId: string | null;\n metadataDocumentId: string | null;\n systemSlot: string | null;\n name: string;\n icon: string | null;\n localCreatedAt: string;\n localUpdatedAt: string;\n serverCreatedAt: string | null;\n serverUpdatedAt: string | null;\n}\n\nfunction toDisplayContainerRecord(record: ContainerRecord): ContainerRecord {\n return {\n ...record,\n createdAt: record.serverCreatedAt ?? record.localCreatedAt ?? null,\n updatedAt: record.serverUpdatedAt ?? record.localUpdatedAt ?? null,\n };\n}\n\nfunction applyContainerTimestampDefaults(\n record: ContainerRecord,\n localUpdatedAt: string,\n): ContainerRecordWithTimestampDefaults {\n const hasServerTimestamps =\n record.serverCreatedAt !== undefined ||\n record.serverUpdatedAt !== undefined;\n const localCreatedAt =\n record.localCreatedAt ??\n (!hasServerTimestamps ? record.createdAt : null) ??\n localUpdatedAt;\n const serverCreatedAt = record.serverCreatedAt ?? null;\n const serverUpdatedAt = record.serverUpdatedAt ?? null;\n\n return {\n ...record,\n createdAt: serverCreatedAt ?? localCreatedAt,\n localCreatedAt,\n localUpdatedAt,\n serverCreatedAt,\n serverUpdatedAt,\n updatedAt: serverUpdatedAt ?? localUpdatedAt,\n };\n}\n\nfunction mapSelectedContainerRecord(\n row: SelectedContainerRecord,\n): ContainerRecord {\n const systemSlot = isContainerSystemSlot(row.systemSlot)\n ? row.systemSlot\n : null;\n\n return toDisplayContainerRecord({\n effectiveAccessLevel: normalizeEffectiveAccessLevel(\n row.effectiveAccessLevel,\n ),\n id: String(row.id ?? \"\"),\n organizationId: row.organizationId,\n parentId: row.parentId,\n metadataDocumentId: row.metadataDocumentId,\n systemSlot,\n name: row.name,\n icon: row.icon && row.icon.length > 0 ? row.icon : null,\n localCreatedAt: row.localCreatedAt,\n localUpdatedAt: row.localUpdatedAt,\n serverCreatedAt: row.serverCreatedAt,\n serverUpdatedAt: row.serverUpdatedAt,\n });\n}\n\nexport async function saveContainerRows(input: {\n tx: ClientSQLiteTransactionScope;\n record: ContainerRecord;\n localUpdatedAt: string;\n}): Promise<ContainerRecord> {\n const { record, tx, localUpdatedAt } = input;\n const nextRecord = applyContainerTimestampDefaults(record, localUpdatedAt);\n const containerRow = {\n effectiveAccessLevel:\n nextRecord.effectiveAccessLevel ?? DEFAULT_EFFECTIVE_ACCESS_LEVEL,\n id: nextRecord.id,\n organizationId: nextRecord.organizationId,\n parentId: nextRecord.parentId,\n metadataDocumentId: nextRecord.metadataDocumentId,\n systemSlot: nextRecord.systemSlot ?? null,\n displayName: nextRecord.name,\n icon: nextRecord.icon,\n localCreatedAt: nextRecord.localCreatedAt,\n localUpdatedAt,\n serverCreatedAt: nextRecord.serverCreatedAt,\n serverUpdatedAt: nextRecord.serverUpdatedAt,\n };\n await tx\n .insert(containers)\n .values(containerRow)\n .onConflictDoUpdate({\n target: containers.id,\n set: {\n organizationId: containerRow.organizationId,\n parentId: containerRow.parentId,\n metadataDocumentId: containerRow.metadataDocumentId,\n systemSlot: containerRow.systemSlot,\n displayName: containerRow.displayName,\n icon: containerRow.icon,\n effectiveAccessLevel: containerRow.effectiveAccessLevel,\n localUpdatedAt: containerRow.localUpdatedAt,\n serverCreatedAt:\n record.serverCreatedAt === undefined\n ? sql`${containers.serverCreatedAt}`\n : containerRow.serverCreatedAt,\n serverUpdatedAt:\n record.serverUpdatedAt === undefined\n ? sql`${containers.serverUpdatedAt}`\n : containerRow.serverUpdatedAt,\n },\n })\n .run();\n\n return nextRecord;\n}\n\nexport async function loadContainers(\n execSql: ExecSql,\n): Promise<ContainerRecord[]> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n effectiveAccessLevel: containers.effectiveAccessLevel,\n id: containers.id,\n organizationId: containers.organizationId,\n parentId: containers.parentId,\n metadataDocumentId: containers.metadataDocumentId,\n systemSlot: containers.systemSlot,\n name: containerDisplayName,\n icon: containers.icon,\n localCreatedAt: containers.localCreatedAt,\n localUpdatedAt: containers.localUpdatedAt,\n serverCreatedAt: containers.serverCreatedAt,\n serverUpdatedAt: containers.serverUpdatedAt,\n })\n .from(containers)\n .orderBy(asc(sql`${containerDisplayName} COLLATE NOCASE`));\n\n return rows.map((row) => mapSelectedContainerRecord(row));\n}\n\nexport async function loadContainerById(\n execSql: ExecSql,\n containerId: string,\n): Promise<ContainerRecord | null> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n effectiveAccessLevel: containers.effectiveAccessLevel,\n id: containers.id,\n organizationId: containers.organizationId,\n parentId: containers.parentId,\n metadataDocumentId: containers.metadataDocumentId,\n systemSlot: containers.systemSlot,\n name: containerDisplayName,\n icon: containers.icon,\n localCreatedAt: containers.localCreatedAt,\n localUpdatedAt: containers.localUpdatedAt,\n serverCreatedAt: containers.serverCreatedAt,\n serverUpdatedAt: containers.serverUpdatedAt,\n })\n .from(containers)\n .where(eq(containers.id, containerId))\n .limit(1);\n\n return rows[0] ? mapSelectedContainerRecord(rows[0]) : null;\n}\n\nexport async function loadContainerDisplayNamesByIds(\n execSql: ExecSql,\n ids: ReadonlyArray<string>,\n): Promise<Map<string, string>> {\n const uniqueIds = Array.from(new Set(ids));\n if (uniqueIds.length === 0) {\n return new Map();\n }\n\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n id: containers.id,\n name: containerDisplayName,\n })\n .from(containers)\n .where(inArray(containers.id, uniqueIds));\n\n return new Map(\n rows.flatMap((row) => {\n const displayName = row.name.trim();\n if (!row.id || displayName.length === 0) {\n return [];\n }\n\n return [[row.id, displayName]];\n }),\n );\n}\n\nexport async function saveContainer(\n execSql: ExecSql,\n record: ContainerRecord,\n): Promise<void> {\n const localUpdatedAt = new Date().toISOString();\n\n await getClientSQLitePersistenceRuntime(execSql).transaction(async (tx) => {\n await saveContainerRows({\n record,\n tx,\n localUpdatedAt,\n });\n });\n}\n\n/**\n * The container-row half of the delete cascade, statement-only so callers\n * can compose it inside one atomic transaction.\n */\nexport async function deleteContainerRowsInTransaction(\n tx: ClientSQLiteTransactionScope,\n ids: ReadonlyArray<string>,\n): Promise<void> {\n const uniqueIds = Array.from(new Set(ids));\n if (uniqueIds.length === 0) {\n return;\n }\n await tx.delete(containers).where(inArray(containers.id, uniqueIds)).run();\n}\n"]}
@@ -31,9 +31,9 @@ export declare function containerSyncWatermarkLaneKey(lane: ContainerSyncWaterma
31
31
  laneKind: string;
32
32
  };
33
33
  /**
34
- * The watermark half of the container-delete cascade, statement-only so the
35
- * caller can run it inside one atomic transaction alongside the rest of the
36
- * cascade.
34
+ * The lane-state half of the container-delete cascade (cursor and freshness
35
+ * marker share one row), statement-only so the caller can run it inside one
36
+ * atomic transaction alongside the rest of the cascade.
37
37
  */
38
38
  export declare function deleteContainerWatermarksInTransaction(tx: ClientSQLiteDatabase | ClientSQLiteTransactionScope, containerIds: ReadonlyArray<string>): Promise<void>;
39
39
  export declare const sqlContainerSyncWatermarkPersistence: {
@@ -43,6 +43,14 @@ export declare const sqlContainerSyncWatermarkPersistence: {
43
43
  loadCheckRecords(execSql: ExecSql, lanes: ReadonlyArray<ContainerSyncWatermarkLane>): Promise<Array<ContainerSyncLaneCheckRecord | null>>;
44
44
  markChecked(execSql: ExecSql, lane: ContainerSyncWatermarkLane, options?: ContainerSyncMarkerMutationOptions): Promise<boolean>;
45
45
  saveWatermark(execSql: ExecSql, lane: ContainerSyncWatermarkLane, watermark: SyncWatermark, options?: ContainerSyncMarkerMutationOptions): Promise<boolean>;
46
+ /**
47
+ * Upserts one lane row in a single statement: the cursor when `watermark`
48
+ * is set and the freshness marker when `checked`; the other half is kept.
49
+ */
50
+ recordLaneFetch(execSql: ExecSql, lane: ContainerSyncWatermarkLane, fetch: {
51
+ readonly checked: boolean;
52
+ readonly watermark: SyncWatermark | null;
53
+ }, options?: ContainerSyncMarkerMutationOptions): Promise<boolean>;
46
54
  deleteWatermarksForContainers(execSql: ExecSql, containerIds: ReadonlyArray<string>): Promise<void>;
47
55
  };
48
56
  export {};