@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,5 +1,5 @@
1
- import { and, eq, inArray, or } from "drizzle-orm";
2
- import { containerSyncLaneChecks, containerSyncWatermarks, containerSyncWatermarkTables, } from "../../sqlite/schema.js";
1
+ import { and, eq, inArray, isNotNull, or } from "drizzle-orm";
2
+ import { containerSyncWatermarks, containerSyncWatermarkTables, } from "../../sqlite/schema.js";
3
3
  import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
4
4
  import { ensureSqlTables } from "../../sqlite/sqlSchema.js";
5
5
  const CONTAINER_PARENT_LANE = "container_parent";
@@ -37,9 +37,9 @@ function containerParentLaneId(parentId) {
37
37
  : `${CONTAINER_PARENT_LANE_ID_PREFIX}${parentId}`;
38
38
  }
39
39
  /**
40
- * The watermark half of the container-delete cascade, statement-only so the
41
- * caller can run it inside one atomic transaction alongside the rest of the
42
- * cascade.
40
+ * The lane-state half of the container-delete cascade (cursor and freshness
41
+ * marker share one row), statement-only so the caller can run it inside one
42
+ * atomic transaction alongside the rest of the cascade.
43
43
  */
44
44
  export async function deleteContainerWatermarksInTransaction(tx, containerIds) {
45
45
  const uniqueContainerIds = Array.from(new Set(containerIds));
@@ -52,12 +52,14 @@ export async function deleteContainerWatermarksInTransaction(tx, containerIds) {
52
52
  .delete(containerSyncWatermarks)
53
53
  .where(or(and(eq(containerSyncWatermarks.laneKind, CONTAINER_PARENT_LANE), inArray(containerSyncWatermarks.laneId, parentLaneIds)), and(eq(containerSyncWatermarks.laneKind, CONTAINER_DOCUMENTS_LANE), inArray(containerSyncWatermarks.laneId, documentLaneIds))))
54
54
  .run();
55
- await tx
56
- .delete(containerSyncLaneChecks)
57
- .where(or(and(eq(containerSyncLaneChecks.laneKind, CONTAINER_PARENT_LANE), inArray(containerSyncLaneChecks.laneId, parentLaneIds)), and(eq(containerSyncLaneChecks.laneKind, CONTAINER_DOCUMENTS_LANE), inArray(containerSyncLaneChecks.laneId, documentLaneIds))))
58
- .run();
59
55
  }
60
56
  function mapSelectedWatermarkRecord(row) {
57
+ // A lane checked before any cursor was saved has no watermark yet.
58
+ if (row.watermarkId === null ||
59
+ row.watermarkUpdatedAt === null ||
60
+ row.updatedAt === null) {
61
+ return null;
62
+ }
61
63
  return {
62
64
  laneId: row.laneId,
63
65
  laneKind: row.laneKind,
@@ -121,14 +123,14 @@ async function selectWatermarkRows(execSql, lanes) {
121
123
  async function selectCheckRows(execSql, lanes) {
122
124
  await ensureSqlTables(execSql, containerSyncWatermarkTables);
123
125
  const { db } = getClientSQLitePersistenceRuntime(execSql);
124
- return selectLaneRowsInBatches(lanes, ({ laneId, laneKind }) => and(eq(containerSyncLaneChecks.laneKind, laneKind), eq(containerSyncLaneChecks.laneId, laneId)), (whereClause) => db
126
+ return selectLaneRowsInBatches(lanes, ({ laneId, laneKind }) => and(eq(containerSyncWatermarks.laneKind, laneKind), eq(containerSyncWatermarks.laneId, laneId)), async (whereClause) => (await db
125
127
  .select({
126
- checkedAt: containerSyncLaneChecks.checkedAt,
127
- laneId: containerSyncLaneChecks.laneId,
128
- laneKind: containerSyncLaneChecks.laneKind,
128
+ checkedAt: containerSyncWatermarks.checkedAt,
129
+ laneId: containerSyncWatermarks.laneId,
130
+ laneKind: containerSyncWatermarks.laneKind,
129
131
  })
130
- .from(containerSyncLaneChecks)
131
- .where(whereClause));
132
+ .from(containerSyncWatermarks)
133
+ .where(and(whereClause, isNotNull(containerSyncWatermarks.checkedAt)))).flatMap(({ checkedAt, laneId, laneKind }) => checkedAt === null ? [] : [{ checkedAt, laneId, laneKind }]));
132
134
  }
133
135
  export const sqlContainerSyncWatermarkPersistence = {
134
136
  async ensureSchema(execSql) {
@@ -145,7 +147,8 @@ export const sqlContainerSyncWatermarkPersistence = {
145
147
  const recordsByKey = new Map();
146
148
  for (const row of rows) {
147
149
  const record = mapSelectedWatermarkRecord(row);
148
- recordsByKey.set(laneKeyString(record), record);
150
+ if (record)
151
+ recordsByKey.set(laneKeyString(record), record);
149
152
  }
150
153
  return lanes.map((lane) => {
151
154
  const laneKey = containerSyncWatermarkLaneKey(lane);
@@ -163,65 +166,50 @@ export const sqlContainerSyncWatermarkPersistence = {
163
166
  return recordsByKey.get(laneKeyString(laneKey)) ?? null;
164
167
  });
165
168
  },
166
- async markChecked(execSql, lane, options) {
167
- await sqlContainerSyncWatermarkPersistence.ensureSchema(execSql);
168
- const { laneId, laneKind } = containerSyncWatermarkLaneKey(lane);
169
- const row = {
170
- checkedAt: new Date().toISOString(),
171
- laneId,
172
- laneKind,
173
- };
174
- const mark = async (db) => {
175
- await db
176
- .insert(containerSyncLaneChecks)
177
- .values(row)
178
- .onConflictDoUpdate({
179
- target: [
180
- containerSyncLaneChecks.laneKind,
181
- containerSyncLaneChecks.laneId,
182
- ],
183
- set: row,
184
- })
185
- .run();
186
- };
187
- const runtime = getClientSQLitePersistenceRuntime(execSql);
188
- if (!options?.stillCurrent) {
189
- await runtime.runMutation(mark);
190
- return true;
191
- }
192
- return (await runtime.guardedTransaction(mark, options.stillCurrent))
193
- .committed;
169
+ markChecked(execSql, lane, options) {
170
+ return sqlContainerSyncWatermarkPersistence.recordLaneFetch(execSql, lane, { checked: true, watermark: null }, options);
194
171
  },
195
- async saveWatermark(execSql, lane, watermark, options) {
172
+ saveWatermark(execSql, lane, watermark, options) {
173
+ return sqlContainerSyncWatermarkPersistence.recordLaneFetch(execSql, lane, { checked: false, watermark }, options);
174
+ },
175
+ /**
176
+ * Upserts one lane row in a single statement: the cursor when `watermark`
177
+ * is set and the freshness marker when `checked`; the other half is kept.
178
+ */
179
+ async recordLaneFetch(execSql, lane, fetch, options) {
180
+ if (!fetch.checked && !fetch.watermark)
181
+ return true;
196
182
  await sqlContainerSyncWatermarkPersistence.ensureSchema(execSql);
197
- const { laneId, laneKind } = containerSyncWatermarkLaneKey(lane);
198
- const updatedAt = new Date().toISOString();
199
- const row = {
200
- laneId,
201
- laneKind,
202
- watermarkId: watermark.id,
203
- watermarkUpdatedAt: watermark.updatedAt,
204
- updatedAt,
183
+ const now = new Date().toISOString();
184
+ const changed = {
185
+ ...(fetch.watermark
186
+ ? {
187
+ updatedAt: now,
188
+ watermarkId: fetch.watermark.id,
189
+ watermarkUpdatedAt: fetch.watermark.updatedAt,
190
+ }
191
+ : {}),
192
+ ...(fetch.checked ? { checkedAt: now } : {}),
205
193
  };
206
- const save = async (db) => {
194
+ const record = async (db) => {
207
195
  await db
208
196
  .insert(containerSyncWatermarks)
209
- .values(row)
197
+ .values({ ...containerSyncWatermarkLaneKey(lane), ...changed })
210
198
  .onConflictDoUpdate({
211
199
  target: [
212
200
  containerSyncWatermarks.laneKind,
213
201
  containerSyncWatermarks.laneId,
214
202
  ],
215
- set: row,
203
+ set: changed,
216
204
  })
217
205
  .run();
218
206
  };
219
207
  const runtime = getClientSQLitePersistenceRuntime(execSql);
220
208
  if (!options?.stillCurrent) {
221
- await runtime.runMutation(save);
209
+ await runtime.runMutation(record);
222
210
  return true;
223
211
  }
224
- return (await runtime.guardedTransaction(save, options.stillCurrent))
212
+ return (await runtime.guardedTransaction(record, options.stillCurrent))
225
213
  .committed;
226
214
  },
227
215
  async deleteWatermarksForContainers(execSql, containerIds) {
@@ -1 +1 @@
1
- {"version":3,"file":"containerSyncWatermarkPersistence.js","sourceRoot":"","sources":["containerSyncWatermarkPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SyncWatermark } from \"@tearleads/validators/response\";\nimport { and, eq, inArray, or, type SQL } from \"drizzle-orm\";\nimport {\n containerSyncLaneChecks,\n containerSyncWatermarks,\n containerSyncWatermarkTables,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteDatabase,\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\n\nconst CONTAINER_PARENT_LANE = \"container_parent\";\nconst CONTAINER_DOCUMENTS_LANE = \"container_documents\";\nconst ROOT_CONTAINER_PARENT_LANE_ID = \"root\";\nconst CONTAINER_PARENT_LANE_ID_PREFIX = \"parent:\";\n// Each lane predicate binds two values, so keep batches comfortably under\n// SQLite's variable limit when a caller reads many lanes at once.\nconst LANE_SELECT_BATCH_SIZE = 100;\n\nexport type ContainerSyncWatermarkLane =\n | {\n kind: typeof CONTAINER_PARENT_LANE;\n parentId: string | null;\n }\n | {\n kind: typeof CONTAINER_DOCUMENTS_LANE;\n containerId: string;\n };\n\ninterface SelectedContainerSyncWatermark {\n laneId: string;\n laneKind: string;\n updatedAt: string;\n watermarkId: string;\n watermarkUpdatedAt: string;\n}\n\ninterface ContainerSyncWatermarkRecord {\n laneId: string;\n laneKind: string;\n updatedAt: string;\n watermark: SyncWatermark;\n}\n\nexport interface ContainerSyncLaneCheckRecord {\n checkedAt: string;\n laneId: string;\n laneKind: string;\n}\n\ninterface ContainerSyncMarkerMutationOptions {\n readonly stillCurrent?: (() => boolean) | undefined;\n}\n\nexport const containerParentSyncLane = (\n parentId: string | null,\n): ContainerSyncWatermarkLane => ({\n kind: CONTAINER_PARENT_LANE,\n parentId,\n});\n\nexport const containerContentsSyncLane = (\n containerId: string,\n): ContainerSyncWatermarkLane => ({\n kind: CONTAINER_DOCUMENTS_LANE,\n containerId,\n});\n\nexport function containerSyncWatermarkLaneKey(\n lane: ContainerSyncWatermarkLane,\n): {\n laneId: string;\n laneKind: string;\n} {\n switch (lane.kind) {\n case CONTAINER_PARENT_LANE:\n return {\n laneKind: lane.kind,\n laneId: containerParentLaneId(lane.parentId),\n };\n case CONTAINER_DOCUMENTS_LANE:\n return {\n laneKind: lane.kind,\n laneId: lane.containerId,\n };\n }\n}\n\nfunction containerParentLaneId(parentId: string | null): string {\n return parentId === null\n ? ROOT_CONTAINER_PARENT_LANE_ID\n : `${CONTAINER_PARENT_LANE_ID_PREFIX}${parentId}`;\n}\n\n/**\n * The watermark half of the container-delete cascade, statement-only so the\n * caller can run it inside one atomic transaction alongside the rest of the\n * cascade.\n */\nexport async function deleteContainerWatermarksInTransaction(\n tx: ClientSQLiteDatabase | ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<void> {\n const uniqueContainerIds = Array.from(new Set(containerIds));\n if (uniqueContainerIds.length === 0) {\n return;\n }\n const parentLaneIds = uniqueContainerIds.map(containerParentLaneId);\n const documentLaneIds = uniqueContainerIds;\n await tx\n .delete(containerSyncWatermarks)\n .where(\n or(\n and(\n eq(containerSyncWatermarks.laneKind, CONTAINER_PARENT_LANE),\n inArray(containerSyncWatermarks.laneId, parentLaneIds),\n ),\n and(\n eq(containerSyncWatermarks.laneKind, CONTAINER_DOCUMENTS_LANE),\n inArray(containerSyncWatermarks.laneId, documentLaneIds),\n ),\n ),\n )\n .run();\n await tx\n .delete(containerSyncLaneChecks)\n .where(\n or(\n and(\n eq(containerSyncLaneChecks.laneKind, CONTAINER_PARENT_LANE),\n inArray(containerSyncLaneChecks.laneId, parentLaneIds),\n ),\n and(\n eq(containerSyncLaneChecks.laneKind, CONTAINER_DOCUMENTS_LANE),\n inArray(containerSyncLaneChecks.laneId, documentLaneIds),\n ),\n ),\n )\n .run();\n}\n\nfunction mapSelectedWatermarkRecord(\n row: SelectedContainerSyncWatermark,\n): ContainerSyncWatermarkRecord {\n return {\n laneId: row.laneId,\n laneKind: row.laneKind,\n updatedAt: row.updatedAt,\n watermark: {\n id: row.watermarkId,\n updatedAt: row.watermarkUpdatedAt,\n },\n };\n}\n\nfunction laneKeyString(input: { laneId: string; laneKind: string }): string {\n return `${input.laneKind}\\0${input.laneId}`;\n}\n\nfunction uniqueLaneKeys(\n lanes: ReadonlyArray<ContainerSyncWatermarkLane>,\n): Array<{ laneId: string; laneKind: string }> {\n const seen = new Set<string>();\n const laneKeys: Array<{ laneId: string; laneKind: string }> = [];\n\n for (const lane of lanes) {\n const laneKey = containerSyncWatermarkLaneKey(lane);\n const key = laneKeyString(laneKey);\n if (seen.has(key)) {\n continue;\n }\n seen.add(key);\n laneKeys.push(laneKey);\n }\n\n return laneKeys;\n}\n\n// The ONE batched lane-select shape for this file: both row readers must\n// chunk, because a caller may pass unboundedly many lanes and each lane\n// predicate binds two variables.\nasync function selectLaneRowsInBatches<TRow>(\n lanes: ReadonlyArray<ContainerSyncWatermarkLane>,\n buildLanePredicate: (laneKey: {\n laneId: string;\n laneKind: string;\n }) => SQL | undefined,\n selectBatch: (whereClause: SQL) => Promise<TRow[]>,\n): Promise<TRow[]> {\n const laneKeys = uniqueLaneKeys(lanes);\n const rows: TRow[] = [];\n\n for (\n let index = 0;\n index < laneKeys.length;\n index += LANE_SELECT_BATCH_SIZE\n ) {\n const lanePredicates = laneKeys\n .slice(index, index + LANE_SELECT_BATCH_SIZE)\n .map(buildLanePredicate)\n .filter((predicate): predicate is SQL => predicate !== undefined);\n const whereClause =\n lanePredicates.length === 1 ? lanePredicates[0] : or(...lanePredicates);\n if (!whereClause) {\n continue;\n }\n\n rows.push(...(await selectBatch(whereClause)));\n }\n\n return rows;\n}\n\nasync function selectWatermarkRows(\n execSql: ExecSql,\n lanes: ReadonlyArray<ContainerSyncWatermarkLane>,\n): Promise<SelectedContainerSyncWatermark[]> {\n await ensureSqlTables(execSql, containerSyncWatermarkTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n\n return selectLaneRowsInBatches(\n lanes,\n ({ laneId, laneKind }) =>\n and(\n eq(containerSyncWatermarks.laneKind, laneKind),\n eq(containerSyncWatermarks.laneId, laneId),\n ),\n (whereClause) =>\n db\n .select({\n laneId: containerSyncWatermarks.laneId,\n laneKind: containerSyncWatermarks.laneKind,\n updatedAt: containerSyncWatermarks.updatedAt,\n watermarkId: containerSyncWatermarks.watermarkId,\n watermarkUpdatedAt: containerSyncWatermarks.watermarkUpdatedAt,\n })\n .from(containerSyncWatermarks)\n .where(whereClause),\n );\n}\n\nasync function selectCheckRows(\n execSql: ExecSql,\n lanes: ReadonlyArray<ContainerSyncWatermarkLane>,\n): Promise<ContainerSyncLaneCheckRecord[]> {\n await ensureSqlTables(execSql, containerSyncWatermarkTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n\n return selectLaneRowsInBatches(\n lanes,\n ({ laneId, laneKind }) =>\n and(\n eq(containerSyncLaneChecks.laneKind, laneKind),\n eq(containerSyncLaneChecks.laneId, laneId),\n ),\n (whereClause) =>\n db\n .select({\n checkedAt: containerSyncLaneChecks.checkedAt,\n laneId: containerSyncLaneChecks.laneId,\n laneKind: containerSyncLaneChecks.laneKind,\n })\n .from(containerSyncLaneChecks)\n .where(whereClause),\n );\n}\n\nexport const sqlContainerSyncWatermarkPersistence = {\n async ensureSchema(execSql: ExecSql): Promise<void> {\n await ensureSqlTables(execSql, containerSyncWatermarkTables);\n },\n\n async loadWatermark(\n execSql: ExecSql,\n lane: ContainerSyncWatermarkLane,\n ): Promise<SyncWatermark | null> {\n const [record] =\n await sqlContainerSyncWatermarkPersistence.loadWatermarkRecords(execSql, [\n lane,\n ]);\n return record?.watermark ?? null;\n },\n\n async loadWatermarkRecords(\n execSql: ExecSql,\n lanes: ReadonlyArray<ContainerSyncWatermarkLane>,\n ): Promise<Array<ContainerSyncWatermarkRecord | null>> {\n const rows = await selectWatermarkRows(execSql, lanes);\n const recordsByKey = new Map<string, ContainerSyncWatermarkRecord>();\n\n for (const row of rows) {\n const record = mapSelectedWatermarkRecord(row);\n recordsByKey.set(laneKeyString(record), record);\n }\n\n return lanes.map((lane) => {\n const laneKey = containerSyncWatermarkLaneKey(lane);\n return recordsByKey.get(laneKeyString(laneKey)) ?? null;\n });\n },\n\n async loadCheckRecords(\n execSql: ExecSql,\n lanes: ReadonlyArray<ContainerSyncWatermarkLane>,\n ): Promise<Array<ContainerSyncLaneCheckRecord | null>> {\n const rows = await selectCheckRows(execSql, lanes);\n const recordsByKey = new Map<string, ContainerSyncLaneCheckRecord>();\n\n for (const row of rows) {\n recordsByKey.set(laneKeyString(row), row);\n }\n\n return lanes.map((lane) => {\n const laneKey = containerSyncWatermarkLaneKey(lane);\n return recordsByKey.get(laneKeyString(laneKey)) ?? null;\n });\n },\n\n async markChecked(\n execSql: ExecSql,\n lane: ContainerSyncWatermarkLane,\n options?: ContainerSyncMarkerMutationOptions,\n ): Promise<boolean> {\n await sqlContainerSyncWatermarkPersistence.ensureSchema(execSql);\n const { laneId, laneKind } = containerSyncWatermarkLaneKey(lane);\n const row = {\n checkedAt: new Date().toISOString(),\n laneId,\n laneKind,\n };\n\n const mark = async (\n db: ClientSQLiteDatabase | ClientSQLiteTransactionScope,\n ) => {\n await db\n .insert(containerSyncLaneChecks)\n .values(row)\n .onConflictDoUpdate({\n target: [\n containerSyncLaneChecks.laneKind,\n containerSyncLaneChecks.laneId,\n ],\n set: row,\n })\n .run();\n };\n const runtime = getClientSQLitePersistenceRuntime(execSql);\n if (!options?.stillCurrent) {\n await runtime.runMutation(mark);\n return true;\n }\n return (await runtime.guardedTransaction(mark, options.stillCurrent))\n .committed;\n },\n\n async saveWatermark(\n execSql: ExecSql,\n lane: ContainerSyncWatermarkLane,\n watermark: SyncWatermark,\n options?: ContainerSyncMarkerMutationOptions,\n ): Promise<boolean> {\n await sqlContainerSyncWatermarkPersistence.ensureSchema(execSql);\n const { laneId, laneKind } = containerSyncWatermarkLaneKey(lane);\n const updatedAt = new Date().toISOString();\n const row = {\n laneId,\n laneKind,\n watermarkId: watermark.id,\n watermarkUpdatedAt: watermark.updatedAt,\n updatedAt,\n };\n\n const save = async (\n db: ClientSQLiteDatabase | ClientSQLiteTransactionScope,\n ) => {\n await db\n .insert(containerSyncWatermarks)\n .values(row)\n .onConflictDoUpdate({\n target: [\n containerSyncWatermarks.laneKind,\n containerSyncWatermarks.laneId,\n ],\n set: row,\n })\n .run();\n };\n const runtime = getClientSQLitePersistenceRuntime(execSql);\n if (!options?.stillCurrent) {\n await runtime.runMutation(save);\n return true;\n }\n return (await runtime.guardedTransaction(save, options.stillCurrent))\n .committed;\n },\n\n async deleteWatermarksForContainers(\n execSql: ExecSql,\n containerIds: ReadonlyArray<string>,\n ): Promise<void> {\n const uniqueContainerIds = Array.from(new Set(containerIds));\n if (uniqueContainerIds.length === 0) {\n return;\n }\n\n await sqlContainerSyncWatermarkPersistence.ensureSchema(execSql);\n await getClientSQLitePersistenceRuntime(execSql).runMutation(async (db) => {\n await deleteContainerWatermarksInTransaction(db, uniqueContainerIds);\n });\n },\n};\n"]}
1
+ {"version":3,"file":"containerSyncWatermarkPersistence.js","sourceRoot":"","sources":["containerSyncWatermarkPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SyncWatermark } from \"@tearleads/validators/response\";\nimport { and, eq, inArray, isNotNull, or, type SQL } from \"drizzle-orm\";\nimport {\n containerSyncWatermarks,\n containerSyncWatermarkTables,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteDatabase,\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\n\nconst CONTAINER_PARENT_LANE = \"container_parent\";\nconst CONTAINER_DOCUMENTS_LANE = \"container_documents\";\nconst ROOT_CONTAINER_PARENT_LANE_ID = \"root\";\nconst CONTAINER_PARENT_LANE_ID_PREFIX = \"parent:\";\n// Each lane predicate binds two values, so keep batches comfortably under\n// SQLite's variable limit when a caller reads many lanes at once.\nconst LANE_SELECT_BATCH_SIZE = 100;\n\nexport type ContainerSyncWatermarkLane =\n | {\n kind: typeof CONTAINER_PARENT_LANE;\n parentId: string | null;\n }\n | {\n kind: typeof CONTAINER_DOCUMENTS_LANE;\n containerId: string;\n };\n\ninterface SelectedContainerSyncWatermark {\n laneId: string;\n laneKind: string;\n updatedAt: string | null;\n watermarkId: string | null;\n watermarkUpdatedAt: string | null;\n}\n\ninterface ContainerSyncWatermarkRecord {\n laneId: string;\n laneKind: string;\n updatedAt: string;\n watermark: SyncWatermark;\n}\n\nexport interface ContainerSyncLaneCheckRecord {\n checkedAt: string;\n laneId: string;\n laneKind: string;\n}\n\ninterface ContainerSyncMarkerMutationOptions {\n readonly stillCurrent?: (() => boolean) | undefined;\n}\n\nexport const containerParentSyncLane = (\n parentId: string | null,\n): ContainerSyncWatermarkLane => ({\n kind: CONTAINER_PARENT_LANE,\n parentId,\n});\n\nexport const containerContentsSyncLane = (\n containerId: string,\n): ContainerSyncWatermarkLane => ({\n kind: CONTAINER_DOCUMENTS_LANE,\n containerId,\n});\n\nexport function containerSyncWatermarkLaneKey(\n lane: ContainerSyncWatermarkLane,\n): {\n laneId: string;\n laneKind: string;\n} {\n switch (lane.kind) {\n case CONTAINER_PARENT_LANE:\n return {\n laneKind: lane.kind,\n laneId: containerParentLaneId(lane.parentId),\n };\n case CONTAINER_DOCUMENTS_LANE:\n return {\n laneKind: lane.kind,\n laneId: lane.containerId,\n };\n }\n}\n\nfunction containerParentLaneId(parentId: string | null): string {\n return parentId === null\n ? ROOT_CONTAINER_PARENT_LANE_ID\n : `${CONTAINER_PARENT_LANE_ID_PREFIX}${parentId}`;\n}\n\n/**\n * The lane-state half of the container-delete cascade (cursor and freshness\n * marker share one row), statement-only so the caller can run it inside one\n * atomic transaction alongside the rest of the cascade.\n */\nexport async function deleteContainerWatermarksInTransaction(\n tx: ClientSQLiteDatabase | ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<void> {\n const uniqueContainerIds = Array.from(new Set(containerIds));\n if (uniqueContainerIds.length === 0) {\n return;\n }\n const parentLaneIds = uniqueContainerIds.map(containerParentLaneId);\n const documentLaneIds = uniqueContainerIds;\n await tx\n .delete(containerSyncWatermarks)\n .where(\n or(\n and(\n eq(containerSyncWatermarks.laneKind, CONTAINER_PARENT_LANE),\n inArray(containerSyncWatermarks.laneId, parentLaneIds),\n ),\n and(\n eq(containerSyncWatermarks.laneKind, CONTAINER_DOCUMENTS_LANE),\n inArray(containerSyncWatermarks.laneId, documentLaneIds),\n ),\n ),\n )\n .run();\n}\n\nfunction mapSelectedWatermarkRecord(\n row: SelectedContainerSyncWatermark,\n): ContainerSyncWatermarkRecord | null {\n // A lane checked before any cursor was saved has no watermark yet.\n if (\n row.watermarkId === null ||\n row.watermarkUpdatedAt === null ||\n row.updatedAt === null\n ) {\n return null;\n }\n return {\n laneId: row.laneId,\n laneKind: row.laneKind,\n updatedAt: row.updatedAt,\n watermark: {\n id: row.watermarkId,\n updatedAt: row.watermarkUpdatedAt,\n },\n };\n}\n\nfunction laneKeyString(input: { laneId: string; laneKind: string }): string {\n return `${input.laneKind}\\0${input.laneId}`;\n}\n\nfunction uniqueLaneKeys(\n lanes: ReadonlyArray<ContainerSyncWatermarkLane>,\n): Array<{ laneId: string; laneKind: string }> {\n const seen = new Set<string>();\n const laneKeys: Array<{ laneId: string; laneKind: string }> = [];\n\n for (const lane of lanes) {\n const laneKey = containerSyncWatermarkLaneKey(lane);\n const key = laneKeyString(laneKey);\n if (seen.has(key)) {\n continue;\n }\n seen.add(key);\n laneKeys.push(laneKey);\n }\n\n return laneKeys;\n}\n\n// The ONE batched lane-select shape for this file: both row readers must\n// chunk, because a caller may pass unboundedly many lanes and each lane\n// predicate binds two variables.\nasync function selectLaneRowsInBatches<TRow>(\n lanes: ReadonlyArray<ContainerSyncWatermarkLane>,\n buildLanePredicate: (laneKey: {\n laneId: string;\n laneKind: string;\n }) => SQL | undefined,\n selectBatch: (whereClause: SQL) => Promise<TRow[]>,\n): Promise<TRow[]> {\n const laneKeys = uniqueLaneKeys(lanes);\n const rows: TRow[] = [];\n\n for (\n let index = 0;\n index < laneKeys.length;\n index += LANE_SELECT_BATCH_SIZE\n ) {\n const lanePredicates = laneKeys\n .slice(index, index + LANE_SELECT_BATCH_SIZE)\n .map(buildLanePredicate)\n .filter((predicate): predicate is SQL => predicate !== undefined);\n const whereClause =\n lanePredicates.length === 1 ? lanePredicates[0] : or(...lanePredicates);\n if (!whereClause) {\n continue;\n }\n\n rows.push(...(await selectBatch(whereClause)));\n }\n\n return rows;\n}\n\nasync function selectWatermarkRows(\n execSql: ExecSql,\n lanes: ReadonlyArray<ContainerSyncWatermarkLane>,\n): Promise<SelectedContainerSyncWatermark[]> {\n await ensureSqlTables(execSql, containerSyncWatermarkTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n\n return selectLaneRowsInBatches(\n lanes,\n ({ laneId, laneKind }) =>\n and(\n eq(containerSyncWatermarks.laneKind, laneKind),\n eq(containerSyncWatermarks.laneId, laneId),\n ),\n (whereClause) =>\n db\n .select({\n laneId: containerSyncWatermarks.laneId,\n laneKind: containerSyncWatermarks.laneKind,\n updatedAt: containerSyncWatermarks.updatedAt,\n watermarkId: containerSyncWatermarks.watermarkId,\n watermarkUpdatedAt: containerSyncWatermarks.watermarkUpdatedAt,\n })\n .from(containerSyncWatermarks)\n .where(whereClause),\n );\n}\n\nasync function selectCheckRows(\n execSql: ExecSql,\n lanes: ReadonlyArray<ContainerSyncWatermarkLane>,\n): Promise<ContainerSyncLaneCheckRecord[]> {\n await ensureSqlTables(execSql, containerSyncWatermarkTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n\n return selectLaneRowsInBatches(\n lanes,\n ({ laneId, laneKind }) =>\n and(\n eq(containerSyncWatermarks.laneKind, laneKind),\n eq(containerSyncWatermarks.laneId, laneId),\n ),\n async (whereClause) =>\n (\n await db\n .select({\n checkedAt: containerSyncWatermarks.checkedAt,\n laneId: containerSyncWatermarks.laneId,\n laneKind: containerSyncWatermarks.laneKind,\n })\n .from(containerSyncWatermarks)\n .where(and(whereClause, isNotNull(containerSyncWatermarks.checkedAt)))\n ).flatMap(({ checkedAt, laneId, laneKind }) =>\n checkedAt === null ? [] : [{ checkedAt, laneId, laneKind }],\n ),\n );\n}\n\nexport const sqlContainerSyncWatermarkPersistence = {\n async ensureSchema(execSql: ExecSql): Promise<void> {\n await ensureSqlTables(execSql, containerSyncWatermarkTables);\n },\n\n async loadWatermark(\n execSql: ExecSql,\n lane: ContainerSyncWatermarkLane,\n ): Promise<SyncWatermark | null> {\n const [record] =\n await sqlContainerSyncWatermarkPersistence.loadWatermarkRecords(execSql, [\n lane,\n ]);\n return record?.watermark ?? null;\n },\n\n async loadWatermarkRecords(\n execSql: ExecSql,\n lanes: ReadonlyArray<ContainerSyncWatermarkLane>,\n ): Promise<Array<ContainerSyncWatermarkRecord | null>> {\n const rows = await selectWatermarkRows(execSql, lanes);\n const recordsByKey = new Map<string, ContainerSyncWatermarkRecord>();\n\n for (const row of rows) {\n const record = mapSelectedWatermarkRecord(row);\n if (record) recordsByKey.set(laneKeyString(record), record);\n }\n\n return lanes.map((lane) => {\n const laneKey = containerSyncWatermarkLaneKey(lane);\n return recordsByKey.get(laneKeyString(laneKey)) ?? null;\n });\n },\n\n async loadCheckRecords(\n execSql: ExecSql,\n lanes: ReadonlyArray<ContainerSyncWatermarkLane>,\n ): Promise<Array<ContainerSyncLaneCheckRecord | null>> {\n const rows = await selectCheckRows(execSql, lanes);\n const recordsByKey = new Map<string, ContainerSyncLaneCheckRecord>();\n\n for (const row of rows) {\n recordsByKey.set(laneKeyString(row), row);\n }\n\n return lanes.map((lane) => {\n const laneKey = containerSyncWatermarkLaneKey(lane);\n return recordsByKey.get(laneKeyString(laneKey)) ?? null;\n });\n },\n\n markChecked(\n execSql: ExecSql,\n lane: ContainerSyncWatermarkLane,\n options?: ContainerSyncMarkerMutationOptions,\n ): Promise<boolean> {\n return sqlContainerSyncWatermarkPersistence.recordLaneFetch(\n execSql,\n lane,\n { checked: true, watermark: null },\n options,\n );\n },\n\n saveWatermark(\n execSql: ExecSql,\n lane: ContainerSyncWatermarkLane,\n watermark: SyncWatermark,\n options?: ContainerSyncMarkerMutationOptions,\n ): Promise<boolean> {\n return sqlContainerSyncWatermarkPersistence.recordLaneFetch(\n execSql,\n lane,\n { checked: false, watermark },\n options,\n );\n },\n\n /**\n * Upserts one lane row in a single statement: the cursor when `watermark`\n * is set and the freshness marker when `checked`; the other half is kept.\n */\n async recordLaneFetch(\n execSql: ExecSql,\n lane: ContainerSyncWatermarkLane,\n fetch: {\n readonly checked: boolean;\n readonly watermark: SyncWatermark | null;\n },\n options?: ContainerSyncMarkerMutationOptions,\n ): Promise<boolean> {\n if (!fetch.checked && !fetch.watermark) return true;\n await sqlContainerSyncWatermarkPersistence.ensureSchema(execSql);\n const now = new Date().toISOString();\n const changed = {\n ...(fetch.watermark\n ? {\n updatedAt: now,\n watermarkId: fetch.watermark.id,\n watermarkUpdatedAt: fetch.watermark.updatedAt,\n }\n : {}),\n ...(fetch.checked ? { checkedAt: now } : {}),\n };\n const record = async (\n db: ClientSQLiteDatabase | ClientSQLiteTransactionScope,\n ) => {\n await db\n .insert(containerSyncWatermarks)\n .values({ ...containerSyncWatermarkLaneKey(lane), ...changed })\n .onConflictDoUpdate({\n target: [\n containerSyncWatermarks.laneKind,\n containerSyncWatermarks.laneId,\n ],\n set: changed,\n })\n .run();\n };\n const runtime = getClientSQLitePersistenceRuntime(execSql);\n if (!options?.stillCurrent) {\n await runtime.runMutation(record);\n return true;\n }\n return (await runtime.guardedTransaction(record, options.stillCurrent))\n .committed;\n },\n\n async deleteWatermarksForContainers(\n execSql: ExecSql,\n containerIds: ReadonlyArray<string>,\n ): Promise<void> {\n const uniqueContainerIds = Array.from(new Set(containerIds));\n if (uniqueContainerIds.length === 0) {\n return;\n }\n\n await sqlContainerSyncWatermarkPersistence.ensureSchema(execSql);\n await getClientSQLitePersistenceRuntime(execSql).runMutation(async (db) => {\n await deleteContainerWatermarksInTransaction(db, uniqueContainerIds);\n });\n },\n};\n"]}
@@ -1,5 +1,5 @@
1
1
  import { asc, eq, inArray } from "drizzle-orm";
2
- import { containerDocumentTombstoneHolds, documentContainerProjection, documentContainerProjectionTables, documentMoveIntentTables, documentTables, } from "../../sqlite/schema.js";
2
+ import { containerDocumentTombstoneHolds, documentContainerProjection, documentContainerProjectionTables, documentPlacementIntentTables, documentTables, } from "../../sqlite/schema.js";
3
3
  import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
4
4
  import { ensureSqlTables } from "../../sqlite/sqlSchema.js";
5
5
  import { deleteContainerDocumentTombstoneHoldRowsForLinks, deleteContainerDocumentTombstoneHoldsForDocuments, } from "../documents/containerDocumentTombstoneHoldsPersistence.js";
@@ -85,7 +85,7 @@ export const sqlDocumentContainerProjectionPersistence = {
85
85
  async replaceDocumentLinksBatch(execSql, inputs, options) {
86
86
  await ensureSqlTables(execSql, [
87
87
  ...documentContainerProjectionTables,
88
- ...documentMoveIntentTables,
88
+ ...documentPlacementIntentTables,
89
89
  ...documentTables,
90
90
  ]);
91
91
  const latest = new Map(inputs.map((input) => [input.documentId, input]));
@@ -1 +1 @@
1
- {"version":3,"file":"documentContainerProjectionPersistence.js","sourceRoot":"","sources":["documentContainerProjectionPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EACL,+BAA+B,EAC/B,2BAA2B,EAC3B,iCAAiC,EACjC,wBAAwB,EACxB,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEL,iCAAiC,GAClC,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAgB,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EACL,gDAAgD,EAChD,iDAAiD,GAClD,MAAM,yDAAyD,CAAC;AAEjE,OAAO,EAGL,gCAAgC,GACjC,MAAM,qBAAqB,CAAC;AA6B7B,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,yCAAyC,CAC7D,EAAgC,EAChC,YAAmC;IAEnC,MAAM,EAAE;SACL,MAAM,CAAC,2BAA2B,CAAC;SACnC,KAAK,CAAC,OAAO,CAAC,2BAA2B,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;SACrE,GAAG,EAAE,CAAC;IACT,MAAM,EAAE;SACL,MAAM,CAAC,+BAA+B,CAAC;SACvC,KAAK,CAAC,OAAO,CAAC,+BAA+B,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;SACzE,GAAG,EAAE,CAAC;AACX,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wCAAwC,CAC5D,EAAgC,EAChC,WAAkC;IAElC,MAAM,EAAE;SACL,MAAM,CAAC,2BAA2B,CAAC;SACnC,KAAK,CAAC,OAAO,CAAC,2BAA2B,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;SACnE,GAAG,EAAE,CAAC;IACT,MAAM,EAAE;SACL,MAAM,CAAC,+BAA+B,CAAC;SACvC,KAAK,CAAC,OAAO,CAAC,+BAA+B,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;SACvE,GAAG,EAAE,CAAC;AACX,CAAC;AAED,MAAM,CAAC,MAAM,yCAAyC,GACpD;IACE,KAAK,CAAC,YAAY,CAAC,OAAO;QACxB,MAAM,eAAe,CAAC,OAAO,EAAE,iCAAiC,CAAC,CAAC;IACpE,CAAC;IACD,KAAK,CAAC,sBAAsB,CAAC,OAAO,EAAE,UAAU;QAC9C,MAAM,eAAe,CAAC,OAAO,EAAE,iCAAiC,CAAC,CAAC;QAClE,MAAM,EAAE,EAAE,EAAE,GAAG,iCAAiC,CAAC,OAAO,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,MAAM,EAAE;aAClB,MAAM,CAAC,EAAE,WAAW,EAAE,2BAA2B,CAAC,WAAW,EAAE,CAAC;aAChE,IAAI,CAAC,2BAA2B,CAAC;aACjC,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;aAC7D,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,WAAW,CAAC,CAAC,CAAC;QAEzD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC5C,CAAC;IACD,KAAK,CAAC,mCAAmC,CAAC,OAAO,EAAE,WAAW;QAC5D,MAAM,eAAe,CAAC,OAAO,EAAE,iCAAiC,CAAC,CAAC;QAClE,MAAM,iBAAiB,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;QAC3D,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnC,OAAO,IAAI,GAAG,EAAE,CAAC;QACnB,CAAC;QAED,MAAM,EAAE,EAAE,EAAE,GAAG,iCAAiC,CAAC,OAAO,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,MAAM,EAAE;aAClB,MAAM,CAAC;YACN,UAAU,EAAE,2BAA2B,CAAC,UAAU;YAClD,WAAW,EAAE,2BAA2B,CAAC,WAAW;SACrD,CAAC;aACD,IAAI,CAAC,2BAA2B,CAAC;aACjC,KAAK,CACJ,OAAO,CAAC,2BAA2B,CAAC,UAAU,EAAE,iBAAiB,CAAC,CACnE;aACA,OAAO,CACN,GAAG,CAAC,2BAA2B,CAAC,UAAU,CAAC,EAC3C,GAAG,CAAC,2BAA2B,CAAC,WAAW,CAAC,CAC7C,CAAC;QAEJ,MAAM,8BAA8B,GAAG,IAAI,GAAG,EAAoB,CAAC;QACnE,KAAK,MAAM,UAAU,IAAI,iBAAiB,EAAE,CAAC;YAC3C,8BAA8B,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QACrD,CAAC;QAED,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,kBAAkB,GACtB,8BAA8B,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;YAC3D,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACzC,8BAA8B,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC;QACzE,CAAC;QAED,OAAO,8BAA8B,CAAC;IACxC,CAAC;IACD,KAAK,CAAC,6BAA6B,CAAC,OAAO,EAAE,YAAY;QACvD,MAAM,eAAe,CAAC,OAAO,EAAE,iCAAiC,CAAC,CAAC;QAClE,MAAM,kBAAkB,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;QAC7D,IAAI,kBAAkB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,EAAE,EAAE,EAAE,GAAG,iCAAiC,CAAC,OAAO,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,MAAM,EAAE;aAClB,cAAc,CAAC,EAAE,UAAU,EAAE,2BAA2B,CAAC,UAAU,EAAE,CAAC;aACtE,IAAI,CAAC,2BAA2B,CAAC;aACjC,KAAK,CACJ,OAAO,CAAC,2BAA2B,CAAC,WAAW,EAAE,kBAAkB,CAAC,CACrE;aACA,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,UAAU,CAAC,CAAC,CAAC;QAExD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC3C,CAAC;IACD,KAAK,CAAC,oBAAoB,CAAC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO;QACnE,MAAM,yCAAyC,CAAC,yBAAyB,CACvE,OAAO,EACP,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,EAC9B,OAAO,CACR,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,yBAAyB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO;QACtD,MAAM,eAAe,CAAC,OAAO,EAAE;YAC7B,GAAG,iCAAiC;YACpC,GAAG,wBAAwB;YAC3B,GAAG,cAAc;SAClB,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;QACzE,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO;QAC9B,MAAM,OAAO,GAAG,iCAAiC,CAAC,OAAO,CAAC,CAAC;QAC3D,MAAM,OAAO,GAAG,KAAK,EAAE,EAAgC,EAAE,EAAE;YACzD,MAAM,QAAQ,GAAG,MAAM,gCAAgC,CACrD,EAAE,EACF,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EACpB,OAAO,CACR,CAAC;YACF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YAClC,+DAA+D;YAC/D,sEAAsE;YACtE,+DAA+D;YAC/D,IAAI,OAAO,EAAE,YAAY,EAAE,CAAC;gBAC1B,MAAM,iDAAiD,CACrD,EAAE,EACF,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAC1C,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,MAAM,gDAAgD,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;YACvE,CAAC;YACD,MAAM,EAAE;iBACL,MAAM,CAAC,2BAA2B,CAAC;iBACnC,KAAK,CACJ,OAAO,CACL,2BAA2B,CAAC,UAAU,EACtC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAC1C,CACF;iBACA,GAAG,EAAE,CAAC;YACT,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC3C,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CACtC,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;iBACpC,IAAI,EAAE;iBACN,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;gBACrB,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,WAAW;gBACX,SAAS;aACV,CAAC,CAAC,CACN,CAAC;YACF,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;gBACjB,MAAM,EAAE,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;QACpE,CAAC,CAAC;QACF,IAAI,OAAO,EAAE,YAAY,EAAE,CAAC;YAC1B,MAAM,OAAO,CAAC,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;YAChE,OAAO;QACT,CAAC;QACD,MAAM,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;CACF,CAAC","sourcesContent":["import { asc, eq, inArray } from \"drizzle-orm\";\nimport {\n containerDocumentTombstoneHolds,\n documentContainerProjection,\n documentContainerProjectionTables,\n documentMoveIntentTables,\n documentTables,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\nimport {\n deleteContainerDocumentTombstoneHoldRowsForLinks,\n deleteContainerDocumentTombstoneHoldsForDocuments,\n} from \"../documents/containerDocumentTombstoneHoldsPersistence\";\n\nimport {\n type DocumentPlacementInput,\n type DocumentPlacementWriteOptions,\n filterWritableDocumentPlacements,\n} from \"./documentPlacement\";\n\ninterface DocumentContainerProjectionPersistence {\n ensureSchema: (execSql: ExecSql) => Promise<void>;\n listLinkedContainerIds: (\n execSql: ExecSql,\n documentId: string,\n ) => Promise<ReadonlyArray<string>>;\n listLinkedContainerIdsByDocumentIds: (\n execSql: ExecSql,\n documentIds: ReadonlyArray<string>,\n ) => Promise<ReadonlyMap<string, ReadonlyArray<string>>>;\n listDocumentIdsByContainerIds: (\n execSql: ExecSql,\n containerIds: ReadonlyArray<string>,\n ) => Promise<ReadonlyArray<string>>;\n replaceDocumentLinks: (\n execSql: ExecSql,\n documentId: string,\n containerIds: ReadonlyArray<string>,\n options?: DocumentPlacementWriteOptions,\n ) => Promise<void>;\n replaceDocumentLinksBatch: (\n execSql: ExecSql,\n inputs: ReadonlyArray<DocumentPlacementInput>,\n options?: DocumentPlacementWriteOptions,\n ) => Promise<void>;\n}\n\n/** Link rows and their tombstone holds leave together on a remote reset. */\nexport async function deleteDocumentPlacementRowsByContainerIds(\n tx: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<void> {\n await tx\n .delete(documentContainerProjection)\n .where(inArray(documentContainerProjection.containerId, containerIds))\n .run();\n await tx\n .delete(containerDocumentTombstoneHolds)\n .where(inArray(containerDocumentTombstoneHolds.containerId, containerIds))\n .run();\n}\n\nexport async function deleteDocumentPlacementRowsByDocumentIds(\n tx: ClientSQLiteTransactionScope,\n documentIds: ReadonlyArray<string>,\n): Promise<void> {\n await tx\n .delete(documentContainerProjection)\n .where(inArray(documentContainerProjection.documentId, documentIds))\n .run();\n await tx\n .delete(containerDocumentTombstoneHolds)\n .where(inArray(containerDocumentTombstoneHolds.documentId, documentIds))\n .run();\n}\n\nexport const sqlDocumentContainerProjectionPersistence: DocumentContainerProjectionPersistence =\n {\n async ensureSchema(execSql) {\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n },\n async listLinkedContainerIds(execSql, documentId) {\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({ containerId: documentContainerProjection.containerId })\n .from(documentContainerProjection)\n .where(eq(documentContainerProjection.documentId, documentId))\n .orderBy(asc(documentContainerProjection.containerId));\n\n return rows.map((row) => row.containerId);\n },\n async listLinkedContainerIdsByDocumentIds(execSql, documentIds) {\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const uniqueDocumentIds = Array.from(new Set(documentIds));\n if (uniqueDocumentIds.length === 0) {\n return new Map();\n }\n\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n documentId: documentContainerProjection.documentId,\n containerId: documentContainerProjection.containerId,\n })\n .from(documentContainerProjection)\n .where(\n inArray(documentContainerProjection.documentId, uniqueDocumentIds),\n )\n .orderBy(\n asc(documentContainerProjection.documentId),\n asc(documentContainerProjection.containerId),\n );\n\n const linkedContainerIdsByDocumentId = new Map<string, string[]>();\n for (const documentId of uniqueDocumentIds) {\n linkedContainerIdsByDocumentId.set(documentId, []);\n }\n\n for (const row of rows) {\n const linkedContainerIds =\n linkedContainerIdsByDocumentId.get(row.documentId) ?? [];\n linkedContainerIds.push(row.containerId);\n linkedContainerIdsByDocumentId.set(row.documentId, linkedContainerIds);\n }\n\n return linkedContainerIdsByDocumentId;\n },\n async listDocumentIdsByContainerIds(execSql, containerIds) {\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const uniqueContainerIds = Array.from(new Set(containerIds));\n if (uniqueContainerIds.length === 0) {\n return [];\n }\n\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .selectDistinct({ documentId: documentContainerProjection.documentId })\n .from(documentContainerProjection)\n .where(\n inArray(documentContainerProjection.containerId, uniqueContainerIds),\n )\n .orderBy(asc(documentContainerProjection.documentId));\n\n return rows.map((row) => row.documentId);\n },\n async replaceDocumentLinks(execSql, documentId, containerIds, options) {\n await sqlDocumentContainerProjectionPersistence.replaceDocumentLinksBatch(\n execSql,\n [{ documentId, containerIds }],\n options,\n );\n },\n async replaceDocumentLinksBatch(execSql, inputs, options) {\n await ensureSqlTables(execSql, [\n ...documentContainerProjectionTables,\n ...documentMoveIntentTables,\n ...documentTables,\n ]);\n const latest = new Map(inputs.map((input) => [input.documentId, input]));\n if (latest.size === 0) return;\n const runtime = getClientSQLitePersistenceRuntime(execSql);\n const replace = async (tx: ClientSQLiteTransactionScope) => {\n const writable = await filterWritableDocumentPlacements(\n tx,\n [...latest.values()],\n options,\n );\n if (writable.length === 0) return;\n // A placement the writer re-asserts is no longer tombstoned: a\n // listing that links it again, or a local move that owns it, releases\n // the hold so the document is not hidden until the next retry.\n if (options?.moveIntentId) {\n await deleteContainerDocumentTombstoneHoldsForDocuments(\n tx,\n writable.map((input) => input.documentId),\n );\n } else {\n await deleteContainerDocumentTombstoneHoldRowsForLinks(tx, writable);\n }\n await tx\n .delete(documentContainerProjection)\n .where(\n inArray(\n documentContainerProjection.documentId,\n writable.map((input) => input.documentId),\n ),\n )\n .run();\n const updatedAt = new Date().toISOString();\n const rows = writable.flatMap((input) =>\n Array.from(new Set(input.containerIds))\n .sort()\n .map((containerId) => ({\n documentId: input.documentId,\n containerId,\n updatedAt,\n })),\n );\n if (rows.length > 0)\n await tx.insert(documentContainerProjection).values(rows).run();\n };\n if (options?.stillCurrent) {\n await runtime.guardedTransaction(replace, options.stillCurrent);\n return;\n }\n await runtime.transaction(replace);\n },\n };\n"]}
1
+ {"version":3,"file":"documentContainerProjectionPersistence.js","sourceRoot":"","sources":["documentContainerProjectionPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { asc, eq, inArray } from \"drizzle-orm\";\nimport {\n containerDocumentTombstoneHolds,\n documentContainerProjection,\n documentContainerProjectionTables,\n documentPlacementIntentTables,\n documentTables,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\nimport {\n deleteContainerDocumentTombstoneHoldRowsForLinks,\n deleteContainerDocumentTombstoneHoldsForDocuments,\n} from \"../documents/containerDocumentTombstoneHoldsPersistence\";\n\nimport {\n type DocumentPlacementInput,\n type DocumentPlacementWriteOptions,\n filterWritableDocumentPlacements,\n} from \"./documentPlacement\";\n\ninterface DocumentContainerProjectionPersistence {\n ensureSchema: (execSql: ExecSql) => Promise<void>;\n listLinkedContainerIds: (\n execSql: ExecSql,\n documentId: string,\n ) => Promise<ReadonlyArray<string>>;\n listLinkedContainerIdsByDocumentIds: (\n execSql: ExecSql,\n documentIds: ReadonlyArray<string>,\n ) => Promise<ReadonlyMap<string, ReadonlyArray<string>>>;\n listDocumentIdsByContainerIds: (\n execSql: ExecSql,\n containerIds: ReadonlyArray<string>,\n ) => Promise<ReadonlyArray<string>>;\n replaceDocumentLinks: (\n execSql: ExecSql,\n documentId: string,\n containerIds: ReadonlyArray<string>,\n options?: DocumentPlacementWriteOptions,\n ) => Promise<void>;\n replaceDocumentLinksBatch: (\n execSql: ExecSql,\n inputs: ReadonlyArray<DocumentPlacementInput>,\n options?: DocumentPlacementWriteOptions,\n ) => Promise<void>;\n}\n\n/** Link rows and their tombstone holds leave together on a remote reset. */\nexport async function deleteDocumentPlacementRowsByContainerIds(\n tx: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<void> {\n await tx\n .delete(documentContainerProjection)\n .where(inArray(documentContainerProjection.containerId, containerIds))\n .run();\n await tx\n .delete(containerDocumentTombstoneHolds)\n .where(inArray(containerDocumentTombstoneHolds.containerId, containerIds))\n .run();\n}\n\nexport async function deleteDocumentPlacementRowsByDocumentIds(\n tx: ClientSQLiteTransactionScope,\n documentIds: ReadonlyArray<string>,\n): Promise<void> {\n await tx\n .delete(documentContainerProjection)\n .where(inArray(documentContainerProjection.documentId, documentIds))\n .run();\n await tx\n .delete(containerDocumentTombstoneHolds)\n .where(inArray(containerDocumentTombstoneHolds.documentId, documentIds))\n .run();\n}\n\nexport const sqlDocumentContainerProjectionPersistence: DocumentContainerProjectionPersistence =\n {\n async ensureSchema(execSql) {\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n },\n async listLinkedContainerIds(execSql, documentId) {\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({ containerId: documentContainerProjection.containerId })\n .from(documentContainerProjection)\n .where(eq(documentContainerProjection.documentId, documentId))\n .orderBy(asc(documentContainerProjection.containerId));\n\n return rows.map((row) => row.containerId);\n },\n async listLinkedContainerIdsByDocumentIds(execSql, documentIds) {\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const uniqueDocumentIds = Array.from(new Set(documentIds));\n if (uniqueDocumentIds.length === 0) {\n return new Map();\n }\n\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n documentId: documentContainerProjection.documentId,\n containerId: documentContainerProjection.containerId,\n })\n .from(documentContainerProjection)\n .where(\n inArray(documentContainerProjection.documentId, uniqueDocumentIds),\n )\n .orderBy(\n asc(documentContainerProjection.documentId),\n asc(documentContainerProjection.containerId),\n );\n\n const linkedContainerIdsByDocumentId = new Map<string, string[]>();\n for (const documentId of uniqueDocumentIds) {\n linkedContainerIdsByDocumentId.set(documentId, []);\n }\n\n for (const row of rows) {\n const linkedContainerIds =\n linkedContainerIdsByDocumentId.get(row.documentId) ?? [];\n linkedContainerIds.push(row.containerId);\n linkedContainerIdsByDocumentId.set(row.documentId, linkedContainerIds);\n }\n\n return linkedContainerIdsByDocumentId;\n },\n async listDocumentIdsByContainerIds(execSql, containerIds) {\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const uniqueContainerIds = Array.from(new Set(containerIds));\n if (uniqueContainerIds.length === 0) {\n return [];\n }\n\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .selectDistinct({ documentId: documentContainerProjection.documentId })\n .from(documentContainerProjection)\n .where(\n inArray(documentContainerProjection.containerId, uniqueContainerIds),\n )\n .orderBy(asc(documentContainerProjection.documentId));\n\n return rows.map((row) => row.documentId);\n },\n async replaceDocumentLinks(execSql, documentId, containerIds, options) {\n await sqlDocumentContainerProjectionPersistence.replaceDocumentLinksBatch(\n execSql,\n [{ documentId, containerIds }],\n options,\n );\n },\n async replaceDocumentLinksBatch(execSql, inputs, options) {\n await ensureSqlTables(execSql, [\n ...documentContainerProjectionTables,\n ...documentPlacementIntentTables,\n ...documentTables,\n ]);\n const latest = new Map(inputs.map((input) => [input.documentId, input]));\n if (latest.size === 0) return;\n const runtime = getClientSQLitePersistenceRuntime(execSql);\n const replace = async (tx: ClientSQLiteTransactionScope) => {\n const writable = await filterWritableDocumentPlacements(\n tx,\n [...latest.values()],\n options,\n );\n if (writable.length === 0) return;\n // A placement the writer re-asserts is no longer tombstoned: a\n // listing that links it again, or a local move that owns it, releases\n // the hold so the document is not hidden until the next retry.\n if (options?.moveIntentId) {\n await deleteContainerDocumentTombstoneHoldsForDocuments(\n tx,\n writable.map((input) => input.documentId),\n );\n } else {\n await deleteContainerDocumentTombstoneHoldRowsForLinks(tx, writable);\n }\n await tx\n .delete(documentContainerProjection)\n .where(\n inArray(\n documentContainerProjection.documentId,\n writable.map((input) => input.documentId),\n ),\n )\n .run();\n const updatedAt = new Date().toISOString();\n const rows = writable.flatMap((input) =>\n Array.from(new Set(input.containerIds))\n .sort()\n .map((containerId) => ({\n documentId: input.documentId,\n containerId,\n updatedAt,\n })),\n );\n if (rows.length > 0)\n await tx.insert(documentContainerProjection).values(rows).run();\n };\n if (options?.stillCurrent) {\n await runtime.guardedTransaction(replace, options.stillCurrent);\n return;\n }\n await runtime.transaction(replace);\n },\n };\n"]}
@@ -1,15 +1,15 @@
1
1
  import { and, eq, inArray } from "drizzle-orm";
2
- import { documentMoveIntents, documents } from "../../sqlite/schema.js";
2
+ import { documents, inLocalIntentLane, LOCAL_INTENT_LANES, localIntents, } from "../../sqlite/schema.js";
3
3
  import { DOCUMENTS_APP_KIND } from "../documents/internal/constants.js";
4
4
  export async function loadDocumentMovePlacement(tx, documentId) {
5
5
  const [intent] = await tx
6
6
  .select({
7
- id: documentMoveIntents.id,
8
- intentType: documentMoveIntents.intentType,
9
- targetContainerId: documentMoveIntents.targetContainerId,
7
+ id: localIntents.id,
8
+ intentType: localIntents.intentType,
9
+ targetContainerId: localIntents.targetContainerId,
10
10
  })
11
- .from(documentMoveIntents)
12
- .where(eq(documentMoveIntents.documentId, documentId))
11
+ .from(localIntents)
12
+ .where(and(inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement), eq(localIntents.subjectId, documentId)))
13
13
  .limit(1);
14
14
  return intent;
15
15
  }
@@ -18,11 +18,11 @@ export async function filterWritableDocumentPlacements(tx, inputs, options) {
18
18
  const ids = inputs.map((input) => input.documentId);
19
19
  const intents = new Map((await tx
20
20
  .select({
21
- documentId: documentMoveIntents.documentId,
22
- id: documentMoveIntents.id,
21
+ documentId: localIntents.subjectId,
22
+ id: localIntents.id,
23
23
  })
24
- .from(documentMoveIntents)
25
- .where(inArray(documentMoveIntents.documentId, ids))).map((row) => [row.documentId, row.id]));
24
+ .from(localIntents)
25
+ .where(and(inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement), inArray(localIntents.subjectId, ids)))).map((row) => [row.documentId, row.id]));
26
26
  const versionedIds = inputs
27
27
  .filter((input) => input.accessEpoch !== undefined)
28
28
  .map((input) => input.documentId);
@@ -1 +1 @@
1
- {"version":3,"file":"documentPlacement.js","sourceRoot":"","sources":["documentPlacement.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, inArray } from \"drizzle-orm\";\nimport { documentMoveIntents, documents } from \"../../sqlite/schema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { DOCUMENTS_APP_KIND } from \"../documents/internal/constants\";\n\nexport interface DocumentPlacementInput {\n documentId: string;\n containerIds: ReadonlyArray<string>;\n accessEpoch?: number | undefined;\n}\n\nexport interface DocumentPlacementWriteOptions {\n stillCurrent?: (() => boolean) | undefined;\n moveIntentId?: string | undefined;\n}\n\nexport async function loadDocumentMovePlacement(\n tx: ClientSQLiteTransactionScope,\n documentId: string,\n) {\n const [intent] = await tx\n .select({\n id: documentMoveIntents.id,\n intentType: documentMoveIntents.intentType,\n targetContainerId: documentMoveIntents.targetContainerId,\n })\n .from(documentMoveIntents)\n .where(eq(documentMoveIntents.documentId, documentId))\n .limit(1);\n return intent;\n}\n\n/** Check at commit time: neither intermediate replay nor discovery owns a pending move. */\nexport async function filterWritableDocumentPlacements(\n tx: ClientSQLiteTransactionScope,\n inputs: readonly DocumentPlacementInput[],\n options?: DocumentPlacementWriteOptions,\n): Promise<readonly DocumentPlacementInput[]> {\n const ids = inputs.map((input) => input.documentId);\n const intents = new Map(\n (\n await tx\n .select({\n documentId: documentMoveIntents.documentId,\n id: documentMoveIntents.id,\n })\n .from(documentMoveIntents)\n .where(inArray(documentMoveIntents.documentId, ids))\n ).map((row) => [row.documentId, row.id]),\n );\n const versionedIds = inputs\n .filter((input) => input.accessEpoch !== undefined)\n .map((input) => input.documentId);\n const epochs = new Map<string, number>();\n if (versionedIds.length > 0) {\n const rows = await tx\n .select({\n documentId: documents.documentId,\n accessEpoch: documents.accessEpoch,\n })\n .from(documents)\n .where(\n and(\n eq(documents.appKind, DOCUMENTS_APP_KIND),\n inArray(documents.documentId, versionedIds),\n ),\n );\n for (const row of rows)\n if (row.documentId)\n epochs.set(\n row.documentId,\n Math.max(epochs.get(row.documentId) ?? 0, row.accessEpoch),\n );\n }\n return inputs.filter((input) => {\n if (options?.moveIntentId)\n return intents.get(input.documentId) === options.moveIntentId;\n if (intents.has(input.documentId)) return false;\n return (\n input.accessEpoch === undefined ||\n input.accessEpoch >= (epochs.get(input.documentId) ?? 0)\n );\n });\n}\n"]}
1
+ {"version":3,"file":"documentPlacement.js","sourceRoot":"","sources":["documentPlacement.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, inArray } from \"drizzle-orm\";\nimport {\n documents,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { DOCUMENTS_APP_KIND } from \"../documents/internal/constants\";\n\nexport interface DocumentPlacementInput {\n documentId: string;\n containerIds: ReadonlyArray<string>;\n accessEpoch?: number | undefined;\n}\n\nexport interface DocumentPlacementWriteOptions {\n stillCurrent?: (() => boolean) | undefined;\n moveIntentId?: string | undefined;\n}\n\nexport async function loadDocumentMovePlacement(\n tx: ClientSQLiteTransactionScope,\n documentId: string,\n) {\n const [intent] = await tx\n .select({\n id: localIntents.id,\n intentType: localIntents.intentType,\n targetContainerId: localIntents.targetContainerId,\n })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n eq(localIntents.subjectId, documentId),\n ),\n )\n .limit(1);\n return intent;\n}\n\n/** Check at commit time: neither intermediate replay nor discovery owns a pending move. */\nexport async function filterWritableDocumentPlacements(\n tx: ClientSQLiteTransactionScope,\n inputs: readonly DocumentPlacementInput[],\n options?: DocumentPlacementWriteOptions,\n): Promise<readonly DocumentPlacementInput[]> {\n const ids = inputs.map((input) => input.documentId);\n const intents = new Map(\n (\n await tx\n .select({\n documentId: localIntents.subjectId,\n id: localIntents.id,\n })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n inArray(localIntents.subjectId, ids),\n ),\n )\n ).map((row) => [row.documentId, row.id]),\n );\n const versionedIds = inputs\n .filter((input) => input.accessEpoch !== undefined)\n .map((input) => input.documentId);\n const epochs = new Map<string, number>();\n if (versionedIds.length > 0) {\n const rows = await tx\n .select({\n documentId: documents.documentId,\n accessEpoch: documents.accessEpoch,\n })\n .from(documents)\n .where(\n and(\n eq(documents.appKind, DOCUMENTS_APP_KIND),\n inArray(documents.documentId, versionedIds),\n ),\n );\n for (const row of rows)\n if (row.documentId)\n epochs.set(\n row.documentId,\n Math.max(epochs.get(row.documentId) ?? 0, row.accessEpoch),\n );\n }\n return inputs.filter((input) => {\n if (options?.moveIntentId)\n return intents.get(input.documentId) === options.moveIntentId;\n if (intents.has(input.documentId)) return false;\n return (\n input.accessEpoch === undefined ||\n input.accessEpoch >= (epochs.get(input.documentId) ?? 0)\n );\n });\n}\n"]}
@@ -3,7 +3,7 @@ import { DEFAULT_DOCUMENT_KIND } from "../../../documents/documentConstants.js";
3
3
  import { resolveDocumentProjectorRegistry, } from "../../../documents/documentKinds.js";
4
4
  import { deleteDocumentHistory } from "../../../sqlite/documentHistoryPersistence.js";
5
5
  import { clearDocumentSyncFailure } from "../../../sqlite/documentPersistence.js";
6
- import { documentAttachmentBlobProjection, documentMoveIntents, documentMoveIntentTables, documentPendingAttachments, } from "../../../sqlite/schema.js";
6
+ import { documentAttachmentBlobProjection, documentPendingAttachments, documentPlacementIntentTables, inLocalIntentLane, LOCAL_INTENT_LANES, localIntents, } from "../../../sqlite/schema.js";
7
7
  import { getClientSQLitePersistenceRuntime, } from "../../../sqlite/sqlitePersistenceRuntime.js";
8
8
  import { ensureSqlTables, runSerializedSqlMutation, } from "../../../sqlite/sqlSchema.js";
9
9
  import { getDocumentScope, resolveDocumentSaveTimestamp, saveDocumentRows, } from "./documentRows.js";
@@ -42,9 +42,9 @@ async function loadDiscardCandidate(input) {
42
42
  return null;
43
43
  }
44
44
  const moveIntentRows = await tx
45
- .select({ id: documentMoveIntents.id })
46
- .from(documentMoveIntents)
47
- .where(eq(documentMoveIntents.localId, localId))
45
+ .select({ id: localIntents.id })
46
+ .from(localIntents)
47
+ .where(and(inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement), eq(localIntents.localId, localId)))
48
48
  .limit(1);
49
49
  if (moveIntentRows.length > 0)
50
50
  return null;
@@ -153,7 +153,7 @@ async function discardDocumentRowsToShell(input) {
153
153
  export async function discardStoredDocumentToShell(input) {
154
154
  const { expectedDocumentId, localId, persistence } = input;
155
155
  return runSerializedSqlMutation(input.execSql, async (lockedExecSql) => {
156
- await ensureSqlTables(lockedExecSql, documentMoveIntentTables);
156
+ await ensureSqlTables(lockedExecSql, documentPlacementIntentTables);
157
157
  return discardDocumentRowsToShell({
158
158
  documentProjectors: resolveDocumentProjectorRegistry(input.documentProjectors),
159
159
  expectedDocumentId,
@@ -1 +1 @@
1
- {"version":3,"file":"discardDocument.js","sourceRoot":"","sources":["discardDocument.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, isNotNull } from \"drizzle-orm\";\nimport { DEFAULT_DOCUMENT_KIND } from \"../../../documents/documentConstants\";\nimport {\n type DocumentProjectorRegistry,\n type DocumentProjectorRegistryInput,\n resolveDocumentProjectorRegistry,\n} from \"../../../documents/documentKinds\";\nimport { deleteDocumentHistory } from \"../../../sqlite/documentHistoryPersistence\";\nimport { clearDocumentSyncFailure } from \"../../../sqlite/documentPersistence\";\nimport {\n documentAttachmentBlobProjection,\n documentMoveIntents,\n documentMoveIntentTables,\n documentPendingAttachments,\n} from \"../../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../../sqlite/sqlSchema\";\nimport type {\n DiscardDocumentToShellResult,\n DocumentsPersistence,\n StoredDocumentRecord,\n} from \"../types\";\nimport {\n getDocumentScope,\n resolveDocumentSaveTimestamp,\n saveDocumentRows,\n} from \"./documentRows\";\nimport { queueDocumentAttachmentStorageKeys } from \"./orphanSideRows\";\n\nfunction buildDiscardShellDocument(\n localId: string,\n existingDocument: StoredDocumentRecord,\n): StoredDocumentRecord {\n return {\n id: localId,\n accessEpoch: existingDocument.accessEpoch,\n accessStateHash: existingDocument.accessStateHash ?? null,\n containerId: existingDocument.containerId,\n contentKeyBundle: null,\n documentId: existingDocument.documentId,\n documentKekTargets: null,\n documentManifestBundle: null,\n effectiveAccessLevel: existingDocument.effectiveAccessLevel ?? null,\n lastCommitLsn: null,\n pendingBaseVersion: null,\n pullContinuation: null,\n recoveryGeneration: existingDocument.recoveryGeneration ?? 0,\n snapshotEndVersion: \"\",\n text: \"\",\n ...(existingDocument.documentKind === undefined\n ? {}\n : { documentKind: existingDocument.documentKind }),\n ...(existingDocument.title === undefined\n ? {}\n : { title: existingDocument.title }),\n };\n}\n\ninterface DiscardCandidate {\n detachedStorageKeys: string[];\n existingDocument: StoredDocumentRecord;\n pendingAttachments: ReadonlyArray<{ slotId: string; storageKey: string }>;\n}\n\nasync function loadDiscardCandidate(input: {\n expectedDocumentId: string;\n localId: string;\n lockedExecSql: ExecSql;\n persistence: Pick<\n DocumentsPersistence,\n \"listPendingAttachments\" | \"loadDocument\"\n >;\n tx: ClientSQLiteTransactionScope;\n}): Promise<DiscardCandidate | null> {\n const { expectedDocumentId, localId, lockedExecSql, persistence, tx } = input;\n const existingDocument = await persistence.loadDocument(\n lockedExecSql,\n localId,\n );\n if (\n !existingDocument?.documentId ||\n existingDocument.documentId !== expectedDocumentId ||\n !existingDocument.containerId\n ) {\n return null;\n }\n const moveIntentRows = await tx\n .select({ id: documentMoveIntents.id })\n .from(documentMoveIntents)\n .where(eq(documentMoveIntents.localId, localId))\n .limit(1);\n if (moveIntentRows.length > 0) return null;\n\n const pendingAttachments = await persistence.listPendingAttachments(\n lockedExecSql,\n localId,\n );\n const detachedRows = await tx\n .select({ storageKey: documentAttachmentBlobProjection.storageKey })\n .from(documentAttachmentBlobProjection)\n .where(\n and(\n eq(documentAttachmentBlobProjection.localId, localId),\n isNotNull(documentAttachmentBlobProjection.detachedAt),\n ),\n );\n return {\n detachedStorageKeys: detachedRows.map((row) => row.storageKey),\n existingDocument,\n pendingAttachments,\n };\n}\n\n// Staged-upload rows AND detached local-attachment markers both go: a marker\n// for a locally-discarded detach would otherwise keep filtering the slot out\n// after a re-pull restores it, leaving the attachment permanently invisible.\nasync function clearDiscardedDocumentRows(input: {\n candidate: DiscardCandidate;\n localId: string;\n lockedExecSql: ExecSql;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const { candidate, localId, lockedExecSql, tx } = input;\n await tx\n .delete(documentPendingAttachments)\n .where(eq(documentPendingAttachments.localId, localId))\n .run();\n for (const pendingAttachment of candidate.pendingAttachments) {\n await tx\n .delete(documentAttachmentBlobProjection)\n .where(\n and(\n eq(documentAttachmentBlobProjection.localId, localId),\n eq(documentAttachmentBlobProjection.slotId, pendingAttachment.slotId),\n eq(\n documentAttachmentBlobProjection.storageKey,\n pendingAttachment.storageKey,\n ),\n ),\n )\n .run();\n }\n await tx\n .delete(documentAttachmentBlobProjection)\n .where(\n and(\n eq(documentAttachmentBlobProjection.localId, localId),\n isNotNull(documentAttachmentBlobProjection.detachedAt),\n ),\n )\n .run();\n await deleteDocumentHistory(lockedExecSql, getDocumentScope(localId));\n await clearDocumentSyncFailure(lockedExecSql, getDocumentScope(localId));\n}\n\nasync function saveDiscardShell(input: {\n candidate: DiscardCandidate;\n documentProjectors: DocumentProjectorRegistry;\n localId: string;\n lockedExecSql: ExecSql;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const { candidate, documentProjectors, localId, lockedExecSql, tx } = input;\n const shellDocument = buildDiscardShellDocument(\n localId,\n candidate.existingDocument,\n );\n // The document-kind projection derives from discarded content, so clear it\n // inside the transaction and let the next pull rebuild it.\n await documentProjectors.deleteStoredDocumentClientProjection({\n documentKind: shellDocument.documentKind ?? DEFAULT_DOCUMENT_KIND,\n execSql: lockedExecSql,\n localId,\n });\n const updatedAt = await resolveDocumentSaveTimestamp({\n document: shellDocument,\n tx,\n });\n if (!(await saveDocumentRows({ document: shellDocument, tx, updatedAt }))) {\n throw new Error(\"Document recovery generation changed during discard\");\n }\n}\n\n// Eligibility, teardown, client-projection clearing, and shell replacement\n// share one immediate transaction, so a second connection cannot relink the\n// local id between identity validation and destructive writes.\nasync function discardDocumentRowsToShell(input: {\n documentProjectors: DocumentProjectorRegistry;\n expectedDocumentId: string;\n localId: string;\n lockedExecSql: ExecSql;\n persistence: Pick<\n DocumentsPersistence,\n \"listPendingAttachments\" | \"loadDocument\"\n >;\n}): Promise<DiscardDocumentToShellResult> {\n const clientProjectionTables =\n input.documentProjectors.getClientProjectionTables();\n if (clientProjectionTables.length > 0) {\n await ensureSqlTables(input.lockedExecSql, clientProjectionTables);\n }\n return getClientSQLitePersistenceRuntime(input.lockedExecSql).transaction(\n async (tx) => {\n const candidate = await loadDiscardCandidate({ ...input, tx });\n if (!candidate) return { discarded: false };\n await clearDiscardedDocumentRows({\n candidate,\n localId: input.localId,\n lockedExecSql: input.lockedExecSql,\n tx,\n });\n await saveDiscardShell({ ...input, candidate, tx });\n // The deleted rows were these bytes' only pointers from this document,\n // but a hydrated copy is shared by every slot holding the same blob.\n // Queue them for the reference-checked reclaim instead of deleting.\n await queueDocumentAttachmentStorageKeys(tx, [\n ...new Set([\n ...candidate.pendingAttachments.map((row) => row.storageKey),\n ...candidate.detachedStorageKeys,\n ]),\n ]);\n return {\n discarded: true,\n documentKind:\n candidate.existingDocument.documentKind ?? DEFAULT_DOCUMENT_KIND,\n };\n },\n { behavior: \"immediate\" },\n );\n}\n\n/**\n * Atomically convert a stuck document's local state to the\n * freshly-discovered-share shell: drop its queued updates, staged attachment\n * rows (and their settled local-attachment halves, which share the staged\n * storage keys the caller reclaims), detached attachment markers, durable\n * history, recorded sync failure, and document-kind client projection, then\n * overwrite the record with an empty snapshot that keeps its identity,\n * placement, title, and kind. Everything commits in ONE transaction.\n *\n * Refused under the serialized mutation when the document is local-only or\n * unlinked (its rows are the only copy), when its documentId no longer\n * matches the caller's expectation (a stale caller or racing relink must not\n * discard a different identity's edits), or when any move intent references\n * it (the local containerId is then the move's optimistic placement, and\n * reseeding it as server truth would silently commit the move locally while\n * discarding the intent that was meant to perform it).\n */\nexport async function discardStoredDocumentToShell(input: {\n documentProjectors: DocumentProjectorRegistryInput;\n execSql: ExecSql;\n expectedDocumentId: string;\n localId: string;\n persistence: Pick<\n DocumentsPersistence,\n \"listPendingAttachments\" | \"loadDocument\"\n >;\n}): Promise<DiscardDocumentToShellResult> {\n const { expectedDocumentId, localId, persistence } = input;\n return runSerializedSqlMutation(input.execSql, async (lockedExecSql) => {\n await ensureSqlTables(lockedExecSql, documentMoveIntentTables);\n return discardDocumentRowsToShell({\n documentProjectors: resolveDocumentProjectorRegistry(\n input.documentProjectors,\n ),\n expectedDocumentId,\n localId,\n lockedExecSql,\n persistence,\n });\n });\n}\n"]}
1
+ {"version":3,"file":"discardDocument.js","sourceRoot":"","sources":["discardDocument.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EAGL,gCAAgC,GACjC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACnF,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,EACL,gCAAgC,EAChC,0BAA0B,EAC1B,6BAA6B,EAC7B,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,GACb,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAEL,iCAAiC,GAClC,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAEL,eAAe,EACf,wBAAwB,GACzB,MAAM,2BAA2B,CAAC;AAMnC,OAAO,EACL,gBAAgB,EAChB,4BAA4B,EAC5B,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kCAAkC,EAAE,MAAM,kBAAkB,CAAC;AAEtE,SAAS,yBAAyB,CAChC,OAAe,EACf,gBAAsC;IAEtC,OAAO;QACL,EAAE,EAAE,OAAO;QACX,WAAW,EAAE,gBAAgB,CAAC,WAAW;QACzC,eAAe,EAAE,gBAAgB,CAAC,eAAe,IAAI,IAAI;QACzD,WAAW,EAAE,gBAAgB,CAAC,WAAW;QACzC,gBAAgB,EAAE,IAAI;QACtB,UAAU,EAAE,gBAAgB,CAAC,UAAU;QACvC,kBAAkB,EAAE,IAAI;QACxB,sBAAsB,EAAE,IAAI;QAC5B,oBAAoB,EAAE,gBAAgB,CAAC,oBAAoB,IAAI,IAAI;QACnE,aAAa,EAAE,IAAI;QACnB,kBAAkB,EAAE,IAAI;QACxB,gBAAgB,EAAE,IAAI;QACtB,kBAAkB,EAAE,gBAAgB,CAAC,kBAAkB,IAAI,CAAC;QAC5D,kBAAkB,EAAE,EAAE;QACtB,IAAI,EAAE,EAAE;QACR,GAAG,CAAC,gBAAgB,CAAC,YAAY,KAAK,SAAS;YAC7C,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,YAAY,EAAE,gBAAgB,CAAC,YAAY,EAAE,CAAC;QACpD,GAAG,CAAC,gBAAgB,CAAC,KAAK,KAAK,SAAS;YACtC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,CAAC,KAAK,EAAE,CAAC;KACvC,CAAC;AACJ,CAAC;AAQD,KAAK,UAAU,oBAAoB,CAAC,KASnC;IACC,MAAM,EAAE,kBAAkB,EAAE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC;IAC9E,MAAM,gBAAgB,GAAG,MAAM,WAAW,CAAC,YAAY,CACrD,aAAa,EACb,OAAO,CACR,CAAC;IACF,IACE,CAAC,gBAAgB,EAAE,UAAU;QAC7B,gBAAgB,CAAC,UAAU,KAAK,kBAAkB;QAClD,CAAC,gBAAgB,CAAC,WAAW,EAC7B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,cAAc,GAAG,MAAM,EAAE;SAC5B,MAAM,CAAC,EAAE,EAAE,EAAE,YAAY,CAAC,EAAE,EAAE,CAAC;SAC/B,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,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAE3C,MAAM,kBAAkB,GAAG,MAAM,WAAW,CAAC,sBAAsB,CACjE,aAAa,EACb,OAAO,CACR,CAAC;IACF,MAAM,YAAY,GAAG,MAAM,EAAE;SAC1B,MAAM,CAAC,EAAE,UAAU,EAAE,gCAAgC,CAAC,UAAU,EAAE,CAAC;SACnE,IAAI,CAAC,gCAAgC,CAAC;SACtC,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,gCAAgC,CAAC,OAAO,EAAE,OAAO,CAAC,EACrD,SAAS,CAAC,gCAAgC,CAAC,UAAU,CAAC,CACvD,CACF,CAAC;IACJ,OAAO;QACL,mBAAmB,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC;QAC9D,gBAAgB;QAChB,kBAAkB;KACnB,CAAC;AACJ,CAAC;AAED,6EAA6E;AAC7E,6EAA6E;AAC7E,6EAA6E;AAC7E,KAAK,UAAU,0BAA0B,CAAC,KAKzC;IACC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC;IACxD,MAAM,EAAE;SACL,MAAM,CAAC,0BAA0B,CAAC;SAClC,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;SACtD,GAAG,EAAE,CAAC;IACT,KAAK,MAAM,iBAAiB,IAAI,SAAS,CAAC,kBAAkB,EAAE,CAAC;QAC7D,MAAM,EAAE;aACL,MAAM,CAAC,gCAAgC,CAAC;aACxC,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,gCAAgC,CAAC,OAAO,EAAE,OAAO,CAAC,EACrD,EAAE,CAAC,gCAAgC,CAAC,MAAM,EAAE,iBAAiB,CAAC,MAAM,CAAC,EACrE,EAAE,CACA,gCAAgC,CAAC,UAAU,EAC3C,iBAAiB,CAAC,UAAU,CAC7B,CACF,CACF;aACA,GAAG,EAAE,CAAC;IACX,CAAC;IACD,MAAM,EAAE;SACL,MAAM,CAAC,gCAAgC,CAAC;SACxC,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,gCAAgC,CAAC,OAAO,EAAE,OAAO,CAAC,EACrD,SAAS,CAAC,gCAAgC,CAAC,UAAU,CAAC,CACvD,CACF;SACA,GAAG,EAAE,CAAC;IACT,MAAM,qBAAqB,CAAC,aAAa,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;IACtE,MAAM,wBAAwB,CAAC,aAAa,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,KAM/B;IACC,MAAM,EAAE,SAAS,EAAE,kBAAkB,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC;IAC5E,MAAM,aAAa,GAAG,yBAAyB,CAC7C,OAAO,EACP,SAAS,CAAC,gBAAgB,CAC3B,CAAC;IACF,2EAA2E;IAC3E,2DAA2D;IAC3D,MAAM,kBAAkB,CAAC,oCAAoC,CAAC;QAC5D,YAAY,EAAE,aAAa,CAAC,YAAY,IAAI,qBAAqB;QACjE,OAAO,EAAE,aAAa;QACtB,OAAO;KACR,CAAC,CAAC;IACH,MAAM,SAAS,GAAG,MAAM,4BAA4B,CAAC;QACnD,QAAQ,EAAE,aAAa;QACvB,EAAE;KACH,CAAC,CAAC;IACH,IAAI,CAAC,CAAC,MAAM,gBAAgB,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC;QAC1E,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,4EAA4E;AAC5E,+DAA+D;AAC/D,KAAK,UAAU,0BAA0B,CAAC,KASzC;IACC,MAAM,sBAAsB,GAC1B,KAAK,CAAC,kBAAkB,CAAC,yBAAyB,EAAE,CAAC;IACvD,IAAI,sBAAsB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtC,MAAM,eAAe,CAAC,KAAK,CAAC,aAAa,EAAE,sBAAsB,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,iCAAiC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,WAAW,CACvE,KAAK,EAAE,EAAE,EAAE,EAAE;QACX,MAAM,SAAS,GAAG,MAAM,oBAAoB,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/D,IAAI,CAAC,SAAS;YAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QAC5C,MAAM,0BAA0B,CAAC;YAC/B,SAAS;YACT,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,EAAE;SACH,CAAC,CAAC;QACH,MAAM,gBAAgB,CAAC,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;QACpD,uEAAuE;QACvE,qEAAqE;QACrE,oEAAoE;QACpE,MAAM,kCAAkC,CAAC,EAAE,EAAE;YAC3C,GAAG,IAAI,GAAG,CAAC;gBACT,GAAG,SAAS,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC;gBAC5D,GAAG,SAAS,CAAC,mBAAmB;aACjC,CAAC;SACH,CAAC,CAAC;QACH,OAAO;YACL,SAAS,EAAE,IAAI;YACf,YAAY,EACV,SAAS,CAAC,gBAAgB,CAAC,YAAY,IAAI,qBAAqB;SACnE,CAAC;IACJ,CAAC,EACD,EAAE,QAAQ,EAAE,WAAW,EAAE,CAC1B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,KASlD;IACC,MAAM,EAAE,kBAAkB,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC;IAC3D,OAAO,wBAAwB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE;QACrE,MAAM,eAAe,CAAC,aAAa,EAAE,6BAA6B,CAAC,CAAC;QACpE,OAAO,0BAA0B,CAAC;YAChC,kBAAkB,EAAE,gCAAgC,CAClD,KAAK,CAAC,kBAAkB,CACzB;YACD,kBAAkB;YAClB,OAAO;YACP,aAAa;YACb,WAAW;SACZ,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { and, eq, isNotNull } from \"drizzle-orm\";\nimport { DEFAULT_DOCUMENT_KIND } from \"../../../documents/documentConstants\";\nimport {\n type DocumentProjectorRegistry,\n type DocumentProjectorRegistryInput,\n resolveDocumentProjectorRegistry,\n} from \"../../../documents/documentKinds\";\nimport { deleteDocumentHistory } from \"../../../sqlite/documentHistoryPersistence\";\nimport { clearDocumentSyncFailure } from \"../../../sqlite/documentPersistence\";\nimport {\n documentAttachmentBlobProjection,\n documentPendingAttachments,\n documentPlacementIntentTables,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../../sqlite/sqlSchema\";\nimport type {\n DiscardDocumentToShellResult,\n DocumentsPersistence,\n StoredDocumentRecord,\n} from \"../types\";\nimport {\n getDocumentScope,\n resolveDocumentSaveTimestamp,\n saveDocumentRows,\n} from \"./documentRows\";\nimport { queueDocumentAttachmentStorageKeys } from \"./orphanSideRows\";\n\nfunction buildDiscardShellDocument(\n localId: string,\n existingDocument: StoredDocumentRecord,\n): StoredDocumentRecord {\n return {\n id: localId,\n accessEpoch: existingDocument.accessEpoch,\n accessStateHash: existingDocument.accessStateHash ?? null,\n containerId: existingDocument.containerId,\n contentKeyBundle: null,\n documentId: existingDocument.documentId,\n documentKekTargets: null,\n documentManifestBundle: null,\n effectiveAccessLevel: existingDocument.effectiveAccessLevel ?? null,\n lastCommitLsn: null,\n pendingBaseVersion: null,\n pullContinuation: null,\n recoveryGeneration: existingDocument.recoveryGeneration ?? 0,\n snapshotEndVersion: \"\",\n text: \"\",\n ...(existingDocument.documentKind === undefined\n ? {}\n : { documentKind: existingDocument.documentKind }),\n ...(existingDocument.title === undefined\n ? {}\n : { title: existingDocument.title }),\n };\n}\n\ninterface DiscardCandidate {\n detachedStorageKeys: string[];\n existingDocument: StoredDocumentRecord;\n pendingAttachments: ReadonlyArray<{ slotId: string; storageKey: string }>;\n}\n\nasync function loadDiscardCandidate(input: {\n expectedDocumentId: string;\n localId: string;\n lockedExecSql: ExecSql;\n persistence: Pick<\n DocumentsPersistence,\n \"listPendingAttachments\" | \"loadDocument\"\n >;\n tx: ClientSQLiteTransactionScope;\n}): Promise<DiscardCandidate | null> {\n const { expectedDocumentId, localId, lockedExecSql, persistence, tx } = input;\n const existingDocument = await persistence.loadDocument(\n lockedExecSql,\n localId,\n );\n if (\n !existingDocument?.documentId ||\n existingDocument.documentId !== expectedDocumentId ||\n !existingDocument.containerId\n ) {\n return null;\n }\n const moveIntentRows = await tx\n .select({ id: localIntents.id })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n eq(localIntents.localId, localId),\n ),\n )\n .limit(1);\n if (moveIntentRows.length > 0) return null;\n\n const pendingAttachments = await persistence.listPendingAttachments(\n lockedExecSql,\n localId,\n );\n const detachedRows = await tx\n .select({ storageKey: documentAttachmentBlobProjection.storageKey })\n .from(documentAttachmentBlobProjection)\n .where(\n and(\n eq(documentAttachmentBlobProjection.localId, localId),\n isNotNull(documentAttachmentBlobProjection.detachedAt),\n ),\n );\n return {\n detachedStorageKeys: detachedRows.map((row) => row.storageKey),\n existingDocument,\n pendingAttachments,\n };\n}\n\n// Staged-upload rows AND detached local-attachment markers both go: a marker\n// for a locally-discarded detach would otherwise keep filtering the slot out\n// after a re-pull restores it, leaving the attachment permanently invisible.\nasync function clearDiscardedDocumentRows(input: {\n candidate: DiscardCandidate;\n localId: string;\n lockedExecSql: ExecSql;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const { candidate, localId, lockedExecSql, tx } = input;\n await tx\n .delete(documentPendingAttachments)\n .where(eq(documentPendingAttachments.localId, localId))\n .run();\n for (const pendingAttachment of candidate.pendingAttachments) {\n await tx\n .delete(documentAttachmentBlobProjection)\n .where(\n and(\n eq(documentAttachmentBlobProjection.localId, localId),\n eq(documentAttachmentBlobProjection.slotId, pendingAttachment.slotId),\n eq(\n documentAttachmentBlobProjection.storageKey,\n pendingAttachment.storageKey,\n ),\n ),\n )\n .run();\n }\n await tx\n .delete(documentAttachmentBlobProjection)\n .where(\n and(\n eq(documentAttachmentBlobProjection.localId, localId),\n isNotNull(documentAttachmentBlobProjection.detachedAt),\n ),\n )\n .run();\n await deleteDocumentHistory(lockedExecSql, getDocumentScope(localId));\n await clearDocumentSyncFailure(lockedExecSql, getDocumentScope(localId));\n}\n\nasync function saveDiscardShell(input: {\n candidate: DiscardCandidate;\n documentProjectors: DocumentProjectorRegistry;\n localId: string;\n lockedExecSql: ExecSql;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const { candidate, documentProjectors, localId, lockedExecSql, tx } = input;\n const shellDocument = buildDiscardShellDocument(\n localId,\n candidate.existingDocument,\n );\n // The document-kind projection derives from discarded content, so clear it\n // inside the transaction and let the next pull rebuild it.\n await documentProjectors.deleteStoredDocumentClientProjection({\n documentKind: shellDocument.documentKind ?? DEFAULT_DOCUMENT_KIND,\n execSql: lockedExecSql,\n localId,\n });\n const updatedAt = await resolveDocumentSaveTimestamp({\n document: shellDocument,\n tx,\n });\n if (!(await saveDocumentRows({ document: shellDocument, tx, updatedAt }))) {\n throw new Error(\"Document recovery generation changed during discard\");\n }\n}\n\n// Eligibility, teardown, client-projection clearing, and shell replacement\n// share one immediate transaction, so a second connection cannot relink the\n// local id between identity validation and destructive writes.\nasync function discardDocumentRowsToShell(input: {\n documentProjectors: DocumentProjectorRegistry;\n expectedDocumentId: string;\n localId: string;\n lockedExecSql: ExecSql;\n persistence: Pick<\n DocumentsPersistence,\n \"listPendingAttachments\" | \"loadDocument\"\n >;\n}): Promise<DiscardDocumentToShellResult> {\n const clientProjectionTables =\n input.documentProjectors.getClientProjectionTables();\n if (clientProjectionTables.length > 0) {\n await ensureSqlTables(input.lockedExecSql, clientProjectionTables);\n }\n return getClientSQLitePersistenceRuntime(input.lockedExecSql).transaction(\n async (tx) => {\n const candidate = await loadDiscardCandidate({ ...input, tx });\n if (!candidate) return { discarded: false };\n await clearDiscardedDocumentRows({\n candidate,\n localId: input.localId,\n lockedExecSql: input.lockedExecSql,\n tx,\n });\n await saveDiscardShell({ ...input, candidate, tx });\n // The deleted rows were these bytes' only pointers from this document,\n // but a hydrated copy is shared by every slot holding the same blob.\n // Queue them for the reference-checked reclaim instead of deleting.\n await queueDocumentAttachmentStorageKeys(tx, [\n ...new Set([\n ...candidate.pendingAttachments.map((row) => row.storageKey),\n ...candidate.detachedStorageKeys,\n ]),\n ]);\n return {\n discarded: true,\n documentKind:\n candidate.existingDocument.documentKind ?? DEFAULT_DOCUMENT_KIND,\n };\n },\n { behavior: \"immediate\" },\n );\n}\n\n/**\n * Atomically convert a stuck document's local state to the\n * freshly-discovered-share shell: drop its queued updates, staged attachment\n * rows (and their settled local-attachment halves, which share the staged\n * storage keys the caller reclaims), detached attachment markers, durable\n * history, recorded sync failure, and document-kind client projection, then\n * overwrite the record with an empty snapshot that keeps its identity,\n * placement, title, and kind. Everything commits in ONE transaction.\n *\n * Refused under the serialized mutation when the document is local-only or\n * unlinked (its rows are the only copy), when its documentId no longer\n * matches the caller's expectation (a stale caller or racing relink must not\n * discard a different identity's edits), or when any move intent references\n * it (the local containerId is then the move's optimistic placement, and\n * reseeding it as server truth would silently commit the move locally while\n * discarding the intent that was meant to perform it).\n */\nexport async function discardStoredDocumentToShell(input: {\n documentProjectors: DocumentProjectorRegistryInput;\n execSql: ExecSql;\n expectedDocumentId: string;\n localId: string;\n persistence: Pick<\n DocumentsPersistence,\n \"listPendingAttachments\" | \"loadDocument\"\n >;\n}): Promise<DiscardDocumentToShellResult> {\n const { expectedDocumentId, localId, persistence } = input;\n return runSerializedSqlMutation(input.execSql, async (lockedExecSql) => {\n await ensureSqlTables(lockedExecSql, documentPlacementIntentTables);\n return discardDocumentRowsToShell({\n documentProjectors: resolveDocumentProjectorRegistry(\n input.documentProjectors,\n ),\n expectedDocumentId,\n localId,\n lockedExecSql,\n persistence,\n });\n });\n}\n"]}
@@ -2,7 +2,7 @@ import { and, eq, inArray, ne } from "drizzle-orm";
2
2
  import { deleteDocumentHistory } from "../../../sqlite/documentHistoryPersistence.js";
3
3
  import { clearDocumentSyncFailure, deleteDocumentRecord, loadDocumentRecord, } from "../../../sqlite/documentPersistence.js";
4
4
  import { documentIntentLinkTargets } from "../../../sqlite/documentPlacementIntentSchema.js";
5
- import { documentAttachmentBlobProjection, documentContainerProjection, documentMoveIntents, documentMoveIntentTables, documentPendingAttachments, documentProjection, documentProjectionText, documents, } from "../../../sqlite/schema.js";
5
+ import { documentAttachmentBlobProjection, documentContainerProjection, documentPendingAttachments, documentPlacementIntentTables, documentProjection, documentProjectionText, documents, inLocalIntentLane, LOCAL_INTENT_LANES, localIntents, } from "../../../sqlite/schema.js";
6
6
  import { getClientSQLitePersistenceRuntime, } from "../../../sqlite/sqlitePersistenceRuntime.js";
7
7
  import { ensureSqlTables, runSerializedSqlMutation, } from "../../../sqlite/sqlSchema.js";
8
8
  import { DOCUMENTS_APP_KIND } from "./constants.js";
@@ -41,17 +41,15 @@ async function deleteStoredDocumentRows(input) {
41
41
  await deleteContainerDocumentTombstoneHoldsForDocuments(tx, [
42
42
  existingDocumentId,
43
43
  ]);
44
+ const placementIntent = and(inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement), eq(localIntents.subjectId, existingDocumentId));
44
45
  await tx
45
46
  .delete(documentIntentLinkTargets)
46
47
  .where(inArray(documentIntentLinkTargets.intentId, tx
47
- .select({ id: documentMoveIntents.id })
48
- .from(documentMoveIntents)
49
- .where(eq(documentMoveIntents.documentId, existingDocumentId))))
50
- .run();
51
- await tx
52
- .delete(documentMoveIntents)
53
- .where(eq(documentMoveIntents.documentId, existingDocumentId))
48
+ .select({ id: localIntents.id })
49
+ .from(localIntents)
50
+ .where(placementIntent)))
54
51
  .run();
52
+ await tx.delete(localIntents).where(placementIntent).run();
55
53
  }
56
54
  const scope = getDocumentScope(localId);
57
55
  await deleteDocumentHistory(lockedExecSql, scope);
@@ -61,7 +59,7 @@ async function deleteStoredDocumentRows(input) {
61
59
  async function ensureDocumentDeletionTables(execSql) {
62
60
  // Move intents live in the container-contents schema, which document-only
63
61
  // callers may not have initialized yet.
64
- await ensureSqlTables(execSql, documentMoveIntentTables);
62
+ await ensureSqlTables(execSql, documentPlacementIntentTables);
65
63
  }
66
64
  async function hasAnotherDocumentAlias(input) {
67
65
  const rows = await input.tx