@farukada/aws-langgraph-dynamodb-ts 0.3.2 → 0.6.0

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 (148) hide show
  1. package/README.md +10 -3
  2. package/dist/checkpointer/actions/delete-thread.js +1 -1
  3. package/dist/checkpointer/actions/delete-thread.js.map +1 -1
  4. package/dist/checkpointer/actions/list.d.ts.map +1 -1
  5. package/dist/checkpointer/actions/list.js +3 -1
  6. package/dist/checkpointer/actions/list.js.map +1 -1
  7. package/dist/checkpointer/actions/put-writes.d.ts +4 -4
  8. package/dist/checkpointer/actions/put-writes.d.ts.map +1 -1
  9. package/dist/checkpointer/actions/put-writes.js +21 -20
  10. package/dist/checkpointer/actions/put-writes.js.map +1 -1
  11. package/dist/checkpointer/internal/item-writer.d.ts +18 -9
  12. package/dist/checkpointer/internal/item-writer.d.ts.map +1 -1
  13. package/dist/checkpointer/internal/item-writer.js +28 -11
  14. package/dist/checkpointer/internal/item-writer.js.map +1 -1
  15. package/dist/checkpointer/internal/query.d.ts +5 -1
  16. package/dist/checkpointer/internal/query.d.ts.map +1 -1
  17. package/dist/checkpointer/internal/query.js +5 -2
  18. package/dist/checkpointer/internal/query.js.map +1 -1
  19. package/dist/checkpointer/internal/setup.d.ts.map +1 -1
  20. package/dist/checkpointer/internal/setup.js +8 -1
  21. package/dist/checkpointer/internal/setup.js.map +1 -1
  22. package/dist/checkpointer/internal/special-write-cleanup.d.ts +18 -0
  23. package/dist/checkpointer/internal/special-write-cleanup.d.ts.map +1 -0
  24. package/dist/checkpointer/internal/special-write-cleanup.js +108 -0
  25. package/dist/checkpointer/internal/special-write-cleanup.js.map +1 -0
  26. package/dist/factory/factory.d.ts +3 -1
  27. package/dist/factory/factory.d.ts.map +1 -1
  28. package/dist/factory/factory.js +4 -6
  29. package/dist/factory/factory.js.map +1 -1
  30. package/dist/history/actions/add-messages.d.ts.map +1 -1
  31. package/dist/history/actions/add-messages.js +4 -3
  32. package/dist/history/actions/add-messages.js.map +1 -1
  33. package/dist/history/actions/clear.js +1 -1
  34. package/dist/history/actions/clear.js.map +1 -1
  35. package/dist/history/actions/list-sessions.d.ts +1 -0
  36. package/dist/history/actions/list-sessions.d.ts.map +1 -1
  37. package/dist/history/actions/list-sessions.js +1 -0
  38. package/dist/history/actions/list-sessions.js.map +1 -1
  39. package/dist/history/actions/reconcile-count.d.ts +4 -1
  40. package/dist/history/actions/reconcile-count.d.ts.map +1 -1
  41. package/dist/history/actions/reconcile-count.js +21 -9
  42. package/dist/history/actions/reconcile-count.js.map +1 -1
  43. package/dist/history/chat-message-history.d.ts +1 -0
  44. package/dist/history/chat-message-history.d.ts.map +1 -1
  45. package/dist/history/chat-message-history.js.map +1 -1
  46. package/dist/history/internal/append-saga.d.ts +1 -0
  47. package/dist/history/internal/append-saga.d.ts.map +1 -1
  48. package/dist/history/internal/append-saga.js +25 -21
  49. package/dist/history/internal/append-saga.js.map +1 -1
  50. package/dist/history/internal/message-transaction.d.ts +11 -6
  51. package/dist/history/internal/message-transaction.d.ts.map +1 -1
  52. package/dist/history/internal/message-transaction.js +40 -13
  53. package/dist/history/internal/message-transaction.js.map +1 -1
  54. package/dist/history/internal/query.d.ts +5 -1
  55. package/dist/history/internal/query.d.ts.map +1 -1
  56. package/dist/history/internal/query.js +5 -2
  57. package/dist/history/internal/query.js.map +1 -1
  58. package/dist/history/internal/session-count.d.ts +20 -0
  59. package/dist/history/internal/session-count.d.ts.map +1 -0
  60. package/dist/history/internal/session-count.js +58 -0
  61. package/dist/history/internal/session-count.js.map +1 -0
  62. package/dist/history/internal/session-update.d.ts +9 -1
  63. package/dist/history/internal/session-update.d.ts.map +1 -1
  64. package/dist/history/internal/session-update.js +26 -2
  65. package/dist/history/internal/session-update.js.map +1 -1
  66. package/dist/history/internal/setup.d.ts.map +1 -1
  67. package/dist/history/internal/setup.js +8 -1
  68. package/dist/history/internal/setup.js.map +1 -1
  69. package/dist/history/internal/ttl-anchor.d.ts +20 -7
  70. package/dist/history/internal/ttl-anchor.d.ts.map +1 -1
  71. package/dist/history/internal/ttl-anchor.js +19 -7
  72. package/dist/history/internal/ttl-anchor.js.map +1 -1
  73. package/dist/index.d.ts +1 -1
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +2 -1
  76. package/dist/index.js.map +1 -1
  77. package/dist/shared/codec/s3/client.d.ts +7 -1
  78. package/dist/shared/codec/s3/client.d.ts.map +1 -1
  79. package/dist/shared/codec/s3/client.js +8 -2
  80. package/dist/shared/codec/s3/client.js.map +1 -1
  81. package/dist/shared/codec/s3/config.d.ts +2 -0
  82. package/dist/shared/codec/s3/config.d.ts.map +1 -1
  83. package/dist/shared/codec/s3/config.js +5 -0
  84. package/dist/shared/codec/s3/config.js.map +1 -1
  85. package/dist/shared/codec/s3/offloader.js +1 -1
  86. package/dist/shared/codec/s3/offloader.js.map +1 -1
  87. package/dist/shared/codec/s3/read-write.d.ts.map +1 -1
  88. package/dist/shared/codec/s3/read-write.js +3 -6
  89. package/dist/shared/codec/s3/read-write.js.map +1 -1
  90. package/dist/shared/constants.d.ts +3 -0
  91. package/dist/shared/constants.d.ts.map +1 -1
  92. package/dist/shared/constants.js +3 -0
  93. package/dist/shared/constants.js.map +1 -1
  94. package/dist/shared/dynamodb/batch-write.d.ts +15 -2
  95. package/dist/shared/dynamodb/batch-write.d.ts.map +1 -1
  96. package/dist/shared/dynamodb/batch-write.js +38 -3
  97. package/dist/shared/dynamodb/batch-write.js.map +1 -1
  98. package/dist/shared/dynamodb/cancellation.d.ts +7 -0
  99. package/dist/shared/dynamodb/cancellation.d.ts.map +1 -0
  100. package/dist/shared/dynamodb/cancellation.js +8 -0
  101. package/dist/shared/dynamodb/cancellation.js.map +1 -0
  102. package/dist/shared/dynamodb/client.js +1 -1
  103. package/dist/shared/dynamodb/client.js.map +1 -1
  104. package/dist/shared/dynamodb/retry-classifier.d.ts.map +1 -1
  105. package/dist/shared/dynamodb/retry-classifier.js +5 -1
  106. package/dist/shared/dynamodb/retry-classifier.js.map +1 -1
  107. package/dist/shared/errors/errors.d.ts +18 -0
  108. package/dist/shared/errors/errors.d.ts.map +1 -1
  109. package/dist/shared/errors/errors.js +28 -1
  110. package/dist/shared/errors/errors.js.map +1 -1
  111. package/dist/store/actions/list-namespaces.d.ts.map +1 -1
  112. package/dist/store/actions/list-namespaces.js +10 -2
  113. package/dist/store/actions/list-namespaces.js.map +1 -1
  114. package/dist/store/actions/put.d.ts +5 -5
  115. package/dist/store/actions/put.d.ts.map +1 -1
  116. package/dist/store/actions/put.js +20 -36
  117. package/dist/store/actions/put.js.map +1 -1
  118. package/dist/store/actions/search.d.ts.map +1 -1
  119. package/dist/store/actions/search.js +29 -11
  120. package/dist/store/actions/search.js.map +1 -1
  121. package/dist/store/internal/filter.d.ts.map +1 -1
  122. package/dist/store/internal/filter.js +15 -8
  123. package/dist/store/internal/filter.js.map +1 -1
  124. package/dist/store/internal/index-reconcile.d.ts.map +1 -1
  125. package/dist/store/internal/index-reconcile.js +4 -1
  126. package/dist/store/internal/index-reconcile.js.map +1 -1
  127. package/dist/store/internal/item-mapper.d.ts +2 -1
  128. package/dist/store/internal/item-mapper.d.ts.map +1 -1
  129. package/dist/store/internal/item-mapper.js +1 -1
  130. package/dist/store/internal/item-mapper.js.map +1 -1
  131. package/dist/store/internal/persist.d.ts +11 -0
  132. package/dist/store/internal/persist.d.ts.map +1 -0
  133. package/dist/store/internal/persist.js +38 -0
  134. package/dist/store/internal/persist.js.map +1 -0
  135. package/dist/store/internal/ranker.d.ts.map +1 -1
  136. package/dist/store/internal/ranker.js +3 -1
  137. package/dist/store/internal/ranker.js.map +1 -1
  138. package/dist/store/internal/setup.d.ts +1 -0
  139. package/dist/store/internal/setup.d.ts.map +1 -1
  140. package/dist/store/internal/setup.js +8 -1
  141. package/dist/store/internal/setup.js.map +1 -1
  142. package/dist/store/internal/write-verify.d.ts +12 -0
  143. package/dist/store/internal/write-verify.d.ts.map +1 -0
  144. package/dist/store/internal/write-verify.js +28 -0
  145. package/dist/store/internal/write-verify.js.map +1 -0
  146. package/dist/store/types.d.ts +2 -0
  147. package/dist/store/types.d.ts.map +1 -1
  148. package/package.json +9 -8
