@farukada/aws-langgraph-dynamodb-ts 1.0.0-rc.1 → 1.0.0-rc.2

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 (324) hide show
  1. package/README.md +1621 -288
  2. package/dist/backfill/backfill.d.ts +168 -0
  3. package/dist/backfill/backfill.js +393 -0
  4. package/dist/checkpointer/actions/delete-thread.d.ts +44 -4
  5. package/dist/checkpointer/actions/delete-thread.js +56 -22
  6. package/dist/checkpointer/actions/get-tuple.d.ts +29 -5
  7. package/dist/checkpointer/actions/get-tuple.js +41 -13
  8. package/dist/checkpointer/actions/list.d.ts +42 -10
  9. package/dist/checkpointer/actions/list.js +86 -57
  10. package/dist/checkpointer/actions/put-writes.d.ts +41 -9
  11. package/dist/checkpointer/actions/put-writes.js +62 -48
  12. package/dist/checkpointer/actions/put.d.ts +82 -10
  13. package/dist/checkpointer/actions/put.js +166 -44
  14. package/dist/checkpointer/internal/delta-history.d.ts +112 -0
  15. package/dist/checkpointer/internal/delta-history.js +252 -0
  16. package/dist/checkpointer/internal/listing.d.ts +149 -0
  17. package/dist/checkpointer/internal/listing.js +245 -0
  18. package/dist/checkpointer/internal/parse.d.ts +262 -0
  19. package/dist/checkpointer/internal/parse.js +372 -0
  20. package/dist/checkpointer/internal/pending-writes.d.ts +275 -0
  21. package/dist/checkpointer/internal/pending-writes.js +588 -0
  22. package/dist/checkpointer/internal/read.d.ts +130 -0
  23. package/dist/checkpointer/internal/read.js +264 -0
  24. package/dist/checkpointer/internal/rows.d.ts +571 -0
  25. package/dist/checkpointer/internal/rows.js +834 -0
  26. package/dist/checkpointer/internal/setup.d.ts +42 -21
  27. package/dist/checkpointer/internal/setup.js +65 -31
  28. package/dist/checkpointer/saver.d.ts +242 -42
  29. package/dist/checkpointer/saver.js +257 -53
  30. package/dist/checkpointer/types.d.ts +37 -44
  31. package/dist/checkpointer/types.js +10 -0
  32. package/dist/factory/factory.d.ts +120 -11
  33. package/dist/factory/factory.js +195 -16
  34. package/dist/factory/types.d.ts +23 -4
  35. package/dist/factory/types.js +8 -0
  36. package/dist/history/actions/add-messages.d.ts +30 -2
  37. package/dist/history/actions/add-messages.js +37 -57
  38. package/dist/history/actions/clear.d.ts +46 -4
  39. package/dist/history/actions/clear.js +64 -15
  40. package/dist/history/actions/get-messages.d.ts +41 -0
  41. package/dist/history/actions/get-messages.js +94 -32
  42. package/dist/history/actions/list-sessions.d.ts +52 -10
  43. package/dist/history/actions/list-sessions.js +141 -38
  44. package/dist/history/actions/reconcile-count.d.ts +41 -8
  45. package/dist/history/actions/reconcile-count.js +44 -57
  46. package/dist/history/chat-message-history.d.ts +200 -38
  47. package/dist/history/chat-message-history.js +218 -51
  48. package/dist/history/internal/append.d.ts +212 -0
  49. package/dist/history/internal/append.js +500 -0
  50. package/dist/history/internal/message-read.d.ts +84 -0
  51. package/dist/history/internal/message-read.js +204 -0
  52. package/dist/history/internal/parse.d.ts +153 -0
  53. package/dist/history/internal/parse.js +252 -0
  54. package/dist/history/internal/rows.d.ts +195 -0
  55. package/dist/history/internal/rows.js +250 -0
  56. package/dist/history/internal/session.d.ts +331 -0
  57. package/dist/history/internal/session.js +628 -0
  58. package/dist/history/internal/setup.d.ts +52 -19
  59. package/dist/history/internal/setup.js +90 -27
  60. package/dist/history/session-adapter.d.ts +95 -6
  61. package/dist/history/session-adapter.js +99 -8
  62. package/dist/history/types.d.ts +61 -31
  63. package/dist/history/types.js +10 -0
  64. package/dist/index.d.ts +38 -14
  65. package/dist/index.js +31 -12
  66. package/dist/shared/adapter.d.ts +135 -0
  67. package/dist/shared/adapter.js +143 -0
  68. package/dist/shared/clock.d.ts +48 -4
  69. package/dist/shared/clock.js +51 -4
  70. package/dist/shared/codec/codec.d.ts +275 -9
  71. package/dist/shared/codec/codec.js +394 -19
  72. package/dist/shared/codec/compression.d.ts +43 -6
  73. package/dist/shared/codec/compression.js +52 -11
  74. package/dist/shared/codec/json-serde.d.ts +76 -3
  75. package/dist/shared/codec/json-serde.js +181 -7
  76. package/dist/shared/codec/s3/client-types.d.ts +20 -1
  77. package/dist/shared/codec/s3/client-types.js +20 -1
  78. package/dist/shared/codec/s3/client.d.ts +35 -8
  79. package/dist/shared/codec/s3/client.js +61 -10
  80. package/dist/shared/codec/s3/config.d.ts +226 -22
  81. package/dist/shared/codec/s3/config.js +274 -24
  82. package/dist/shared/codec/s3/lifecycle.d.ts +163 -11
  83. package/dist/shared/codec/s3/lifecycle.js +328 -36
  84. package/dist/shared/codec/s3/offloader.d.ts +390 -19
  85. package/dist/shared/codec/s3/offloader.js +590 -50
  86. package/dist/shared/concurrency.d.ts +40 -3
  87. package/dist/shared/concurrency.js +55 -7
  88. package/dist/shared/dynamodb/abort.d.ts +45 -7
  89. package/dist/shared/dynamodb/abort.js +46 -14
  90. package/dist/shared/dynamodb/batch-write.d.ts +77 -13
  91. package/dist/shared/dynamodb/batch-write.js +146 -26
  92. package/dist/shared/dynamodb/cancellation.d.ts +121 -3
  93. package/dist/shared/dynamodb/cancellation.js +147 -2
  94. package/dist/shared/dynamodb/client.d.ts +159 -12
  95. package/dist/shared/dynamodb/client.js +135 -10
  96. package/dist/shared/dynamodb/idempotent-write.d.ts +551 -0
  97. package/dist/shared/dynamodb/idempotent-write.js +593 -0
  98. package/dist/shared/dynamodb/paginate.d.ts +105 -8
  99. package/dist/shared/dynamodb/paginate.js +175 -6
  100. package/dist/shared/dynamodb/partition-delete.d.ts +181 -16
  101. package/dist/shared/dynamodb/partition-delete.js +312 -43
  102. package/dist/shared/dynamodb/recency-index.d.ts +231 -0
  103. package/dist/shared/dynamodb/recency-index.js +377 -0
  104. package/dist/shared/dynamodb/retry.d.ts +265 -10
  105. package/dist/shared/dynamodb/retry.js +426 -20
  106. package/dist/shared/dynamodb/table-schema.d.ts +190 -0
  107. package/dist/shared/dynamodb/table-schema.js +209 -0
  108. package/dist/shared/errors/base-error.d.ts +174 -8
  109. package/dist/shared/errors/base-error.js +154 -7
  110. package/dist/shared/errors/boundary.d.ts +63 -10
  111. package/dist/shared/errors/boundary.js +110 -13
  112. package/dist/shared/errors/classify.d.ts +97 -0
  113. package/dist/shared/errors/classify.js +257 -0
  114. package/dist/shared/errors/error-code.d.ts +76 -1
  115. package/dist/shared/errors/error-code.js +83 -1
  116. package/dist/shared/errors/errors.d.ts +157 -58
  117. package/dist/shared/errors/errors.js +218 -93
  118. package/dist/shared/logging/logger.d.ts +63 -1
  119. package/dist/shared/logging/logger.js +97 -2
  120. package/dist/shared/logging/redaction.d.ts +86 -6
  121. package/dist/shared/logging/redaction.js +265 -24
  122. package/dist/shared/logging/secret-patterns.d.ts +136 -24
  123. package/dist/shared/logging/secret-patterns.js +165 -30
  124. package/dist/shared/logging/truncate.d.ts +197 -0
  125. package/dist/shared/logging/truncate.js +231 -0
  126. package/dist/shared/options.d.ts +52 -5
  127. package/dist/shared/options.js +9 -0
  128. package/dist/shared/ulid.d.ts +71 -14
  129. package/dist/shared/ulid.js +77 -14
  130. package/dist/shared/validation/collaborators.d.ts +141 -0
  131. package/dist/shared/validation/collaborators.js +188 -0
  132. package/dist/shared/validation/option-shape.d.ts +89 -0
  133. package/dist/shared/validation/option-shape.js +113 -0
  134. package/dist/shared/validation/options.d.ts +142 -5
  135. package/dist/shared/validation/options.js +284 -46
  136. package/dist/shared/validation/primitives.d.ts +287 -24
  137. package/dist/shared/validation/primitives.js +347 -42
  138. package/dist/shared/validation/ttl.d.ts +61 -12
  139. package/dist/shared/validation/ttl.js +102 -22
  140. package/dist/store/actions/list-namespaces.d.ts +76 -5
  141. package/dist/store/actions/list-namespaces.js +160 -22
  142. package/dist/store/actions/put.d.ts +33 -7
  143. package/dist/store/actions/put.js +53 -71
  144. package/dist/store/actions/reconcile-vector-index.d.ts +28 -8
  145. package/dist/store/actions/reconcile-vector-index.js +33 -13
  146. package/dist/store/actions/search.d.ts +34 -5
  147. package/dist/store/actions/search.js +52 -20
  148. package/dist/store/internal/batch-plan.d.ts +24 -9
  149. package/dist/store/internal/batch-plan.js +91 -34
  150. package/dist/store/internal/filter.d.ts +36 -2
  151. package/dist/store/internal/filter.js +66 -14
  152. package/dist/store/internal/get-item.d.ts +45 -0
  153. package/dist/store/internal/get-item.js +115 -0
  154. package/dist/store/internal/item-write.d.ts +230 -0
  155. package/dist/store/internal/item-write.js +463 -0
  156. package/dist/store/internal/parse.d.ts +225 -0
  157. package/dist/store/internal/parse.js +350 -0
  158. package/dist/store/internal/rows.d.ts +355 -0
  159. package/dist/store/internal/rows.js +447 -0
  160. package/dist/store/internal/semantic-search.d.ts +146 -12
  161. package/dist/store/internal/semantic-search.js +317 -15
  162. package/dist/store/internal/setup.d.ts +86 -21
  163. package/dist/store/internal/setup.js +209 -26
  164. package/dist/store/internal/table-search.d.ts +100 -0
  165. package/dist/store/internal/table-search.js +213 -0
  166. package/dist/store/internal/vector-index.d.ts +247 -0
  167. package/dist/store/internal/vector-index.js +546 -0
  168. package/dist/store/store.d.ts +258 -33
  169. package/dist/store/store.js +309 -53
  170. package/dist/store/types.d.ts +74 -28
  171. package/dist/store/types.js +13 -0
  172. package/dist/store/vector-backend.d.ts +64 -3
  173. package/dist/store/vector-backend.js +15 -0
  174. package/package.json +41 -25
  175. package/dist/checkpointer/internal/assemble.d.ts +0 -17
  176. package/dist/checkpointer/internal/assemble.js +0 -38
  177. package/dist/checkpointer/internal/checkpoint-write-verify.d.ts +0 -24
  178. package/dist/checkpointer/internal/checkpoint-write-verify.js +0 -67
  179. package/dist/checkpointer/internal/configurable.d.ts +0 -12
  180. package/dist/checkpointer/internal/configurable.js +0 -24
  181. package/dist/checkpointer/internal/fetch.d.ts +0 -19
  182. package/dist/checkpointer/internal/fetch.js +0 -88
  183. package/dist/checkpointer/internal/filter-match.d.ts +0 -13
  184. package/dist/checkpointer/internal/filter-match.js +0 -15
  185. package/dist/checkpointer/internal/item-reader.d.ts +0 -66
  186. package/dist/checkpointer/internal/item-reader.js +0 -113
  187. package/dist/checkpointer/internal/item-writer.d.ts +0 -35
  188. package/dist/checkpointer/internal/item-writer.js +0 -107
  189. package/dist/checkpointer/internal/keys.d.ts +0 -41
  190. package/dist/checkpointer/internal/keys.js +0 -117
  191. package/dist/checkpointer/internal/list-scope.d.ts +0 -61
  192. package/dist/checkpointer/internal/list-scope.js +0 -108
  193. package/dist/checkpointer/internal/pending-sends.d.ts +0 -13
  194. package/dist/checkpointer/internal/pending-sends.js +0 -29
  195. package/dist/checkpointer/internal/query.d.ts +0 -21
  196. package/dist/checkpointer/internal/query.js +0 -42
  197. package/dist/checkpointer/internal/regular-write.d.ts +0 -28
  198. package/dist/checkpointer/internal/regular-write.js +0 -66
  199. package/dist/checkpointer/internal/special-write-cas.d.ts +0 -29
  200. package/dist/checkpointer/internal/special-write-cas.js +0 -104
  201. package/dist/checkpointer/internal/special-write-cleanup.d.ts +0 -23
  202. package/dist/checkpointer/internal/special-write-cleanup.js +0 -50
  203. package/dist/checkpointer/internal/special-write-verify.d.ts +0 -53
  204. package/dist/checkpointer/internal/special-write-verify.js +0 -80
  205. package/dist/checkpointer/internal/stored-channels.d.ts +0 -31
  206. package/dist/checkpointer/internal/stored-channels.js +0 -70
  207. package/dist/checkpointer/internal/validation.d.ts +0 -18
  208. package/dist/checkpointer/internal/validation.js +0 -40
  209. package/dist/checkpointer/internal/write-guard.d.ts +0 -20
  210. package/dist/checkpointer/internal/write-guard.js +0 -48
  211. package/dist/checkpointer/internal/write-index.d.ts +0 -36
  212. package/dist/checkpointer/internal/write-index.js +0 -41
  213. package/dist/history/internal/append-saga.d.ts +0 -25
  214. package/dist/history/internal/append-saga.js +0 -79
  215. package/dist/history/internal/compensation.d.ts +0 -25
  216. package/dist/history/internal/compensation.js +0 -92
  217. package/dist/history/internal/item-mapper.d.ts +0 -9
  218. package/dist/history/internal/item-mapper.js +0 -27
  219. package/dist/history/internal/keys.d.ts +0 -16
  220. package/dist/history/internal/keys.js +0 -48
  221. package/dist/history/internal/message-chunker.d.ts +0 -13
  222. package/dist/history/internal/message-chunker.js +0 -67
  223. package/dist/history/internal/message-transaction.d.ts +0 -25
  224. package/dist/history/internal/message-transaction.js +0 -61
  225. package/dist/history/internal/message-window.d.ts +0 -17
  226. package/dist/history/internal/message-window.js +0 -49
  227. package/dist/history/internal/query.d.ts +0 -23
  228. package/dist/history/internal/query.js +0 -39
  229. package/dist/history/internal/session-count.d.ts +0 -47
  230. package/dist/history/internal/session-count.js +0 -115
  231. package/dist/history/internal/session-title.d.ts +0 -19
  232. package/dist/history/internal/session-title.js +0 -43
  233. package/dist/history/internal/session-update.d.ts +0 -27
  234. package/dist/history/internal/session-update.js +0 -69
  235. package/dist/history/internal/title-generator.d.ts +0 -12
  236. package/dist/history/internal/title-generator.js +0 -41
  237. package/dist/history/internal/ttl-anchor.d.ts +0 -24
  238. package/dist/history/internal/ttl-anchor.js +0 -38
  239. package/dist/history/internal/validation.d.ts +0 -25
  240. package/dist/history/internal/validation.js +0 -53
  241. package/dist/shared/codec/descriptor-keys.d.ts +0 -12
  242. package/dist/shared/codec/descriptor-keys.js +0 -14
  243. package/dist/shared/codec/encode.d.ts +0 -13
  244. package/dist/shared/codec/encode.js +0 -43
  245. package/dist/shared/codec/payload-loss.d.ts +0 -12
  246. package/dist/shared/codec/payload-loss.js +0 -38
  247. package/dist/shared/codec/s3/adapter-config.d.ts +0 -13
  248. package/dist/shared/codec/s3/adapter-config.js +0 -22
  249. package/dist/shared/codec/s3/bounded-body.d.ts +0 -14
  250. package/dist/shared/codec/s3/bounded-body.js +0 -47
  251. package/dist/shared/codec/s3/delete.d.ts +0 -7
  252. package/dist/shared/codec/s3/delete.js +0 -28
  253. package/dist/shared/codec/s3/key-scope.d.ts +0 -23
  254. package/dist/shared/codec/s3/key-scope.js +0 -44
  255. package/dist/shared/codec/s3/orphans.d.ts +0 -23
  256. package/dist/shared/codec/s3/orphans.js +0 -71
  257. package/dist/shared/codec/s3/read-write.d.ts +0 -18
  258. package/dist/shared/codec/s3/read-write.js +0 -51
  259. package/dist/shared/codec/s3/retry.d.ts +0 -5
  260. package/dist/shared/codec/s3/retry.js +0 -22
  261. package/dist/shared/constants.d.ts +0 -112
  262. package/dist/shared/constants.js +0 -115
  263. package/dist/shared/dynamodb/backoff.d.ts +0 -15
  264. package/dist/shared/dynamodb/backoff.js +0 -48
  265. package/dist/shared/dynamodb/conditional-put.d.ts +0 -67
  266. package/dist/shared/dynamodb/conditional-put.js +0 -76
  267. package/dist/shared/dynamodb/drain-unprocessed.d.ts +0 -21
  268. package/dist/shared/dynamodb/drain-unprocessed.js +0 -43
  269. package/dist/shared/dynamodb/expiry.d.ts +0 -16
  270. package/dist/shared/dynamodb/expiry.js +0 -29
  271. package/dist/shared/dynamodb/paginate-core.d.ts +0 -24
  272. package/dist/shared/dynamodb/paginate-core.js +0 -82
  273. package/dist/shared/dynamodb/retry-classifier.d.ts +0 -17
  274. package/dist/shared/dynamodb/retry-classifier.js +0 -118
  275. package/dist/shared/dynamodb/retry-policy.d.ts +0 -26
  276. package/dist/shared/dynamodb/retry-policy.js +0 -22
  277. package/dist/shared/dynamodb/scan.d.ts +0 -14
  278. package/dist/shared/dynamodb/scan.js +0 -19
  279. package/dist/shared/dynamodb/types.d.ts +0 -23
  280. package/dist/shared/dynamodb/types.js +0 -2
  281. package/dist/shared/errors/upstream-error.d.ts +0 -16
  282. package/dist/shared/errors/upstream-error.js +0 -27
  283. package/dist/shared/errors/wrap-error.d.ts +0 -7
  284. package/dist/shared/errors/wrap-error.js +0 -15
  285. package/dist/shared/logging/redaction-walk.d.ts +0 -23
  286. package/dist/shared/logging/redaction-walk.js +0 -112
  287. package/dist/store/actions/get.d.ts +0 -13
  288. package/dist/store/actions/get.js +0 -73
  289. package/dist/store/internal/backend-search.d.ts +0 -4
  290. package/dist/store/internal/backend-search.js +0 -74
  291. package/dist/store/internal/candidates.d.ts +0 -23
  292. package/dist/store/internal/candidates.js +0 -94
  293. package/dist/store/internal/index-reconcile.d.ts +0 -21
  294. package/dist/store/internal/index-reconcile.js +0 -118
  295. package/dist/store/internal/index-sync.d.ts +0 -10
  296. package/dist/store/internal/index-sync.js +0 -25
  297. package/dist/store/internal/item-mapper.d.ts +0 -27
  298. package/dist/store/internal/item-mapper.js +0 -61
  299. package/dist/store/internal/keys.d.ts +0 -17
  300. package/dist/store/internal/keys.js +0 -41
  301. package/dist/store/internal/namespace-match.d.ts +0 -11
  302. package/dist/store/internal/namespace-match.js +0 -40
  303. package/dist/store/internal/option-validation.d.ts +0 -13
  304. package/dist/store/internal/option-validation.js +0 -77
  305. package/dist/store/internal/overwrite-swap.d.ts +0 -32
  306. package/dist/store/internal/overwrite-swap.js +0 -68
  307. package/dist/store/internal/persist.d.ts +0 -28
  308. package/dist/store/internal/persist.js +0 -64
  309. package/dist/store/internal/query.d.ts +0 -11
  310. package/dist/store/internal/query.js +0 -48
  311. package/dist/store/internal/ranker.d.ts +0 -18
  312. package/dist/store/internal/ranker.js +0 -39
  313. package/dist/store/internal/read-existing.d.ts +0 -23
  314. package/dist/store/internal/read-existing.js +0 -41
  315. package/dist/store/internal/score-direction.d.ts +0 -31
  316. package/dist/store/internal/score-direction.js +0 -38
  317. package/dist/store/internal/search-filter.d.ts +0 -3
  318. package/dist/store/internal/search-filter.js +0 -10
  319. package/dist/store/internal/text-path.d.ts +0 -17
  320. package/dist/store/internal/text-path.js +0 -147
  321. package/dist/store/internal/validation.d.ts +0 -18
  322. package/dist/store/internal/validation.js +0 -51
  323. package/dist/store/internal/write-verify.d.ts +0 -42
  324. package/dist/store/internal/write-verify.js +0 -71
