@tearleads/client-sdk 0.1.42 → 0.1.43

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 (161) hide show
  1. package/data/blobs/transferPool.js +35 -8
  2. package/data/blobs/transferPool.js.map +1 -1
  3. package/data/containers/shared/projection.js +12 -0
  4. package/data/containers/shared/projection.js.map +1 -1
  5. package/data/documents/blob/shared/types.d.ts +2 -3
  6. package/data/documents/blob/shared/types.js.map +1 -1
  7. package/data/documents/shared/events.js +0 -1
  8. package/data/documents/shared/events.js.map +1 -1
  9. package/data/persistence/container-contents/containerContentsPersistence.js +6 -22
  10. package/data/persistence/container-contents/containerContentsPersistence.js.map +1 -1
  11. package/data/persistence/container-contents/containerMetadataMutationPersistence.js +5 -15
  12. package/data/persistence/container-contents/containerMetadataMutationPersistence.js.map +1 -1
  13. package/data/persistence/container-contents/containerPendingUpdatePersistence.js +6 -15
  14. package/data/persistence/container-contents/containerPendingUpdatePersistence.js.map +1 -1
  15. package/data/persistence/container-contents/containerRecoveryPersistence.js +3 -4
  16. package/data/persistence/container-contents/containerRecoveryPersistence.js.map +1 -1
  17. package/data/persistence/container-contents/containerRemovalWork.js +3 -5
  18. package/data/persistence/container-contents/containerRemovalWork.js.map +1 -1
  19. package/data/persistence/container-contents/dormantContainerMetadata.js +1 -5
  20. package/data/persistence/container-contents/dormantContainerMetadata.js.map +1 -1
  21. package/data/persistence/documents/documentsPersistence.js +4 -9
  22. package/data/persistence/documents/documentsPersistence.js.map +1 -1
  23. package/data/persistence/documents/internal/discardDocument.js +1 -2
  24. package/data/persistence/documents/internal/discardDocument.js.map +1 -1
  25. package/data/persistence/documents/internal/documentDeletionPersistence.js +1 -2
  26. package/data/persistence/documents/internal/documentDeletionPersistence.js.map +1 -1
  27. package/data/persistence/documents/internal/documentMutationPersistence.js +17 -25
  28. package/data/persistence/documents/internal/documentMutationPersistence.js.map +1 -1
  29. package/data/persistence/documents/internal/orphanSideRows.js +2 -14
  30. package/data/persistence/documents/internal/orphanSideRows.js.map +1 -1
  31. package/data/persistence/documents/internal/syncQueuePersistence.js +3 -3
  32. package/data/persistence/documents/internal/syncQueuePersistence.js.map +1 -1
  33. package/data/sqlite/documentHistoryPersistence.d.ts +9 -3
  34. package/data/sqlite/documentHistoryPersistence.js +31 -8
  35. package/data/sqlite/documentHistoryPersistence.js.map +1 -1
  36. package/data/sqlite/documentPendingUpdatePersistence.d.ts +58 -11
  37. package/data/sqlite/documentPendingUpdatePersistence.js +137 -63
  38. package/data/sqlite/documentPendingUpdatePersistence.js.map +1 -1
  39. package/data/sqlite/documentPersistence.d.ts +1 -1
  40. package/data/sqlite/documentPersistence.js +1 -1
  41. package/data/sqlite/documentPersistence.js.map +1 -1
  42. package/data/sqlite/documentPersistenceTypes.d.ts +2 -2
  43. package/data/sqlite/documentPersistenceTypes.js.map +1 -1
  44. package/data/sqlite/documentRecordPersistence.js +5 -4
  45. package/data/sqlite/documentRecordPersistence.js.map +1 -1
  46. package/data/sqlite/schema.d.ts +127 -377
  47. package/data/sqlite/schema.js +46 -47
  48. package/data/sqlite/schema.js.map +1 -1
  49. package/internal/api-client/ApiClient.d.ts +13 -8
  50. package/internal/api-client/ApiClient.js +2 -5
  51. package/internal/api-client/ApiClient.js.map +1 -1
  52. package/internal/api-client/binaryRequestOperationTransport.d.ts +18 -0
  53. package/internal/api-client/index.d.ts +1 -0
  54. package/internal/api-client/index.js +1 -0
  55. package/internal/api-client/index.js.map +1 -1
  56. package/internal/api-client/operationTransportFactory.d.ts +6 -0
  57. package/internal/api-client/routes/blobs/get.d.ts +7 -0
  58. package/internal/api-client/routes/blobs/get.js.map +1 -1
  59. package/internal/api-client/routes/blobs/multipart.js +1 -11
  60. package/internal/api-client/routes/blobs/multipart.js.map +1 -1
  61. package/internal/api-client/routes/blobs/multipartUpload.d.ts +14 -0
  62. package/internal/api-client/routes/blobs/multipartUpload.js +94 -43
  63. package/internal/api-client/routes/blobs/multipartUpload.js.map +1 -1
  64. package/internal/validators/operation/attachments.d.ts +80 -160
  65. package/internal/validators/operation/auth.d.ts +760 -1664
  66. package/internal/validators/operation/blobDownloads.js +1 -1
  67. package/internal/validators/operation/blobDownloads.js.map +1 -1
  68. package/internal/validators/operation/containerMutations.d.ts +416 -856
  69. package/internal/validators/operation/documentMutations.d.ts +120 -264
  70. package/internal/validators/operation/documentSync.d.ts +40 -80
  71. package/internal/validators/operation/index.d.ts +1 -1
  72. package/internal/validators/operation/index.js +1 -1
  73. package/internal/validators/operation/index.js.map +1 -1
  74. package/internal/validators/operation/multipartBlobs.d.ts +70 -190
  75. package/internal/validators/operation/multipartBlobs.js +2 -24
  76. package/internal/validators/operation/multipartBlobs.js.map +1 -1
  77. package/internal/validators/operation/multipartPartUploads.d.ts +5 -5
  78. package/internal/validators/operation/multipartPartUploads.js +6 -20
  79. package/internal/validators/operation/multipartPartUploads.js.map +1 -1
  80. package/internal/validators/operation/organizations.d.ts +882 -1906
  81. package/internal/validators/operation/principals.d.ts +120 -240
  82. package/internal/validators/operation/registry.d.ts +1 -2
  83. package/internal/validators/operation/registry.js +1 -2
  84. package/internal/validators/operation/registry.js.map +1 -1
  85. package/internal/validators/request/blob.d.ts +100 -201
  86. package/internal/validators/request/blob.js +6 -13
  87. package/internal/validators/request/blob.js.map +1 -1
  88. package/internal/validators/request/container.d.ts +136 -272
  89. package/internal/validators/request/container.js +16 -10
  90. package/internal/validators/request/container.js.map +1 -1
  91. package/internal/validators/request/containerManifestRef.d.ts +54 -0
  92. package/internal/validators/request/containerManifestRef.js +28 -0
  93. package/internal/validators/request/containerManifestRef.js.map +1 -0
  94. package/internal/validators/request/containerMetadata.d.ts +160 -368
  95. package/internal/validators/request/document.d.ts +100 -245
  96. package/internal/validators/request/document.js +1 -2
  97. package/internal/validators/request/document.js.map +1 -1
  98. package/internal/validators/request/documentSyncSchema.d.ts +42 -109
  99. package/internal/validators/request/documentSyncSchema.js +8 -25
  100. package/internal/validators/request/documentSyncSchema.js.map +1 -1
  101. package/internal/validators/request/index.d.ts +1 -1
  102. package/internal/validators/request/index.js +1 -1
  103. package/internal/validators/request/index.js.map +1 -1
  104. package/internal/validators/request/organization.d.ts +160 -320
  105. package/internal/validators/request/organizationProvisioning.d.ts +2424 -5312
  106. package/internal/validators/request/principal.d.ts +160 -320
  107. package/internal/validators/request/registration.d.ts +760 -1664
  108. package/internal/validators/response/blob.d.ts +48 -27
  109. package/internal/validators/response/blob.js +3 -10
  110. package/internal/validators/response/blob.js.map +1 -1
  111. package/internal/validators/response/index.d.ts +1 -1
  112. package/internal/validators/response/index.js +1 -1
  113. package/internal/validators/response/index.js.map +1 -1
  114. package/internal/validators/util/blob.d.ts +7 -0
  115. package/internal/validators/util/blob.js +7 -0
  116. package/internal/validators/util/blob.js.map +1 -1
  117. package/internal/validators/util/blobTransferSchemas.d.ts +41 -0
  118. package/internal/validators/util/blobTransferSchemas.js +39 -0
  119. package/internal/validators/util/blobTransferSchemas.js.map +1 -0
  120. package/internal/validators/util/containerLimits.d.ts +7 -0
  121. package/internal/validators/util/containerLimits.js +7 -0
  122. package/internal/validators/util/containerLimits.js.map +1 -1
  123. package/internal/validators/util/index.d.ts +2 -1
  124. package/internal/validators/util/index.js +2 -1
  125. package/internal/validators/util/index.js.map +1 -1
  126. package/package.json +1 -1
  127. package/workflows/blobs/multipartFirstParts.js +82 -0
  128. package/workflows/blobs/multipartFirstParts.js.map +1 -0
  129. package/workflows/blobs/multipartStageResolution.js +83 -0
  130. package/workflows/blobs/multipartStageResolution.js.map +1 -0
  131. package/workflows/blobs/multipartUpload.js +45 -89
  132. package/workflows/blobs/multipartUpload.js.map +1 -1
  133. package/workflows/container-contents/documentPriming.js +2 -1
  134. package/workflows/container-contents/documentPriming.js.map +1 -1
  135. package/workflows/container-contents/documentQueries/sql.js +4 -4
  136. package/workflows/container-contents/documentQueries/sql.js.map +1 -1
  137. package/workflows/container-contents/pendingWrites/deferredTails.js +6 -3
  138. package/workflows/container-contents/pendingWrites/deferredTails.js.map +1 -1
  139. package/workflows/container-contents/pendingWrites/sources.js +2 -1
  140. package/workflows/container-contents/pendingWrites/sources.js.map +1 -1
  141. package/workflows/container-contents/pendingWrites.js +2 -1
  142. package/workflows/container-contents/pendingWrites.js.map +1 -1
  143. package/workflows/containers/child/create.js +3 -3
  144. package/workflows/containers/child/create.js.map +1 -1
  145. package/workflows/containers/child/move.js +2 -2
  146. package/workflows/containers/child/move.js.map +1 -1
  147. package/workflows/containers/child/mutationRequestCore.js +5 -4
  148. package/workflows/containers/child/mutationRequestCore.js.map +1 -1
  149. package/workflows/containers/child/recitePlan.js +3 -2
  150. package/workflows/containers/child/recitePlan.js.map +1 -1
  151. package/workflows/containers/child/sharePlanCore.js +2 -2
  152. package/workflows/containers/child/sharePlanCore.js.map +1 -1
  153. package/workflows/containers/root/create.js +2 -2
  154. package/workflows/containers/root/create.js.map +1 -1
  155. package/workflows/registration/provisioningBootstrapBuilders.d.ts +20 -46
  156. package/workflows/sync/remoteReset.js +9 -9
  157. package/workflows/sync/remoteReset.js.map +1 -1
  158. package/workflows/sync/remoteResetPlans.js +6 -3
  159. package/workflows/sync/remoteResetPlans.js.map +1 -1
  160. package/internal/validators/operation/blobTransferSchemas.js +0 -11
  161. package/internal/validators/operation/blobTransferSchemas.js.map +0 -1
@@ -2,20 +2,51 @@
2
2
  export const BLOB_TRANSFER_CONCURRENCY = 4;
3
3
  const TRANSFER_IDLE_TIMEOUT_MS = 120_000;
4
4
  const pools = new WeakMap();