package/README.md CHANGED
@@ -192,16 +192,21 @@ All adapters share a common base. Provide **either** a prebuilt `client` (which
192
192
  | `index` | `IndexConfig` | store only | `{ dims, embeddings, fields? }` for semantic search |
193
193
  | `vectorBackend` | `VectorBackend` | store only | delegate similarity search to an external index; DynamoDB keeps the canonical item |
194
194
  | `maxSearchCandidates` | `number` | store only | cap for the in-DB ranker before it errors (default 1000) |
195
+ | `maxScanItems` | `number` | store only | cap on items scanned into memory during a plain (non-semantic) `search()` before it errors (default 10000, the shared in-memory cap used by every paginated read) |
195
196
 
196
197
  `S3OffloadConfig`: `{ bucketName, keyPrefix?, thresholdBytes?, serverSideEncryption?, sseKmsKeyId?, clientConfig? }`.
197
198
 
199
+ When `keyPrefix` is omitted, each adapter defaults to its own sub-prefix under the shared base (`langgraph-checkpoints/store/`, `langgraph-checkpoints/checkpointer/`, `langgraph-checkpoints/history/`) so that multiple adapters can safely share one bucket — their offloaded object keys and `ensureS3LifecycleRule()` TTL rules never collide. An explicit `keyPrefix` is always honored verbatim, including across adapters if you want them to share one; at that point avoiding a lifecycle-rule collision (e.g. by giving them the same TTL) is your responsibility, same as with any other explicit override.
200
+
198
201
  ## Features
199
202
 
200
203
  **Gzip compression** — set `compression: { enabled: true }`. Payloads at or above `minSizeBytes` (default 1 KB) are gzipped transparently; decompression auto-detects on read and is guarded against decompression-bomb expansion (`maxDecompressedBytes`, default 50 MiB).
201
204
 
202
- **S3 offloading** — set `s3: { bucketName }`. Any serialized payload at or above `thresholdBytes` (default 350 KB) is written to S3, with only a reference stored in DynamoDB; reads rehydrate transparently. Requires the optional `@aws-sdk/client-s3` peer. Deleting a checkpoint thread / chat session also best-effort deletes its offloaded objects. When a `ttl` is also configured, call `ensureS3LifecycleRule()` once (e.g. during deployment) to best-effort install a matching S3 lifecycle expiration rule (logged, never fatal) — this is opt-in rather than automatic, since it requires the broader `s3:PutLifecycleConfiguration` bucket-level permission and is not safe to fire on every adapter construction. If you configure `ttl` + `s3` but never call it, nothing reclaims objects that best-effort cleanup misses (a failed delete, or a write that lost the checkpointer's first-write-wins race) — they stay in the bucket until you remove them or add a lifecycle rule yourself.
205
+ **S3 offloading** — set `s3: { bucketName }`. Any serialized payload at or above `thresholdBytes` (default 350 KB) is written to S3, with only a reference stored in DynamoDB; reads rehydrate transparently. Requires the optional `@aws-sdk/client-s3` peer. Deleting a checkpoint thread / chat session also best-effort deletes its offloaded objects. When a `ttl` is also configured, call `ensureS3LifecycleRule()` once (e.g. during deployment) to best-effort install a matching S3 lifecycle expiration rule (logged, never fatal) — this is opt-in rather than automatic, since it requires the broader `s3:PutLifecycleConfiguration` bucket-level permission and is not safe to fire on every adapter construction. If you configure `ttl` + `s3` but never call it, nothing reclaims objects that best-effort cleanup misses (a failed delete, or a write that lost the checkpointer's first-write-wins race) — they stay in the bucket until you remove them or add a lifecycle rule yourself. The store has an analogous, smaller-scale race: two concurrent `put`s to the same key that both read the previous descriptor before either commits each upload to their own distinct (nonced) key and each then try to clean up that same previous descriptor — the loser's own upload becomes an orphan (bounded at one per race), the same class of leak, reclaimed the same way.
206
+
207
+ **TTL expiry** — set `ttl: { days }` or `ttl: { seconds }`. The `ttl` attribute is written as a Unix-epoch-seconds timestamp; enable DynamoDB TTL on the `ttl` attribute for automatic deletion. Chat history anchors a single **uniform whole-conversation TTL** on the session's metadata row, shared by every message: normally it's set once, at session creation, via `if_not_exists`; but if the previously-stored anchor is ever found missing or already expired (DynamoDB's own TTL sweep can lag up to ~48h), the next append heals it with a plain overwrite instead of staying stuck. Every message written at any point in time shares whatever the current anchor is; expired messages are also filtered out on read. If the append that triggers a stale-anchor heal is itself later rolled back (a later chunk in the same call failed), the healed ttl is not reverted — the session simply keeps the fresher, never-shorter expiry rather than risk regressing a value a concurrent legitimate extension may have since written; this is a deliberate, self-healing tradeoff, not a bug.
203
208
 
204
- **TTL expiry** — set `ttl: { days }` or `ttl: { seconds }`. The `ttl` attribute is written as a Unix-epoch-seconds timestamp; enable DynamoDB TTL on the `ttl` attribute for automatic deletion. Chat history anchors a single **uniform whole-conversation TTL** at session creation (via `if_not_exists`), shared by every message, so a live session never develops mid-history gaps; expired messages are also filtered out on read.
209
+ **Plain (metadata) search** (store) — a `search()` call with no `query` (or with a `query` but no `index`/`vectorBackend` configured) decodes every row under the `namespacePrefix` — applying `filter` in-process — before slicing to `offset`/`limit`. That full-namespace decode is bounded by `maxScanItems` (default 10,000, the same in-memory cap shared by every other paginated read in the library); exceeding it throws rather than silently returning a partial result. This is a different cap from `maxSearchCandidates` below: `maxScanItems` gates the plain in-memory scan, `maxSearchCandidates` gates the in-DB semantic ranker. Raise `maxScanItems` for a one-off oversized namespace, but for namespaces that routinely exceed the default, prefer a `vectorBackend` or a narrower `namespacePrefix` over raising the cap indefinitely.
205
210
 
206
211
  **Semantic search** (store) — provide `index` with a LangChain `Embeddings` implementation. On `put`, the configured `fields` are embedded; on `search` with a `query`, results are ranked by cosine similarity. By default the embedding is stored on the item and ranking happens in-process over the scoped candidate set (bounded by `maxSearchCandidates`, default 1000 — exceeding it throws, steering you to an external index). For large corpora, pass a `vectorBackend`: the embedding is sent there instead, similarity search is delegated to it, and DynamoDB still holds the canonical item. Per-item indexing can be overridden via the `index` argument to `put` (`false` to skip, or a `string[]` of fields).
207
212
 
@@ -225,7 +230,9 @@ try {
225
230
  }
226
231
  ```
227
232
 
228
- `ErrorCode` values: `VALIDATION`, `CONDITION_CONFLICT`, `RETRY_EXHAUSTED`, `BATCH_WRITE_INCOMPLETE`, `COMPRESSION_LIMIT`, `S3_OFFLOAD_FAILED`, `ABORTED`. Typed subclasses are exported where callers commonly branch: `ValidationError`, `ConflictError`, `RetryExhaustedError`, `BatchWriteIncompleteError`, `AbortError`.
233
+ `ErrorCode` values: `VALIDATION`, `CONDITION_CONFLICT`, `RETRY_EXHAUSTED`, `BATCH_WRITE_INCOMPLETE`, `COMPRESSION_LIMIT`, `S3_OFFLOAD_FAILED`, `RESULT_TRUNCATED`, `ABORTED`, `COMPENSATION_FAILED`. Typed subclasses are exported where callers commonly branch: `ValidationError`, `ConflictError`, `RetryExhaustedError`, `BatchWriteIncompleteError`, `BatchWriteAllIncompleteError`, `ResultTruncatedError`, `AbortError`, `CompensationFailedError`.
234
+
235
+ `BatchWriteAllIncompleteError` is thrown directly by `deleteThread`, `clearSession`, and `putWrites` when a multi-chunk `BatchWriteItem` sequence doesn't fully drain. The chat-history append-rollback path can hit the same underlying failure while deleting a partially-committed batch's rows, but there it's never thrown directly — it surfaces as the `rollbackError` property of a `CompensationFailedError` (the append's original trigger error still needs reporting too), so check `err.rollbackError instanceof BatchWriteAllIncompleteError` there instead of `err instanceof BatchWriteAllIncompleteError`. Where the error may have crossed a package-copy boundary (e.g. a bundler duplicating this package), prefer `err.rollbackError.code === ErrorCode.BATCH_WRITE_INCOMPLETE` or `err.rollbackError.name === 'BatchWriteAllIncompleteError'` over `instanceof` — this library's own code avoids `instanceof` internally for the same reason. It carries `succeededChunks` / `totalChunks` / `failedChunks` / `succeededCount` so a caller can tell how much of the batch actually persisted before the failure, rather than just seeing the first chunk's raw error.
229
236
 
230
237
  ## Logging
231
238
 
@@ -37,7 +37,7 @@ async function flushBuffer(context, buffer) {
37
37
  */
38
38
  async function deleteThread(context, threadId) {
39
39
  (0, primitives_1.validateNonEmptyString)(threadId, 'threadId');
40
- const params = (0, query_1.partitionQuery)(context.tableName, (0, keys_1.partitionKey)(threadId));
40
+ const params = (0, query_1.partitionQuery)(context.tableName, (0, keys_1.partitionKey)(threadId), { consistent: true });
41
41
  const buffer = { keys: [], descriptors: [] };
42
42
  const pages = (0, paginate_1.paginateQuery)({
43
43
  client: context.client,
@@ -1 +1 @@
1
- {"version":3,"file":"delete-thread.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/delete-thread.ts"],"names":[],"mappings":";;;AACA,wEAAmE;AACnE,2DAAiE;AACjE,sDAAyD;AACzD,mEAAkE;AAClE,6DAA+D;AAC/D,mEAA4E;AAC5E,2CAAgD;AAChD,6CAAmD;AAiBnD,uFAAuF;AACvF,SAAS,UAAU,CAAC,MAAoB,EAAE,IAAmB;IAC3D,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/C,KAAK,MAAM,UAAU,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACtE,IAAI,UAAU;YAAE,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAED,sFAAsF;AACtF,KAAK,UAAU,WAAW,CAAC,OAA4B,EAAE,MAAoB;IAC3E,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACrC,MAAM,IAAA,2BAAa,EACjB,OAAO,CAAC,MAAM,EACd,OAAO,CAAC,SAAS,EACjB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,aAAa,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CACvD,CAAC;IACF,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,MAAM,IAAA,0BAAgB,EACpB,OAAO,CAAC,SAAS,EACjB,IAAA,+BAAa,EAAC,MAAM,CAAC,WAAW,CAAC,EACjC,cAAc,EACd,OAAO,CAAC,MAAM,CACf,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;IACjB,MAAM,CAAC,WAAW,GAAG,EAAE,CAAC;AAC1B,CAAC;AAED;;;;;;GAMG;AACI,KAAK,uBAAuB,OAA4B,EAAE,QAAgB;IAC/E,IAAA,mCAAsB,EAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,IAAA,sBAAc,EAAC,OAAO,CAAC,SAAS,EAAE,IAAA,mBAAY,EAAC,QAAQ,CAAC,CAAC,CAAC;IACzE,MAAM,MAAM,GAAiB,EAAE,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;IAC3D,MAAM,KAAK,GAAG,IAAA,wBAAa,EAAC;QAC1B,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM;QACN,QAAQ,EAAE,MAAM,CAAC,iBAAiB;QAClC,aAAa,EAAE,MAAM,CAAC,iBAAiB;KACxC,CAAC,CAAC;IACH,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QAC9B,UAAU,CAAC,MAAM,EAAE,GAAoB,CAAC,CAAC;QACzC,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,2BAAe;YAAE,MAAM,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AACrC,CAAC"}
1
+ {"version":3,"file":"delete-thread.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/delete-thread.ts"],"names":[],"mappings":";;;AACA,wEAAmE;AACnE,2DAAiE;AACjE,sDAAyD;AACzD,mEAAkE;AAClE,6DAA+D;AAC/D,mEAA4E;AAC5E,2CAAgD;AAChD,6CAAmD;AAiBnD,uFAAuF;AACvF,SAAS,UAAU,CAAC,MAAoB,EAAE,IAAmB;IAC3D,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/C,KAAK,MAAM,UAAU,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACtE,IAAI,UAAU;YAAE,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAED,sFAAsF;AACtF,KAAK,UAAU,WAAW,CAAC,OAA4B,EAAE,MAAoB;IAC3E,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACrC,MAAM,IAAA,2BAAa,EACjB,OAAO,CAAC,MAAM,EACd,OAAO,CAAC,SAAS,EACjB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,aAAa,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CACvD,CAAC;IACF,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,MAAM,IAAA,0BAAgB,EACpB,OAAO,CAAC,SAAS,EACjB,IAAA,+BAAa,EAAC,MAAM,CAAC,WAAW,CAAC,EACjC,cAAc,EACd,OAAO,CAAC,MAAM,CACf,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;IACjB,MAAM,CAAC,WAAW,GAAG,EAAE,CAAC;AAC1B,CAAC;AAED;;;;;;GAMG;AACI,KAAK,uBAAuB,OAA4B,EAAE,QAAgB;IAC/E,IAAA,mCAAsB,EAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,IAAA,sBAAc,EAAC,OAAO,CAAC,SAAS,EAAE,IAAA,mBAAY,EAAC,QAAQ,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/F,MAAM,MAAM,GAAiB,EAAE,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;IAC3D,MAAM,KAAK,GAAG,IAAA,wBAAa,EAAC;QAC1B,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM;QACN,QAAQ,EAAE,MAAM,CAAC,iBAAiB;QAClC,aAAa,EAAE,MAAM,CAAC,iBAAiB;KACxC,CAAC,CAAC;IACH,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QAC9B,UAAU,CAAC,MAAM,EAAE,GAAoB,CAAC,CAAC;QACzC,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,2BAAe;YAAE,MAAM,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AACrC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAS9F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AA2B7D;;;;GAIG;AACH,wBAAuB,eAAe,CACpC,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,cAAc,CAAC,eAAe,CAAC,CAoBjC"}
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAS9F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AA2B7D;;;;GAIG;AACH,wBAAuB,eAAe,CACpC,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,cAAc,CAAC,eAAe,CAAC,CAqBjC"}
@@ -29,7 +29,7 @@ async function passesMetadataFilter(context, meta, filter) {
29
29
  * (metadata equality), and `options.limit` (max tuples yielded).
30
30
  */
31
31
  async function* listCheckpoints(context, config, options) {
32
- const { threadId, checkpointNs } = (0, configurable_1.readConfigurable)(config);
32
+ const { threadId, checkpointNs, checkpointId } = (0, configurable_1.readConfigurable)(config);
33
33
  const { before, filter, limit } = readListOptions(options);
34
34
  const params = (0, query_1.beginsWithQuery)(context.tableName, (0, keys_1.partitionKey)(threadId), (0, keys_1.metaSortKeyPrefix)(checkpointNs));
35
35
  let yielded = 0;
@@ -37,6 +37,8 @@ async function* listCheckpoints(context, config, options) {
37
37
  if (limit !== undefined && yielded >= limit)
38
38
  return;
39
39
  const meta = raw;
40
+ if (checkpointId !== undefined && meta.checkpointId !== checkpointId)
41
+ continue;
40
42
  if (before !== undefined && meta.checkpointId >= before)
41
43
  continue;
42
44
  if (!(await passesMetadataFilter(context, meta, filter)))
@@ -1 +1 @@
1
- {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/list.ts"],"names":[],"mappings":";;;AAGA,6DAA+D;AAC/D,mDAAqD;AACrD,2DAA4D;AAC5D,2DAA2E;AAC3E,yDAAuD;AACvD,2CAAmE;AACnE,6CAAoD;AAIpD,kDAAkD;AAClD,SAAS,eAAe,CAAC,OAA+B;IAKtD,OAAO;QACL,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,aAAmC;QAC1E,MAAM,EAAE,OAAO,EAAE,MAAiD;QAClE,KAAK,EAAE,OAAO,EAAE,KAAK;KACtB,CAAC;AACJ,CAAC;AAED,uEAAuE;AACvE,KAAK,UAAU,oBAAoB,CACjC,OAA4B,EAC5B,IAAwB,EACxB,MAA+C;IAE/C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAA,0BAAY,EAAC,OAAO,EAAE,IAAI,CAAC,CAAgC,CAAC;IACpF,OAAO,IAAA,4BAAa,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACI,KAAK,SAAS,CAAC,iBACpB,OAA4B,EAC5B,MAAsB,EACtB,OAA+B;IAE/B,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAA,+BAAgB,EAAC,MAAM,CAAC,CAAC;IAC5D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,IAAA,uBAAe,EAC5B,OAAO,CAAC,SAAS,EACjB,IAAA,mBAAY,EAAC,QAAQ,CAAC,EACtB,IAAA,wBAAiB,EAAC,YAAY,CAAC,CAChC,CAAC;IACF,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,IAAA,wBAAa,EAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAC1E,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,IAAI,KAAK;YAAE,OAAO;QACpD,MAAM,IAAI,GAAG,GAAyB,CAAC;QACvC,IAAI,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,YAAY,IAAI,MAAM;YAAE,SAAS;QAClE,IAAI,CAAC,CAAC,MAAM,oBAAoB,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YAAE,SAAS;QACnE,MAAM,KAAK,GAAG,MAAM,IAAA,wBAAa,EAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;QACzE,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,KAAK,CAAC;YACZ,OAAO,IAAI,CAAC,CAAC;QACf,CAAC;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/list.ts"],"names":[],"mappings":";;;AAGA,6DAA+D;AAC/D,mDAAqD;AACrD,2DAA4D;AAC5D,2DAA2E;AAC3E,yDAAuD;AACvD,2CAAmE;AACnE,6CAAoD;AAIpD,kDAAkD;AAClD,SAAS,eAAe,CAAC,OAA+B;IAKtD,OAAO;QACL,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,aAAmC;QAC1E,MAAM,EAAE,OAAO,EAAE,MAAiD;QAClE,KAAK,EAAE,OAAO,EAAE,KAAK;KACtB,CAAC;AACJ,CAAC;AAED,uEAAuE;AACvE,KAAK,UAAU,oBAAoB,CACjC,OAA4B,EAC5B,IAAwB,EACxB,MAA+C;IAE/C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAA,0BAAY,EAAC,OAAO,EAAE,IAAI,CAAC,CAAgC,CAAC;IACpF,OAAO,IAAA,4BAAa,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACI,KAAK,SAAS,CAAC,iBACpB,OAA4B,EAC5B,MAAsB,EACtB,OAA+B;IAE/B,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,IAAA,+BAAgB,EAAC,MAAM,CAAC,CAAC;IAC1E,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,IAAA,uBAAe,EAC5B,OAAO,CAAC,SAAS,EACjB,IAAA,mBAAY,EAAC,QAAQ,CAAC,EACtB,IAAA,wBAAiB,EAAC,YAAY,CAAC,CAChC,CAAC;IACF,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,IAAA,wBAAa,EAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAC1E,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,IAAI,KAAK;YAAE,OAAO;QACpD,MAAM,IAAI,GAAG,GAAyB,CAAC;QACvC,IAAI,YAAY,KAAK,SAAS,IAAI,IAAI,CAAC,YAAY,KAAK,YAAY;YAAE,SAAS;QAC/E,IAAI,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,YAAY,IAAI,MAAM;YAAE,SAAS;QAClE,IAAI,CAAC,CAAC,MAAM,oBAAoB,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YAAE,SAAS;QACnE,MAAM,KAAK,GAAG,MAAM,IAAA,wBAAa,EAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;QACzE,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,KAAK,CAAC;YACZ,OAAO,IAAI,CAAC,CAAC;QACf,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -6,10 +6,10 @@ import type { CheckpointerContext } from '../internal/setup';
6
6
  * Requires `checkpoint_id` in the config — writes always attach to a checkpoint.
7
7
  * Regular writes are first-write-wins (matching the reference checkpointer
8
8
  * contract); special negative-index writes always overwrite (see
9
- * {@link writeSpecialItems}). Failure cleanup only ever targets regular items
10
- * known never to have committed (see {@link RegularWriteOutcome}) — never a
11
- * special item or a lost guard, so an upload can leak but a live row can
12
- * never be stranded pointing at a deleted object.
9
+ * {@link writeSpecialItemsWithCleanup}). Regular-write failure cleanup only
10
+ * ever targets items known never to have committed (see
11
+ * {@link RegularWriteOutcome}) — never a lost guard, so an upload can leak
12
+ * but a live row can never be stranded pointing at a deleted object.
13
13
  */
14
14
  export declare function putWrites(context: CheckpointerContext, config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void>;
15
15
  //# sourceMappingURL=put-writes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"put-writes.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/put-writes.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAUpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AA4F7D;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,YAAY,EAAE,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CA4Bf"}
1
+ {"version":3,"file":"put-writes.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/put-writes.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AASpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAoF7D;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,YAAY,EAAE,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CA4Bf"}
@@ -4,18 +4,17 @@ exports.putWrites = putWrites;
4
4
  const node_crypto_1 = require("node:crypto");
5
5
  const descriptor_keys_1 = require("../../shared/codec/descriptor-keys");
6
6
  const orphans_1 = require("../../shared/codec/s3/orphans");
7
- const batch_write_1 = require("../../shared/dynamodb/batch-write");
8
7
  const retry_1 = require("../../shared/dynamodb/retry");
9
8
  const errors_1 = require("../../shared/errors/errors");
10
9
  const ttl_1 = require("../../shared/validation/ttl");
11
10
  const configurable_1 = require("../internal/configurable");
12
11
  const item_writer_1 = require("../internal/item-writer");
12
+ const special_write_cleanup_1 = require("../internal/special-write-cleanup");
13
13
  const validation_1 = require("../internal/validation");
14
14
  /**
15
- * True when the first-write-wins guard rejected a write — NOT evidence a
16
- * *competitor* wrote that row. A `PutCommand` retried after its response was
17
- * lost (`ETIMEDOUT`/`NetworkingError`/`ECONNRESET`) re-hits the row it
18
- * committed itself and fails identically; the two cases are indistinguishable.
15
+ * True when the guard rejected a write — NOT evidence a competitor won: a
16
+ * `PutCommand` retried after its response was lost can re-hit its own
17
+ * committed row and fail identically; the two cases are indistinguishable.
19
18
  */
20
19
  function isConditionalCheckFailed(error) {
21
20
  return error.name === 'ConditionalCheckFailedException';
@@ -27,16 +26,18 @@ async function cleanUpItems(context, items) {
27
26
  await (0, orphans_1.cleanUpS3Orphans)(context.offloader, (0, descriptor_keys_1.collectS3Keys)(items.map((item) => item.value)), 'putWrites', context.logger);
28
27
  }
29
28
  /**
30
- * Write special (negative-index) items unconditionally — overwrite is correct
31
- * there, matching every reference checkpointer implementation. Their key is
32
- * deterministic, so a failed batch never triggers S3 cleanup here either: the
33
- * next write to the same channel overwrites the same key (self-healing);
34
- * deleting now could strand a row a previous call already committed.
29
+ * Dedup writes to the same special channel (last-write-wins) before upload,
30
+ * so a discarded duplicate is never uploaded. A regular write's index comes
31
+ * from its position here (post-dedup), not the caller's raw array — stable
32
+ * across retries of an identical array, which is what first-write-wins needs.
35
33
  */
36
- async function writeSpecialItems(context, items) {
37
- if (items.length === 0)
38
- return;
39
- await (0, batch_write_1.batchWriteAll)(context.client, context.tableName, items.map((item) => ({ PutRequest: { Item: item } })));
34
+ function dedupeWritesByIndex(writes) {
35
+ const byIndex = new Map();
36
+ writes.forEach((write, positional) => {
37
+ const index = (0, item_writer_1.resolveWriteIndex)(write[0], positional);
38
+ byIndex.set(index, write);
39
+ });
40
+ return [...byIndex.values()];
40
41
  }
41
42
  /**
42
43
  * Write regular items with a first-write-wins guard. Every `PutCommand` fully
@@ -69,10 +70,10 @@ async function writeRegularItems(context, items) {
69
70
  * Requires `checkpoint_id` in the config — writes always attach to a checkpoint.
70
71
  * Regular writes are first-write-wins (matching the reference checkpointer
71
72
  * contract); special negative-index writes always overwrite (see
72
- * {@link writeSpecialItems}). Failure cleanup only ever targets regular items
73
- * known never to have committed (see {@link RegularWriteOutcome}) — never a
74
- * special item or a lost guard, so an upload can leak but a live row can
75
- * never be stranded pointing at a deleted object.
73
+ * {@link writeSpecialItemsWithCleanup}). Regular-write failure cleanup only
74
+ * ever targets items known never to have committed (see
75
+ * {@link RegularWriteOutcome}) — never a lost guard, so an upload can leak
76
+ * but a live row can never be stranded pointing at a deleted object.
76
77
  */
77
78
  async function putWrites(context, config, writes, taskId) {
78
79
  (0, validation_1.validateTaskId)(taskId);
@@ -83,11 +84,11 @@ async function putWrites(context, config, writes, taskId) {
83
84
  if (writes.length === 0)
84
85
  return;
85
86
  const ttlTimestamp = context.ttl ? (0, ttl_1.calculateTtlTimestamp)(context.ttl) : undefined;
86
- const items = await (0, item_writer_1.buildWriteItems)(context, threadId, checkpointNs, checkpointId, taskId, writes, (0, node_crypto_1.randomUUID)(), ttlTimestamp);
87
+ const items = await (0, item_writer_1.buildWriteItems)(context, threadId, checkpointNs, checkpointId, taskId, dedupeWritesByIndex(writes), (0, node_crypto_1.randomUUID)(), ttlTimestamp);
87
88
  const special = items.filter((item) => item.index < 0);
88
89
  const regular = items.filter((item) => item.index >= 0);
89
90
  const [specialError, regularOutcome] = await Promise.all([
90
- writeSpecialItems(context, special).catch((error) => error),
91
+ (0, special_write_cleanup_1.writeSpecialItemsWithCleanup)(context, special),
91
92
  writeRegularItems(context, regular),
92
93
  ]);
93
94
  const firstError = specialError ?? regularOutcome.error;
@@ -1 +1 @@
1
- {"version":3,"file":"put-writes.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/put-writes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"put-writes.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/put-writes.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,14 @@ import type { CheckpointMetaItem, CheckpointPayloadItem, CheckpointWriteItem } f
4
4
  import type { CheckpointerContext } from './setup';
5
5
  /** Map a context to the codec collaborators. */
6
6
  export declare function codecDeps(context: CheckpointerContext): CodecDeps;
7
+ /**
8
+ * Resolve a write's DynamoDB sort-key index: WRITES_IDX_MAP's slot for a
9
+ * known special channel, else its position. `Object.hasOwn` guards against
10
+ * WRITES_IDX_MAP's own `Object.prototype` chain — a channel literally named
11
+ * `constructor`/`toString`/etc. must fall through to `positional`, not
12
+ * resolve to an inherited function reference.
13
+ */
14
+ export declare function resolveWriteIndex(channel: string, positional: number): number;
7
15
  /** Encode a checkpoint + metadata into its META and PAYLOAD items. */
8
16
  export declare function buildCheckpointItems(context: CheckpointerContext, threadId: string, checkpointNs: string, checkpoint: Checkpoint, metadata: CheckpointMetadata, parentCheckpointId?: string, ttlTimestamp?: number): Promise<{
9
17
  meta: CheckpointMetaItem;
@@ -11,15 +19,16 @@ export declare function buildCheckpointItems(context: CheckpointerContext, threa
11
19
  }>;
12
20
  /**
13
21
  * Encode a task's pending writes into one item per write. `nonce` must be
14
- * unique per `putWrites` *call* (not per write) and is appended to the S3
15
- * offload keyParts of **regular (non-negative-index) writes only** — the ones
16
- * written conditionally, first-write-wins. There, repeated/concurrent attempts
17
- * for the same (thread, checkpoint, task, index) must never share an S3
18
- * location, so a failed attempt's cleanup can only ever delete its own upload.
19
- * Special (negative-index) writes are overwritten in place, so their key stays
20
- * deterministic: nonce'ing it would strand the previous upload — referenced by
21
- * nothing, tracked by nothing, cleaned up by nothing — on every rewrite of the
22
- * same special channel.
22
+ * unique per `putWrites` *call* (not per write) and is appended to every
23
+ * write's S3 offload keyParts — regular and special alike — so a repeated
24
+ * write (a retried task re-emitting the same channel, or two calls to the
25
+ * same special channel) never shares an S3 location with any earlier
26
+ * attempt. Special (negative-index) writes still overwrite their DynamoDB
27
+ * row in place (see {@link writeSpecialItemsWithCleanup}), but that
28
+ * overwrite-safety now comes from reading the previous descriptor before
29
+ * writing and only cleaning it up after the new row is confirmed committed —
30
+ * the same pattern store/actions/put.ts uses for the identical "overwrite in
31
+ * place, nonce every upload" shape.
23
32
  */
24
33
  export declare function buildWriteItems(context: CheckpointerContext, threadId: string, checkpointNs: string, checkpointId: string, taskId: string, writes: PendingWrite[], nonce: string, ttlTimestamp?: number): Promise<CheckpointWriteItem[]>;
25
34
  //# sourceMappingURL=item-writer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"item-writer.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/item-writer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAGpG,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,0BAA0B,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,gDAAgD;AAChD,wBAAgB,SAAS,CAAC,OAAO,EAAE,mBAAmB,GAAG,SAAS,CAEjE;AAOD,sEAAsE;AACtE,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,kBAAkB,EAC5B,kBAAkB,CAAC,EAAE,MAAM,EAC3B,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,qBAAqB,CAAA;CAAE,CAAC,CAwBvE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,EAAE,EACtB,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAsBhC"}
1
+ {"version":3,"file":"item-writer.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/item-writer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAGpG,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,0BAA0B,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,gDAAgD;AAChD,wBAAgB,SAAS,CAAC,OAAO,EAAE,mBAAmB,GAAG,SAAS,CAEjE;AAOD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,sEAAsE;AACtE,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,kBAAkB,EAC5B,kBAAkB,CAAC,EAAE,MAAM,EAC3B,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,qBAAqB,CAAA;CAAE,CAAC,CAwBvE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,EAAE,EACtB,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,mBAAmB,EAAE,CAAC,CA2BhC"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.codecDeps = codecDeps;
4
+ exports.resolveWriteIndex = resolveWriteIndex;
4
5
  exports.buildCheckpointItems = buildCheckpointItems;
5
6
  exports.buildWriteItems = buildWriteItems;
6
7
  const langgraph_checkpoint_1 = require("@langchain/langgraph-checkpoint");
@@ -15,6 +16,16 @@ function withTtl(item, ttlTimestamp) {
15
16
  item.ttl = ttlTimestamp;
16
17
  return item;
17
18
  }
19
+ /**
20
+ * Resolve a write's DynamoDB sort-key index: WRITES_IDX_MAP's slot for a
21
+ * known special channel, else its position. `Object.hasOwn` guards against
22
+ * WRITES_IDX_MAP's own `Object.prototype` chain — a channel literally named
23
+ * `constructor`/`toString`/etc. must fall through to `positional`, not
24
+ * resolve to an inherited function reference.
25
+ */
26
+ function resolveWriteIndex(channel, positional) {
27
+ return Object.hasOwn(langgraph_checkpoint_1.WRITES_IDX_MAP, channel) ? langgraph_checkpoint_1.WRITES_IDX_MAP[channel] : positional;
28
+ }
18
29
  /** Encode a checkpoint + metadata into its META and PAYLOAD items. */
19
30
  async function buildCheckpointItems(context, threadId, checkpointNs, checkpoint, metadata, parentCheckpointId, ttlTimestamp) {
20
31
  const deps = codecDeps(context);
@@ -44,15 +55,16 @@ async function buildCheckpointItems(context, threadId, checkpointNs, checkpoint,
44
55
  }
45
56
  /**
46
57
  * Encode a task's pending writes into one item per write. `nonce` must be
47
- * unique per `putWrites` *call* (not per write) and is appended to the S3
48
- * offload keyParts of **regular (non-negative-index) writes only** — the ones
49
- * written conditionally, first-write-wins. There, repeated/concurrent attempts
50
- * for the same (thread, checkpoint, task, index) must never share an S3
51
- * location, so a failed attempt's cleanup can only ever delete its own upload.
52
- * Special (negative-index) writes are overwritten in place, so their key stays
53
- * deterministic: nonce'ing it would strand the previous upload — referenced by
54
- * nothing, tracked by nothing, cleaned up by nothing — on every rewrite of the
55
- * same special channel.
58
+ * unique per `putWrites` *call* (not per write) and is appended to every
59
+ * write's S3 offload keyParts — regular and special alike — so a repeated
60
+ * write (a retried task re-emitting the same channel, or two calls to the
61
+ * same special channel) never shares an S3 location with any earlier
62
+ * attempt. Special (negative-index) writes still overwrite their DynamoDB
63
+ * row in place (see {@link writeSpecialItemsWithCleanup}), but that
64
+ * overwrite-safety now comes from reading the previous descriptor before
65
+ * writing and only cleaning it up after the new row is confirmed committed —
66
+ * the same pattern store/actions/put.ts uses for the identical "overwrite in
67
+ * place, nonce every upload" shape.
56
68
  */
57
69
  async function buildWriteItems(context, threadId, checkpointNs, checkpointId, taskId, writes, nonce, ttlTimestamp) {
58
70
  const deps = codecDeps(context);
@@ -60,10 +72,15 @@ async function buildWriteItems(context, threadId, checkpointNs, checkpointId, ta
60
72
  const items = [];
61
73
  for (let positional = 0; positional < writes.length; positional++) {
62
74
  const [channel, value] = writes[positional];
63
- const index = langgraph_checkpoint_1.WRITES_IDX_MAP[channel] ?? positional;
75
+ /**
76
+ * `positional` indexes this (post-dedup) array, not the caller's original
77
+ * one — stable across retries of an identical array, which is all
78
+ * first-write-wins requires (see dedupeWritesByIndex).
79
+ */
80
+ const index = resolveWriteIndex(channel, positional);
64
81
  const baseKeyParts = [threadId, checkpointNs, checkpointId, taskId, `write-${index}`];
65
82
  const descriptor = await (0, codec_1.encodePayload)(value, deps, {
66
- keyParts: index < 0 ? baseKeyParts : [...baseKeyParts, nonce],
83
+ keyParts: [...baseKeyParts, nonce],
67
84
  });
68
85
  const item = {
69
86
  PK: pk,
@@ -1 +1 @@
1
- {"version":3,"file":"item-writer.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/item-writer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"item-writer.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/item-writer.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,8 @@
1
1
  import type { QueryCommandInput } from '@aws-sdk/lib-dynamodb';
2
+ /** Options for {@link partitionQuery}. */
3
+ export interface PartitionQueryOptions {
4
+ consistent?: boolean;
5
+ }
2
6
  /** Options for {@link beginsWithQuery}. */
3
7
  export interface BeginsWithQueryOptions {
4
8
  limit?: number;
@@ -6,7 +10,7 @@ export interface BeginsWithQueryOptions {
6
10
  consistent?: boolean;
7
11
  }
8
12
  /** Build a Query input selecting every item in a partition (all sort keys). */
9
- export declare function partitionQuery(tableName: string, partition: string): QueryCommandInput;
13
+ export declare function partitionQuery(tableName: string, partition: string, options?: PartitionQueryOptions): QueryCommandInput;
10
14
  /**
11
15
  * Build a Query input selecting items whose partition equals `partition` and
12
16
  * whose sort key begins with `skPrefix`. Names are aliased so reserved words
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,2CAA2C;AAC3C,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,iBAAiB,CAOtF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,sBAA2B,GACnC,iBAAiB,CAWnB"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,0CAA0C;AAC1C,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,2CAA2C;AAC3C,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,qBAA0B,GAClC,iBAAiB,CASnB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,sBAA2B,GACnC,iBAAiB,CAWnB"}
@@ -3,13 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.partitionQuery = partitionQuery;
4
4
  exports.beginsWithQuery = beginsWithQuery;
5
5
  /** Build a Query input selecting every item in a partition (all sort keys). */
6
- function partitionQuery(tableName, partition) {
7
- return {
6
+ function partitionQuery(tableName, partition, options = {}) {
7
+ const params = {
8
8
  TableName: tableName,
9
9
  KeyConditionExpression: '#pk = :pk',
10
10
  ExpressionAttributeNames: { '#pk': 'PK' },
11
11
  ExpressionAttributeValues: { ':pk': partition },
12
12
  };
13
+ if (options.consistent)
14
+ params.ConsistentRead = true;
15
+ return params;
13
16
  }
14
17
  /**
15
18
  * Build a Query input selecting items whose partition equals `partition` and
@@ -1 +1 @@
1
- {"version":3,"file":"query.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/query.ts"],"names":[],"mappings":";;;;AASA,+EAA+E;AAC/E,wBAA+B,SAAiB,EAAE,SAAiB;IACjE,OAAO;QACL,SAAS,EAAE,SAAS;QACpB,sBAAsB,EAAE,WAAW;QACnC,wBAAwB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;QACzC,yBAAyB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE;KAChD,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,yBACE,SAAiB,EACjB,SAAiB,EACjB,QAAgB,EAChB,OAAO,GAA2B,EAAE;IAEpC,MAAM,MAAM,GAAsB;QAChC,SAAS,EAAE,SAAS;QACpB,sBAAsB,EAAE,2CAA2C;QACnE,wBAAwB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;QACtD,yBAAyB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE;QACtE,gBAAgB,EAAE,OAAO,CAAC,SAAS,IAAI,KAAK;KAC7C,CAAC;IACF,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC9D,IAAI,OAAO,CAAC,UAAU;QAAE,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC;IACrD,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/query.ts"],"names":[],"mappings":";;;;AAcA,+EAA+E;AAC/E,wBACE,SAAiB,EACjB,SAAiB,EACjB,OAAO,GAA0B,EAAE;IAEnC,MAAM,MAAM,GAAsB;QAChC,SAAS,EAAE,SAAS;QACpB,sBAAsB,EAAE,WAAW;QACnC,wBAAwB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;QACzC,yBAAyB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE;KAChD,CAAC;IACF,IAAI,OAAO,CAAC,UAAU;QAAE,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC;IACrD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,yBACE,SAAiB,EACjB,SAAiB,EACjB,QAAgB,EAChB,OAAO,GAA2B,EAAE;IAEpC,MAAM,MAAM,GAAsB;QAChC,SAAS,EAAE,SAAS;QACpB,sBAAsB,EAAE,2CAA2C;QACnE,wBAAwB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;QACtD,yBAAyB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE;QACtE,gBAAgB,EAAE,OAAO,CAAC,SAAS,IAAI,KAAK;KAC7C,CAAC;IACF,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC9D,IAAI,OAAO,CAAC,UAAU;QAAE,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC;IACrD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAE1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE9D,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,6BAA6B,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAErD,kEAAkE;AAClE,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,2DAA2D;AAC3D,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,mBAAmB,CAAC;IAC7B,SAAS,EAAE,cAAc,GAAG,SAAS,CAAC;IACtC,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,oBAAoB,EAC7B,KAAK,EAAE,kBAAkB,GACxB,iBAAiB,CAgBnB"}
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAE1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAExE,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAG9D,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,6BAA6B,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAErD,kEAAkE;AAClE,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,2DAA2D;AAC3D,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,mBAAmB,CAAC;IAC7B,SAAS,EAAE,cAAc,GAAG,SAAS,CAAC;IACtC,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,oBAAoB,EAC7B,KAAK,EAAE,kBAAkB,GACxB,iBAAiB,CAsBnB"}
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.setUpCheckpointer = setUpCheckpointer;
4
+ const config_1 = require("../../shared/codec/s3/config");
4
5
  const offloader_1 = require("../../shared/codec/s3/offloader");
6
+ const constants_1 = require("../../shared/constants");
5
7
  const client_1 = require("../../shared/dynamodb/client");
6
8
  const logger_1 = require("../../shared/logging/logger");
7
9
  /**
@@ -11,7 +13,12 @@ const logger_1 = require("../../shared/logging/logger");
11
13
  */
12
14
  function setUpCheckpointer(options, serde) {
13
15
  const resolved = (0, client_1.resolveDynamoDBClient)(options);
14
- const offloader = options.s3 ? new offloader_1.S3Offloader(options.s3) : undefined;
16
+ const offloader = options.s3
17
+ ? new offloader_1.S3Offloader({
18
+ ...options.s3,
19
+ keyPrefix: options.s3.keyPrefix ?? (0, config_1.defaultAdapterKeyPrefix)(constants_1.DEFAULT_S3_KEY_PREFIX, 'checkpointer'),
20
+ })
21
+ : undefined;
15
22
  return {
16
23
  context: {
17
24
  client: resolved.client,
@@ -1 +1 @@
1
- {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/setup.ts"],"names":[],"mappings":";;;AAKA,+DAA8D;AAC9D,yDAAqE;AACrE,wDAAyE;AAsBzE;;;;GAIG;AACH,2BACE,OAA6B,EAC7B,KAAyB;IAEzB,MAAM,QAAQ,GAAG,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAChD,MAAM,SAAS,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,uBAAW,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACvE,OAAO;QACL,OAAO,EAAE;YACP,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,KAAK;YACL,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,SAAS;YACT,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,MAAM,EAAE,IAAA,sBAAa,EAAC,OAAO,CAAC,MAAM,CAAC;SACtC;QACD,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,UAAU,EAAE,QAAQ,CAAC,UAAU;KAChC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/setup.ts"],"names":[],"mappings":";;;AAKA,yDAAuE;AACvE,+DAA8D;AAC9D,sDAA+D;AAC/D,yDAAqE;AACrE,wDAAyE;AAsBzE;;;;GAIG;AACH,2BACE,OAA6B,EAC7B,KAAyB;IAEzB,MAAM,QAAQ,GAAG,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAChD,MAAM,SAAS,GAAG,OAAO,CAAC,EAAE;QAC1B,CAAC,CAAC,IAAI,uBAAW,CAAC;YACd,GAAG,OAAO,CAAC,EAAE;YACb,SAAS,EACP,OAAO,CAAC,EAAE,CAAC,SAAS,IAAI,IAAA,gCAAuB,EAAC,iCAAqB,EAAE,cAAc,CAAC;SACzF,CAAC;QACJ,CAAC,CAAC,SAAS,CAAC;IACd,OAAO;QACL,OAAO,EAAE;YACP,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,KAAK;YACL,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,SAAS;YACT,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,MAAM,EAAE,IAAA,sBAAa,EAAC,OAAO,CAAC,MAAM,CAAC;SACtC;QACD,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,UAAU,EAAE,QAAQ,CAAC,UAAU;KAChC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { CheckpointWriteItem } from '../types';
2
+ import type { CheckpointerContext } from './setup';
3
+ /**
4
+ * Write special (negative-index) items unconditionally — overwrite is
5
+ * correct there, matching every reference checkpointer implementation. When
6
+ * an offloader is configured, reads each item's previous descriptor first,
7
+ * so a settled outcome can clean up the correct side: the previous
8
+ * descriptor on success, each new upload on a confirmed non-commit (batch
9
+ * write failed, or the previous-descriptor read itself failed before the
10
+ * batch write was even attempted), neither when the outcome is genuinely
11
+ * ambiguous (see {@link splitSpecialOutcome}). Never rejects — a failed read
12
+ * is reported the same way a failed write is, via the return value —
13
+ * because the caller runs this concurrently with writeRegularItems via
14
+ * `Promise.all`, whose own regular-write cleanup depends on every branch of
15
+ * that `Promise.all` resolving rather than short-circuiting on a reject.
16
+ */
17
+ export declare function writeSpecialItemsWithCleanup(context: CheckpointerContext, items: CheckpointWriteItem[]): Promise<Error | undefined>;
18
+ //# sourceMappingURL=special-write-cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"special-write-cleanup.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/special-write-cleanup.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAiFnD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,mBAAmB,EAC5B,KAAK,EAAE,mBAAmB,EAAE,GAC3B,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAoC5B"}
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.writeSpecialItemsWithCleanup = writeSpecialItemsWithCleanup;
4
+ const descriptor_keys_1 = require("../../shared/codec/descriptor-keys");
5
+ const orphans_1 = require("../../shared/codec/s3/orphans");
6
+ const batch_write_1 = require("../../shared/dynamodb/batch-write");
7
+ const retry_1 = require("../../shared/dynamodb/retry");
8
+ /**
9
+ * Read each special item's stored descriptor before it's overwritten —
10
+ * mirrors store/actions/put.ts's readExisting. Keyed by SK (unique per
11
+ * special channel per call).
12
+ */
13
+ async function readPreviousDescriptors(context, items) {
14
+ const previous = new Map();
15
+ for (const item of items) {
16
+ const result = await (0, retry_1.withDynamoDBRetry)(() => context.client.get({
17
+ TableName: context.tableName,
18
+ Key: { PK: item.PK, SK: item.SK },
19
+ ConsistentRead: true,
20
+ ProjectionExpression: '#v',
21
+ ExpressionAttributeNames: { '#v': 'value' },
22
+ }));
23
+ previous.set(item.SK, result.Item?.value);
24
+ }
25
+ return previous;
26
+ }
27
+ /**
28
+ * Split `items` by outcome after a failed batch write (always a
29
+ * BatchWriteAllIncompleteError — batchWriteAll's only throw): never-committed
30
+ * items are safe to clean up their own upload, every other item its previous
31
+ * one — unless a failed chunk's shape is unknown, in which case neither side
32
+ * is touched (a leak beats stranding a live row). Uses `.name`, not
33
+ * `instanceof` (banned repo-wide, see base-error.ts).
34
+ */
35
+ function splitSpecialOutcome(items, error) {
36
+ const neverCommittedSks = new Set();
37
+ let everyChunkTracked = true;
38
+ for (const failedChunk of error.failedChunks) {
39
+ if (failedChunk.name !== 'BatchWriteIncompleteError') {
40
+ everyChunkTracked = false;
41
+ continue;
42
+ }
43
+ for (const request of failedChunk.unprocessed) {
44
+ /** Always a PutRequest echoing this submission — special writes never send DeleteRequests. */
45
+ const { SK } = request.PutRequest.Item;
46
+ neverCommittedSks.add(SK);
47
+ }
48
+ }
49
+ if (!everyChunkTracked)
50
+ return { committed: [], neverCommitted: [] };
51
+ return {
52
+ committed: items.filter((item) => !neverCommittedSks.has(item.SK)),
53
+ neverCommitted: items.filter((item) => neverCommittedSks.has(item.SK)),
54
+ };
55
+ }
56
+ /** Best-effort delete S3 objects backing `descriptors`, if an offloader is configured. */
57
+ async function deleteDescriptors(context, descriptors, label) {
58
+ if (!context.offloader)
59
+ return;
60
+ await (0, orphans_1.cleanUpS3Orphans)(context.offloader, (0, descriptor_keys_1.collectS3Keys)(descriptors), label, context.logger);
61
+ }
62
+ /** Best-effort delete the previous descriptors of `items` (now safely superseded). */
63
+ async function cleanUpPrevious(context, items, previous) {
64
+ const descriptors = items
65
+ .map((item) => previous.get(item.SK))
66
+ .filter((descriptor) => descriptor !== undefined);
67
+ await deleteDescriptors(context, descriptors, 'putWrites.special.previous');
68
+ }
69
+ /**
70
+ * Write special (negative-index) items unconditionally — overwrite is
71
+ * correct there, matching every reference checkpointer implementation. When
72
+ * an offloader is configured, reads each item's previous descriptor first,
73
+ * so a settled outcome can clean up the correct side: the previous
74
+ * descriptor on success, each new upload on a confirmed non-commit (batch
75
+ * write failed, or the previous-descriptor read itself failed before the
76
+ * batch write was even attempted), neither when the outcome is genuinely
77
+ * ambiguous (see {@link splitSpecialOutcome}). Never rejects — a failed read
78
+ * is reported the same way a failed write is, via the return value —
79
+ * because the caller runs this concurrently with writeRegularItems via
80
+ * `Promise.all`, whose own regular-write cleanup depends on every branch of
81
+ * that `Promise.all` resolving rather than short-circuiting on a reject.
82
+ */
83
+ async function writeSpecialItemsWithCleanup(context, items) {
84
+ if (items.length === 0)
85
+ return undefined;
86
+ let previous = new Map();
87
+ if (context.offloader) {
88
+ try {
89
+ previous = await readPreviousDescriptors(context, items);
90
+ }
91
+ catch (error) {
92
+ await deleteDescriptors(context, items.map((item) => item.value), 'putWrites.special.newUpload');
93
+ return error;
94
+ }
95
+ }
96
+ try {
97
+ await (0, batch_write_1.batchWriteAll)(context.client, context.tableName, items.map((item) => ({ PutRequest: { Item: item } })));
98
+ }
99
+ catch (error) {
100
+ const { committed, neverCommitted } = splitSpecialOutcome(items, error);
101
+ await deleteDescriptors(context, neverCommitted.map((item) => item.value), 'putWrites.special.newUpload');
102
+ await cleanUpPrevious(context, committed, previous);
103
+ return error;
104
+ }
105
+ await cleanUpPrevious(context, items, previous);
106
+ return undefined;
107
+ }
108
+ //# sourceMappingURL=special-write-cleanup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"special-write-cleanup.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/special-write-cleanup.ts"],"names":[],"mappings":"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"}