@@ -1,20 +0,0 @@
1
- import type { CheckpointWriteItem } from '../types';
2
- import type { CheckpointerContext } from './setup';
3
- /**
4
- * Report a guard rejection. Sort keys carry their channel, so a rejection
5
- * normally means this exact (task, channel, occurrence) row is already
6
- * committed — a genuine duplicate, and the expected outcome of a retry. A row
7
- * held by a *different* channel is not something this adapter can produce, so
8
- * it is reported at `warn`: the write was not persisted and something else
9
- * wrote to this key space. No attributes returned means the outcome cannot be
10
- * told apart, so it is treated as the ordinary duplicate.
11
- */
12
- export declare function reportGuardRejection(context: CheckpointerContext, item: CheckpointWriteItem, error: Error): void;
13
- /**
14
- * True when the guard rejection's returned row provably belongs to another
15
- * `putWrites` call, so this call's own upload is dead and safe to delete. A
16
- * retried put whose response was lost can be rejected by the row it wrote
17
- * itself, so an equal group — or no attributes at all — proves nothing and
18
- * must not be taken as "my upload is dead".
19
- */
20
- export declare function rejectionProvesForeignRow(item: CheckpointWriteItem, error: Error): boolean;
@@ -1,48 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.reportGuardRejection = reportGuardRejection;
4
- exports.rejectionProvesForeignRow = rejectionProvesForeignRow;
5
- const conditional_put_1 = require("../../shared/dynamodb/conditional-put");
6
- /**
7
- * The channel recorded on the row that turned a write away, or undefined when
8
- * the service returned no attributes (`ReturnValuesOnConditionCheckFailure:
9
- * 'ALL_OLD'` attaches the existing item to the exception at no extra round trip).
10
- */
11
- function rejectedChannel(error) {
12
- return (0, conditional_put_1.rejectedItem)(error)?.channel;
13
- }
14
- /**
15
- * Report a guard rejection. Sort keys carry their channel, so a rejection
16
- * normally means this exact (task, channel, occurrence) row is already
17
- * committed — a genuine duplicate, and the expected outcome of a retry. A row
18
- * held by a *different* channel is not something this adapter can produce, so
19
- * it is reported at `warn`: the write was not persisted and something else
20
- * wrote to this key space. No attributes returned means the outcome cannot be
21
- * told apart, so it is treated as the ordinary duplicate.
22
- */
23
- function reportGuardRejection(context, item, error) {
24
- const found = rejectedChannel(error);
25
- if (found !== undefined && found !== item.channel) {
26
- context.logger.warn('putWrites: write row held by an unexpected channel; write not persisted', {
27
- sortKey: item.SK,
28
- expected: item.channel,
29
- found,
30
- });
31
- return;
32
- }
33
- context.logger.debug('putWrites: skipped a write already committed for this task and channel', {
34
- sortKey: item.SK,
35
- channel: item.channel,
36
- });
37
- }
38
- /**
39
- * True when the guard rejection's returned row provably belongs to another
40
- * `putWrites` call, so this call's own upload is dead and safe to delete. A
41
- * retried put whose response was lost can be rejected by the row it wrote
42
- * itself, so an equal group — or no attributes at all — proves nothing and
43
- * must not be taken as "my upload is dead".
44
- */
45
- function rejectionProvesForeignRow(item, error) {
46
- const group = (0, conditional_put_1.rejectedItem)(error)?.writeGroup;
47
- return group !== undefined && group !== item.writeGroup;
48
- }
@@ -1,36 +0,0 @@
1
- import type { PendingWrite, PendingWriteValue } from '@langchain/langgraph-checkpoint';
2
- /** One write with its sort-key index resolved exactly once. */
3
- export interface ResolvedWrite {
4
- channel: string;
5
- value: PendingWriteValue;
6
- index: number;
7
- /**
8
- * How many earlier writes in this same call already used this channel. A
9
- * retry that emits a channel *more* often than the original call produces a
10
- * row at an occurrence no earlier call ever wrote — it collides with nothing
11
- * and commits cleanly, so the read-side dedup must not mistake it for a
12
- * superseding duplicate.
13
- */
14
- occurrence: number;
15
- }
16
- /**
17
- * Assign every write in one `putWrites` call its sort-key index, in a single
18
- * pass — nothing recomputes it downstream, which is what used to let the
19
- * deduped array's positions disagree with the ones the caller's array
20
- * produced.
21
- *
22
- * A regular write's index is its position in the caller's array, exactly as
23
- * the reference `MemorySaver` computes it: that is what makes stored writes
24
- * replay in the order the task emitted them. A special channel takes its
25
- * fixed `WRITES_IDX_MAP` slot instead, and a later duplicate replaces an
26
- * earlier one (last-write-wins, again matching the reference).
27
- *
28
- * Positions are not stable across calls, which is why the *sort key* also
29
- * carries the channel and each call stamps its rows with a shared
30
- * `writeGroup` — see {@link buildWriteItems} and `dropSupersededWrites`.
31
- *
32
- * `Object.hasOwn` guards WRITES_IDX_MAP's own `Object.prototype` chain — a
33
- * channel literally named `constructor`/`toString`/etc. must be treated as
34
- * regular, not resolve to an inherited function reference.
35
- */
36
- export declare function resolveWriteIndices(writes: PendingWrite[]): ResolvedWrite[];
@@ -1,41 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveWriteIndices = resolveWriteIndices;
4
- const langgraph_checkpoint_1 = require("@langchain/langgraph-checkpoint");
5
- /**
6
- * Assign every write in one `putWrites` call its sort-key index, in a single
7
- * pass — nothing recomputes it downstream, which is what used to let the
8
- * deduped array's positions disagree with the ones the caller's array
9
- * produced.
10
- *
11
- * A regular write's index is its position in the caller's array, exactly as
12
- * the reference `MemorySaver` computes it: that is what makes stored writes
13
- * replay in the order the task emitted them. A special channel takes its
14
- * fixed `WRITES_IDX_MAP` slot instead, and a later duplicate replaces an
15
- * earlier one (last-write-wins, again matching the reference).
16
- *
17
- * Positions are not stable across calls, which is why the *sort key* also
18
- * carries the channel and each call stamps its rows with a shared
19
- * `writeGroup` — see {@link buildWriteItems} and `dropSupersededWrites`.
20
- *
21
- * `Object.hasOwn` guards WRITES_IDX_MAP's own `Object.prototype` chain — a
22
- * channel literally named `constructor`/`toString`/etc. must be treated as
23
- * regular, not resolve to an inherited function reference.
24
- */
25
- function resolveWriteIndices(writes) {
26
- const bySpecialIndex = new Map();
27
- const regular = [];
28
- const occurrences = new Map();
29
- writes.forEach(([channel, value], positional) => {
30
- if (Object.hasOwn(langgraph_checkpoint_1.WRITES_IDX_MAP, channel)) {
31
- const index = langgraph_checkpoint_1.WRITES_IDX_MAP[channel];
32
- /** Last write wins per special channel, so a call holds exactly one. */
33
- bySpecialIndex.set(index, { channel, value, index, occurrence: 0 });
34
- return;
35
- }
36
- const occurrence = occurrences.get(channel) ?? 0;
37
- occurrences.set(channel, occurrence + 1);
38
- regular.push({ channel, value, index: positional, occurrence });
39
- });
40
- return [...bySpecialIndex.values(), ...regular];
41
- }
@@ -1,25 +0,0 @@
1
- import type { ChatMessageItem } from '../types';
2
- import type { HistoryContext } from './setup';
3
- /** Shared per-append metadata applied to every chunk's session update. */
4
- export interface AppendFields {
5
- now: string;
6
- title?: string;
7
- ttlTimestamp?: number;
8
- forceTtlRefresh?: boolean;
9
- }
10
- /**
11
- * Append message chunks with caller-observed atomicity. Each chunk commits its
12
- * messages and count in one transaction; if a later chunk fails, every
13
- * already-committed chunk is deleted and its count reverted, and the batch's
14
- * S3 objects are cleaned once their rows are gone, restoring the pre-call
15
- * state before the error is rethrown. Except on a failed rollback, which
16
- * surfaces as {@link CompensationFailedError} and deliberately leaves the
17
- * committed chunks' S3 objects behind, since their rows may survive.
18
- *
19
- * A `RetryExhaustedError` is ambiguous — the transaction may have committed
20
- * and lost its response — so the chunk is read back first: present means it
21
- * committed (continue), absent means it did not (compensate), and a failed
22
- * read compensates but leaks that chunk's objects rather than delete objects
23
- * its possibly-live rows reference.
24
- */
25
- export declare function appendChunks(context: HistoryContext, sessionId: string, chunks: ChatMessageItem[][], fields: AppendFields, signal?: AbortSignal): Promise<void>;
@@ -1,79 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.appendChunks = appendChunks;
4
- const retry_1 = require("../../shared/dynamodb/retry");
5
- const error_code_1 = require("../../shared/errors/error-code");
6
- const wrap_error_1 = require("../../shared/errors/wrap-error");
7
- const compensation_1 = require("./compensation");
8
- const message_transaction_1 = require("./message-transaction");
9
- /** True for the one failure shape that leaves the outcome ambiguous. */
10
- function isAmbiguous(error) {
11
- return error.code === error_code_1.ErrorCode.RETRY_EXHAUSTED;
12
- }
13
- /**
14
- * Read the chunk's first row back with `ConsistentRead`. A chunk commits
15
- * atomically, so one row present means the whole chunk (and its count `ADD`)
16
- * landed and only the response was lost. Message sort keys are per-call
17
- * ULIDs, so a present row can only be this call's own.
18
- */
19
- async function verifyChunkLanded(context, chunk) {
20
- try {
21
- const result = await (0, retry_1.withDynamoDBRetry)(() => context.client.get({
22
- TableName: context.tableName,
23
- Key: { PK: chunk[0].PK, SK: chunk[0].SK },
24
- ConsistentRead: true,
25
- ProjectionExpression: '#sk',
26
- ExpressionAttributeNames: { '#sk': 'SK' },
27
- }), context.retry);
28
- return result.Item ? 'committed' : 'not-committed';
29
- }
30
- catch {
31
- return 'unverified';
32
- }
33
- }
34
- /** Run one chunk's transaction, returning its error instead of throwing. */
35
- async function commitChunk(context, sessionId, chunk, fields, signal) {
36
- try {
37
- await (0, message_transaction_1.writeMessageChunk)(context, chunk, { ...fields, sessionId, count: chunk.length }, { signal });
38
- return undefined;
39
- }
40
- catch (error) {
41
- return (0, wrap_error_1.toError)(error);
42
- }
43
- }
44
- function asCommitted(chunk) {
45
- return { keys: chunk.map((item) => ({ PK: item.PK, SK: item.SK })), count: chunk.length };
46
- }
47
- /**
48
- * Append message chunks with caller-observed atomicity. Each chunk commits its
49
- * messages and count in one transaction; if a later chunk fails, every
50
- * already-committed chunk is deleted and its count reverted, and the batch's
51
- * S3 objects are cleaned once their rows are gone, restoring the pre-call
52
- * state before the error is rethrown. Except on a failed rollback, which
53
- * surfaces as {@link CompensationFailedError} and deliberately leaves the
54
- * committed chunks' S3 objects behind, since their rows may survive.
55
- *
56
- * A `RetryExhaustedError` is ambiguous — the transaction may have committed
57
- * and lost its response — so the chunk is read back first: present means it
58
- * committed (continue), absent means it did not (compensate), and a failed
59
- * read compensates but leaks that chunk's objects rather than delete objects
60
- * its possibly-live rows reference.
61
- */
62
- async function appendChunks(context, sessionId, chunks, fields, signal) {
63
- const committed = [];
64
- for (const chunk of chunks) {
65
- const failure = await commitChunk(context, sessionId, chunk, fields, signal);
66
- if (!failure) {
67
- committed.push(asCommitted(chunk));
68
- continue;
69
- }
70
- const verdict = isAmbiguous(failure)
71
- ? await verifyChunkLanded(context, chunk)
72
- : 'not-committed';
73
- if (verdict === 'committed') {
74
- committed.push(asCommitted(chunk));
75
- continue;
76
- }
77
- await (0, compensation_1.compensate)(context, sessionId, chunks, committed, failure, fields.now, fields.title, verdict === 'unverified');
78
- }
79
- }
@@ -1,25 +0,0 @@
1
- import type { ChatMessageItem } from '../types';
2
- import type { HistoryContext } from './setup';
3
- /** A chunk that committed, retained so it can be rolled back on a later failure. */
4
- export interface CommittedChunk {
5
- keys: {
6
- PK: string;
7
- SK: string;
8
- }[];
9
- count: number;
10
- }
11
- /**
12
- * Undo a failed batch. Always throws. S3 cleanup is split by commit status so
13
- * no live row is ever left pointing at a deleted object: the never-committed
14
- * suffix is cleaned immediately, the committed prefix only after its rows are
15
- * confirmed deleted. If the rollback itself fails, the committed chunks' S3
16
- * objects are deliberately left in place (their rows may survive) and it
17
- * raises {@link CompensationFailedError} carrying both the trigger and the
18
- * rollback error; otherwise it rethrows the trigger.
19
- *
20
- * `uncertain` marks the failed chunk (`chunks[committed.length]`) as one whose
21
- * outcome could not be verified: its rows may be live, so its objects are
22
- * leaked rather than deleted, while the never-attempted chunks after it are
23
- * still cleaned.
24
- */
25
- export declare function compensate(context: HistoryContext, sessionId: string, chunks: ChatMessageItem[][], committed: CommittedChunk[], trigger: Error, now: string, title: string | undefined, uncertain: boolean): Promise<never>;
@@ -1,92 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.compensate = compensate;
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 errors_1 = require("../../shared/errors/errors");
8
- const wrap_error_1 = require("../../shared/errors/wrap-error");
9
- const session_count_1 = require("./session-count");
10
- /**
11
- * Best-effort delete the offloaded S3 objects of the `chunks` slice given.
12
- * {@link compensate} calls it once per commit status, never for the whole
13
- * batch, so a committed chunk's objects arrive only once its rows are gone.
14
- */
15
- async function cleanBatchS3(context, chunks) {
16
- if (!context.offloader)
17
- return;
18
- const descriptors = chunks.flat().map((item) => item.message);
19
- await (0, orphans_1.cleanUpS3Orphans)(context.offloader, (0, descriptor_keys_1.collectS3Keys)(descriptors), 'history.addMessages', context.logger);
20
- }
21
- /**
22
- * Delete every committed chunk's items, then undo their effect on the session
23
- * row — deleting it outright when this call created it (see
24
- * {@link revertSessionCreation}), so a failed first append leaves no ghost
25
- * session holding the rolled-back message's title. A *partial* delete is not a
26
- * clean creation to undo, so that branch reverts only the count.
27
- */
28
- async function rollbackCommitted(context, sessionId, committed, now, title) {
29
- const keys = committed.flatMap((chunk) => chunk.keys);
30
- const total = committed.reduce((sum, chunk) => sum + chunk.count, 0);
31
- if (keys.length === 0) {
32
- await (0, session_count_1.revertSessionCreation)(context, sessionId, total, now, title);
33
- return;
34
- }
35
- try {
36
- await (0, batch_write_1.batchWriteAll)(context.client, context.tableName, keys.map((Key) => ({ DeleteRequest: { Key } })), { retry: context.retry });
37
- }
38
- catch (error) {
39
- /**
40
- * batchWriteAll has exactly one throw site and it is always a
41
- * BatchWriteAllIncompleteError (see Task 9) — asserted, not instanceof-
42
- * checked, since the false case is unreachable and this project
43
- * enforces 100% branch coverage with no exceptions.
44
- */
45
- const deleted = error.succeededCount;
46
- await (0, session_count_1.revertSessionCount)(context, sessionId, deleted, now);
47
- throw error;
48
- }
49
- await (0, session_count_1.revertSessionCreation)(context, sessionId, total, now, title);
50
- }
51
- /**
52
- * Undo a failed batch. Always throws. S3 cleanup is split by commit status so
53
- * no live row is ever left pointing at a deleted object: the never-committed
54
- * suffix is cleaned immediately, the committed prefix only after its rows are
55
- * confirmed deleted. If the rollback itself fails, the committed chunks' S3
56
- * objects are deliberately left in place (their rows may survive) and it
57
- * raises {@link CompensationFailedError} carrying both the trigger and the
58
- * rollback error; otherwise it rethrows the trigger.
59
- *
60
- * `uncertain` marks the failed chunk (`chunks[committed.length]`) as one whose
61
- * outcome could not be verified: its rows may be live, so its objects are
62
- * leaked rather than deleted, while the never-attempted chunks after it are
63
- * still cleaned.
64
- */
65
- async function compensate(context, sessionId, chunks, committed, trigger, now, title, uncertain) {
66
- if (committed.length > 0) {
67
- context.logger.warn('history.addMessages compensating committed chunks after a chunk failed', {
68
- sessionId,
69
- committedChunks: committed.length,
70
- });
71
- }
72
- /**
73
- * The never-attempted suffix never had a DynamoDB row, so it is safe to
74
- * clean now; an uncertain failed chunk is skipped because its rows may live.
75
- */
76
- const firstDead = committed.length + (uncertain ? 1 : 0);
77
- await cleanBatchS3(context, chunks.slice(firstDead));
78
- try {
79
- await rollbackCommitted(context, sessionId, committed, now, title);
80
- }
81
- catch (rollbackError) {
82
- context.logger.error('history.addMessages rollback failed; messageCount may have drifted', {
83
- sessionId,
84
- committedChunks: committed.length,
85
- });
86
- /** Skip S3 cleanup here: rollback may have failed, so committed rows might still reference these objects. */
87
- throw new errors_1.CompensationFailedError(trigger, (0, wrap_error_1.toError)(rollbackError));
88
- }
89
- /** Only now that committed rows are confirmed deleted is it safe to delete their S3 objects. */
90
- await cleanBatchS3(context, chunks.slice(0, committed.length));
91
- throw trigger;
92
- }
@@ -1,9 +0,0 @@
1
- import type { StoredMessage } from '@langchain/core/messages';
2
- import type { ChatMessageItem } from '../types';
3
- import type { HistoryContext } from './setup';
4
- /**
5
- * Encode a single stored message into its DynamoDB item, keyed by the session
6
- * partition and a `MSG#<ulid>` sort key. A `ttlTimestamp` stamps the uniform
7
- * whole-conversation expiry shared by every message in the session.
8
- */
9
- export declare function buildMessageItem(context: HistoryContext, sessionId: string, ulid: string, message: StoredMessage, ttlTimestamp?: number): Promise<ChatMessageItem>;
@@ -1,27 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildMessageItem = buildMessageItem;
4
- const encode_1 = require("../../shared/codec/encode");
5
- const keys_1 = require("./keys");
6
- function codecDeps(context) {
7
- return { serde: context.serde, compression: context.compression, offloader: context.offloader };
8
- }
9
- /**
10
- * Encode a single stored message into its DynamoDB item, keyed by the session
11
- * partition and a `MSG#<ulid>` sort key. A `ttlTimestamp` stamps the uniform
12
- * whole-conversation expiry shared by every message in the session.
13
- */
14
- async function buildMessageItem(context, sessionId, ulid, message, ttlTimestamp) {
15
- const descriptor = await (0, encode_1.encodePayload)(message, codecDeps(context), {
16
- keyParts: [sessionId, ulid],
17
- });
18
- const item = {
19
- PK: (0, keys_1.sessionPartition)(sessionId),
20
- SK: (0, keys_1.messageSortKey)(ulid),
21
- sessionId,
22
- message: descriptor,
23
- };
24
- if (ttlTimestamp !== undefined)
25
- item.ttl = ttlTimestamp;
26
- return item;
27
- }
@@ -1,16 +0,0 @@
1
- /** Reserved separator joining sort-key segments; forbidden inside a session id. */
2
- export declare const SORT_KEY_SEPARATOR = "#";
3
- /** Fixed sort key for the per-session metadata item. */
4
- export declare const SESSION_SORT_KEY = "HISTORY#SESSION";
5
- /** Partition key for a chat session: the adapter tag plus the session id. */
6
- export declare function sessionPartition(sessionId: string): string;
7
- /** Sort key for a single message item, ordered by its monotonic ULID. */
8
- export declare function messageSortKey(ulid: string): string;
9
- /**
10
- * True when `sortKey` is one this adapter writes. A partition query carries no
11
- * sort-key condition, so a partition-wide delete uses this to leave any row it
12
- * does not own in place rather than deleting the whole partition blindly.
13
- */
14
- export declare function isHistorySortKey(sortKey: string): boolean;
15
- /** `begins_with` prefix selecting every message item in a session. */
16
- export declare function messageSortKeyPrefix(): string;
@@ -1,48 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SESSION_SORT_KEY = exports.SORT_KEY_SEPARATOR = void 0;
4
- exports.sessionPartition = sessionPartition;
5
- exports.messageSortKey = messageSortKey;
6
- exports.isHistorySortKey = isHistorySortKey;
7
- exports.messageSortKeyPrefix = messageSortKeyPrefix;
8
- /** Reserved separator joining sort-key segments; forbidden inside a session id. */
9
- exports.SORT_KEY_SEPARATOR = '#';
10
- /**
11
- * Item-kind tag distinguishing this adapter's sort keys from another
12
- * adapter's on a table shared via `DynamoDBFactory.createAll()` — matches the
13
- * pattern the checkpointer module already uses for its own META#/PAYLOAD#/
14
- * WRITE# keys. Without it, `SESSION_SORT_KEY` alone was a bare, common-word
15
- * literal a store caller could produce by accident (e.g.
16
- * `store.put([sessionId], 'SESSION', ...)`, since `sortKey` collapses a
17
- * single-element namespace down to just the key).
18
- */
19
- const ADAPTER_PREFIX = `HISTORY${exports.SORT_KEY_SEPARATOR}`;
20
- /** Fixed sort key for the per-session metadata item. */
21
- exports.SESSION_SORT_KEY = `${ADAPTER_PREFIX}SESSION`;
22
- const MESSAGE_PREFIX = `${ADAPTER_PREFIX}MSG#`;
23
- /**
24
- * Adapter tag prefixed to every chat-history partition key — see the
25
- * equivalent in checkpointer/internal/keys.ts for why the three adapters'
26
- * partitions must not overlap on a shared table.
27
- */
28
- const ADAPTER_PARTITION_PREFIX = `HIST${exports.SORT_KEY_SEPARATOR}`;
29
- /** Partition key for a chat session: the adapter tag plus the session id. */
30
- function sessionPartition(sessionId) {
31
- return `${ADAPTER_PARTITION_PREFIX}${sessionId}`;
32
- }
33
- /** Sort key for a single message item, ordered by its monotonic ULID. */
34
- function messageSortKey(ulid) {
35
- return `${MESSAGE_PREFIX}${ulid}`;
36
- }
37
- /**
38
- * True when `sortKey` is one this adapter writes. A partition query carries no
39
- * sort-key condition, so a partition-wide delete uses this to leave any row it
40
- * does not own in place rather than deleting the whole partition blindly.
41
- */
42
- function isHistorySortKey(sortKey) {
43
- return sortKey.startsWith(ADAPTER_PREFIX);
44
- }
45
- /** `begins_with` prefix selecting every message item in a session. */
46
- function messageSortKeyPrefix() {
47
- return MESSAGE_PREFIX;
48
- }
@@ -1,13 +0,0 @@
1
- import type { ChatMessageItem } from '../types';
2
- /**
3
- * Conservatively estimate a message item's stored size, so chunks can stay under
4
- * the DynamoDB transaction byte limit. The overhead allowance keeps the estimate
5
- * on the safe side of the real marshalled size.
6
- */
7
- export declare function estimateItemBytes(item: ChatMessageItem): number;
8
- /**
9
- * Split message items into transaction-sized chunks bounded by both the item
10
- * count and the aggregate byte budget. A single item larger than the budget is
11
- * placed alone rather than dropped.
12
- */
13
- export declare function chunkBySize(items: ChatMessageItem[], maxItems: number, maxBytes: number): ChatMessageItem[][];
@@ -1,67 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.estimateItemBytes = estimateItemBytes;
4
- exports.chunkBySize = chunkBySize;
5
- const codec_1 = require("../../shared/codec/codec");
6
- /**
7
- * Per-item allowance added to the measured field bytes to cover what the size
8
- * estimate does not count directly: DynamoDB attribute names, the document
9
- * marshalling envelope, and descriptor scaffolding. Deliberately generous so the
10
- * estimate stays at or above the real marshalled item size and chunks never
11
- * overshoot the transaction byte limit.
12
- */
13
- const ITEM_OVERHEAD_BYTES = 256;
14
- /**
15
- * Byte length of a string as DynamoDB stores it. `String.length` counts UTF-16
16
- * code units, which understates every non-ASCII character — the wrong
17
- * direction for an estimate documented to sit at or above the real size.
18
- */
19
- function utf8Bytes(value) {
20
- return Buffer.byteLength(value, 'utf8');
21
- }
22
- function descriptorBytes(descriptor) {
23
- const body = descriptor.location === codec_1.PayloadLocation.INLINE
24
- ? descriptor.bytes.length
25
- : utf8Bytes(descriptor.s3Key);
26
- return body + utf8Bytes(descriptor.serdeType);
27
- }
28
- /**
29
- * Conservatively estimate a message item's stored size, so chunks can stay under
30
- * the DynamoDB transaction byte limit. The overhead allowance keeps the estimate
31
- * on the safe side of the real marshalled size.
32
- */
33
- function estimateItemBytes(item) {
34
- return (utf8Bytes(item.PK) +
35
- utf8Bytes(item.SK) +
36
- utf8Bytes(item.sessionId) +
37
- descriptorBytes(item.message) +
38
- ITEM_OVERHEAD_BYTES);
39
- }
40
- function shouldFlush(count, bytes, next, max, maxBytes) {
41
- if (count === 0)
42
- return false;
43
- return count >= max || bytes + next > maxBytes;
44
- }
45
- /**
46
- * Split message items into transaction-sized chunks bounded by both the item
47
- * count and the aggregate byte budget. A single item larger than the budget is
48
- * placed alone rather than dropped.
49
- */
50
- function chunkBySize(items, maxItems, maxBytes) {
51
- const chunks = [];
52
- let current = [];
53
- let currentBytes = 0;
54
- for (const item of items) {
55
- const size = estimateItemBytes(item);
56
- if (shouldFlush(current.length, currentBytes, size, maxItems, maxBytes)) {
57
- chunks.push(current);
58
- current = [];
59
- currentBytes = 0;
60
- }
61
- current.push(item);
62
- currentBytes += size;
63
- }
64
- if (current.length > 0)
65
- chunks.push(current);
66
- return chunks;
67
- }
@@ -1,25 +0,0 @@
1
- import type { ChatMessageItem } from '../types';
2
- import { type SessionUpdateFields } from './session-update';
3
- import type { HistoryContext } from './setup';
4
- /** Per-call retry seams (injected in tests to keep backoff instant). */
5
- export interface ChunkRetryOptions {
6
- rng?: () => number;
7
- signal?: AbortSignal;
8
- }
9
- /**
10
- * Atomically write a chunk of message items together with the session-metadata
11
- * count update in one {@link https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html | TransactWriteItems}
12
- * call, so `messageCount` can never disagree with the messages that landed. A
13
- * single `ClientRequestToken` is used per attempt so a re-sent commit (e.g.
14
- * after a lost response) is idempotent and never double-applies the count
15
- * `ADD`. When `fields.forceTtlRefresh` is set, the session update carries a
16
- * monotonic ConditionExpression (see session-update.ts); if — and only if —
17
- * that specific condition loses a race against a concurrent caller who just
18
- * healed the same anchor, this retries the identical chunk once with
19
- * `forceTtlRefresh: false` (safe: `if_not_exists` then converges to whatever
20
- * already won) rather than losing the message writes to a benign ttl race. A
21
- * cancellation caused by any other item (a genuine message-row conflict) is
22
- * not retried here — it propagates for the normal transient-conflict retry
23
- * budget inside `withDynamoDBRetry` to handle, or to the caller otherwise.
24
- */
25
- export declare function writeMessageChunk(context: HistoryContext, items: ChatMessageItem[], fields: SessionUpdateFields, retry?: ChunkRetryOptions): Promise<void>;
@@ -1,61 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.writeMessageChunk = writeMessageChunk;
4
- const node_crypto_1 = require("node:crypto");
5
- const constants_1 = require("../../shared/constants");
6
- const cancellation_1 = require("../../shared/dynamodb/cancellation");
7
- const retry_1 = require("../../shared/dynamodb/retry");
8
- const session_update_1 = require("./session-update");
9
- /** True when a TransactWriteItems cancellation was caused solely by the SESSION update's ttl condition (always TransactItems index 0 — see buildInput below), not by any message item. */
10
- function isTtlConditionLoss(error) {
11
- const reasons = (0, cancellation_1.getCancellationReasons)(error);
12
- return (reasons?.[0]?.Code === 'ConditionalCheckFailed' &&
13
- reasons.slice(1).every((reason) => reason.Code === 'None'));
14
- }
15
- function buildInput(context, items, fields) {
16
- return {
17
- TransactItems: [
18
- (0, session_update_1.buildSessionUpdateItem)(context.tableName, fields),
19
- ...items.map((item) => ({ Put: { TableName: context.tableName, Item: item } })),
20
- ],
21
- ClientRequestToken: (0, node_crypto_1.randomUUID)(),
22
- };
23
- }
24
- async function attempt(context, items, fields, retry) {
25
- const input = buildInput(context, items, fields);
26
- await (0, retry_1.withDynamoDBRetry)(() => context.client.transactWrite(input), {
27
- ...context.retry,
28
- /** The contention floor: a caller policy may raise the budget, never lower it. */
29
- maxAttempts: Math.max(constants_1.MESSAGE_APPEND_RETRY_MAX_ATTEMPTS, context.retry?.maxAttempts ?? 0),
30
- rng: retry.rng,
31
- signal: retry.signal,
32
- });
33
- }
34
- /**
35
- * Atomically write a chunk of message items together with the session-metadata
36
- * count update in one {@link https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html | TransactWriteItems}
37
- * call, so `messageCount` can never disagree with the messages that landed. A
38
- * single `ClientRequestToken` is used per attempt so a re-sent commit (e.g.
39
- * after a lost response) is idempotent and never double-applies the count
40
- * `ADD`. When `fields.forceTtlRefresh` is set, the session update carries a
41
- * monotonic ConditionExpression (see session-update.ts); if — and only if —
42
- * that specific condition loses a race against a concurrent caller who just
43
- * healed the same anchor, this retries the identical chunk once with
44
- * `forceTtlRefresh: false` (safe: `if_not_exists` then converges to whatever
45
- * already won) rather than losing the message writes to a benign ttl race. A
46
- * cancellation caused by any other item (a genuine message-row conflict) is
47
- * not retried here — it propagates for the normal transient-conflict retry
48
- * budget inside `withDynamoDBRetry` to handle, or to the caller otherwise.
49
- */
50
- async function writeMessageChunk(context, items, fields, retry = {}) {
51
- try {
52
- await attempt(context, items, fields, retry);
53
- }
54
- catch (error) {
55
- if (fields.forceTtlRefresh && isTtlConditionLoss(error)) {
56
- await attempt(context, items, { ...fields, forceTtlRefresh: false }, retry);
57
- return;
58
- }
59
- throw error;
60
- }
61
- }