5
- export async function runBlobTransfer(client, transfer, timeoutMs = TRANSFER_IDLE_TIMEOUT_MS) {
6
- if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
7
- throw new RangeError("Blob transfer timeout must be positive and finite");
5
+ function poolFor(client) {
8
6
  let pool = pools.get(client);
9
7
  if (!pool) {
10
8
  pool = { active: 0, waiting: [] };
11
9
  pools.set(client, pool);
12
10
  }
11
+ return pool;
12
+ }
13
+ function permitFrom(pool) {
14
+ let held = true;
15
+ return {
16
+ release() {
17
+ if (!held)
18
+ return;
19
+ held = false;
20
+ const next = pool.waiting.shift();
21
+ if (next)
22
+ next();
23
+ else
24
+ pool.active -= 1;
25
+ },
26
+ };
27
+ }
28
+ export async function acquireBlobTransferPermit(client) {
29
+ const pool = poolFor(client);
13
30
  if (pool.active >= BLOB_TRANSFER_CONCURRENCY) {
14
31
  await new Promise((resolve) => pool.waiting.push(resolve));
15
32
  }
16
33
  else {
17
34
  pool.active += 1;
18
35
  }
36
+ return permitFrom(pool);
37
+ }
38
+ /** A permit only if one is free now; never queues behind other transfers. */
39
+ export function tryAcquireBlobTransferPermit(client) {
40
+ const pool = poolFor(client);
41
+ if (pool.active >= BLOB_TRANSFER_CONCURRENCY)
42
+ return null;
43
+ pool.active += 1;
44
+ return permitFrom(pool);
45
+ }
46
+ export async function runBlobTransfer(client, transfer, timeoutMs = TRANSFER_IDLE_TIMEOUT_MS, heldPermit) {
47
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
48
+ throw new RangeError("Blob transfer timeout must be positive and finite");
49
+ const permit = heldPermit ?? (await acquireBlobTransferPermit(client));
19
50
  const controller = new AbortController();
20
51
  let timer;
21
52
  const progress = () => {
@@ -43,11 +74,7 @@ export async function runBlobTransfer(client, transfer, timeoutMs = TRANSFER_IDL
43
74
  }
44
75
  finally {
45
76
  clearTimeout(timer);
46
- const next = pool.waiting.shift();
47
- if (next)
48
- next();
49
- else
50
- pool.active -= 1;
77
+ permit.release();
51
78
  }
52
79
  }
53
80
  //# sourceMappingURL=transferPool.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"transferPool.js","sourceRoot":"","sources":["transferPool.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAC3C,MAAM,wBAAwB,GAAG,OAAO,CAAC;AAKzC,MAAM,KAAK,GAAG,IAAI,OAAO,EAAwB,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAc,EACd,QAAmE,EACnE,SAAS,GAAG,wBAAwB;IAEpC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC;QAC/C,MAAM,IAAI,UAAU,CAAC,mDAAmD,CAAC,CAAC;IAC5E,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;QAClC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC1B,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,IAAI,yBAAyB,EAAE,CAAC;QAC7C,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACnE,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,IAAI,KAAgD,CAAC;IACrD,MAAM,QAAQ,GAAG,GAAG,EAAE;QACpB,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;QACnC,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,KAAK,GAAG,UAAU,CAChB,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC,EAC5D,SAAS,CACV,CAAC;IACJ,CAAC,CAAC;IACF,QAAQ,EAAE,CAAC;IACX,IAAI,CAAC;QACH,0EAA0E;QAC1E,4EAA4E;QAC5E,OAAO,MAAM,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC9C,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACvD,UAAU,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACrE,KAAK,OAAO,CAAC,OAAO,EAAE;iBACnB,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;iBACjD,IAAI,CACH,CAAC,KAAK,EAAE,EAAE;gBACR,UAAU,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBACxD,OAAO,CAAC,KAAK,CAAC,CAAC;YACjB,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;gBACjB,UAAU,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBACxD,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC,CACF,CAAC;QACN,CAAC,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAClC,IAAI,IAAI;YAAE,IAAI,EAAE,CAAC;;YACZ,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IACxB,CAAC;AACH,CAAC","sourcesContent":["/** Bound byte I/O across documents sharing a client, including multipart parts. */\nexport const BLOB_TRANSFER_CONCURRENCY = 4;\nconst TRANSFER_IDLE_TIMEOUT_MS = 120_000;\ninterface TransferPool {\n active: number;\n waiting: Array<() => void>;\n}\nconst pools = new WeakMap<object, TransferPool>();\n\nexport async function runBlobTransfer<T>(\n client: object,\n transfer: (signal: AbortSignal, progress: () => void) => Promise<T>,\n timeoutMs = TRANSFER_IDLE_TIMEOUT_MS,\n): Promise<T> {\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)\n throw new RangeError(\"Blob transfer timeout must be positive and finite\");\n let pool = pools.get(client);\n if (!pool) {\n pool = { active: 0, waiting: [] };\n pools.set(client, pool);\n }\n if (pool.active >= BLOB_TRANSFER_CONCURRENCY) {\n await new Promise<void>((resolve) => pool.waiting.push(resolve));\n } else {\n pool.active += 1;\n }\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const progress = () => {\n controller.signal.throwIfAborted();\n clearTimeout(timer);\n timer = setTimeout(\n () => controller.abort(new Error(\"Blob transfer timed out\")),\n timeoutMs,\n );\n };\n progress();\n try {\n // A native bridge or custom transport may ignore abort. Bound its logical\n // ownership anyway; callers must check the signal before accepting results.\n return await new Promise<T>((resolve, reject) => {\n const aborted = () => reject(controller.signal.reason);\n controller.signal.addEventListener(\"abort\", aborted, { once: true });\n void Promise.resolve()\n .then(() => transfer(controller.signal, progress))\n .then(\n (value) => {\n controller.signal.removeEventListener(\"abort\", aborted);\n resolve(value);\n },\n (error: unknown) => {\n controller.signal.removeEventListener(\"abort\", aborted);\n reject(error);\n },\n );\n });\n } finally {\n clearTimeout(timer);\n const next = pool.waiting.shift();\n if (next) next();\n else pool.active -= 1;\n }\n}\n"]}
1
+ {"version":3,"file":"transferPool.js","sourceRoot":"","sources":["transferPool.ts"],"names":[],"mappings":"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","sourcesContent":["/** Bound byte I/O across documents sharing a client, including multipart parts. */\nexport const BLOB_TRANSFER_CONCURRENCY = 4;\nconst TRANSFER_IDLE_TIMEOUT_MS = 120_000;\ninterface TransferPool {\n active: number;\n waiting: Array<() => void>;\n}\nconst pools = new WeakMap<object, TransferPool>();\n\n/**\n * One slot of a client's transfer pool, held ahead of its transfer by bytes\n * that must exist before the transfer can start (a multipart stage's first\n * parts are encrypted to describe them). Releasing is idempotent.\n */\nexport interface BlobTransferPermit {\n release(): void;\n}\n\nfunction poolFor(client: object): TransferPool {\n let pool = pools.get(client);\n if (!pool) {\n pool = { active: 0, waiting: [] };\n pools.set(client, pool);\n }\n return pool;\n}\n\nfunction permitFrom(pool: TransferPool): BlobTransferPermit {\n let held = true;\n return {\n release() {\n if (!held) return;\n held = false;\n const next = pool.waiting.shift();\n if (next) next();\n else pool.active -= 1;\n },\n };\n}\n\nexport async function acquireBlobTransferPermit(\n client: object,\n): Promise<BlobTransferPermit> {\n const pool = poolFor(client);\n if (pool.active >= BLOB_TRANSFER_CONCURRENCY) {\n await new Promise<void>((resolve) => pool.waiting.push(resolve));\n } else {\n pool.active += 1;\n }\n return permitFrom(pool);\n}\n\n/** A permit only if one is free now; never queues behind other transfers. */\nexport function tryAcquireBlobTransferPermit(\n client: object,\n): BlobTransferPermit | null {\n const pool = poolFor(client);\n if (pool.active >= BLOB_TRANSFER_CONCURRENCY) return null;\n pool.active += 1;\n return permitFrom(pool);\n}\n\nexport async function runBlobTransfer<T>(\n client: object,\n transfer: (signal: AbortSignal, progress: () => void) => Promise<T>,\n timeoutMs = TRANSFER_IDLE_TIMEOUT_MS,\n heldPermit?: BlobTransferPermit,\n): Promise<T> {\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)\n throw new RangeError(\"Blob transfer timeout must be positive and finite\");\n const permit = heldPermit ?? (await acquireBlobTransferPermit(client));\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const progress = () => {\n controller.signal.throwIfAborted();\n clearTimeout(timer);\n timer = setTimeout(\n () => controller.abort(new Error(\"Blob transfer timed out\")),\n timeoutMs,\n );\n };\n progress();\n try {\n // A native bridge or custom transport may ignore abort. Bound its logical\n // ownership anyway; callers must check the signal before accepting results.\n return await new Promise<T>((resolve, reject) => {\n const aborted = () => reject(controller.signal.reason);\n controller.signal.addEventListener(\"abort\", aborted, { once: true });\n void Promise.resolve()\n .then(() => transfer(controller.signal, progress))\n .then(\n (value) => {\n controller.signal.removeEventListener(\"abort\", aborted);\n resolve(value);\n },\n (error: unknown) => {\n controller.signal.removeEventListener(\"abort\", aborted);\n reject(error);\n },\n );\n });\n } finally {\n clearTimeout(timer);\n permit.release();\n }\n}\n"]}
@@ -37,6 +37,18 @@ export function getParentCreateContext(parentProjection) {
37
37
  export function asContainerManifestBundle(bundle) {
38
38
  return readCanonicalManifestBundle(bundle, "Container manifest bundle");
39
39
  }
40
+ /**
41
+ * Cite a manifest the server already stores. Mutation requests carry these
42
+ * references instead of echoing the bundles a writer projection served; the
43
+ * server resolves each from its own verified store.
44
+ */
45
+ export function containerManifestRef(bundle) {
46
+ const containerId = Reflect.get(bundle.state, "containerId");
47
+ if (typeof containerId !== "string" || containerId.length === 0) {
48
+ throw new Error("Container manifest is missing a container id");
49
+ }
50
+ return { containerId, manifestHash: bundle.manifestHash };
51
+ }
40
52
  export function readContainerState(bundle) {
41
53
  return readContainerAccessManifestState(bundle.state, "Container manifest state");
42
54
  }
@@ -1 +1 @@
1
- {"version":3,"file":"projection.js","sourceRoot":"","sources":["projection.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type ContainerGrantPrincipalHead,\n type ContainerKekRecipientTarget,\n type ContainerKeyWrap,\n type ContainerUserRecipientKey,\n derivePrincipalRecipientKeyEpochId,\n wrapDekForRecipients,\n} from \"@tearleads/crypto\";\nimport { base64ToBytes, bytesToBase64 } from \"@tearleads/encoding\";\nimport type { AccessManifestBundleWire } from \"@tearleads/validators/request\";\nimport type {\n ContainerKekResponse,\n ContainerWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { readManifestContainerId } from \"../../documents/shared/readers\";\nimport {\n readCanonicalManifestBundle,\n readContainerAccessManifestState,\n} from \"./readers\";\nimport type { ParentContainerCreateContext } from \"./types\";\n\nexport function uniqueSortedManifestHashes(\n path: readonly ContainerWriterProjectionResponse[\"path\"][number][],\n): string[] {\n return [...new Set(path.map((bundle) => bundle.manifestHash))].sort();\n}\n\ninterface LeafContainerContextErrors {\n readonly emptyProjection: string;\n readonly inconsistentKek: string;\n readonly inconsistentPath: string;\n readonly inconsistentTarget: string;\n readonly staleKek: string;\n}\n\nfunction getLeafContainerContext(\n projection: ContainerWriterProjectionResponse,\n errors: LeafContainerContextErrors,\n): ParentContainerCreateContext {\n if (projection.path.length !== projection.containerKeks.length) {\n throw new Error(errors.inconsistentPath);\n }\n\n const manifest = projection.path.at(-1);\n const kek = projection.containerKeks.at(-1);\n if (!manifest || !kek) {\n throw new Error(errors.emptyProjection);\n }\n if (readManifestContainerId(manifest) !== projection.containerId) {\n throw new Error(errors.inconsistentTarget);\n }\n if (kek.containerId !== projection.containerId) {\n throw new Error(errors.inconsistentKek);\n }\n if (kek.accessManifestHash !== manifest.manifestHash) {\n throw new Error(errors.staleKek);\n }\n\n return { manifest, kek };\n}\n\nexport function getParentCreateContext(\n parentProjection: ContainerWriterProjectionResponse,\n): ParentContainerCreateContext {\n return getLeafContainerContext(parentProjection, {\n emptyProjection: \"Container parent projection is empty\",\n inconsistentKek: \"Container parent KEK target is inconsistent\",\n inconsistentPath:\n \"Container parent projection path and KEKs are inconsistent\",\n inconsistentTarget: \"Container parent projection target is inconsistent\",\n staleKek: \"Container parent KEK is stale\",\n });\n}\n\nexport function asContainerManifestBundle(\n bundle: ContainerWriterProjectionResponse[\"path\"][number],\n): AccessManifestBundleWire {\n return readCanonicalManifestBundle(bundle, \"Container manifest bundle\");\n}\n\nexport function readContainerState(\n bundle: ContainerWriterProjectionResponse[\"path\"][number],\n): ReturnType<typeof readContainerAccessManifestState> {\n return readContainerAccessManifestState(\n bundle.state,\n \"Container manifest state\",\n );\n}\n\nexport function getTargetContainerContext(\n projection: ContainerWriterProjectionResponse,\n): ParentContainerCreateContext {\n return getLeafContainerContext(projection, {\n emptyProjection: \"Container projection is empty\",\n inconsistentKek: \"Container target KEK is inconsistent\",\n inconsistentPath: \"Container projection path and KEKs are inconsistent\",\n inconsistentTarget: \"Container projection target is inconsistent\",\n staleKek: \"Container target KEK is stale\",\n });\n}\n\nexport function getParentKekForTarget(\n projection: ContainerWriterProjectionResponse,\n): ContainerKekResponse | null {\n const targetState = readContainerState(\n getTargetContainerContext(projection).manifest,\n );\n if (!targetState.parentContainerId) {\n return null;\n }\n\n const parentKek = projection.containerKeks.at(-2);\n if (!parentKek || parentKek.containerId !== targetState.parentContainerId) {\n throw new Error(\"Container parent KEK is unavailable\");\n }\n\n return parentKek;\n}\n\nexport async function wrapContainerKeyToParent(input: {\n containerKey: Uint8Array;\n containerKeyEpochId: string;\n manifestHash: string;\n parentKek: ContainerKekResponse;\n parentPublicKey: string;\n}): Promise<ContainerKeyWrap> {\n const [wrapped] = await wrapDekForRecipients(input.containerKey, [\n base64ToBytes(input.parentPublicKey),\n ]);\n if (!wrapped) throw new Error(\"Container parent wrap is unavailable\");\n\n return {\n containerKeyEpochId: input.containerKeyEpochId,\n recipientKind: \"container\",\n recipientId: input.parentKek.containerId,\n recipientKeyEpochId: input.parentKek.containerKeyEpochId,\n recipientKeyFingerprint: input.parentKek.keyEpochHash,\n kemCipherText: bytesToBase64(wrapped.kemCipherText),\n wrappedKey: bytesToBase64(wrapped.wrappedKey),\n wrapManifestHash: input.manifestHash,\n };\n}\n\nexport async function wrapContainerKeyToRootUser(input: {\n containerKey: Uint8Array;\n containerKeyEpochId: string;\n manifestHash: string;\n recipientEncapsulationPublicKey: Uint8Array;\n userId: string;\n}): Promise<{\n recipientTarget: ContainerKekRecipientTarget;\n userRecipientKey: ContainerUserRecipientKey;\n wrap: ContainerKeyWrap;\n}> {\n const [recipient] = await wrapDekForRecipients(input.containerKey, [\n input.recipientEncapsulationPublicKey,\n ]);\n if (!recipient) {\n throw new Error(\"Container root recipient wrap is unavailable\");\n }\n\n const userRecipientKey: ContainerUserRecipientKey = {\n userId: input.userId,\n recipientKeyEpochId: `user:${input.userId}:encapsulation:${recipient.keyFingerprint}`,\n recipientKeyFingerprint: recipient.keyFingerprint,\n };\n const recipientTarget: ContainerKekRecipientTarget = {\n recipientKind: \"user\",\n recipientId: input.userId,\n recipientKeyEpochId: userRecipientKey.recipientKeyEpochId,\n recipientKeyFingerprint: userRecipientKey.recipientKeyFingerprint,\n };\n\n return {\n recipientTarget,\n userRecipientKey,\n wrap: {\n containerKeyEpochId: input.containerKeyEpochId,\n recipientKind: \"user\",\n recipientId: input.userId,\n recipientKeyEpochId: userRecipientKey.recipientKeyEpochId,\n recipientKeyFingerprint: userRecipientKey.recipientKeyFingerprint,\n kemCipherText: bytesToBase64(recipient.kemCipherText),\n wrappedKey: bytesToBase64(recipient.wrappedKey),\n wrapManifestHash: input.manifestHash,\n },\n };\n}\n\nexport async function wrapContainerKeyToManagedPrincipal(input: {\n containerKey: Uint8Array;\n containerKeyEpochId: string;\n manifestHash: string;\n principalEncapsulationPublicKey: string;\n principalHead: ContainerGrantPrincipalHead;\n}): Promise<{\n recipientTarget: ContainerKekRecipientTarget;\n wrap: ContainerKeyWrap;\n}> {\n const [recipient] = await wrapDekForRecipients(input.containerKey, [\n base64ToBytes(input.principalEncapsulationPublicKey),\n ]);\n if (!recipient) {\n throw new Error(\n \"Container managed principal recipient wrap is unavailable\",\n );\n }\n\n const recipientTarget: ContainerKekRecipientTarget = {\n recipientKind: input.principalHead.principalType,\n recipientId: input.principalHead.principalId,\n recipientKeyEpochId: derivePrincipalRecipientKeyEpochId(\n input.principalHead,\n ),\n recipientKeyFingerprint: input.principalHead.keyFingerprint,\n };\n\n return {\n recipientTarget,\n wrap: {\n containerKeyEpochId: input.containerKeyEpochId,\n recipientKind: recipientTarget.recipientKind,\n recipientId: recipientTarget.recipientId,\n recipientKeyEpochId: recipientTarget.recipientKeyEpochId,\n recipientKeyFingerprint: recipientTarget.recipientKeyFingerprint,\n kemCipherText: bytesToBase64(recipient.kemCipherText),\n wrappedKey: bytesToBase64(recipient.wrappedKey),\n wrapManifestHash: input.manifestHash,\n },\n };\n}\n\nexport function getParentWrappingPublicKey(\n projection: ContainerWriterProjectionResponse,\n): string | null {\n const targetState = readContainerState(\n getTargetContainerContext(projection).manifest,\n );\n if (!targetState.parentContainerId) {\n return null;\n }\n\n const parent = projection.path.at(-2);\n if (!parent) {\n throw new Error(\"Container parent wrapping public key is unavailable\");\n }\n const parentState = readContainerState(parent);\n if (parentState.containerId !== targetState.parentContainerId) {\n throw new Error(\"Container parent wrapping public key is unavailable\");\n }\n const publicKey = parentState.containerKeyPublicKey;\n if (!publicKey)\n throw new Error(\"Container parent wrapping public key is missing\");\n return publicKey;\n}\n"]}
1
+ {"version":3,"file":"projection.js","sourceRoot":"","sources":["projection.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,kCAAkC,EAClC,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AASnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EACL,2BAA2B,EAC3B,gCAAgC,GACjC,MAAM,WAAW,CAAC;AAGnB,MAAM,UAAU,0BAA0B,CACxC,IAAkE;IAElE,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACxE,CAAC;AAUD,SAAS,uBAAuB,CAC9B,UAA6C,EAC7C,MAAkC;IAElC,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC;QAC/D,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,IAAI,CAAC,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,uBAAuB,CAAC,QAAQ,CAAC,KAAK,UAAU,CAAC,WAAW,EAAE,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,GAAG,CAAC,WAAW,KAAK,UAAU,CAAC,WAAW,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,GAAG,CAAC,kBAAkB,KAAK,QAAQ,CAAC,YAAY,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,gBAAmD;IAEnD,OAAO,uBAAuB,CAAC,gBAAgB,EAAE;QAC/C,eAAe,EAAE,sCAAsC;QACvD,eAAe,EAAE,6CAA6C;QAC9D,gBAAgB,EACd,4DAA4D;QAC9D,kBAAkB,EAAE,oDAAoD;QACxE,QAAQ,EAAE,+BAA+B;KAC1C,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,MAAyD;IAEzD,OAAO,2BAA2B,CAAC,MAAM,EAAE,2BAA2B,CAAC,CAAC;AAC1E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAGpC;IACC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IAC7D,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,MAAyD;IAEzD,OAAO,gCAAgC,CACrC,MAAM,CAAC,KAAK,EACZ,0BAA0B,CAC3B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,UAA6C;IAE7C,OAAO,uBAAuB,CAAC,UAAU,EAAE;QACzC,eAAe,EAAE,+BAA+B;QAChD,eAAe,EAAE,sCAAsC;QACvD,gBAAgB,EAAE,qDAAqD;QACvE,kBAAkB,EAAE,6CAA6C;QACjE,QAAQ,EAAE,+BAA+B;KAC1C,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,UAA6C;IAE7C,MAAM,WAAW,GAAG,kBAAkB,CACpC,yBAAyB,CAAC,UAAU,CAAC,CAAC,QAAQ,CAC/C,CAAC;IACF,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,SAAS,GAAG,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,WAAW,KAAK,WAAW,CAAC,iBAAiB,EAAE,CAAC;QAC1E,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,KAM9C;IACC,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC,YAAY,EAAE;QAC/D,aAAa,CAAC,KAAK,CAAC,eAAe,CAAC;KACrC,CAAC,CAAC;IACH,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAEtE,OAAO;QACL,mBAAmB,EAAE,KAAK,CAAC,mBAAmB;QAC9C,aAAa,EAAE,WAAW;QAC1B,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW;QACxC,mBAAmB,EAAE,KAAK,CAAC,SAAS,CAAC,mBAAmB;QACxD,uBAAuB,EAAE,KAAK,CAAC,SAAS,CAAC,YAAY;QACrD,aAAa,EAAE,aAAa,CAAC,OAAO,CAAC,aAAa,CAAC;QACnD,UAAU,EAAE,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC;QAC7C,gBAAgB,EAAE,KAAK,CAAC,YAAY;KACrC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,KAMhD;IAKC,MAAM,CAAC,SAAS,CAAC,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC,YAAY,EAAE;QACjE,KAAK,CAAC,+BAA+B;KACtC,CAAC,CAAC;IACH,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,gBAAgB,GAA8B;QAClD,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,mBAAmB,EAAE,QAAQ,KAAK,CAAC,MAAM,kBAAkB,SAAS,CAAC,cAAc,EAAE;QACrF,uBAAuB,EAAE,SAAS,CAAC,cAAc;KAClD,CAAC;IACF,MAAM,eAAe,GAAgC;QACnD,aAAa,EAAE,MAAM;QACrB,WAAW,EAAE,KAAK,CAAC,MAAM;QACzB,mBAAmB,EAAE,gBAAgB,CAAC,mBAAmB;QACzD,uBAAuB,EAAE,gBAAgB,CAAC,uBAAuB;KAClE,CAAC;IAEF,OAAO;QACL,eAAe;QACf,gBAAgB;QAChB,IAAI,EAAE;YACJ,mBAAmB,EAAE,KAAK,CAAC,mBAAmB;YAC9C,aAAa,EAAE,MAAM;YACrB,WAAW,EAAE,KAAK,CAAC,MAAM;YACzB,mBAAmB,EAAE,gBAAgB,CAAC,mBAAmB;YACzD,uBAAuB,EAAE,gBAAgB,CAAC,uBAAuB;YACjE,aAAa,EAAE,aAAa,CAAC,SAAS,CAAC,aAAa,CAAC;YACrD,UAAU,EAAE,aAAa,CAAC,SAAS,CAAC,UAAU,CAAC;YAC/C,gBAAgB,EAAE,KAAK,CAAC,YAAY;SACrC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kCAAkC,CAAC,KAMxD;IAIC,MAAM,CAAC,SAAS,CAAC,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC,YAAY,EAAE;QACjE,aAAa,CAAC,KAAK,CAAC,+BAA+B,CAAC;KACrD,CAAC,CAAC;IACH,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,2DAA2D,CAC5D,CAAC;IACJ,CAAC;IAED,MAAM,eAAe,GAAgC;QACnD,aAAa,EAAE,KAAK,CAAC,aAAa,CAAC,aAAa;QAChD,WAAW,EAAE,KAAK,CAAC,aAAa,CAAC,WAAW;QAC5C,mBAAmB,EAAE,kCAAkC,CACrD,KAAK,CAAC,aAAa,CACpB;QACD,uBAAuB,EAAE,KAAK,CAAC,aAAa,CAAC,cAAc;KAC5D,CAAC;IAEF,OAAO;QACL,eAAe;QACf,IAAI,EAAE;YACJ,mBAAmB,EAAE,KAAK,CAAC,mBAAmB;YAC9C,aAAa,EAAE,eAAe,CAAC,aAAa;YAC5C,WAAW,EAAE,eAAe,CAAC,WAAW;YACxC,mBAAmB,EAAE,eAAe,CAAC,mBAAmB;YACxD,uBAAuB,EAAE,eAAe,CAAC,uBAAuB;YAChE,aAAa,EAAE,aAAa,CAAC,SAAS,CAAC,aAAa,CAAC;YACrD,UAAU,EAAE,aAAa,CAAC,SAAS,CAAC,UAAU,CAAC;YAC/C,gBAAgB,EAAE,KAAK,CAAC,YAAY;SACrC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,0BAA0B,CACxC,UAA6C;IAE7C,MAAM,WAAW,GAAG,kBAAkB,CACpC,yBAAyB,CAAC,UAAU,CAAC,CAAC,QAAQ,CAC/C,CAAC;IACF,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACtC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,WAAW,CAAC,WAAW,KAAK,WAAW,CAAC,iBAAiB,EAAE,CAAC;QAC9D,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,SAAS,GAAG,WAAW,CAAC,qBAAqB,CAAC;IACpD,IAAI,CAAC,SAAS;QACZ,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["import {\n type ContainerGrantPrincipalHead,\n type ContainerKekRecipientTarget,\n type ContainerKeyWrap,\n type ContainerUserRecipientKey,\n derivePrincipalRecipientKeyEpochId,\n wrapDekForRecipients,\n} from \"@tearleads/crypto\";\nimport { base64ToBytes, bytesToBase64 } from \"@tearleads/encoding\";\nimport type {\n AccessManifestBundleWire,\n ContainerManifestRef,\n} from \"@tearleads/validators/request\";\nimport type {\n ContainerKekResponse,\n ContainerWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { readManifestContainerId } from \"../../documents/shared/readers\";\nimport {\n readCanonicalManifestBundle,\n readContainerAccessManifestState,\n} from \"./readers\";\nimport type { ParentContainerCreateContext } from \"./types\";\n\nexport function uniqueSortedManifestHashes(\n path: readonly ContainerWriterProjectionResponse[\"path\"][number][],\n): string[] {\n return [...new Set(path.map((bundle) => bundle.manifestHash))].sort();\n}\n\ninterface LeafContainerContextErrors {\n readonly emptyProjection: string;\n readonly inconsistentKek: string;\n readonly inconsistentPath: string;\n readonly inconsistentTarget: string;\n readonly staleKek: string;\n}\n\nfunction getLeafContainerContext(\n projection: ContainerWriterProjectionResponse,\n errors: LeafContainerContextErrors,\n): ParentContainerCreateContext {\n if (projection.path.length !== projection.containerKeks.length) {\n throw new Error(errors.inconsistentPath);\n }\n\n const manifest = projection.path.at(-1);\n const kek = projection.containerKeks.at(-1);\n if (!manifest || !kek) {\n throw new Error(errors.emptyProjection);\n }\n if (readManifestContainerId(manifest) !== projection.containerId) {\n throw new Error(errors.inconsistentTarget);\n }\n if (kek.containerId !== projection.containerId) {\n throw new Error(errors.inconsistentKek);\n }\n if (kek.accessManifestHash !== manifest.manifestHash) {\n throw new Error(errors.staleKek);\n }\n\n return { manifest, kek };\n}\n\nexport function getParentCreateContext(\n parentProjection: ContainerWriterProjectionResponse,\n): ParentContainerCreateContext {\n return getLeafContainerContext(parentProjection, {\n emptyProjection: \"Container parent projection is empty\",\n inconsistentKek: \"Container parent KEK target is inconsistent\",\n inconsistentPath:\n \"Container parent projection path and KEKs are inconsistent\",\n inconsistentTarget: \"Container parent projection target is inconsistent\",\n staleKek: \"Container parent KEK is stale\",\n });\n}\n\nexport function asContainerManifestBundle(\n bundle: ContainerWriterProjectionResponse[\"path\"][number],\n): AccessManifestBundleWire {\n return readCanonicalManifestBundle(bundle, \"Container manifest bundle\");\n}\n\n/**\n * Cite a manifest the server already stores. Mutation requests carry these\n * references instead of echoing the bundles a writer projection served; the\n * server resolves each from its own verified store.\n */\nexport function containerManifestRef(bundle: {\n readonly manifestHash: string;\n readonly state: Record<string, unknown>;\n}): ContainerManifestRef {\n const containerId = Reflect.get(bundle.state, \"containerId\");\n if (typeof containerId !== \"string\" || containerId.length === 0) {\n throw new Error(\"Container manifest is missing a container id\");\n }\n return { containerId, manifestHash: bundle.manifestHash };\n}\n\nexport function readContainerState(\n bundle: ContainerWriterProjectionResponse[\"path\"][number],\n): ReturnType<typeof readContainerAccessManifestState> {\n return readContainerAccessManifestState(\n bundle.state,\n \"Container manifest state\",\n );\n}\n\nexport function getTargetContainerContext(\n projection: ContainerWriterProjectionResponse,\n): ParentContainerCreateContext {\n return getLeafContainerContext(projection, {\n emptyProjection: \"Container projection is empty\",\n inconsistentKek: \"Container target KEK is inconsistent\",\n inconsistentPath: \"Container projection path and KEKs are inconsistent\",\n inconsistentTarget: \"Container projection target is inconsistent\",\n staleKek: \"Container target KEK is stale\",\n });\n}\n\nexport function getParentKekForTarget(\n projection: ContainerWriterProjectionResponse,\n): ContainerKekResponse | null {\n const targetState = readContainerState(\n getTargetContainerContext(projection).manifest,\n );\n if (!targetState.parentContainerId) {\n return null;\n }\n\n const parentKek = projection.containerKeks.at(-2);\n if (!parentKek || parentKek.containerId !== targetState.parentContainerId) {\n throw new Error(\"Container parent KEK is unavailable\");\n }\n\n return parentKek;\n}\n\nexport async function wrapContainerKeyToParent(input: {\n containerKey: Uint8Array;\n containerKeyEpochId: string;\n manifestHash: string;\n parentKek: ContainerKekResponse;\n parentPublicKey: string;\n}): Promise<ContainerKeyWrap> {\n const [wrapped] = await wrapDekForRecipients(input.containerKey, [\n base64ToBytes(input.parentPublicKey),\n ]);\n if (!wrapped) throw new Error(\"Container parent wrap is unavailable\");\n\n return {\n containerKeyEpochId: input.containerKeyEpochId,\n recipientKind: \"container\",\n recipientId: input.parentKek.containerId,\n recipientKeyEpochId: input.parentKek.containerKeyEpochId,\n recipientKeyFingerprint: input.parentKek.keyEpochHash,\n kemCipherText: bytesToBase64(wrapped.kemCipherText),\n wrappedKey: bytesToBase64(wrapped.wrappedKey),\n wrapManifestHash: input.manifestHash,\n };\n}\n\nexport async function wrapContainerKeyToRootUser(input: {\n containerKey: Uint8Array;\n containerKeyEpochId: string;\n manifestHash: string;\n recipientEncapsulationPublicKey: Uint8Array;\n userId: string;\n}): Promise<{\n recipientTarget: ContainerKekRecipientTarget;\n userRecipientKey: ContainerUserRecipientKey;\n wrap: ContainerKeyWrap;\n}> {\n const [recipient] = await wrapDekForRecipients(input.containerKey, [\n input.recipientEncapsulationPublicKey,\n ]);\n if (!recipient) {\n throw new Error(\"Container root recipient wrap is unavailable\");\n }\n\n const userRecipientKey: ContainerUserRecipientKey = {\n userId: input.userId,\n recipientKeyEpochId: `user:${input.userId}:encapsulation:${recipient.keyFingerprint}`,\n recipientKeyFingerprint: recipient.keyFingerprint,\n };\n const recipientTarget: ContainerKekRecipientTarget = {\n recipientKind: \"user\",\n recipientId: input.userId,\n recipientKeyEpochId: userRecipientKey.recipientKeyEpochId,\n recipientKeyFingerprint: userRecipientKey.recipientKeyFingerprint,\n };\n\n return {\n recipientTarget,\n userRecipientKey,\n wrap: {\n containerKeyEpochId: input.containerKeyEpochId,\n recipientKind: \"user\",\n recipientId: input.userId,\n recipientKeyEpochId: userRecipientKey.recipientKeyEpochId,\n recipientKeyFingerprint: userRecipientKey.recipientKeyFingerprint,\n kemCipherText: bytesToBase64(recipient.kemCipherText),\n wrappedKey: bytesToBase64(recipient.wrappedKey),\n wrapManifestHash: input.manifestHash,\n },\n };\n}\n\nexport async function wrapContainerKeyToManagedPrincipal(input: {\n containerKey: Uint8Array;\n containerKeyEpochId: string;\n manifestHash: string;\n principalEncapsulationPublicKey: string;\n principalHead: ContainerGrantPrincipalHead;\n}): Promise<{\n recipientTarget: ContainerKekRecipientTarget;\n wrap: ContainerKeyWrap;\n}> {\n const [recipient] = await wrapDekForRecipients(input.containerKey, [\n base64ToBytes(input.principalEncapsulationPublicKey),\n ]);\n if (!recipient) {\n throw new Error(\n \"Container managed principal recipient wrap is unavailable\",\n );\n }\n\n const recipientTarget: ContainerKekRecipientTarget = {\n recipientKind: input.principalHead.principalType,\n recipientId: input.principalHead.principalId,\n recipientKeyEpochId: derivePrincipalRecipientKeyEpochId(\n input.principalHead,\n ),\n recipientKeyFingerprint: input.principalHead.keyFingerprint,\n };\n\n return {\n recipientTarget,\n wrap: {\n containerKeyEpochId: input.containerKeyEpochId,\n recipientKind: recipientTarget.recipientKind,\n recipientId: recipientTarget.recipientId,\n recipientKeyEpochId: recipientTarget.recipientKeyEpochId,\n recipientKeyFingerprint: recipientTarget.recipientKeyFingerprint,\n kemCipherText: bytesToBase64(recipient.kemCipherText),\n wrappedKey: bytesToBase64(recipient.wrappedKey),\n wrapManifestHash: input.manifestHash,\n },\n };\n}\n\nexport function getParentWrappingPublicKey(\n projection: ContainerWriterProjectionResponse,\n): string | null {\n const targetState = readContainerState(\n getTargetContainerContext(projection).manifest,\n );\n if (!targetState.parentContainerId) {\n return null;\n }\n\n const parent = projection.path.at(-2);\n if (!parent) {\n throw new Error(\"Container parent wrapping public key is unavailable\");\n }\n const parentState = readContainerState(parent);\n if (parentState.containerId !== targetState.parentContainerId) {\n throw new Error(\"Container parent wrapping public key is unavailable\");\n }\n const publicKey = parentState.containerKeyPublicKey;\n if (!publicKey)\n throw new Error(\"Container parent wrapping public key is missing\");\n return publicKey;\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import type { RequestFailureKind, RequestResultOptions, UploadMultipartBlobPartBytesRequest } from "../../../../internal/api-client/index.js";
2
2
  import type { BlobContentKeyTarget, BlobEnvelopeHeaderRecord, BlobEnvelopeRecord, WriteHeader } from "../../../../internal/crypto/index.js";
3
- import type { BlobAttachmentBindRequest, BlobAttachmentDetachRequest, BlobContentKeyBundleRequest, CompleteMultipartBlobStageRequest, InitiateMultipartBlobStageRequest } from "../../../../internal/validators/request/index.js";
4
- import type { BlobAttachmentBindResponse, BlobAttachmentDetachResponse, BlobAttachmentSummary, CompleteMultipartBlobStageResponse, DocumentWriterProjectionResponse, InitiateMultipartBlobStageResponse, MultipartBlobStageStatusResponse, UploadMultipartBlobPartResponse } from "../../../../internal/validators/response/index.js";
3
+ import type { BlobAttachmentBindRequest, BlobAttachmentDetachRequest, BlobContentKeyBundleRequest, InitiateMultipartBlobStageRequest } from "../../../../internal/validators/request/index.js";
4
+ import type { BlobAttachmentBindResponse, BlobAttachmentDetachResponse, BlobAttachmentSummary, DocumentWriterProjectionResponse, InitiateMultipartBlobStageResponse, MultipartBlobStageStatusResponse, UploadMultipartBlobPartResponse } from "../../../../internal/validators/response/index.js";
5
5
  import type { BlobByteSourceInput, BlobBytes } from "../../../blobContracts.js";
6
6
  import type { ProjectionUserKeyResolver, ReferencedPrincipalPolicyWarmer } from "../../../keyingProjectionVerification.js";
7
7
  import type { ExecSql } from "../../../sqlite/sqlSchema.js";
@@ -27,7 +27,6 @@ export interface BlobAttachmentApi {
27
27
  readonly message: string;
28
28
  readonly status: number | null;
29
29
  } | null;
30
- completeMultipartBlobStage(stageId: string, input: CompleteMultipartBlobStageRequest): Promise<CompleteMultipartBlobStageResponse | null>;
31
30
  getDocumentWriterProjection(documentId: string): Promise<DocumentWriterProjectionResponse | null>;
32
31
  getMultipartBlobStage(stageId: string): Promise<MultipartBlobStageStatusResponse | null>;
33
32
  initiateMultipartBlobStage(input: InitiateMultipartBlobStageRequest): Promise<InitiateMultipartBlobStageResponse | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAoCA,MAAM,CAAC,MAAM,mCAAmC,GAC9C,wCAAwC,CAAC;AAC3C,MAAM,CAAC,MAAM,8BAA8B,GACzC,mCAAmC,CAAC;AACtC,MAAM,CAAC,MAAM,gCAAgC,GAC3C,qCAAqC,CAAC;AACxC,MAAM,CAAC,MAAM,6BAA6B,GACxC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,yCAAyC,CAAC,CAAC;AACtE,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,WAAW,EAAE,CAAC","sourcesContent":["import type {\n RequestFailureKind,\n RequestResultOptions,\n UploadMultipartBlobPartBytesRequest,\n} from \"@tearleads/api-client\";\nimport type {\n BlobContentKeyTarget,\n BlobEnvelopeHeaderRecord,\n BlobEnvelopeRecord,\n WriteHeader,\n} from \"@tearleads/crypto\";\nimport type {\n BlobAttachmentBindRequest,\n BlobAttachmentDetachRequest,\n BlobContentKeyBundleRequest,\n CompleteMultipartBlobStageRequest,\n InitiateMultipartBlobStageRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n BlobAttachmentBindResponse,\n BlobAttachmentDetachResponse,\n BlobAttachmentSummary,\n CompleteMultipartBlobStageResponse,\n DocumentWriterProjectionResponse,\n InitiateMultipartBlobStageResponse,\n MultipartBlobStageStatusResponse,\n UploadMultipartBlobPartResponse,\n} from \"@tearleads/validators/response\";\nimport type { BlobByteSourceInput, BlobBytes } from \"../../../blobContracts\";\nimport type {\n ProjectionUserKeyResolver,\n ReferencedPrincipalPolicyWarmer,\n} from \"../../../keyingProjectionVerification\";\nimport type { ExecSql } from \"../../../sqlite/sqlSchema\";\nimport type { DocumentCreateAuthor } from \"../../shared/types\";\n\nexport const BLOB_CONTENT_RECORD_KEY_INFO_DOMAIN =\n \"tearleads.blob.content-record-key-info\";\nexport const BLOB_CONTENT_RECORD_AAD_DOMAIN =\n \"tearleads.blob.content-record-aad\";\nexport const BLOB_CONTENT_RECORD_NONCE_DOMAIN =\n \"tearleads.blob.content-record-nonce\";\nexport const BLOB_CONTENT_RECORD_HKDF_SALT: Uint8Array<ArrayBuffer> =\n new TextEncoder().encode(\"tearleads.blob.content-record-hkdf-salt\");\nexport const TEXT_ENCODER = new TextEncoder();\n\nexport interface BlobAttachmentRequestOptions {\n readonly expectedPaymentRequiredOrganizationId?: string | undefined;\n}\n\nexport interface BlobAttachmentApi {\n bindBlobAttachment(\n blobId: string,\n input: BlobAttachmentBindRequest,\n options?: BlobAttachmentRequestOptions,\n ): Promise<BlobAttachmentBindResponse | null>;\n clearWriterProjectionCaches?(): void;\n evictDocumentWriterProjection?(documentId: string): void;\n getRequestFailure?(input: {\n method: \"DELETE\" | \"GET\" | \"POST\" | \"PUT\";\n path: string;\n }): {\n readonly code?: string | undefined;\n readonly kind: RequestFailureKind;\n readonly message: string;\n readonly status: number | null;\n } | null;\n completeMultipartBlobStage(\n stageId: string,\n input: CompleteMultipartBlobStageRequest,\n ): Promise<CompleteMultipartBlobStageResponse | null>;\n getDocumentWriterProjection(\n documentId: string,\n ): Promise<DocumentWriterProjectionResponse | null>;\n getMultipartBlobStage(\n stageId: string,\n ): Promise<MultipartBlobStageStatusResponse | null>;\n initiateMultipartBlobStage(\n input: InitiateMultipartBlobStageRequest,\n ): Promise<InitiateMultipartBlobStageResponse | null>;\n uploadMultipartBlobPartBytes(\n stageId: string,\n partNumber: number,\n input: UploadMultipartBlobPartBytesRequest,\n options?: RequestResultOptions,\n ): Promise<UploadMultipartBlobPartResponse | null>;\n}\n\nexport interface BlobAttachmentDetachApi {\n detachBlobAttachment(\n blobId: string,\n bindingId: string,\n input: BlobAttachmentDetachRequest,\n options?: BlobAttachmentRequestOptions,\n ): Promise<BlobAttachmentDetachResponse | null>;\n getDocumentWriterProjection(\n documentId: string,\n ): Promise<DocumentWriterProjectionResponse | null>;\n}\n\nexport type { BlobContentKeyTarget };\n\n/**\n * Aliases of the crypto parser's own types. The parser is shared with the API,\n * so restating these shapes here would let the two drift apart silently.\n */\nexport type BlobEncryptedBytesRecord = BlobEnvelopeRecord;\n\n/**\n * The envelope as the header establishes it. Streaming readers hold this\n * before any ciphertext is read, so it has no `chunks`: an empty array there\n * would read as an empty object rather than an unread one.\n */\nexport type BlobEncryptedBytesHeader = BlobEnvelopeHeaderRecord;\n\nexport type BlobEncryptedChunk = BlobEnvelopeRecord[\"chunks\"][number];\n\nexport interface BlobSourceSnapshot {\n readonly byteLength: number;\n readonly chunkSha256: readonly string[];\n readonly chunkSize: number;\n readonly sha256: string;\n}\n\nexport interface BlobEncryptionPlan {\n readonly byteLength: number;\n readonly chunkSize: number;\n readonly metadataHash: string;\n readonly partCount: number;\n readonly plaintextByteLength: number;\n readonly plaintextSha256: string;\n readonly sha256: string;\n encryptPart(index: number): Promise<BlobBytes>;\n getPartByteLength(index: number): number;\n}\n\nexport interface DocumentManifestIdentity {\n documentId: string;\n manifestHash: string;\n organizationId: string;\n}\n\nexport interface BlobAttachmentMaterial {\n blobAccessManifestHash: string;\n contentKeyBundle: BlobContentKeyBundleRequest;\n encrypted: BlobEncryptionPlan;\n manifestIdentity: DocumentManifestIdentity;\n targetHash: string;\n targets: BlobContentKeyTarget[];\n writerProjection: DocumentWriterProjectionResponse;\n}\n\n/**\n * Progress for an in-flight multipart blob upload.\n */\nexport interface MultipartUploadProgress {\n readonly bytesTotal: number;\n readonly bytesUploaded: number;\n readonly partsCompleted: number;\n readonly partsTotal: number;\n}\n\nexport type MultipartUploadProgressListener = (\n progress: MultipartUploadProgress,\n) => void;\n\n/**\n * Fires once a multipart stage has been opened (or resumed), with its id and\n * plaintext chunk size. Callers persist both so a later attempt can reproduce\n * the same encrypted parts and resume instead of orphaning the partial upload.\n */\nexport type MultipartStageResolvedListener = (input: {\n readonly partSize: number;\n readonly stageId: string;\n}) => void | Promise<void>;\n\nexport interface UploadDocumentAttachmentInput {\n apiClient: BlobAttachmentApi;\n author: DocumentCreateAuthor;\n blobId?: string | undefined;\n bindingId?: string | undefined;\n bytes: BlobByteSourceInput;\n contentKey?: Uint8Array | undefined;\n contentKeyEpoch?: number | undefined;\n documentId: string;\n eventId?: string | undefined;\n execSql: ExecSql;\n expectedBindingId: string | null;\n isRemoteSyncBlocked?: ((organizationId: string) => boolean) | undefined;\n // A persisted nonce seed. The plaintext digest derives the envelope IV from\n // it, then each chunk derives a unique IV. Same-source retries reproduce the\n // encrypted parts; changed bytes cannot reuse their AES-GCM nonces.\n nonceSeed?: Uint8Array | undefined;\n multipart?: MultipartBlobUploadOptions | undefined;\n onMultipartProgress?: MultipartUploadProgressListener | undefined;\n onStageResolved?: MultipartStageResolvedListener | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n signedAt?: string | undefined;\n slotId: string;\n targetSecretKey: Uint8Array;\n warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;\n writerProjection?: DocumentWriterProjectionResponse | undefined;\n}\n\nexport interface MultipartBlobUploadOptions {\n /** Exactly 5 MiB per plaintext encryption chunk; transport parts include envelope overhead. */\n readonly partSize: number;\n // The id of a previously opened stage to resume. When set, the upload refetches\n // that stage and skips parts the server already has instead of opening a new\n // one; if the stage is gone (expired), it falls back to a fresh stage.\n readonly resumeStageId?: string | undefined;\n readonly uploadConcurrency?: number | undefined;\n}\n\nexport interface UploadDocumentAttachmentResult {\n blobId: string;\n bindingId: string;\n request: BlobAttachmentBindRequest;\n response: BlobAttachmentBindResponse;\n sha256: string;\n byteLength: number;\n writeHeader: WriteHeader;\n writeHeaderHash: string;\n writerProjection: DocumentWriterProjectionResponse;\n}\n\nexport interface DetachDocumentAttachmentInput {\n apiClient: BlobAttachmentDetachApi;\n author: DocumentCreateAuthor;\n bindingId: string;\n blobId: string;\n documentId: string;\n eventId?: string | undefined;\n execSql: ExecSql;\n isRemoteSyncBlocked?: ((organizationId: string) => boolean) | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n signedAt?: string | undefined;\n slotId: string;\n stillCurrent?: (() => boolean) | undefined;\n warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;\n writerProjection?: DocumentWriterProjectionResponse | undefined;\n}\n\nexport interface DetachDocumentAttachmentResult {\n request: BlobAttachmentDetachRequest;\n response: BlobAttachmentDetachResponse;\n writerProjection: DocumentWriterProjectionResponse;\n}\n\nexport interface DecryptDocumentAttachmentBlobInput {\n binding: BlobAttachmentSummary;\n encryptedBytes: BlobBytes;\n expectedDocumentId: string;\n expectedSlotId: string;\n execSql: ExecSql;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n targetSecretKey: Uint8Array;\n stillCurrent?: (() => boolean) | undefined;\n warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;\n writerProjection: DocumentWriterProjectionResponse;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAkCA,MAAM,CAAC,MAAM,mCAAmC,GAC9C,wCAAwC,CAAC;AAC3C,MAAM,CAAC,MAAM,8BAA8B,GACzC,mCAAmC,CAAC;AACtC,MAAM,CAAC,MAAM,gCAAgC,GAC3C,qCAAqC,CAAC;AACxC,MAAM,CAAC,MAAM,6BAA6B,GACxC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,yCAAyC,CAAC,CAAC;AACtE,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,WAAW,EAAE,CAAC","sourcesContent":["import type {\n RequestFailureKind,\n RequestResultOptions,\n UploadMultipartBlobPartBytesRequest,\n} from \"@tearleads/api-client\";\nimport type {\n BlobContentKeyTarget,\n BlobEnvelopeHeaderRecord,\n BlobEnvelopeRecord,\n WriteHeader,\n} from \"@tearleads/crypto\";\nimport type {\n BlobAttachmentBindRequest,\n BlobAttachmentDetachRequest,\n BlobContentKeyBundleRequest,\n InitiateMultipartBlobStageRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n BlobAttachmentBindResponse,\n BlobAttachmentDetachResponse,\n BlobAttachmentSummary,\n DocumentWriterProjectionResponse,\n InitiateMultipartBlobStageResponse,\n MultipartBlobStageStatusResponse,\n UploadMultipartBlobPartResponse,\n} from \"@tearleads/validators/response\";\nimport type { BlobByteSourceInput, BlobBytes } from \"../../../blobContracts\";\nimport type {\n ProjectionUserKeyResolver,\n ReferencedPrincipalPolicyWarmer,\n} from \"../../../keyingProjectionVerification\";\nimport type { ExecSql } from \"../../../sqlite/sqlSchema\";\nimport type { DocumentCreateAuthor } from \"../../shared/types\";\n\nexport const BLOB_CONTENT_RECORD_KEY_INFO_DOMAIN =\n \"tearleads.blob.content-record-key-info\";\nexport const BLOB_CONTENT_RECORD_AAD_DOMAIN =\n \"tearleads.blob.content-record-aad\";\nexport const BLOB_CONTENT_RECORD_NONCE_DOMAIN =\n \"tearleads.blob.content-record-nonce\";\nexport const BLOB_CONTENT_RECORD_HKDF_SALT: Uint8Array<ArrayBuffer> =\n new TextEncoder().encode(\"tearleads.blob.content-record-hkdf-salt\");\nexport const TEXT_ENCODER = new TextEncoder();\n\nexport interface BlobAttachmentRequestOptions {\n readonly expectedPaymentRequiredOrganizationId?: string | undefined;\n}\n\nexport interface BlobAttachmentApi {\n bindBlobAttachment(\n blobId: string,\n input: BlobAttachmentBindRequest,\n options?: BlobAttachmentRequestOptions,\n ): Promise<BlobAttachmentBindResponse | null>;\n clearWriterProjectionCaches?(): void;\n evictDocumentWriterProjection?(documentId: string): void;\n getRequestFailure?(input: {\n method: \"DELETE\" | \"GET\" | \"POST\" | \"PUT\";\n path: string;\n }): {\n readonly code?: string | undefined;\n readonly kind: RequestFailureKind;\n readonly message: string;\n readonly status: number | null;\n } | null;\n getDocumentWriterProjection(\n documentId: string,\n ): Promise<DocumentWriterProjectionResponse | null>;\n getMultipartBlobStage(\n stageId: string,\n ): Promise<MultipartBlobStageStatusResponse | null>;\n initiateMultipartBlobStage(\n input: InitiateMultipartBlobStageRequest,\n ): Promise<InitiateMultipartBlobStageResponse | null>;\n uploadMultipartBlobPartBytes(\n stageId: string,\n partNumber: number,\n input: UploadMultipartBlobPartBytesRequest,\n options?: RequestResultOptions,\n ): Promise<UploadMultipartBlobPartResponse | null>;\n}\n\nexport interface BlobAttachmentDetachApi {\n detachBlobAttachment(\n blobId: string,\n bindingId: string,\n input: BlobAttachmentDetachRequest,\n options?: BlobAttachmentRequestOptions,\n ): Promise<BlobAttachmentDetachResponse | null>;\n getDocumentWriterProjection(\n documentId: string,\n ): Promise<DocumentWriterProjectionResponse | null>;\n}\n\nexport type { BlobContentKeyTarget };\n\n/**\n * Aliases of the crypto parser's own types. The parser is shared with the API,\n * so restating these shapes here would let the two drift apart silently.\n */\nexport type BlobEncryptedBytesRecord = BlobEnvelopeRecord;\n\n/**\n * The envelope as the header establishes it. Streaming readers hold this\n * before any ciphertext is read, so it has no `chunks`: an empty array there\n * would read as an empty object rather than an unread one.\n */\nexport type BlobEncryptedBytesHeader = BlobEnvelopeHeaderRecord;\n\nexport type BlobEncryptedChunk = BlobEnvelopeRecord[\"chunks\"][number];\n\nexport interface BlobSourceSnapshot {\n readonly byteLength: number;\n readonly chunkSha256: readonly string[];\n readonly chunkSize: number;\n readonly sha256: string;\n}\n\nexport interface BlobEncryptionPlan {\n readonly byteLength: number;\n readonly chunkSize: number;\n readonly metadataHash: string;\n readonly partCount: number;\n readonly plaintextByteLength: number;\n readonly plaintextSha256: string;\n readonly sha256: string;\n encryptPart(index: number): Promise<BlobBytes>;\n getPartByteLength(index: number): number;\n}\n\nexport interface DocumentManifestIdentity {\n documentId: string;\n manifestHash: string;\n organizationId: string;\n}\n\nexport interface BlobAttachmentMaterial {\n blobAccessManifestHash: string;\n contentKeyBundle: BlobContentKeyBundleRequest;\n encrypted: BlobEncryptionPlan;\n manifestIdentity: DocumentManifestIdentity;\n targetHash: string;\n targets: BlobContentKeyTarget[];\n writerProjection: DocumentWriterProjectionResponse;\n}\n\n/**\n * Progress for an in-flight multipart blob upload.\n */\nexport interface MultipartUploadProgress {\n readonly bytesTotal: number;\n readonly bytesUploaded: number;\n readonly partsCompleted: number;\n readonly partsTotal: number;\n}\n\nexport type MultipartUploadProgressListener = (\n progress: MultipartUploadProgress,\n) => void;\n\n/**\n * Fires once a multipart stage has been opened (or resumed), with its id and\n * plaintext chunk size. Callers persist both so a later attempt can reproduce\n * the same encrypted parts and resume instead of orphaning the partial upload.\n */\nexport type MultipartStageResolvedListener = (input: {\n readonly partSize: number;\n readonly stageId: string;\n}) => void | Promise<void>;\n\nexport interface UploadDocumentAttachmentInput {\n apiClient: BlobAttachmentApi;\n author: DocumentCreateAuthor;\n blobId?: string | undefined;\n bindingId?: string | undefined;\n bytes: BlobByteSourceInput;\n contentKey?: Uint8Array | undefined;\n contentKeyEpoch?: number | undefined;\n documentId: string;\n eventId?: string | undefined;\n execSql: ExecSql;\n expectedBindingId: string | null;\n isRemoteSyncBlocked?: ((organizationId: string) => boolean) | undefined;\n // A persisted nonce seed. The plaintext digest derives the envelope IV from\n // it, then each chunk derives a unique IV. Same-source retries reproduce the\n // encrypted parts; changed bytes cannot reuse their AES-GCM nonces.\n nonceSeed?: Uint8Array | undefined;\n multipart?: MultipartBlobUploadOptions | undefined;\n onMultipartProgress?: MultipartUploadProgressListener | undefined;\n onStageResolved?: MultipartStageResolvedListener | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n signedAt?: string | undefined;\n slotId: string;\n targetSecretKey: Uint8Array;\n warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;\n writerProjection?: DocumentWriterProjectionResponse | undefined;\n}\n\nexport interface MultipartBlobUploadOptions {\n /** Exactly 5 MiB per plaintext encryption chunk; transport parts include envelope overhead. */\n readonly partSize: number;\n // The id of a previously opened stage to resume. When set, the upload refetches\n // that stage and skips parts the server already has instead of opening a new\n // one; if the stage is gone (expired), it falls back to a fresh stage.\n readonly resumeStageId?: string | undefined;\n readonly uploadConcurrency?: number | undefined;\n}\n\nexport interface UploadDocumentAttachmentResult {\n blobId: string;\n bindingId: string;\n request: BlobAttachmentBindRequest;\n response: BlobAttachmentBindResponse;\n sha256: string;\n byteLength: number;\n writeHeader: WriteHeader;\n writeHeaderHash: string;\n writerProjection: DocumentWriterProjectionResponse;\n}\n\nexport interface DetachDocumentAttachmentInput {\n apiClient: BlobAttachmentDetachApi;\n author: DocumentCreateAuthor;\n bindingId: string;\n blobId: string;\n documentId: string;\n eventId?: string | undefined;\n execSql: ExecSql;\n isRemoteSyncBlocked?: ((organizationId: string) => boolean) | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n signedAt?: string | undefined;\n slotId: string;\n stillCurrent?: (() => boolean) | undefined;\n warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;\n writerProjection?: DocumentWriterProjectionResponse | undefined;\n}\n\nexport interface DetachDocumentAttachmentResult {\n request: BlobAttachmentDetachRequest;\n response: BlobAttachmentDetachResponse;\n writerProjection: DocumentWriterProjectionResponse;\n}\n\nexport interface DecryptDocumentAttachmentBlobInput {\n binding: BlobAttachmentSummary;\n encryptedBytes: BlobBytes;\n expectedDocumentId: string;\n expectedSlotId: string;\n execSql: ExecSql;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n targetSecretKey: Uint8Array;\n stillCurrent?: (() => boolean) | undefined;\n warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;\n writerProjection: DocumentWriterProjectionResponse;\n}\n"]}
@@ -62,7 +62,6 @@ export async function buildDocumentCreatePlan({ author, containerProjection, con
62
62
  body: readCanonicalRecord(body, "Document create body"),
63
63
  expectedManifestHash: manifestHash,
64
64
  manifest: readCanonicalRecord(manifest, "Document create manifest"),
65
- previousManifest: null,
66
65
  targetContainerPathRefs: containerPathRefs(containerProjection.path),
67
66
  contentKeyBundle: {
68
67
  contentKeyEpoch,
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","sourceRoot":"","sources":["events.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n computeAccessEventBodyHash,\n computeAccessEventHash,\n computeAccessManifestHash,\n computeDocumentContentKeyTargetHash,\n type DocumentLinkAccessEventBody,\n type DocumentLinkSetManifestState,\n deriveDocumentLinkSetManifest,\n signAccessEvent,\n type UnsignedAccessEvent,\n} from \"@tearleads/crypto\";\nimport type { ContainerManifestRef } from \"@tearleads/validators/request\";\nimport type { DocumentWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport {\n readCanonicalJson,\n readCanonicalRecord,\n} from \"../../keyingCanonicalJson\";\nimport {\n authorizingContainerPathRefsForLinkSet,\n containerPathRefs,\n deriveDocumentCreateTargets,\n mergeTargetEnvelopes,\n} from \"./projection\";\nimport { sortDocumentTargets, uniqueSortedStrings } from \"./readers\";\nimport type {\n BuildDocumentCreatePlanInput,\n DocumentCreateAuthor,\n DocumentCreatePlan,\n DocumentLinkSetEventPlan,\n DocumentLinkSetMutationBody,\n DocumentLinkSetMutationOperation,\n DocumentLinkSetTargetState,\n} from \"./types\";\n\nexport async function buildDocumentCreatePlan({\n author,\n containerProjection,\n contentKeyEpoch = 1,\n documentId = crypto.randomUUID(),\n eventId = crypto.randomUUID(),\n signedAt = new Date().toISOString(),\n targetEnvelopes,\n}: BuildDocumentCreatePlanInput): Promise<DocumentCreatePlan> {\n // The document belongs to its container's organization, not the author's. A\n // member authoring a document under another org's shared container creates a\n // document owned by that org; the author supplies only the signer identity.\n const organizationId = containerProjection.organizationId;\n\n const targets = deriveDocumentCreateTargets(containerProjection);\n const targetEnvelopesForRequest = mergeTargetEnvelopes(\n targets,\n targetEnvelopes,\n );\n const targetContainerManifestHash = targets[0]?.containerManifestHash;\n const targetContainerId = targets[0]?.containerId;\n if (!targetContainerManifestHash || !targetContainerId) {\n throw new Error(\"Document create target is unavailable\");\n }\n\n const body: DocumentLinkAccessEventBody = {\n blobRewraps: [],\n eventType: \"document.link\",\n containerId: targetContainerId,\n containerManifestHash: targetContainerManifestHash,\n };\n const bodyHash = await computeAccessEventBodyHash(\n readCanonicalJson(body, \"Document create body\"),\n );\n const unsignedEvent: UnsignedAccessEvent = {\n version: 1,\n eventId,\n eventType: \"document.link\",\n objectKind: \"document\",\n objectId: documentId,\n organizationId,\n previousManifestHash: null,\n dependencyManifestHashes: uniqueSortedStrings(\n containerProjection.path.map((bundle) => bundle.manifestHash),\n ),\n bodyHash,\n signerUserId: author.signerUserId,\n signerDeviceId: author.signerDeviceId,\n signerKeyFingerprint: author.signerKeyFingerprint,\n signedAt,\n };\n const event = await signAccessEvent(unsignedEvent, author.signerPrivateKey);\n const eventHash = await computeAccessEventHash(event);\n const state: DocumentLinkSetManifestState = {\n version: 1,\n documentId,\n organizationId,\n epoch: 1,\n previousManifestHash: null,\n eventHash,\n linkedContainerIds: [targetContainerId],\n };\n const manifest = await deriveDocumentLinkSetManifest(state);\n const manifestHash = await computeAccessManifestHash(manifest);\n const targetHash = await computeDocumentContentKeyTargetHash(targets);\n\n return {\n body,\n documentId,\n event,\n eventHash,\n manifest,\n manifestHash,\n request: {\n event: readCanonicalRecord(event, \"Document create event\"),\n body: readCanonicalRecord(body, \"Document create body\"),\n expectedManifestHash: manifestHash,\n manifest: readCanonicalRecord(manifest, \"Document create manifest\"),\n previousManifest: null,\n targetContainerPathRefs: containerPathRefs(containerProjection.path),\n contentKeyBundle: {\n contentKeyEpoch,\n linkSetManifestHash: manifestHash,\n targetHash,\n targets: targetEnvelopesForRequest,\n },\n },\n state,\n targetHash,\n targets: sortDocumentTargets(targets),\n };\n}\n\nexport async function buildDocumentLinkSetEventPlan(input: {\n blobRewraps: DocumentLinkAccessEventBody[\"blobRewraps\"];\n author: DocumentCreateAuthor;\n eventId: string;\n operation: DocumentLinkSetMutationOperation;\n // The document's existing organization (from its manifest). Link/unlink does\n // not change ownership, so the event keeps the document's org, which is the\n // container's org — never the acting member's personal org.\n organizationId: string;\n signedAt: string;\n targetContainerPathRefs: readonly ContainerManifestRef[];\n targetState: DocumentLinkSetTargetState;\n writerProjection: DocumentWriterProjectionResponse;\n}): Promise<DocumentLinkSetEventPlan> {\n const eventType =\n input.operation === \"link\" ? \"document.link\" : \"document.unlink\";\n const body: DocumentLinkSetMutationBody = {\n blobRewraps: input.blobRewraps,\n eventType,\n containerId: input.targetState.target.containerId,\n containerManifestHash: input.targetState.target.containerManifestHash,\n };\n const bodyHash = await computeAccessEventBodyHash(\n readCanonicalJson(body, \"Document link-set body\"),\n );\n const authorizingContainerPathRefs = authorizingContainerPathRefsForLinkSet({\n operation: input.operation,\n targetContainerId: input.targetState.target.containerId,\n writerProjection: input.writerProjection,\n });\n const dependencyManifestHashes = uniqueSortedStrings(\n [\n ...input.targetContainerPathRefs,\n ...authorizingContainerPathRefs.flat(),\n ].map((ref) => ref.manifestHash),\n );\n const unsignedEvent: UnsignedAccessEvent = {\n version: 1,\n eventId: input.eventId,\n eventType,\n objectKind: \"document\",\n objectId: input.writerProjection.documentId,\n organizationId: input.organizationId,\n previousManifestHash: input.writerProjection.documentManifest.manifestHash,\n dependencyManifestHashes,\n bodyHash,\n signerUserId: input.author.signerUserId,\n signerDeviceId: input.author.signerDeviceId,\n signerKeyFingerprint: input.author.signerKeyFingerprint,\n signedAt: input.signedAt,\n };\n const event = await signAccessEvent(\n unsignedEvent,\n input.author.signerPrivateKey,\n );\n const eventHash = await computeAccessEventHash(event);\n\n return {\n authorizingContainerPathRefs,\n body,\n event,\n eventHash,\n };\n}\n"]}
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["events.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n computeAccessEventBodyHash,\n computeAccessEventHash,\n computeAccessManifestHash,\n computeDocumentContentKeyTargetHash,\n type DocumentLinkAccessEventBody,\n type DocumentLinkSetManifestState,\n deriveDocumentLinkSetManifest,\n signAccessEvent,\n type UnsignedAccessEvent,\n} from \"@tearleads/crypto\";\nimport type { ContainerManifestRef } from \"@tearleads/validators/request\";\nimport type { DocumentWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport {\n readCanonicalJson,\n readCanonicalRecord,\n} from \"../../keyingCanonicalJson\";\nimport {\n authorizingContainerPathRefsForLinkSet,\n containerPathRefs,\n deriveDocumentCreateTargets,\n mergeTargetEnvelopes,\n} from \"./projection\";\nimport { sortDocumentTargets, uniqueSortedStrings } from \"./readers\";\nimport type {\n BuildDocumentCreatePlanInput,\n DocumentCreateAuthor,\n DocumentCreatePlan,\n DocumentLinkSetEventPlan,\n DocumentLinkSetMutationBody,\n DocumentLinkSetMutationOperation,\n DocumentLinkSetTargetState,\n} from \"./types\";\n\nexport async function buildDocumentCreatePlan({\n author,\n containerProjection,\n contentKeyEpoch = 1,\n documentId = crypto.randomUUID(),\n eventId = crypto.randomUUID(),\n signedAt = new Date().toISOString(),\n targetEnvelopes,\n}: BuildDocumentCreatePlanInput): Promise<DocumentCreatePlan> {\n // The document belongs to its container's organization, not the author's. A\n // member authoring a document under another org's shared container creates a\n // document owned by that org; the author supplies only the signer identity.\n const organizationId = containerProjection.organizationId;\n\n const targets = deriveDocumentCreateTargets(containerProjection);\n const targetEnvelopesForRequest = mergeTargetEnvelopes(\n targets,\n targetEnvelopes,\n );\n const targetContainerManifestHash = targets[0]?.containerManifestHash;\n const targetContainerId = targets[0]?.containerId;\n if (!targetContainerManifestHash || !targetContainerId) {\n throw new Error(\"Document create target is unavailable\");\n }\n\n const body: DocumentLinkAccessEventBody = {\n blobRewraps: [],\n eventType: \"document.link\",\n containerId: targetContainerId,\n containerManifestHash: targetContainerManifestHash,\n };\n const bodyHash = await computeAccessEventBodyHash(\n readCanonicalJson(body, \"Document create body\"),\n );\n const unsignedEvent: UnsignedAccessEvent = {\n version: 1,\n eventId,\n eventType: \"document.link\",\n objectKind: \"document\",\n objectId: documentId,\n organizationId,\n previousManifestHash: null,\n dependencyManifestHashes: uniqueSortedStrings(\n containerProjection.path.map((bundle) => bundle.manifestHash),\n ),\n bodyHash,\n signerUserId: author.signerUserId,\n signerDeviceId: author.signerDeviceId,\n signerKeyFingerprint: author.signerKeyFingerprint,\n signedAt,\n };\n const event = await signAccessEvent(unsignedEvent, author.signerPrivateKey);\n const eventHash = await computeAccessEventHash(event);\n const state: DocumentLinkSetManifestState = {\n version: 1,\n documentId,\n organizationId,\n epoch: 1,\n previousManifestHash: null,\n eventHash,\n linkedContainerIds: [targetContainerId],\n };\n const manifest = await deriveDocumentLinkSetManifest(state);\n const manifestHash = await computeAccessManifestHash(manifest);\n const targetHash = await computeDocumentContentKeyTargetHash(targets);\n\n return {\n body,\n documentId,\n event,\n eventHash,\n manifest,\n manifestHash,\n request: {\n event: readCanonicalRecord(event, \"Document create event\"),\n body: readCanonicalRecord(body, \"Document create body\"),\n expectedManifestHash: manifestHash,\n manifest: readCanonicalRecord(manifest, \"Document create manifest\"),\n targetContainerPathRefs: containerPathRefs(containerProjection.path),\n contentKeyBundle: {\n contentKeyEpoch,\n linkSetManifestHash: manifestHash,\n targetHash,\n targets: targetEnvelopesForRequest,\n },\n },\n state,\n targetHash,\n targets: sortDocumentTargets(targets),\n };\n}\n\nexport async function buildDocumentLinkSetEventPlan(input: {\n blobRewraps: DocumentLinkAccessEventBody[\"blobRewraps\"];\n author: DocumentCreateAuthor;\n eventId: string;\n operation: DocumentLinkSetMutationOperation;\n // The document's existing organization (from its manifest). Link/unlink does\n // not change ownership, so the event keeps the document's org, which is the\n // container's org — never the acting member's personal org.\n organizationId: string;\n signedAt: string;\n targetContainerPathRefs: readonly ContainerManifestRef[];\n targetState: DocumentLinkSetTargetState;\n writerProjection: DocumentWriterProjectionResponse;\n}): Promise<DocumentLinkSetEventPlan> {\n const eventType =\n input.operation === \"link\" ? \"document.link\" : \"document.unlink\";\n const body: DocumentLinkSetMutationBody = {\n blobRewraps: input.blobRewraps,\n eventType,\n containerId: input.targetState.target.containerId,\n containerManifestHash: input.targetState.target.containerManifestHash,\n };\n const bodyHash = await computeAccessEventBodyHash(\n readCanonicalJson(body, \"Document link-set body\"),\n );\n const authorizingContainerPathRefs = authorizingContainerPathRefsForLinkSet({\n operation: input.operation,\n targetContainerId: input.targetState.target.containerId,\n writerProjection: input.writerProjection,\n });\n const dependencyManifestHashes = uniqueSortedStrings(\n [\n ...input.targetContainerPathRefs,\n ...authorizingContainerPathRefs.flat(),\n ].map((ref) => ref.manifestHash),\n );\n const unsignedEvent: UnsignedAccessEvent = {\n version: 1,\n eventId: input.eventId,\n eventType,\n objectKind: \"document\",\n objectId: input.writerProjection.documentId,\n organizationId: input.organizationId,\n previousManifestHash: input.writerProjection.documentManifest.manifestHash,\n dependencyManifestHashes,\n bodyHash,\n signerUserId: input.author.signerUserId,\n signerDeviceId: input.author.signerDeviceId,\n signerKeyFingerprint: input.author.signerKeyFingerprint,\n signedAt: input.signedAt,\n };\n const event = await signAccessEvent(\n unsignedEvent,\n input.author.signerPrivateKey,\n );\n const eventHash = await computeAccessEventHash(event);\n\n return {\n authorizingContainerPathRefs,\n body,\n event,\n eventHash,\n };\n}\n"]}
@@ -1,12 +1,12 @@
1
- import { and, eq, inArray } from "drizzle-orm";
1
+ import { eq } from "drizzle-orm";
2
+ import { hasQueuedDocumentUpdates, settleQueuedDocumentUpdatesInTransaction, } from "../../sqlite/documentPendingUpdatePersistence.js";
2
3
  import { enqueueDocumentPendingUpdate, ensureDocumentProjectionTables, ensureDocumentTables, } from "../../sqlite/documentPersistence.js";
3
- import { containerCreateIntentTables, containerHydrationTombstones, containerMoveIntentTables, containers, documentContainerProjectionTables, documentPendingUpdates, } from "../../sqlite/schema.js";
4
+ import { containerCreateIntentTables, containerHydrationTombstones, containerMoveIntentTables, containers, documentContainerProjectionTables, } from "../../sqlite/schema.js";
4
5
  import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
5
6
  import { ensureSqlTables, runOncePerConnection, runSerializedSqlMutation, } from "../../sqlite/sqlSchema.js";
6
7
  import { ensureContainerTables, loadContainerById, loadContainers as loadContainerRecords, } from "../containers/containerPersistence.js";
7
8
  import { sqlContainerSyncWatermarkPersistence } from "../containers/containerSyncWatermarkPersistence.js";
8
9
  import { getLatestTimestamp } from "../latestTimestamp.js";
9
- import { CONTAINER_METADATA_APP_KIND } from "./dormantContainerMetadata.js";
10
10
  import { claimDormantMetadataSweepAttempt, completeDormantMetadataSweepRequest, listDormantMetadataSweepRequests, } from "./dormantMetadataSweep.js";
11
11
  export { CONTAINER_METADATA_APP_KIND } from "./dormantContainerMetadata.js";
12
12
  import { deleteStoredContainers } from "./containerDeletionPersistence.js";
@@ -49,14 +49,6 @@ async function saveStoredContainer(execSql, container, record, options) {
49
49
  return outcome.result ?? container;
50
50
  });
51
51
  }
52
- async function hasPendingContainerMetadataUpdates(input) {
53
- const rows = await input.tx
54
- .select({ id: documentPendingUpdates.id })
55
- .from(documentPendingUpdates)
56
- .where(and(eq(documentPendingUpdates.appKind, CONTAINER_METADATA_APP_KIND), eq(documentPendingUpdates.localId, input.containerId)))
57
- .limit(1);
58
- return rows.length > 0;
59
- }
60
52
  export const sqlContainerContentsPersistence = {
61
53
  ...containerIntentPersistence,
62
54
  ...containerMetadataPullContinuationPersistence,
@@ -166,20 +158,12 @@ export const sqlContainerContentsPersistence = {
166
158
  return saveStoredContainer(execSql, container, record, options);
167
159
  },
168
160
  async saveContainerAndDeletePendingUpdates(execSql, container, record, pendingUpdateIds) {
169
- const uniquePendingUpdateIds = [...new Set(pendingUpdateIds)];
170
161
  return runSerializedSqlMutation(execSql, async (lockedExecSql) => {
171
162
  const localUpdatedAt = new Date().toISOString();
172
163
  return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(async (tx) => {
173
- if (uniquePendingUpdateIds.length > 0) {
174
- await tx
175
- .delete(documentPendingUpdates)
176
- .where(and(eq(documentPendingUpdates.appKind, CONTAINER_METADATA_APP_KIND), eq(documentPendingUpdates.localId, container.id), inArray(documentPendingUpdates.id, uniquePendingUpdateIds)))
177
- .run();
178
- }
179
- const hasRemainingPendingUpdates = await hasPendingContainerMetadataUpdates({
180
- containerId: container.id,
181
- tx,
182
- });
164
+ const scope = getContainerMetadataScope(container.id);
165
+ await settleQueuedDocumentUpdatesInTransaction(tx, scope, pendingUpdateIds);
166
+ const hasRemainingPendingUpdates = await hasQueuedDocumentUpdates(tx, scope);
183
167
  const containerToSave = hasRemainingPendingUpdates
184
168
  ? container
185
169
  : {
@@ -1 +1 @@
1
- {"version":3,"file":"containerContentsPersistence.js","sourceRoot":"","sources":["containerContentsPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, inArray } from \"drizzle-orm\";\nimport {\n enqueueDocumentPendingUpdate,\n ensureDocumentProjectionTables,\n ensureDocumentTables,\n} from \"../../sqlite/documentPersistence\";\nimport {\n containerCreateIntentTables,\n containerHydrationTombstones,\n containerMoveIntentTables,\n containers,\n documentContainerProjectionTables,\n documentPendingUpdates,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n ensureSqlTables,\n runOncePerConnection,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport {\n ensureContainerTables,\n loadContainerById,\n loadContainers as loadContainerRecords,\n} from \"../containers/containerPersistence\";\nimport { sqlContainerSyncWatermarkPersistence } from \"../containers/containerSyncWatermarkPersistence\";\nimport { getLatestTimestamp } from \"../latestTimestamp\";\nimport { CONTAINER_METADATA_APP_KIND } from \"./dormantContainerMetadata\";\nimport {\n claimDormantMetadataSweepAttempt,\n completeDormantMetadataSweepRequest,\n listDormantMetadataSweepRequests,\n} from \"./dormantMetadataSweep\";\n\nexport type {\n ContainerContentsPersistence,\n ContainerCreateIntentRecord,\n ContainerHydrationTombstone,\n ContainerMetadataRecord,\n ContainerMoveIntentRecord,\n LocalRootDescendantReparentInput,\n StoredContainerState,\n} from \"./containerContentsPersistenceTypes\";\nexport { CONTAINER_METADATA_APP_KIND } from \"./dormantContainerMetadata\";\n\nimport type {\n ContainerContentsPersistence,\n ContainerMetadataRecord,\n SaveContainerOptions,\n SaveContainerWithPendingUpdateOptions,\n} from \"./containerContentsPersistenceTypes\";\nimport { deleteStoredContainers } from \"./containerDeletionPersistence\";\nimport {\n commitStoredHydratedContainer,\n loadStoredHeldContainerBinding,\n} from \"./containerHydrationPersistence\";\nimport { containerIntentPersistence } from \"./containerIntentPersistence\";\nimport {\n commitStoredMetadataMutation,\n settleStoredMetadataPendingUpdates,\n} from \"./containerMetadataMutationPersistence\";\nimport { containerMetadataPullContinuationPersistence } from \"./containerMetadataPullContinuationPersistence\";\nimport {\n getContainerMetadataScope,\n saveContainerContentsContainerRows,\n selectContainerMetadataRecord,\n} from \"./containerMetadataRows\";\nimport { abandonStoredMoveIntentRevision } from \"./containerMoveAbandonPersistence\";\nimport { containerPendingUpdatePersistence } from \"./containerPendingUpdatePersistence\";\nimport { rebindStoredHeldContainer } from \"./containerRebindPersistence\";\nimport { containerReconcilePersistence } from \"./containerReconcilePersistence\";\nimport { isSupersededContainerBinding } from \"./supersededContainerBindings\";\n\nasync function saveStoredContainer(\n execSql: Parameters<ContainerContentsPersistence[\"saveContainer\"]>[0],\n container: Parameters<ContainerContentsPersistence[\"saveContainer\"]>[1],\n record: ContainerMetadataRecord | null,\n options?:\n | SaveContainerOptions\n | SaveContainerWithPendingUpdateOptions\n | undefined,\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const localUpdatedAt =\n options?.localUpdatedAt ?? options?.updatedAt ?? new Date().toISOString();\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n const save = async (tx: ClientSQLiteTransactionScope) => {\n const saved = await saveContainerContentsContainerRows({\n container,\n createIntent: options?.createIntent,\n moveIntent: options?.moveIntent,\n record,\n serverTimestamps: options?.serverTimestamps,\n tx,\n localUpdatedAt,\n });\n if (options && \"pendingUpdate\" in options) {\n await enqueueDocumentPendingUpdate(\n lockedExecSql,\n getContainerMetadataScope(container.id),\n options.pendingUpdate,\n );\n }\n return saved;\n };\n if (!options?.stillCurrent) {\n return runtime.transaction(save);\n }\n const outcome = await runtime.guardedTransaction(\n save,\n options.stillCurrent,\n { behavior: \"immediate\" },\n );\n // Guarded callers recheck the same monotonic generation immediately after\n // this await. Returning the untouched candidate keeps the long-standing\n // non-null save contract while the transaction itself remains rolled back.\n return outcome.result ?? container;\n });\n}\n\nasync function hasPendingContainerMetadataUpdates(input: {\n tx: ClientSQLiteTransactionScope;\n containerId: string;\n}): Promise<boolean> {\n const rows = await input.tx\n .select({ id: documentPendingUpdates.id })\n .from(documentPendingUpdates)\n .where(\n and(\n eq(documentPendingUpdates.appKind, CONTAINER_METADATA_APP_KIND),\n eq(documentPendingUpdates.localId, input.containerId),\n ),\n )\n .limit(1);\n\n return rows.length > 0;\n}\n\nexport const sqlContainerContentsPersistence: ContainerContentsPersistence = {\n ...containerIntentPersistence,\n ...containerMetadataPullContinuationPersistence,\n commitHydratedContainer: commitStoredHydratedContainer,\n commitMetadataMutation: commitStoredMetadataMutation,\n settleAcceptedMetadataPendingUpdates: settleStoredMetadataPendingUpdates,\n listDormantMetadataSweepRequests,\n ...containerPendingUpdatePersistence,\n ...containerReconcilePersistence,\n claimDormantMetadataSweepAttempt,\n completeDormantMetadataSweepRequest,\n async containerExists(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({ id: containers.id })\n .from(containers)\n .where(eq(containers.id, containerId))\n .limit(1);\n return rows.length > 0;\n },\n async deleteContainer(execSql, containerId, options) {\n await sqlContainerContentsPersistence.deleteContainers(execSql, [\n {\n containerId,\n reason: options?.reason ?? \"deleted\",\n updatedAt: options?.updatedAt ?? new Date().toISOString(),\n },\n ]);\n },\n async deleteContainers(execSql, removals, options) {\n return deleteStoredContainers(execSql, removals, options);\n },\n async ensureSchema(execSql) {\n // Once ensured on this connection, skip the outer mutation lock entirely:\n // ensureSchema runs on every query path, and re-acquiring the lock just to\n // no-op would queue reads behind unrelated writes.\n await runOncePerConnection(execSql, \"ensure:container-contents\", () =>\n runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureContainerTables(lockedExecSql);\n await ensureDocumentTables(lockedExecSql);\n await ensureSqlTables(lockedExecSql, containerCreateIntentTables);\n await ensureSqlTables(lockedExecSql, containerMoveIntentTables);\n await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);\n await ensureDocumentProjectionTables(lockedExecSql);\n await sqlContainerSyncWatermarkPersistence.ensureSchema(lockedExecSql);\n }),\n );\n },\n async loadHeldContainerBinding(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return loadStoredHeldContainerBinding(execSql, containerId);\n },\n async isSupersededContainerBinding(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return isSupersededContainerBinding(execSql, input);\n },\n async abandonMoveIntentRevision(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return abandonStoredMoveIntentRevision(execSql, input);\n },\n async rebindHeldContainer(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return rebindStoredHeldContainer(execSql, input);\n },\n async loadContainerMetadataRecord(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return selectContainerMetadataRecord(execSql, containerId);\n },\n async loadContainerHydrationTombstones(execSql) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n containerId: containerHydrationTombstones.containerId,\n generation: containerHydrationTombstones.generation,\n reason: containerHydrationTombstones.reason,\n updatedAt: containerHydrationTombstones.updatedAt,\n })\n .from(containerHydrationTombstones)\n .where(eq(containerHydrationTombstones.cleared, false));\n return rows.flatMap((row) =>\n row.reason === \"access_revoked\" || row.reason === \"deleted\"\n ? [{ ...row, reason: row.reason }]\n : [],\n );\n },\n async loadContainers(execSql) {\n const containers = await loadContainerRecords(execSql);\n const storedContainers = await Promise.all(\n containers.map(async (container) => ({\n container,\n record: await selectContainerMetadataRecord(execSql, container.id),\n })),\n );\n\n return storedContainers;\n },\n async loadContainerMetadataState(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const container = await loadContainerById(execSql, containerId);\n if (!container) {\n return null;\n }\n return {\n container,\n record: await selectContainerMetadataRecord(execSql, containerId),\n };\n },\n async saveContainer(execSql, container, record, options) {\n return saveStoredContainer(execSql, container, record, options);\n },\n async saveContainerWithPendingUpdate(execSql, container, record, options) {\n return saveStoredContainer(execSql, container, record, options);\n },\n async saveContainerAndDeletePendingUpdates(\n execSql,\n container,\n record,\n pendingUpdateIds,\n ) {\n const uniquePendingUpdateIds = [...new Set(pendingUpdateIds)];\n\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const localUpdatedAt = new Date().toISOString();\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n if (uniquePendingUpdateIds.length > 0) {\n await tx\n .delete(documentPendingUpdates)\n .where(\n and(\n eq(\n documentPendingUpdates.appKind,\n CONTAINER_METADATA_APP_KIND,\n ),\n eq(documentPendingUpdates.localId, container.id),\n inArray(documentPendingUpdates.id, uniquePendingUpdateIds),\n ),\n )\n .run();\n }\n\n const hasRemainingPendingUpdates =\n await hasPendingContainerMetadataUpdates({\n containerId: container.id,\n tx,\n });\n const containerToSave = hasRemainingPendingUpdates\n ? container\n : {\n ...container,\n serverUpdatedAt: getLatestTimestamp(\n container.serverUpdatedAt,\n localUpdatedAt,\n ),\n };\n\n return saveContainerContentsContainerRows({\n container: containerToSave,\n record,\n tx,\n localUpdatedAt,\n });\n },\n );\n });\n },\n};\n"]}
1
+ {"version":3,"file":"containerContentsPersistence.js","sourceRoot":"","sources":["containerContentsPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EACL,wBAAwB,EACxB,wCAAwC,GACzC,MAAM,+CAA+C,CAAC;AACvD,OAAO,EACL,4BAA4B,EAC5B,8BAA8B,EAC9B,oBAAoB,GACrB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,2BAA2B,EAC3B,4BAA4B,EAC5B,yBAAyB,EACzB,UAAU,EACV,iCAAiC,GAClC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEL,iCAAiC,GAClC,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,IAAI,oBAAoB,GACvC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,oCAAoC,EAAE,MAAM,iDAAiD,CAAC;AACvG,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EACL,gCAAgC,EAChC,mCAAmC,EACnC,gCAAgC,GACjC,MAAM,wBAAwB,CAAC;AAWhC,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAQzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EACL,6BAA6B,EAC7B,8BAA8B,GAC/B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EACL,4BAA4B,EAC5B,kCAAkC,GACnC,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,4CAA4C,EAAE,MAAM,gDAAgD,CAAC;AAC9G,OAAO,EACL,yBAAyB,EACzB,kCAAkC,EAClC,6BAA6B,GAC9B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,+BAA+B,EAAE,MAAM,mCAAmC,CAAC;AACpF,OAAO,EAAE,iCAAiC,EAAE,MAAM,qCAAqC,CAAC;AACxF,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAE7E,KAAK,UAAU,mBAAmB,CAChC,OAAqE,EACrE,SAAuE,EACvE,MAAsC,EACtC,OAGa;IAEb,OAAO,wBAAwB,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE;QAC/D,MAAM,cAAc,GAClB,OAAO,EAAE,cAAc,IAAI,OAAO,EAAE,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC5E,MAAM,OAAO,GAAG,iCAAiC,CAAC,aAAa,CAAC,CAAC;QACjE,MAAM,IAAI,GAAG,KAAK,EAAE,EAAgC,EAAE,EAAE;YACtD,MAAM,KAAK,GAAG,MAAM,kCAAkC,CAAC;gBACrD,SAAS;gBACT,YAAY,EAAE,OAAO,EAAE,YAAY;gBACnC,UAAU,EAAE,OAAO,EAAE,UAAU;gBAC/B,MAAM;gBACN,gBAAgB,EAAE,OAAO,EAAE,gBAAgB;gBAC3C,EAAE;gBACF,cAAc;aACf,CAAC,CAAC;YACH,IAAI,OAAO,IAAI,eAAe,IAAI,OAAO,EAAE,CAAC;gBAC1C,MAAM,4BAA4B,CAChC,aAAa,EACb,yBAAyB,CAAC,SAAS,CAAC,EAAE,CAAC,EACvC,OAAO,CAAC,aAAa,CACtB,CAAC;YACJ,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC,CAAC;QACF,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,CAAC;YAC3B,OAAO,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,kBAAkB,CAC9C,IAAI,EACJ,OAAO,CAAC,YAAY,EACpB,EAAE,QAAQ,EAAE,WAAW,EAAE,CAC1B,CAAC;QACF,0EAA0E;QAC1E,wEAAwE;QACxE,2EAA2E;QAC3E,OAAO,OAAO,CAAC,MAAM,IAAI,SAAS,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,+BAA+B,GAAiC;IAC3E,GAAG,0BAA0B;IAC7B,GAAG,4CAA4C;IAC/C,uBAAuB,EAAE,6BAA6B;IACtD,sBAAsB,EAAE,4BAA4B;IACpD,oCAAoC,EAAE,kCAAkC;IACxE,gCAAgC;IAChC,GAAG,iCAAiC;IACpC,GAAG,6BAA6B;IAChC,gCAAgC;IAChC,mCAAmC;IACnC,KAAK,CAAC,eAAe,CAAC,OAAO,EAAE,WAAW;QACxC,MAAM,+BAA+B,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAC5D,MAAM,EAAE,EAAE,EAAE,GAAG,iCAAiC,CAAC,OAAO,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,MAAM,EAAE;aAClB,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC;aAC7B,IAAI,CAAC,UAAU,CAAC;aAChB,KAAK,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;aACrC,KAAK,CAAC,CAAC,CAAC,CAAC;QACZ,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IACzB,CAAC;IACD,KAAK,CAAC,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO;QACjD,MAAM,+BAA+B,CAAC,gBAAgB,CAAC,OAAO,EAAE;YAC9D;gBACE,WAAW;gBACX,MAAM,EAAE,OAAO,EAAE,MAAM,IAAI,SAAS;gBACpC,SAAS,EAAE,OAAO,EAAE,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aAC1D;SACF,CAAC,CAAC;IACL,CAAC;IACD,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO;QAC/C,OAAO,sBAAsB,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC5D,CAAC;IACD,KAAK,CAAC,YAAY,CAAC,OAAO;QACxB,0EAA0E;QAC1E,2EAA2E;QAC3E,mDAAmD;QACnD,MAAM,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,EAAE,GAAG,EAAE,CACpE,wBAAwB,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE;YACxD,MAAM,qBAAqB,CAAC,aAAa,CAAC,CAAC;YAC3C,MAAM,oBAAoB,CAAC,aAAa,CAAC,CAAC;YAC1C,MAAM,eAAe,CAAC,aAAa,EAAE,2BAA2B,CAAC,CAAC;YAClE,MAAM,eAAe,CAAC,aAAa,EAAE,yBAAyB,CAAC,CAAC;YAChE,MAAM,eAAe,CAAC,aAAa,EAAE,iCAAiC,CAAC,CAAC;YACxE,MAAM,8BAA8B,CAAC,aAAa,CAAC,CAAC;YACpD,MAAM,oCAAoC,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;QACzE,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,wBAAwB,CAAC,OAAO,EAAE,WAAW;QACjD,MAAM,+BAA+B,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAC5D,OAAO,8BAA8B,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC9D,CAAC;IACD,KAAK,CAAC,4BAA4B,CAAC,OAAO,EAAE,KAAK;QAC/C,MAAM,+BAA+B,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAC5D,OAAO,4BAA4B,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACtD,CAAC;IACD,KAAK,CAAC,yBAAyB,CAAC,OAAO,EAAE,KAAK;QAC5C,MAAM,+BAA+B,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAC5D,OAAO,+BAA+B,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACzD,CAAC;IACD,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK;QACtC,MAAM,+BAA+B,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAC5D,OAAO,yBAAyB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACnD,CAAC;IACD,KAAK,CAAC,2BAA2B,CAAC,OAAO,EAAE,WAAW;QACpD,MAAM,+BAA+B,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAC5D,OAAO,6BAA6B,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC7D,CAAC;IACD,KAAK,CAAC,gCAAgC,CAAC,OAAO;QAC5C,MAAM,+BAA+B,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAC5D,MAAM,EAAE,EAAE,EAAE,GAAG,iCAAiC,CAAC,OAAO,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,MAAM,EAAE;aAClB,MAAM,CAAC;YACN,WAAW,EAAE,4BAA4B,CAAC,WAAW;YACrD,UAAU,EAAE,4BAA4B,CAAC,UAAU;YACnD,MAAM,EAAE,4BAA4B,CAAC,MAAM;YAC3C,SAAS,EAAE,4BAA4B,CAAC,SAAS;SAClD,CAAC;aACD,IAAI,CAAC,4BAA4B,CAAC;aAClC,KAAK,CAAC,EAAE,CAAC,4BAA4B,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAC1B,GAAG,CAAC,MAAM,KAAK,gBAAgB,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;YACzD,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;YAClC,CAAC,CAAC,EAAE,CACP,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,cAAc,CAAC,OAAO;QAC1B,MAAM,UAAU,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAC;QACvD,MAAM,gBAAgB,GAAG,MAAM,OAAO,CAAC,GAAG,CACxC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;YACnC,SAAS;YACT,MAAM,EAAE,MAAM,6BAA6B,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;SACnE,CAAC,CAAC,CACJ,CAAC;QAEF,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IACD,KAAK,CAAC,0BAA0B,CAAC,OAAO,EAAE,WAAW;QACnD,MAAM,+BAA+B,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAC5D,MAAM,SAAS,GAAG,MAAM,iBAAiB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QAChE,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO;YACL,SAAS;YACT,MAAM,EAAE,MAAM,6BAA6B,CAAC,OAAO,EAAE,WAAW,CAAC;SAClE,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO;QACrD,OAAO,mBAAmB,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAClE,CAAC;IACD,KAAK,CAAC,8BAA8B,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO;QACtE,OAAO,mBAAmB,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAClE,CAAC;IACD,KAAK,CAAC,oCAAoC,CACxC,OAAO,EACP,SAAS,EACT,MAAM,EACN,gBAAgB;QAEhB,OAAO,wBAAwB,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE;YAC/D,MAAM,cAAc,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAChD,OAAO,iCAAiC,CAAC,aAAa,CAAC,CAAC,WAAW,CACjE,KAAK,EAAE,EAAE,EAAE,EAAE;gBACX,MAAM,KAAK,GAAG,yBAAyB,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;gBACtD,MAAM,wCAAwC,CAC5C,EAAE,EACF,KAAK,EACL,gBAAgB,CACjB,CAAC;gBACF,MAAM,0BAA0B,GAAG,MAAM,wBAAwB,CAC/D,EAAE,EACF,KAAK,CACN,CAAC;gBACF,MAAM,eAAe,GAAG,0BAA0B;oBAChD,CAAC,CAAC,SAAS;oBACX,CAAC,CAAC;wBACE,GAAG,SAAS;wBACZ,eAAe,EAAE,kBAAkB,CACjC,SAAS,CAAC,eAAe,EACzB,cAAc,CACf;qBACF,CAAC;gBAEN,OAAO,kCAAkC,CAAC;oBACxC,SAAS,EAAE,eAAe;oBAC1B,MAAM;oBACN,EAAE;oBACF,cAAc;iBACf,CAAC,CAAC;YACL,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAC","sourcesContent":["import { eq } from \"drizzle-orm\";\nimport {\n hasQueuedDocumentUpdates,\n settleQueuedDocumentUpdatesInTransaction,\n} from \"../../sqlite/documentPendingUpdatePersistence\";\nimport {\n enqueueDocumentPendingUpdate,\n ensureDocumentProjectionTables,\n ensureDocumentTables,\n} from \"../../sqlite/documentPersistence\";\nimport {\n containerCreateIntentTables,\n containerHydrationTombstones,\n containerMoveIntentTables,\n containers,\n documentContainerProjectionTables,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n ensureSqlTables,\n runOncePerConnection,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport {\n ensureContainerTables,\n loadContainerById,\n loadContainers as loadContainerRecords,\n} from \"../containers/containerPersistence\";\nimport { sqlContainerSyncWatermarkPersistence } from \"../containers/containerSyncWatermarkPersistence\";\nimport { getLatestTimestamp } from \"../latestTimestamp\";\nimport {\n claimDormantMetadataSweepAttempt,\n completeDormantMetadataSweepRequest,\n listDormantMetadataSweepRequests,\n} from \"./dormantMetadataSweep\";\n\nexport type {\n ContainerContentsPersistence,\n ContainerCreateIntentRecord,\n ContainerHydrationTombstone,\n ContainerMetadataRecord,\n ContainerMoveIntentRecord,\n LocalRootDescendantReparentInput,\n StoredContainerState,\n} from \"./containerContentsPersistenceTypes\";\nexport { CONTAINER_METADATA_APP_KIND } from \"./dormantContainerMetadata\";\n\nimport type {\n ContainerContentsPersistence,\n ContainerMetadataRecord,\n SaveContainerOptions,\n SaveContainerWithPendingUpdateOptions,\n} from \"./containerContentsPersistenceTypes\";\nimport { deleteStoredContainers } from \"./containerDeletionPersistence\";\nimport {\n commitStoredHydratedContainer,\n loadStoredHeldContainerBinding,\n} from \"./containerHydrationPersistence\";\nimport { containerIntentPersistence } from \"./containerIntentPersistence\";\nimport {\n commitStoredMetadataMutation,\n settleStoredMetadataPendingUpdates,\n} from \"./containerMetadataMutationPersistence\";\nimport { containerMetadataPullContinuationPersistence } from \"./containerMetadataPullContinuationPersistence\";\nimport {\n getContainerMetadataScope,\n saveContainerContentsContainerRows,\n selectContainerMetadataRecord,\n} from \"./containerMetadataRows\";\nimport { abandonStoredMoveIntentRevision } from \"./containerMoveAbandonPersistence\";\nimport { containerPendingUpdatePersistence } from \"./containerPendingUpdatePersistence\";\nimport { rebindStoredHeldContainer } from \"./containerRebindPersistence\";\nimport { containerReconcilePersistence } from \"./containerReconcilePersistence\";\nimport { isSupersededContainerBinding } from \"./supersededContainerBindings\";\n\nasync function saveStoredContainer(\n execSql: Parameters<ContainerContentsPersistence[\"saveContainer\"]>[0],\n container: Parameters<ContainerContentsPersistence[\"saveContainer\"]>[1],\n record: ContainerMetadataRecord | null,\n options?:\n | SaveContainerOptions\n | SaveContainerWithPendingUpdateOptions\n | undefined,\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const localUpdatedAt =\n options?.localUpdatedAt ?? options?.updatedAt ?? new Date().toISOString();\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n const save = async (tx: ClientSQLiteTransactionScope) => {\n const saved = await saveContainerContentsContainerRows({\n container,\n createIntent: options?.createIntent,\n moveIntent: options?.moveIntent,\n record,\n serverTimestamps: options?.serverTimestamps,\n tx,\n localUpdatedAt,\n });\n if (options && \"pendingUpdate\" in options) {\n await enqueueDocumentPendingUpdate(\n lockedExecSql,\n getContainerMetadataScope(container.id),\n options.pendingUpdate,\n );\n }\n return saved;\n };\n if (!options?.stillCurrent) {\n return runtime.transaction(save);\n }\n const outcome = await runtime.guardedTransaction(\n save,\n options.stillCurrent,\n { behavior: \"immediate\" },\n );\n // Guarded callers recheck the same monotonic generation immediately after\n // this await. Returning the untouched candidate keeps the long-standing\n // non-null save contract while the transaction itself remains rolled back.\n return outcome.result ?? container;\n });\n}\n\nexport const sqlContainerContentsPersistence: ContainerContentsPersistence = {\n ...containerIntentPersistence,\n ...containerMetadataPullContinuationPersistence,\n commitHydratedContainer: commitStoredHydratedContainer,\n commitMetadataMutation: commitStoredMetadataMutation,\n settleAcceptedMetadataPendingUpdates: settleStoredMetadataPendingUpdates,\n listDormantMetadataSweepRequests,\n ...containerPendingUpdatePersistence,\n ...containerReconcilePersistence,\n claimDormantMetadataSweepAttempt,\n completeDormantMetadataSweepRequest,\n async containerExists(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({ id: containers.id })\n .from(containers)\n .where(eq(containers.id, containerId))\n .limit(1);\n return rows.length > 0;\n },\n async deleteContainer(execSql, containerId, options) {\n await sqlContainerContentsPersistence.deleteContainers(execSql, [\n {\n containerId,\n reason: options?.reason ?? \"deleted\",\n updatedAt: options?.updatedAt ?? new Date().toISOString(),\n },\n ]);\n },\n async deleteContainers(execSql, removals, options) {\n return deleteStoredContainers(execSql, removals, options);\n },\n async ensureSchema(execSql) {\n // Once ensured on this connection, skip the outer mutation lock entirely:\n // ensureSchema runs on every query path, and re-acquiring the lock just to\n // no-op would queue reads behind unrelated writes.\n await runOncePerConnection(execSql, \"ensure:container-contents\", () =>\n runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureContainerTables(lockedExecSql);\n await ensureDocumentTables(lockedExecSql);\n await ensureSqlTables(lockedExecSql, containerCreateIntentTables);\n await ensureSqlTables(lockedExecSql, containerMoveIntentTables);\n await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);\n await ensureDocumentProjectionTables(lockedExecSql);\n await sqlContainerSyncWatermarkPersistence.ensureSchema(lockedExecSql);\n }),\n );\n },\n async loadHeldContainerBinding(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return loadStoredHeldContainerBinding(execSql, containerId);\n },\n async isSupersededContainerBinding(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return isSupersededContainerBinding(execSql, input);\n },\n async abandonMoveIntentRevision(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return abandonStoredMoveIntentRevision(execSql, input);\n },\n async rebindHeldContainer(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return rebindStoredHeldContainer(execSql, input);\n },\n async loadContainerMetadataRecord(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return selectContainerMetadataRecord(execSql, containerId);\n },\n async loadContainerHydrationTombstones(execSql) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n containerId: containerHydrationTombstones.containerId,\n generation: containerHydrationTombstones.generation,\n reason: containerHydrationTombstones.reason,\n updatedAt: containerHydrationTombstones.updatedAt,\n })\n .from(containerHydrationTombstones)\n .where(eq(containerHydrationTombstones.cleared, false));\n return rows.flatMap((row) =>\n row.reason === \"access_revoked\" || row.reason === \"deleted\"\n ? [{ ...row, reason: row.reason }]\n : [],\n );\n },\n async loadContainers(execSql) {\n const containers = await loadContainerRecords(execSql);\n const storedContainers = await Promise.all(\n containers.map(async (container) => ({\n container,\n record: await selectContainerMetadataRecord(execSql, container.id),\n })),\n );\n\n return storedContainers;\n },\n async loadContainerMetadataState(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const container = await loadContainerById(execSql, containerId);\n if (!container) {\n return null;\n }\n return {\n container,\n record: await selectContainerMetadataRecord(execSql, containerId),\n };\n },\n async saveContainer(execSql, container, record, options) {\n return saveStoredContainer(execSql, container, record, options);\n },\n async saveContainerWithPendingUpdate(execSql, container, record, options) {\n return saveStoredContainer(execSql, container, record, options);\n },\n async saveContainerAndDeletePendingUpdates(\n execSql,\n container,\n record,\n pendingUpdateIds,\n ) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const localUpdatedAt = new Date().toISOString();\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const scope = getContainerMetadataScope(container.id);\n await settleQueuedDocumentUpdatesInTransaction(\n tx,\n scope,\n pendingUpdateIds,\n );\n const hasRemainingPendingUpdates = await hasQueuedDocumentUpdates(\n tx,\n scope,\n );\n const containerToSave = hasRemainingPendingUpdates\n ? container\n : {\n ...container,\n serverUpdatedAt: getLatestTimestamp(\n container.serverUpdatedAt,\n localUpdatedAt,\n ),\n };\n\n return saveContainerContentsContainerRows({\n container: containerToSave,\n record,\n tx,\n localUpdatedAt,\n });\n },\n );\n });\n },\n};\n"]}
@@ -1,8 +1,9 @@
1
- import { and, eq, inArray } from "drizzle-orm";
1
+ import { and, eq } from "drizzle-orm";
2
2
  import { assertHeldContainerBinding, heldContainerBinding, } from "../../containers/containerBinding.js";
3
3
  import { documentSyncPullContinuationsEqual } from "../../documents/shared/pullContinuation.js";
4
+ import { hasQueuedDocumentUpdates, settleQueuedDocumentUpdatesInTransaction, } from "../../sqlite/documentPendingUpdatePersistence.js";
4
5
  import { enqueueDocumentPendingUpdate, } from "../../sqlite/documentPersistence.js";
5
- import { containerCreateIntents, containerMoveIntents, documentPendingUpdates, documentSyncFailures, } from "../../sqlite/schema.js";
6
+ import { containerCreateIntents, containerMoveIntents, documentSyncFailures, } from "../../sqlite/schema.js";
6
7
  import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
7
8
  import { runSerializedSqlMutation } from "../../sqlite/sqlSchema.js";
8
9
  import { loadContainerById, } from "../containers/containerPersistence.js";
@@ -65,13 +66,7 @@ async function loadStoredContainerState(execSql, containerId) {
65
66
  };
66
67
  }
67
68
  async function deleteAcceptedPendingUpdates(tx, containerId, ids) {
68
- const uniqueIds = [...new Set(ids)];
69
- if (uniqueIds.length === 0)
70
- return;
71
- await tx
72
- .delete(documentPendingUpdates)
73
- .where(and(eq(documentPendingUpdates.appKind, CONTAINER_METADATA_APP_KIND), eq(documentPendingUpdates.localId, containerId), inArray(documentPendingUpdates.id, uniqueIds)))
74
- .run();
69
+ await settleQueuedDocumentUpdatesInTransaction(tx, getContainerMetadataScope(containerId), ids);
75
70
  }
76
71
  async function clearSyncFailure(tx, containerId) {
77
72
  await tx
@@ -80,12 +75,7 @@ async function clearSyncFailure(tx, containerId) {
80
75
  .run();
81
76
  }
82
77
  async function hasPendingUpdates(tx, containerId) {
83
- const rows = await tx
84
- .select({ id: documentPendingUpdates.id })
85
- .from(documentPendingUpdates)
86
- .where(and(eq(documentPendingUpdates.appKind, CONTAINER_METADATA_APP_KIND), eq(documentPendingUpdates.localId, containerId)))
87
- .limit(1);
88
- return rows.length > 0;
78
+ return hasQueuedDocumentUpdates(tx, getContainerMetadataScope(containerId));
89
79
  }
90
80
  async function hasPendingStructuralIntent(tx, containerId) {
91
81
  const pendingCreates = await tx