@farukada/aws-langgraph-dynamodb-ts 0.9.0 → 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 (512) hide show
  1. package/README.md +1720 -154
  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 +47 -6
  5. package/dist/checkpointer/actions/delete-thread.js +58 -21
  6. package/dist/checkpointer/actions/get-tuple.d.ts +29 -4
  7. package/dist/checkpointer/actions/get-tuple.js +44 -10
  8. package/dist/checkpointer/actions/list.d.ts +46 -4
  9. package/dist/checkpointer/actions/list.js +121 -66
  10. package/dist/checkpointer/actions/put-writes.d.ts +41 -9
  11. package/dist/checkpointer/actions/put-writes.js +62 -77
  12. package/dist/checkpointer/actions/put.d.ts +83 -4
  13. package/dist/checkpointer/actions/put.js +177 -25
  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 -19
  27. package/dist/checkpointer/internal/setup.js +65 -29
  28. package/dist/checkpointer/saver.d.ts +256 -16
  29. package/dist/checkpointer/saver.js +275 -29
  30. package/dist/checkpointer/types.d.ts +39 -39
  31. package/dist/checkpointer/types.js +10 -1
  32. package/dist/factory/factory.d.ts +134 -28
  33. package/dist/factory/factory.js +240 -21
  34. package/dist/factory/types.d.ts +76 -0
  35. package/dist/factory/types.js +10 -0
  36. package/dist/history/actions/add-messages.d.ts +31 -4
  37. package/dist/history/actions/add-messages.js +38 -58
  38. package/dist/history/actions/clear.d.ts +49 -6
  39. package/dist/history/actions/clear.js +66 -14
  40. package/dist/history/actions/get-messages.d.ts +54 -6
  41. package/dist/history/actions/get-messages.js +126 -43
  42. package/dist/history/actions/list-sessions.d.ts +52 -10
  43. package/dist/history/actions/list-sessions.js +139 -40
  44. package/dist/history/actions/reconcile-count.d.ts +42 -10
  45. package/dist/history/actions/reconcile-count.js +45 -45
  46. package/dist/history/chat-message-history.d.ts +220 -33
  47. package/dist/history/chat-message-history.js +240 -43
  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 -17
  59. package/dist/history/internal/setup.js +92 -21
  60. package/dist/history/session-adapter.d.ts +102 -7
  61. package/dist/history/session-adapter.js +103 -9
  62. package/dist/history/types.d.ts +80 -29
  63. package/dist/history/types.js +10 -1
  64. package/dist/index.d.ts +42 -11
  65. package/dist/index.js +33 -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 +51 -2
  69. package/dist/shared/clock.js +57 -2
  70. package/dist/shared/codec/codec.d.ts +288 -13
  71. package/dist/shared/codec/codec.js +416 -19
  72. package/dist/shared/codec/compression.d.ts +43 -7
  73. package/dist/shared/codec/compression.js +53 -13
  74. package/dist/shared/codec/json-serde.d.ts +76 -4
  75. package/dist/shared/codec/json-serde.js +181 -8
  76. package/dist/shared/codec/s3/client-types.d.ts +53 -0
  77. package/dist/shared/codec/s3/client-types.js +26 -0
  78. package/dist/shared/codec/s3/client.d.ts +43 -10
  79. package/dist/shared/codec/s3/client.js +82 -9
  80. package/dist/shared/codec/s3/config.d.ts +242 -11
  81. package/dist/shared/codec/s3/config.js +293 -11
  82. package/dist/shared/codec/s3/lifecycle.d.ts +164 -6
  83. package/dist/shared/codec/s3/lifecycle.js +335 -27
  84. package/dist/shared/codec/s3/offloader.d.ts +393 -18
  85. package/dist/shared/codec/s3/offloader.js +595 -37
  86. package/dist/shared/concurrency.d.ts +43 -0
  87. package/dist/shared/concurrency.js +78 -0
  88. package/dist/shared/dynamodb/abort.d.ts +47 -0
  89. package/dist/shared/dynamodb/abort.js +59 -0
  90. package/dist/shared/dynamodb/batch-write.d.ts +77 -14
  91. package/dist/shared/dynamodb/batch-write.js +146 -27
  92. package/dist/shared/dynamodb/cancellation.d.ts +121 -4
  93. package/dist/shared/dynamodb/cancellation.js +147 -3
  94. package/dist/shared/dynamodb/client.d.ts +162 -8
  95. package/dist/shared/dynamodb/client.js +153 -5
  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 -9
  99. package/dist/shared/dynamodb/paginate.js +175 -7
  100. package/dist/shared/dynamodb/partition-delete.d.ts +185 -14
  101. package/dist/shared/dynamodb/partition-delete.js +314 -44
  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 +276 -8
  105. package/dist/shared/dynamodb/retry.js +433 -23
  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 +184 -10
  109. package/dist/shared/errors/base-error.js +160 -14
  110. package/dist/shared/errors/boundary.d.ts +71 -0
  111. package/dist/shared/errors/boundary.js +143 -0
  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 +77 -2
  115. package/dist/shared/errors/error-code.js +83 -1
  116. package/dist/shared/errors/errors.d.ts +158 -59
  117. package/dist/shared/errors/errors.js +219 -92
  118. package/dist/shared/logging/logger.d.ts +69 -3
  119. package/dist/shared/logging/logger.js +97 -3
  120. package/dist/shared/logging/redaction.d.ts +92 -8
  121. package/dist/shared/logging/redaction.js +273 -17
  122. package/dist/shared/logging/secret-patterns.d.ts +149 -19
  123. package/dist/shared/logging/secret-patterns.js +188 -27
  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 +59 -7
  127. package/dist/shared/options.js +9 -1
  128. package/dist/shared/ulid.d.ts +77 -7
  129. package/dist/shared/ulid.js +103 -8
  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 +145 -0
  135. package/dist/shared/validation/options.js +328 -0
  136. package/dist/shared/validation/primitives.d.ts +288 -21
  137. package/dist/shared/validation/primitives.js +353 -50
  138. package/dist/shared/validation/ttl.d.ts +66 -10
  139. package/dist/shared/validation/ttl.js +113 -15
  140. package/dist/store/actions/list-namespaces.d.ts +76 -6
  141. package/dist/store/actions/list-namespaces.js +166 -24
  142. package/dist/store/actions/put.d.ts +33 -8
  143. package/dist/store/actions/put.js +53 -60
  144. package/dist/store/actions/reconcile-vector-index.d.ts +31 -10
  145. package/dist/store/actions/reconcile-vector-index.js +34 -15
  146. package/dist/store/actions/search.d.ts +34 -6
  147. package/dist/store/actions/search.js +56 -51
  148. package/dist/store/internal/batch-plan.d.ts +26 -0
  149. package/dist/store/internal/batch-plan.js +109 -0
  150. package/dist/store/internal/filter.d.ts +36 -3
  151. package/dist/store/internal/filter.js +66 -15
  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 +161 -6
  161. package/dist/store/internal/semantic-search.js +360 -18
  162. package/dist/store/internal/setup.d.ts +77 -20
  163. package/dist/store/internal/setup.js +178 -47
  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 +270 -17
  169. package/dist/store/store.js +329 -38
  170. package/dist/store/types.d.ts +76 -26
  171. package/dist/store/types.js +13 -1
  172. package/dist/store/vector-backend.d.ts +64 -4
  173. package/dist/store/vector-backend.js +15 -1
  174. package/package.json +58 -36
  175. package/dist/checkpointer/actions/delete-thread.d.ts.map +0 -1
  176. package/dist/checkpointer/actions/delete-thread.js.map +0 -1
  177. package/dist/checkpointer/actions/get-tuple.d.ts.map +0 -1
  178. package/dist/checkpointer/actions/get-tuple.js.map +0 -1
  179. package/dist/checkpointer/actions/list.d.ts.map +0 -1
  180. package/dist/checkpointer/actions/list.js.map +0 -1
  181. package/dist/checkpointer/actions/put-writes.d.ts.map +0 -1
  182. package/dist/checkpointer/actions/put-writes.js.map +0 -1
  183. package/dist/checkpointer/actions/put.d.ts.map +0 -1
  184. package/dist/checkpointer/actions/put.js.map +0 -1
  185. package/dist/checkpointer/internal/assemble.d.ts +0 -10
  186. package/dist/checkpointer/internal/assemble.d.ts.map +0 -1
  187. package/dist/checkpointer/internal/assemble.js +0 -37
  188. package/dist/checkpointer/internal/assemble.js.map +0 -1
  189. package/dist/checkpointer/internal/configurable.d.ts +0 -13
  190. package/dist/checkpointer/internal/configurable.d.ts.map +0 -1
  191. package/dist/checkpointer/internal/configurable.js +0 -23
  192. package/dist/checkpointer/internal/configurable.js.map +0 -1
  193. package/dist/checkpointer/internal/fetch.d.ts +0 -10
  194. package/dist/checkpointer/internal/fetch.d.ts.map +0 -1
  195. package/dist/checkpointer/internal/fetch.js +0 -46
  196. package/dist/checkpointer/internal/fetch.js.map +0 -1
  197. package/dist/checkpointer/internal/filter-match.d.ts +0 -12
  198. package/dist/checkpointer/internal/filter-match.d.ts.map +0 -1
  199. package/dist/checkpointer/internal/filter-match.js +0 -14
  200. package/dist/checkpointer/internal/filter-match.js.map +0 -1
  201. package/dist/checkpointer/internal/item-reader.d.ts +0 -55
  202. package/dist/checkpointer/internal/item-reader.d.ts.map +0 -1
  203. package/dist/checkpointer/internal/item-reader.js +0 -88
  204. package/dist/checkpointer/internal/item-reader.js.map +0 -1
  205. package/dist/checkpointer/internal/item-writer.d.ts +0 -26
  206. package/dist/checkpointer/internal/item-writer.d.ts.map +0 -1
  207. package/dist/checkpointer/internal/item-writer.js +0 -92
  208. package/dist/checkpointer/internal/item-writer.js.map +0 -1
  209. package/dist/checkpointer/internal/keys.d.ts +0 -31
  210. package/dist/checkpointer/internal/keys.d.ts.map +0 -1
  211. package/dist/checkpointer/internal/keys.js +0 -87
  212. package/dist/checkpointer/internal/keys.js.map +0 -1
  213. package/dist/checkpointer/internal/query.d.ts +0 -20
  214. package/dist/checkpointer/internal/query.d.ts.map +0 -1
  215. package/dist/checkpointer/internal/query.js +0 -36
  216. package/dist/checkpointer/internal/query.js.map +0 -1
  217. package/dist/checkpointer/internal/setup.d.ts.map +0 -1
  218. package/dist/checkpointer/internal/setup.js.map +0 -1
  219. package/dist/checkpointer/internal/special-write-cas.d.ts +0 -30
  220. package/dist/checkpointer/internal/special-write-cas.d.ts.map +0 -1
  221. package/dist/checkpointer/internal/special-write-cas.js +0 -104
  222. package/dist/checkpointer/internal/special-write-cas.js.map +0 -1
  223. package/dist/checkpointer/internal/special-write-cleanup.d.ts +0 -24
  224. package/dist/checkpointer/internal/special-write-cleanup.d.ts.map +0 -1
  225. package/dist/checkpointer/internal/special-write-cleanup.js +0 -47
  226. package/dist/checkpointer/internal/special-write-cleanup.js.map +0 -1
  227. package/dist/checkpointer/internal/special-write-verify.d.ts +0 -54
  228. package/dist/checkpointer/internal/special-write-verify.d.ts.map +0 -1
  229. package/dist/checkpointer/internal/special-write-verify.js +0 -65
  230. package/dist/checkpointer/internal/special-write-verify.js.map +0 -1
  231. package/dist/checkpointer/internal/validation.d.ts +0 -13
  232. package/dist/checkpointer/internal/validation.d.ts.map +0 -1
  233. package/dist/checkpointer/internal/validation.js +0 -30
  234. package/dist/checkpointer/internal/validation.js.map +0 -1
  235. package/dist/checkpointer/internal/write-guard.d.ts +0 -13
  236. package/dist/checkpointer/internal/write-guard.d.ts.map +0 -1
  237. package/dist/checkpointer/internal/write-guard.js +0 -39
  238. package/dist/checkpointer/internal/write-guard.js.map +0 -1
  239. package/dist/checkpointer/internal/write-index.d.ts +0 -37
  240. package/dist/checkpointer/internal/write-index.d.ts.map +0 -1
  241. package/dist/checkpointer/internal/write-index.js +0 -42
  242. package/dist/checkpointer/internal/write-index.js.map +0 -1
  243. package/dist/checkpointer/saver.d.ts.map +0 -1
  244. package/dist/checkpointer/saver.js.map +0 -1
  245. package/dist/checkpointer/types.d.ts.map +0 -1
  246. package/dist/checkpointer/types.js.map +0 -1
  247. package/dist/factory/factory.d.ts.map +0 -1
  248. package/dist/factory/factory.js.map +0 -1
  249. package/dist/history/actions/add-messages.d.ts.map +0 -1
  250. package/dist/history/actions/add-messages.js.map +0 -1
  251. package/dist/history/actions/clear.d.ts.map +0 -1
  252. package/dist/history/actions/clear.js.map +0 -1
  253. package/dist/history/actions/get-messages.d.ts.map +0 -1
  254. package/dist/history/actions/get-messages.js.map +0 -1
  255. package/dist/history/actions/list-sessions.d.ts.map +0 -1
  256. package/dist/history/actions/list-sessions.js.map +0 -1
  257. package/dist/history/actions/reconcile-count.d.ts.map +0 -1
  258. package/dist/history/actions/reconcile-count.js.map +0 -1
  259. package/dist/history/chat-message-history.d.ts.map +0 -1
  260. package/dist/history/chat-message-history.js.map +0 -1
  261. package/dist/history/internal/append-saga.d.ts +0 -20
  262. package/dist/history/internal/append-saga.d.ts.map +0 -1
  263. package/dist/history/internal/append-saga.js +0 -35
  264. package/dist/history/internal/append-saga.js.map +0 -1
  265. package/dist/history/internal/compensation.d.ts +0 -21
  266. package/dist/history/internal/compensation.d.ts.map +0 -1
  267. package/dist/history/internal/compensation.js +0 -84
  268. package/dist/history/internal/compensation.js.map +0 -1
  269. package/dist/history/internal/item-mapper.d.ts +0 -12
  270. package/dist/history/internal/item-mapper.d.ts.map +0 -1
  271. package/dist/history/internal/item-mapper.js +0 -33
  272. package/dist/history/internal/item-mapper.js.map +0 -1
  273. package/dist/history/internal/keys.d.ts +0 -17
  274. package/dist/history/internal/keys.d.ts.map +0 -1
  275. package/dist/history/internal/keys.js +0 -49
  276. package/dist/history/internal/keys.js.map +0 -1
  277. package/dist/history/internal/message-chunker.d.ts +0 -14
  278. package/dist/history/internal/message-chunker.d.ts.map +0 -1
  279. package/dist/history/internal/message-chunker.js +0 -68
  280. package/dist/history/internal/message-chunker.js.map +0 -1
  281. package/dist/history/internal/message-transaction.d.ts +0 -26
  282. package/dist/history/internal/message-transaction.d.ts.map +0 -1
  283. package/dist/history/internal/message-transaction.js +0 -60
  284. package/dist/history/internal/message-transaction.js.map +0 -1
  285. package/dist/history/internal/query.d.ts +0 -10
  286. package/dist/history/internal/query.d.ts.map +0 -1
  287. package/dist/history/internal/query.js +0 -31
  288. package/dist/history/internal/query.js.map +0 -1
  289. package/dist/history/internal/session-count.d.ts +0 -41
  290. package/dist/history/internal/session-count.d.ts.map +0 -1
  291. package/dist/history/internal/session-count.js +0 -109
  292. package/dist/history/internal/session-count.js.map +0 -1
  293. package/dist/history/internal/session-title.d.ts +0 -20
  294. package/dist/history/internal/session-title.d.ts.map +0 -1
  295. package/dist/history/internal/session-title.js +0 -44
  296. package/dist/history/internal/session-title.js.map +0 -1
  297. package/dist/history/internal/session-update.d.ts +0 -28
  298. package/dist/history/internal/session-update.d.ts.map +0 -1
  299. package/dist/history/internal/session-update.js +0 -70
  300. package/dist/history/internal/session-update.js.map +0 -1
  301. package/dist/history/internal/setup.d.ts.map +0 -1
  302. package/dist/history/internal/setup.js.map +0 -1
  303. package/dist/history/internal/title-generator.d.ts +0 -13
  304. package/dist/history/internal/title-generator.d.ts.map +0 -1
  305. package/dist/history/internal/title-generator.js +0 -25
  306. package/dist/history/internal/title-generator.js.map +0 -1
  307. package/dist/history/internal/ttl-anchor.d.ts +0 -25
  308. package/dist/history/internal/ttl-anchor.d.ts.map +0 -1
  309. package/dist/history/internal/ttl-anchor.js +0 -38
  310. package/dist/history/internal/ttl-anchor.js.map +0 -1
  311. package/dist/history/internal/validation.d.ts +0 -9
  312. package/dist/history/internal/validation.d.ts.map +0 -1
  313. package/dist/history/internal/validation.js +0 -16
  314. package/dist/history/internal/validation.js.map +0 -1
  315. package/dist/history/session-adapter.d.ts.map +0 -1
  316. package/dist/history/session-adapter.js.map +0 -1
  317. package/dist/history/types.d.ts.map +0 -1
  318. package/dist/history/types.js.map +0 -1
  319. package/dist/index.d.ts.map +0 -1
  320. package/dist/index.js.map +0 -1
  321. package/dist/shared/clock.d.ts.map +0 -1
  322. package/dist/shared/clock.js.map +0 -1
  323. package/dist/shared/codec/codec.d.ts.map +0 -1
  324. package/dist/shared/codec/codec.js.map +0 -1
  325. package/dist/shared/codec/compression.d.ts.map +0 -1
  326. package/dist/shared/codec/compression.js.map +0 -1
  327. package/dist/shared/codec/descriptor-keys.d.ts +0 -4
  328. package/dist/shared/codec/descriptor-keys.d.ts.map +0 -1
  329. package/dist/shared/codec/descriptor-keys.js +0 -14
  330. package/dist/shared/codec/descriptor-keys.js.map +0 -1
  331. package/dist/shared/codec/json-serde.d.ts.map +0 -1
  332. package/dist/shared/codec/json-serde.js.map +0 -1
  333. package/dist/shared/codec/s3/client.d.ts.map +0 -1
  334. package/dist/shared/codec/s3/client.js.map +0 -1
  335. package/dist/shared/codec/s3/config.d.ts.map +0 -1
  336. package/dist/shared/codec/s3/config.js.map +0 -1
  337. package/dist/shared/codec/s3/delete.d.ts +0 -8
  338. package/dist/shared/codec/s3/delete.d.ts.map +0 -1
  339. package/dist/shared/codec/s3/delete.js +0 -29
  340. package/dist/shared/codec/s3/delete.js.map +0 -1
  341. package/dist/shared/codec/s3/lifecycle.d.ts.map +0 -1
  342. package/dist/shared/codec/s3/lifecycle.js.map +0 -1
  343. package/dist/shared/codec/s3/offloader.d.ts.map +0 -1
  344. package/dist/shared/codec/s3/offloader.js.map +0 -1
  345. package/dist/shared/codec/s3/orphans.d.ts +0 -18
  346. package/dist/shared/codec/s3/orphans.d.ts.map +0 -1
  347. package/dist/shared/codec/s3/orphans.js +0 -58
  348. package/dist/shared/codec/s3/orphans.js.map +0 -1
  349. package/dist/shared/codec/s3/read-write.d.ts +0 -14
  350. package/dist/shared/codec/s3/read-write.d.ts.map +0 -1
  351. package/dist/shared/codec/s3/read-write.js +0 -43
  352. package/dist/shared/codec/s3/read-write.js.map +0 -1
  353. package/dist/shared/codec/s3/retry.d.ts +0 -5
  354. package/dist/shared/codec/s3/retry.d.ts.map +0 -1
  355. package/dist/shared/codec/s3/retry.js +0 -25
  356. package/dist/shared/codec/s3/retry.js.map +0 -1
  357. package/dist/shared/constants.d.ts +0 -64
  358. package/dist/shared/constants.d.ts.map +0 -1
  359. package/dist/shared/constants.js +0 -67
  360. package/dist/shared/constants.js.map +0 -1
  361. package/dist/shared/dynamodb/backoff.d.ts +0 -15
  362. package/dist/shared/dynamodb/backoff.d.ts.map +0 -1
  363. package/dist/shared/dynamodb/backoff.js +0 -48
  364. package/dist/shared/dynamodb/backoff.js.map +0 -1
  365. package/dist/shared/dynamodb/batch-write.d.ts.map +0 -1
  366. package/dist/shared/dynamodb/batch-write.js.map +0 -1
  367. package/dist/shared/dynamodb/cancellation.d.ts.map +0 -1
  368. package/dist/shared/dynamodb/cancellation.js.map +0 -1
  369. package/dist/shared/dynamodb/client.d.ts.map +0 -1
  370. package/dist/shared/dynamodb/client.js.map +0 -1
  371. package/dist/shared/dynamodb/conditional-put.d.ts +0 -51
  372. package/dist/shared/dynamodb/conditional-put.d.ts.map +0 -1
  373. package/dist/shared/dynamodb/conditional-put.js +0 -59
  374. package/dist/shared/dynamodb/conditional-put.js.map +0 -1
  375. package/dist/shared/dynamodb/drain-unprocessed.d.ts +0 -19
  376. package/dist/shared/dynamodb/drain-unprocessed.d.ts.map +0 -1
  377. package/dist/shared/dynamodb/drain-unprocessed.js +0 -44
  378. package/dist/shared/dynamodb/drain-unprocessed.js.map +0 -1
  379. package/dist/shared/dynamodb/paginate-core.d.ts +0 -22
  380. package/dist/shared/dynamodb/paginate-core.d.ts.map +0 -1
  381. package/dist/shared/dynamodb/paginate-core.js +0 -52
  382. package/dist/shared/dynamodb/paginate-core.js.map +0 -1
  383. package/dist/shared/dynamodb/paginate.d.ts.map +0 -1
  384. package/dist/shared/dynamodb/paginate.js.map +0 -1
  385. package/dist/shared/dynamodb/partition-delete.d.ts.map +0 -1
  386. package/dist/shared/dynamodb/partition-delete.js.map +0 -1
  387. package/dist/shared/dynamodb/retry-classifier.d.ts +0 -9
  388. package/dist/shared/dynamodb/retry-classifier.d.ts.map +0 -1
  389. package/dist/shared/dynamodb/retry-classifier.js +0 -87
  390. package/dist/shared/dynamodb/retry-classifier.js.map +0 -1
  391. package/dist/shared/dynamodb/retry.d.ts.map +0 -1
  392. package/dist/shared/dynamodb/retry.js.map +0 -1
  393. package/dist/shared/dynamodb/scan.d.ts +0 -15
  394. package/dist/shared/dynamodb/scan.d.ts.map +0 -1
  395. package/dist/shared/dynamodb/scan.js +0 -20
  396. package/dist/shared/dynamodb/scan.js.map +0 -1
  397. package/dist/shared/dynamodb/types.d.ts +0 -24
  398. package/dist/shared/dynamodb/types.d.ts.map +0 -1
  399. package/dist/shared/dynamodb/types.js +0 -3
  400. package/dist/shared/dynamodb/types.js.map +0 -1
  401. package/dist/shared/errors/base-error.d.ts.map +0 -1
  402. package/dist/shared/errors/base-error.js.map +0 -1
  403. package/dist/shared/errors/error-code.d.ts.map +0 -1
  404. package/dist/shared/errors/error-code.js.map +0 -1
  405. package/dist/shared/errors/errors.d.ts.map +0 -1
  406. package/dist/shared/errors/errors.js.map +0 -1
  407. package/dist/shared/errors/wrap-error.d.ts +0 -16
  408. package/dist/shared/errors/wrap-error.d.ts.map +0 -1
  409. package/dist/shared/errors/wrap-error.js +0 -30
  410. package/dist/shared/errors/wrap-error.js.map +0 -1
  411. package/dist/shared/logging/logger.d.ts.map +0 -1
  412. package/dist/shared/logging/logger.js.map +0 -1
  413. package/dist/shared/logging/redaction-walk.d.ts +0 -23
  414. package/dist/shared/logging/redaction-walk.d.ts.map +0 -1
  415. package/dist/shared/logging/redaction-walk.js +0 -92
  416. package/dist/shared/logging/redaction-walk.js.map +0 -1
  417. package/dist/shared/logging/redaction.d.ts.map +0 -1
  418. package/dist/shared/logging/redaction.js.map +0 -1
  419. package/dist/shared/logging/secret-patterns.d.ts.map +0 -1
  420. package/dist/shared/logging/secret-patterns.js.map +0 -1
  421. package/dist/shared/options.d.ts.map +0 -1
  422. package/dist/shared/options.js.map +0 -1
  423. package/dist/shared/ulid.d.ts.map +0 -1
  424. package/dist/shared/ulid.js.map +0 -1
  425. package/dist/shared/validation/primitives.d.ts.map +0 -1
  426. package/dist/shared/validation/primitives.js.map +0 -1
  427. package/dist/shared/validation/ttl.d.ts.map +0 -1
  428. package/dist/shared/validation/ttl.js.map +0 -1
  429. package/dist/store/actions/get.d.ts +0 -5
  430. package/dist/store/actions/get.d.ts.map +0 -1
  431. package/dist/store/actions/get.js +0 -35
  432. package/dist/store/actions/get.js.map +0 -1
  433. package/dist/store/actions/list-namespaces.d.ts.map +0 -1
  434. package/dist/store/actions/list-namespaces.js.map +0 -1
  435. package/dist/store/actions/put.d.ts.map +0 -1
  436. package/dist/store/actions/put.js.map +0 -1
  437. package/dist/store/actions/reconcile-vector-index.d.ts.map +0 -1
  438. package/dist/store/actions/reconcile-vector-index.js.map +0 -1
  439. package/dist/store/actions/search.d.ts.map +0 -1
  440. package/dist/store/actions/search.js.map +0 -1
  441. package/dist/store/internal/backend-search.d.ts +0 -5
  442. package/dist/store/internal/backend-search.d.ts.map +0 -1
  443. package/dist/store/internal/backend-search.js +0 -68
  444. package/dist/store/internal/backend-search.js.map +0 -1
  445. package/dist/store/internal/filter.d.ts.map +0 -1
  446. package/dist/store/internal/filter.js.map +0 -1
  447. package/dist/store/internal/index-reconcile.d.ts +0 -22
  448. package/dist/store/internal/index-reconcile.d.ts.map +0 -1
  449. package/dist/store/internal/index-reconcile.js +0 -105
  450. package/dist/store/internal/index-reconcile.js.map +0 -1
  451. package/dist/store/internal/index-sync.d.ts +0 -11
  452. package/dist/store/internal/index-sync.d.ts.map +0 -1
  453. package/dist/store/internal/index-sync.js +0 -26
  454. package/dist/store/internal/index-sync.js.map +0 -1
  455. package/dist/store/internal/item-mapper.d.ts +0 -25
  456. package/dist/store/internal/item-mapper.d.ts.map +0 -1
  457. package/dist/store/internal/item-mapper.js +0 -53
  458. package/dist/store/internal/item-mapper.js.map +0 -1
  459. package/dist/store/internal/keys.d.ts +0 -18
  460. package/dist/store/internal/keys.d.ts.map +0 -1
  461. package/dist/store/internal/keys.js +0 -42
  462. package/dist/store/internal/keys.js.map +0 -1
  463. package/dist/store/internal/namespace-match.d.ts +0 -12
  464. package/dist/store/internal/namespace-match.d.ts.map +0 -1
  465. package/dist/store/internal/namespace-match.js +0 -41
  466. package/dist/store/internal/namespace-match.js.map +0 -1
  467. package/dist/store/internal/overwrite-swap.d.ts +0 -33
  468. package/dist/store/internal/overwrite-swap.d.ts.map +0 -1
  469. package/dist/store/internal/overwrite-swap.js +0 -62
  470. package/dist/store/internal/overwrite-swap.js.map +0 -1
  471. package/dist/store/internal/persist.d.ts +0 -27
  472. package/dist/store/internal/persist.d.ts.map +0 -1
  473. package/dist/store/internal/persist.js +0 -59
  474. package/dist/store/internal/persist.js.map +0 -1
  475. package/dist/store/internal/query.d.ts +0 -6
  476. package/dist/store/internal/query.d.ts.map +0 -1
  477. package/dist/store/internal/query.js +0 -32
  478. package/dist/store/internal/query.js.map +0 -1
  479. package/dist/store/internal/ranker.d.ts +0 -13
  480. package/dist/store/internal/ranker.d.ts.map +0 -1
  481. package/dist/store/internal/ranker.js +0 -31
  482. package/dist/store/internal/ranker.js.map +0 -1
  483. package/dist/store/internal/read-existing.d.ts +0 -19
  484. package/dist/store/internal/read-existing.d.ts.map +0 -1
  485. package/dist/store/internal/read-existing.js +0 -29
  486. package/dist/store/internal/read-existing.js.map +0 -1
  487. package/dist/store/internal/score-direction.d.ts +0 -32
  488. package/dist/store/internal/score-direction.d.ts.map +0 -1
  489. package/dist/store/internal/score-direction.js +0 -39
  490. package/dist/store/internal/score-direction.js.map +0 -1
  491. package/dist/store/internal/search-filter.d.ts +0 -4
  492. package/dist/store/internal/search-filter.d.ts.map +0 -1
  493. package/dist/store/internal/search-filter.js +0 -11
  494. package/dist/store/internal/search-filter.js.map +0 -1
  495. package/dist/store/internal/semantic-search.d.ts.map +0 -1
  496. package/dist/store/internal/semantic-search.js.map +0 -1
  497. package/dist/store/internal/setup.d.ts.map +0 -1
  498. package/dist/store/internal/setup.js.map +0 -1
  499. package/dist/store/internal/validation.d.ts +0 -13
  500. package/dist/store/internal/validation.d.ts.map +0 -1
  501. package/dist/store/internal/validation.js +0 -35
  502. package/dist/store/internal/validation.js.map +0 -1
  503. package/dist/store/internal/write-verify.d.ts +0 -37
  504. package/dist/store/internal/write-verify.d.ts.map +0 -1
  505. package/dist/store/internal/write-verify.js +0 -68
  506. package/dist/store/internal/write-verify.js.map +0 -1
  507. package/dist/store/store.d.ts.map +0 -1
  508. package/dist/store/store.js.map +0 -1
  509. package/dist/store/types.d.ts.map +0 -1
  510. package/dist/store/types.js.map +0 -1
  511. package/dist/store/vector-backend.d.ts.map +0 -1
  512. package/dist/store/vector-backend.js.map +0 -1
@@ -1,15 +1,53 @@
1
1
  "use strict";
2
+ /**
3
+ * Hides S3 behind one object.
4
+ *
5
+ * The client is built lazily from the optional peer. An upload writes only
6
+ * while its key is free, so a retried upload never replaces another write's
7
+ * object, and carries its row's key as metadata. A download refuses an object
8
+ * over the configured cap before and while reading it. A delete goes out in
9
+ * batches of a thousand, and a best-effort release retries transient failures,
10
+ * never deletes outside the row's own path, and logs rather than throws.
11
+ */
2
12
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.S3Offloader = void 0;
4
- const constants_1 = require("../../constants");
5
- const client_1 = require("./client");
13
+ exports.S3Offloader = exports.S3_DELETE_BATCH_MAX = exports.DEFAULT_MAX_S3_DOWNLOAD_BYTES = exports.DEFAULT_S3_SSE = exports.DEFAULT_S3_THRESHOLD_BYTES = void 0;
14
+ exports.uploadObject = uploadObject;
15
+ exports.downloadObject = downloadObject;
16
+ exports.oversizedObjectError = oversizedObjectError;
17
+ exports.readBodyBounded = readBodyBounded;
18
+ exports.deleteObjects = deleteObjects;
19
+ exports.backlinkMetadata = backlinkMetadata;
20
+ exports.metadataBytes = metadataBytes;
21
+ exports.cleanUpS3Orphans = cleanUpS3Orphans;
22
+ const abort_1 = require("../../dynamodb/abort");
23
+ const client_1 = require("../../dynamodb/client");
24
+ const retry_1 = require("../../dynamodb/retry");
25
+ const base_error_1 = require("../../errors/base-error");
26
+ const classify_1 = require("../../errors/classify");
27
+ const error_code_1 = require("../../errors/error-code");
28
+ const logger_1 = require("../../logging/logger");
29
+ const secret_patterns_1 = require("../../logging/secret-patterns");
30
+ const truncate_1 = require("../../logging/truncate");
31
+ const client_2 = require("./client");
6
32
  const config_1 = require("./config");
7
- const delete_1 = require("./delete");
8
33
  const lifecycle_1 = require("./lifecycle");
9
- const read_write_1 = require("./read-write");
34
+ /** Default payload size that triggers S3 offload (350 KB; 50 KB under the DDB 400 KB cap). */
35
+ exports.DEFAULT_S3_THRESHOLD_BYTES = 350 * 1024;
36
+ /** Default S3 server-side encryption algorithm. */
37
+ exports.DEFAULT_S3_SSE = 'AES256';
10
38
  /**
11
- * Thin holder composing the pure S3 functions. Owns config + the lazily-built
12
- * S3 client and delegates all real work; every method is a small delegation.
39
+ * Default cap on an offloaded object buffered from S3 (50 MiB), checked against
40
+ * `ContentLength` before the body is read and enforced while streaming when
41
+ * the length is unknown. Together with
42
+ * `DEFAULT_MAX_DECOMPRESSED_BYTES` (`src/shared/codec/compression.ts`)
43
+ * it bounds the memory any single payload can claim.
44
+ */
45
+ exports.DEFAULT_MAX_S3_DOWNLOAD_BYTES = 50 * 1024 * 1024;
46
+ /** S3 DeleteObjects maximum keys per request. */
47
+ exports.S3_DELETE_BATCH_MAX = 1000;
48
+ /**
49
+ * Owns the S3 configuration and the lazily-built client; every method is a
50
+ * small delegation to this module's own functions, keyed to that config.
13
51
  */
14
52
  class S3Offloader {
15
53
  clientPromise;
@@ -20,26 +58,52 @@ class S3Offloader {
20
58
  thresholdBytes;
21
59
  sse;
22
60
  sseKmsKeyId;
61
+ maxDownloadBytes;
23
62
  config;
63
+ /**
64
+ * Accepts: `config` — already validated by the adapter that builds this, with
65
+ * its key prefix resolved to the adapter's own path.
66
+ *
67
+ * Returns: an offloader whose S3 client is built lazily, on the first
68
+ * operation that needs one.
69
+ *
70
+ * Throws: nothing. A missing `@aws-sdk/client-s3` is not raised here: the
71
+ * import is warmed so the failure surfaces, typed, on the first S3 operation
72
+ * rather than on the first oversize payload days later.
73
+ */
24
74
  constructor(config) {
25
75
  this.config = config;
26
76
  this.bucketName = config.bucketName;
27
- this.keyPrefix = config.keyPrefix ?? constants_1.DEFAULT_S3_KEY_PREFIX;
28
- this.thresholdBytes = config.thresholdBytes ?? constants_1.DEFAULT_S3_THRESHOLD_BYTES;
29
- this.sse = config.serverSideEncryption ?? constants_1.DEFAULT_S3_SSE;
77
+ this.keyPrefix = config.keyPrefix ?? config_1.DEFAULT_S3_KEY_PREFIX;
78
+ this.thresholdBytes = config.thresholdBytes ?? exports.DEFAULT_S3_THRESHOLD_BYTES;
79
+ this.sse = config.serverSideEncryption ?? exports.DEFAULT_S3_SSE;
30
80
  this.sseKmsKeyId = config.sseKmsKeyId;
81
+ this.maxDownloadBytes = config.maxDownloadBytes ?? exports.DEFAULT_MAX_S3_DOWNLOAD_BYTES;
82
+ // Warm the optional peer's import so a missing `@aws-sdk/client-s3`
83
+ // surfaces on the very first S3 operation, typed, rather than on the first
84
+ // oversize payload days later. The rejection is handled here; whichever
85
+ // operation runs first re-raises it through its own `loadS3Sdk()` call.
86
+ void (0, client_2.loadS3Sdk)().catch(() => undefined);
31
87
  }
32
88
  getClient() {
33
89
  if (!this.clientPromise) {
34
90
  const cfg = this.config.clientConfig ?? {};
91
+ // The hook is typed structurally for consumers; the runtime modules use
92
+ // the real SDK client. It hands over a constructor, not a configuration,
93
+ // so a caller who supplies one has not opted out of the bound: the same
94
+ // default handler {@link createDefaultS3Client} applies reaches it, for
95
+ // the reason recorded there. `cfg` still spreads last, so a caller who
96
+ // does want to replace it puts a `requestHandler` in `clientConfig`.
35
97
  this.clientPromise = (this.config.createS3Client
36
- ? Promise.resolve(this.config.createS3Client({ maxAttempts: 1, ...cfg }))
37
- : (0, client_1.createDefaultS3Client)(cfg)).then((client) => {
98
+ ? Promise.resolve(this.config.createS3Client({
99
+ maxAttempts: 1,
100
+ requestHandler: { socketTimeout: client_1.DEFAULT_SOCKET_TIMEOUT_MS },
101
+ ...cfg,
102
+ }))
103
+ : (0, client_2.createDefaultS3Client)(cfg)).then((client) => {
38
104
  this.resolvedClient = client;
39
- /**
40
- * `destroy()` may have run during this construction, when there was
41
- * no client yet to release. Release it now instead of leaking it.
42
- */
105
+ // `destroy()` may have run during this construction, when there was
106
+ // no client yet to release. Release it now instead of leaking it.
43
107
  if (this.destroyed)
44
108
  client.destroy();
45
109
  return client;
@@ -50,46 +114,167 @@ class S3Offloader {
50
114
  }
51
115
  return this.clientPromise;
52
116
  }
53
- /** True when `data` is large enough to warrant S3 offload. */
117
+ /**
118
+ * Whether `data` is large enough to warrant S3 offload.
119
+ *
120
+ * Accepts: the encoded payload, after compression — what would actually be
121
+ * stored.
122
+ *
123
+ * Returns: whether it reaches `thresholdBytes`. The comparison is inclusive,
124
+ * so a payload exactly at the threshold offloads.
125
+ *
126
+ * Throws: nothing.
127
+ */
54
128
  shouldOffload(data) {
55
129
  return data.length >= this.thresholdBytes;
56
130
  }
57
- /** Build the S3 key for the given key parts. */
58
- buildKey(parts) {
59
- return (0, config_1.buildS3Key)(this.keyPrefix, parts);
131
+ /**
132
+ * Build the S3 key of the object write `objectId` uploads for the row `parts`
133
+ * identify.
134
+ *
135
+ * Accepts: `parts` — at least one; the row's identity. `objectId` — the
136
+ * uploading write's id, appended as it is: key-safe, with no `/` (see
137
+ * {@link buildS3Key}).
138
+ *
139
+ * Returns: the key, under this offloader's prefix.
140
+ *
141
+ * Throws: `VALIDATION` naming `s3Key` for empty `parts` or a key over
142
+ * S3's 1024-byte cap.
143
+ */
144
+ buildKey(parts, objectId) {
145
+ return (0, config_1.buildS3Key)(this.keyPrefix, parts, objectId);
60
146
  }
61
- /** The configured key prefix. */
147
+ /**
148
+ * The configured key prefix.
149
+ *
150
+ * Accepts: nothing.
151
+ *
152
+ * Returns: the prefix every key this offloader builds starts with, which is
153
+ * what a lifecycle rule is scoped to.
154
+ *
155
+ * Throws: nothing.
156
+ */
62
157
  getKeyPrefix() {
63
158
  return this.keyPrefix;
64
159
  }
65
- /** Upload `data` under `key`, returning the key. */
66
- async upload(key, data) {
67
- await (0, read_write_1.uploadObject)(await this.getClient(), {
160
+ /**
161
+ * Whether `key` lies under this offloader's prefix and the `scope` parts'
162
+ * path.
163
+ *
164
+ * Accepts: `key` — any key, including one read off a row. `scope` — the
165
+ * identity of the row that claims it.
166
+ *
167
+ * Returns: whether the row may address that object.
168
+ *
169
+ * Throws: nothing — this is the question form; {@link assertOwnedKey} is the
170
+ * refusing form.
171
+ */
172
+ ownsKey(key, scope) {
173
+ return (0, config_1.isKeyInScope)(key, this.keyPrefix, scope);
174
+ }
175
+ /**
176
+ * Refuse a row-sourced key outside the row's own path.
177
+ *
178
+ * Accepts: as {@link ownsKey}.
179
+ *
180
+ * Returns: nothing: `key` is kept under its declared type, and this checks
181
+ * it against `scope`.
182
+ *
183
+ * Throws: `VALIDATION`. Every download of a key taken from a row goes
184
+ * through here, so a tampered or foreign row cannot make a reader fetch an
185
+ * object belonging to another row.
186
+ */
187
+ assertOwnedKey(key, scope) {
188
+ (0, config_1.assertKeyInScope)(key, this.keyPrefix, scope);
189
+ }
190
+ /**
191
+ * Upload `data` under `key`, writing only while the key is free.
192
+ *
193
+ * Accepts: `key` — built by {@link buildKey} for the write uploading `data`,
194
+ * so no other write uploads to it. `row` — written to the object as the
195
+ * DynamoDB backlink, for an out-of-band sweeper. `signal` — cancels the
196
+ * request itself, not merely the wait before the next attempt.
197
+ *
198
+ * Returns: the key, whether this request stored the object or an earlier
199
+ * attempt of this upload did (see `uploadObject`); the caller's obligation is
200
+ * the same either way.
201
+ *
202
+ * Throws: `S3_OFFLOAD_FAILED` carrying the key; `ABORTED` when the signal
203
+ * fires, which is the caller's own stop rather than a failed offload.
204
+ */
205
+ async upload(key, data, row, signal) {
206
+ await uploadObject(await this.getClient(), {
68
207
  bucket: this.bucketName,
69
208
  key,
70
209
  data,
71
210
  serverSideEncryption: this.sse,
72
211
  sseKmsKeyId: this.sseKmsKeyId,
212
+ metadata: backlinkMetadata(row),
213
+ signal,
73
214
  });
74
215
  return key;
75
216
  }
76
- /** Download the bytes stored under `key`. */
77
- async download(key) {
78
- return (0, read_write_1.downloadObject)(await this.getClient(), this.bucketName, key);
217
+ /**
218
+ * Download the bytes stored under `key`.
219
+ *
220
+ * Accepts: `key` — already checked against the reading row's scope.
221
+ * `signal` — cancels the request, including a body already streaming: the
222
+ * handler's abort listener outlives the response headers and destroys the
223
+ * socket, which is the one bound a stalled transfer has that the request
224
+ * timeout provably does not give it.
225
+ *
226
+ * Returns: the object's bytes.
227
+ *
228
+ * Throws: `S3_OFFLOAD_FAILED` for an object over `maxDownloadBytes` — the cap
229
+ * is enforced on the declared length and again while reading, so a lying
230
+ * `Content-Length` does not get past it — and for a missing object or a
231
+ * transport failure; `ABORTED` when the signal fires.
232
+ */
233
+ async download(key, signal) {
234
+ return downloadObject(await this.getClient(), { bucket: this.bucketName, key, maxBytes: this.maxDownloadBytes }, signal);
79
235
  }
80
- /** Delete `keys`, returning the keys S3 reported as failed. */
236
+ /**
237
+ * Delete `keys`.
238
+ *
239
+ * Accepts: any number of keys, including none.
240
+ *
241
+ * Returns: the keys S3 reported as failed, so the caller can log what leaked
242
+ * rather than assume it is gone.
243
+ *
244
+ * Throws: whatever the delete throws after its own retries; cleanup callers
245
+ * catch it, since a failed cleanup must not fail the operation it follows.
246
+ */
81
247
  async deleteBatch(keys) {
82
- return (0, delete_1.deleteObjects)(await this.getClient(), this.bucketName, keys);
248
+ return deleteObjects(await this.getClient(), this.bucketName, keys);
83
249
  }
84
- /** Ensure a `${ttlDays}`-day expiration lifecycle rule exists for the prefix. */
85
- async ensureLifecycleRule(ttlDays) {
86
- return (0, lifecycle_1.ensureLifecycleRule)(await this.getClient(), this.bucketName, this.keyPrefix, ttlDays);
250
+ /**
251
+ * Ensure a `${ttlDays}`-day expiration lifecycle rule exists for the prefix.
252
+ *
253
+ * Accepts: `ttlDays` — whole days, the only granularity S3 accepts.
254
+ * `logger` — the adapter's, for the bucket's versioning state, which is
255
+ * reported rather than enforced.
256
+ *
257
+ * Returns: nothing. Rules that are already correct are left alone, so this
258
+ * is safe to call on every deploy.
259
+ *
260
+ * Throws: `VALIDATION` naming `s3.keyPrefix` when a rule id this prefix
261
+ * would take is already held by a different prefix; whatever reading or
262
+ * writing the bucket's lifecycle configuration throws.
263
+ */
264
+ async ensureLifecycleRule(ttlDays, logger) {
265
+ return (0, lifecycle_1.ensureLifecycleRule)(await this.getClient(), { bucket: this.bucketName, prefix: this.keyPrefix, days: ttlDays }, logger);
87
266
  }
88
267
  /**
89
- * Release the underlying S3 client. Safe at any point in the client's
90
- * lifecycle: called before construction starts it does nothing, called
91
- * mid-construction it marks the offloader destroyed so the client is
92
- * released the moment it resolves, and called after it releases it directly.
268
+ * Release the underlying S3 client.
269
+ *
270
+ * Accepts: nothing.
271
+ *
272
+ * Returns: nothing. Safe at any point in the client's lifecycle: called
273
+ * before construction starts it does nothing, called mid-construction it
274
+ * marks the offloader destroyed so the client is released the moment it
275
+ * resolves, and called after it releases it directly.
276
+ *
277
+ * Throws: whatever the SDK client's own `destroy` throws.
93
278
  */
94
279
  destroy() {
95
280
  this.destroyed = true;
@@ -97,4 +282,377 @@ class S3Offloader {
97
282
  }
98
283
  }
99
284
  exports.S3Offloader = S3Offloader;
100
- //# sourceMappingURL=offloader.js.map
285
+ /**
286
+ * Re-throw a cancel as it is, before anything rebrands it.
287
+ *
288
+ * Both wrappers turn every failure into `S3_OFFLOAD_FAILED`, which is right
289
+ * for a failure and wrong for a stop the caller asked for: a cancelled
290
+ * `getMessages` would report that its payload could not be offloaded. The
291
+ * check has to sit ahead of the wrapping rather than inside the classifier,
292
+ * because by then `withRetry` has already decided this is a cancel and said
293
+ * so with the only code a caller branches on.
294
+ */
295
+ function rethrowIfCancelled(error) {
296
+ if ((0, abort_1.isAbortError)(error))
297
+ throw error;
298
+ }
299
+ /**
300
+ * True when S3 refused a conditional write because the key is already taken.
301
+ *
302
+ * With `If-None-Match: *` that is a `412 Precondition Failed` (S3 User Guide,
303
+ * *How to prevent object overwrites with conditional writes*). A key names one
304
+ * write's upload, and only that upload's own requests write it, so the object
305
+ * already there was stored by an earlier attempt of this upload. The upload
306
+ * has nothing left to do, and the 412 is the success case, not a failure. The
307
+ * classifier maps both `PreconditionFailed` and a bare 412 to the same code.
308
+ */
309
+ function alreadyStored(error) {
310
+ return (0, classify_1.classifyAwsError)(error) === error_code_1.ErrorCode.CONDITION_CONFLICT;
311
+ }
312
+ /**
313
+ * Upload `data` to S3 unless an object already exists under `key`, wrapping
314
+ * failures as `S3_OFFLOAD_FAILED`.
315
+ *
316
+ * The write is conditional (`If-None-Match: *`), which costs nothing extra: it
317
+ * needs only `s3:PutObject`, the permission this package already requires, and
318
+ * a retried request writes nothing new: it can never overwrite the object an
319
+ * earlier attempt stored. A `409 Conflict` — S3's answer when a delete lands
320
+ * between the check and the write — is classified as transient and retried
321
+ * like any other conflict.
322
+ *
323
+ * Accepts: `params.key` — the key of one write's upload of `params.data`,
324
+ * written by no other write. `params.metadata` — the backlink, sent with every
325
+ * attempt; an object an earlier attempt stored keeps that attempt's metadata,
326
+ * which is the same. `params.signal` — cancels the request in flight, not only
327
+ * the wait before the next attempt.
328
+ *
329
+ * Returns: nothing, both when this request stored the object and when S3
330
+ * answered `412` because an earlier attempt of this upload already had. The
331
+ * two are not distinguished because the caller's obligation is identical: the
332
+ * bytes are at that key.
333
+ *
334
+ * Throws: `ABORTED` when the signal fires, unwrapped; `S3_OFFLOAD_FAILED`
335
+ * carrying the key and the underlying error, after three attempts on a
336
+ * transient failure. Its message quotes the SDK's, with credential shapes
337
+ * redacted — a signing failure names the key it signed with, and this message
338
+ * reaches `err.message` on a public error.
339
+ *
340
+ * Guarantees: a cancelled upload leaves at most the object it was writing, at
341
+ * a key ending in this write's own object id, which no row names because the
342
+ * row that would have named it is written afterwards. The conditional write
343
+ * means it can never have replaced anything. It is an orphan of exactly the
344
+ * kind `ensureS3LifecycleRule` sweeps, never a corrupted or half-written
345
+ * object: S3 stores a single `PutObject` whole or not at all.
346
+ */
347
+ async function uploadObject(client, params) {
348
+ const { PutObjectCommand } = await (0, client_2.loadS3Sdk)();
349
+ try {
350
+ await (0, retry_1.withRetry)((request) => client.send(new PutObjectCommand({
351
+ Bucket: params.bucket,
352
+ Key: params.key,
353
+ Body: params.data,
354
+ ContentType: 'application/octet-stream',
355
+ IfNoneMatch: '*',
356
+ ServerSideEncryption: params.serverSideEncryption,
357
+ ...(params.sseKmsKeyId ? { SSEKMSKeyId: params.sseKmsKeyId } : {}),
358
+ ...(params.metadata ? { Metadata: params.metadata } : {}),
359
+ }), request), { maxAttempts: 3, isRetryable: retry_1.isTransientS3Error, signal: params.signal });
360
+ }
361
+ catch (error) {
362
+ if (alreadyStored(error))
363
+ return;
364
+ rethrowIfCancelled(error);
365
+ throw new base_error_1.DynamoDBLangGraphError((0, secret_patterns_1.redactedMessage)(error), error_code_1.ErrorCode.S3_OFFLOAD_FAILED, { operation: 'upload', key: params.key }, error);
366
+ }
367
+ }
368
+ /**
369
+ * The bytes stored under `object.key`.
370
+ *
371
+ * Accepts: `object.maxBytes` — the largest object this call will buffer.
372
+ * `signal` — cancels the request, and with it a body already streaming.
373
+ *
374
+ * Returns: the object's bytes.
375
+ *
376
+ * Throws: `ABORTED` when the signal fires, unwrapped; `S3_OFFLOAD_FAILED`
377
+ * naming the key — for an object over `maxBytes`, for a response with no body,
378
+ * and for any SDK failure that survives the retries. Its message quotes the
379
+ * underlying one with credential shapes redacted; the SDK error is kept as
380
+ * `cause`, so `NoSuchKey` stays distinguishable
381
+ * ({@link isMissingObjectError}).
382
+ *
383
+ * Guarantees: an object over the cap is refused from its declared
384
+ * `ContentLength` before the body is touched, and while streaming when the
385
+ * length is absent, so a replaced or hostile object cannot exhaust memory.
386
+ * Transient failures are retried by this package alone — the client is built
387
+ * with `maxAttempts: 1`.
388
+ *
389
+ * The body is read *inside* the retried attempt, so the signal covers the
390
+ * whole transfer rather than only the round trip to the headers. A signal that
391
+ * fires mid-body destroys the socket, and the stream rejects with a transport
392
+ * error that every classifier here reads as transient; it never reaches that
393
+ * classification, because the signal is read first. A cancelled download
394
+ * leaves nothing behind at all — no local file, no partial object, and the
395
+ * chunks already buffered are dropped with the call.
396
+ */
397
+ async function downloadObject(client, object, signal) {
398
+ const { GetObjectCommand } = await (0, client_2.loadS3Sdk)();
399
+ const { bucket, key, maxBytes } = object;
400
+ try {
401
+ return await (0, retry_1.withRetry)(async (request) => {
402
+ const response = await client.send(new GetObjectCommand({ Bucket: bucket, Key: key }), request);
403
+ if (typeof response.ContentLength === 'number' && response.ContentLength > maxBytes) {
404
+ throw oversizedObjectError(key, response.ContentLength, maxBytes);
405
+ }
406
+ if (!response.Body) {
407
+ throw new Error(`S3 object body is empty for key: ${(0, truncate_1.truncateForLog)(key)}`);
408
+ }
409
+ return readBodyBounded(response.Body, key, maxBytes);
410
+ }, { maxAttempts: 3, isRetryable: retry_1.isTransientS3Error, signal });
411
+ }
412
+ catch (error) {
413
+ rethrowIfCancelled(error);
414
+ throw new base_error_1.DynamoDBLangGraphError((0, secret_patterns_1.redactedMessage)(error), error_code_1.ErrorCode.S3_OFFLOAD_FAILED, { operation: 'download', key }, error);
415
+ }
416
+ }
417
+ /**
418
+ * The typed error for an object over the download cap.
419
+ *
420
+ * Accepts: `bytes` — what the object declared or what had been read when the
421
+ * cap was passed; the message says which is not distinguished, because either
422
+ * way the download stops.
423
+ *
424
+ * Returns: the error, coded `S3_OFFLOAD_FAILED` and carrying the key. The
425
+ * message names the key bounded by {@link truncateForLog}, since it came off
426
+ * a row; `context.key` carries it whole, because that is the field a caller
427
+ * reads and it is one value per failed download rather than one per row.
428
+ *
429
+ * Throws: nothing — it builds the error, the caller throws it.
430
+ */
431
+ function oversizedObjectError(key, bytes, maxBytes) {
432
+ return new base_error_1.DynamoDBLangGraphError(`S3 object ${(0, truncate_1.truncateForLog)(key)} exceeds the ${maxBytes}-byte maxDownloadBytes cap ` +
433
+ `(${bytes} bytes declared or read)`, error_code_1.ErrorCode.S3_OFFLOAD_FAILED, { operation: 'download', key });
434
+ }
435
+ function concat(chunks, total) {
436
+ const out = new Uint8Array(total);
437
+ let offset = 0;
438
+ for (const chunk of chunks) {
439
+ out.set(chunk, offset);
440
+ offset += chunk.length;
441
+ }
442
+ return out;
443
+ }
444
+ /**
445
+ * The bytes of an S3 body, refused once they pass `maxBytes`.
446
+ *
447
+ * Accepts: `body` — a streaming body (Node's `IncomingMessage`, which the SDK
448
+ * returns) is consumed chunk by chunk; a body offering only
449
+ * `transformToByteArray()` is read whole and then checked. `key` — named in the
450
+ * error. `maxBytes` — the cap; `0` admits only an empty body.
451
+ *
452
+ * Returns: the buffered bytes.
453
+ *
454
+ * Throws: `S3_OFFLOAD_FAILED` naming the key once the total passes `maxBytes`.
455
+ * A streaming body is destroyed at that point, so the rest is never fetched.
456
+ *
457
+ * Guarantees: for a streaming body, peak memory is `maxBytes` plus the one
458
+ * chunk that crossed it — the chunks already held never exceed the cap. For a
459
+ * body without a stream the SDK has already buffered it, so the check bounds
460
+ * what is *returned*, not what was read.
461
+ */
462
+ async function readBodyBounded(body, key, maxBytes) {
463
+ const streaming = body;
464
+ const iterate = streaming[Symbol.asyncIterator];
465
+ if (typeof iterate !== 'function') {
466
+ const whole = new Uint8Array(await body.transformToByteArray());
467
+ if (whole.length > maxBytes)
468
+ throw oversizedObjectError(key, whole.length, maxBytes);
469
+ return whole;
470
+ }
471
+ const chunks = [];
472
+ let total = 0;
473
+ for await (const chunk of { [Symbol.asyncIterator]: iterate.bind(streaming) }) {
474
+ total += chunk.length;
475
+ if (total > maxBytes) {
476
+ streaming.destroy?.();
477
+ throw oversizedObjectError(key, total, maxBytes);
478
+ }
479
+ chunks.push(chunk);
480
+ }
481
+ return concat(chunks, total);
482
+ }
483
+ /**
484
+ * Delete `keys` from `bucket`, in chunks of {@link S3_DELETE_BATCH_MAX} — the
485
+ * limit `DeleteObjects` accepts per request.
486
+ *
487
+ * Accepts: `keys` — any length, including empty, which issues no request.
488
+ *
489
+ * Returns: the keys S3 named in an error entry. A per-key failure is reported
490
+ * this way rather than thrown, so the caller (orphan cleanup) decides how
491
+ * loudly to react. An error entry carrying no `Key` cannot be attributed and is
492
+ * not reported.
493
+ *
494
+ * Throws: whatever the SDK rejects with — a request that never reached S3, or
495
+ * one refused whole (permissions, a missing bucket).
496
+ */
497
+ async function deleteObjects(client, bucket, keys) {
498
+ if (keys.length === 0)
499
+ return [];
500
+ const { DeleteObjectsCommand } = await (0, client_2.loadS3Sdk)();
501
+ const failed = [];
502
+ for (let offset = 0; offset < keys.length; offset += exports.S3_DELETE_BATCH_MAX) {
503
+ const chunk = keys.slice(offset, offset + exports.S3_DELETE_BATCH_MAX);
504
+ const response = await client.send(new DeleteObjectsCommand({
505
+ Bucket: bucket,
506
+ Delete: { Objects: chunk.map((key) => ({ Key: key })), Quiet: true },
507
+ }));
508
+ for (const error of response.Errors ?? []) {
509
+ if (error.Key)
510
+ failed.push(error.Key);
511
+ }
512
+ }
513
+ return failed;
514
+ }
515
+ /**
516
+ * Metadata names carrying the backlink. S3 lower-cases user-metadata keys, so
517
+ * these are already lower-case; the `-b64` suffix states the encoding, because
518
+ * a sweeper has to decode them to query DynamoDB.
519
+ */
520
+ const PK_FIELD = 'dynamodb-pk-b64';
521
+ const SK_FIELD = 'dynamodb-sk-b64';
522
+ /**
523
+ * The S3 user metadata linking an object back to the DynamoDB row that points
524
+ * at it — the maintenance aid AWS names for this layout: "Store the primary key
525
+ * value of the item as Amazon S3 metadata of the object" (*Best practices for
526
+ * storing large items and attributes in DynamoDB*). Nothing in this package
527
+ * reads it; it lets an out-of-band sweeper ask DynamoDB whether an object's
528
+ * parent row still exists without parsing object keys.
529
+ *
530
+ * Both values are base64url-encoded. A `thread_id` or a store key may hold any
531
+ * well-formed UTF-16 text, while S3 metadata travels in HTTP headers and the
532
+ * User Guide asks for US-ASCII there; encoding unconditionally keeps every
533
+ * value header-safe and keeps the decoding rule single, which a conditional
534
+ * encoding would not.
535
+ *
536
+ * The pair fits S3's 2 KB metadata budget by construction, not by luck: the
537
+ * same identifiers are already base64url-encoded into the object key, which
538
+ * `buildS3Key` caps at 1024 bytes, so anything that produces a usable key
539
+ * leaves these values far below the budget. `offloader-backlink.test.ts` pins
540
+ * that.
541
+ *
542
+ * Accepts: `row` — the DynamoDB key of the row that will point at the object.
543
+ *
544
+ * Returns: the two metadata fields, both base64url.
545
+ *
546
+ * Throws: nothing.
547
+ */
548
+ function backlinkMetadata(row) {
549
+ return { [PK_FIELD]: (0, config_1.encodeKeyPart)(row.pk), [SK_FIELD]: (0, config_1.encodeKeyPart)(row.sk) };
550
+ }
551
+ /**
552
+ * The total UTF-8 bytes `metadata` costs against S3's 2 KB user-metadata cap.
553
+ *
554
+ * Accepts: any metadata map. Names and values both count, which is how S3
555
+ * measures it.
556
+ *
557
+ * Returns: the byte total.
558
+ *
559
+ * Throws: nothing.
560
+ */
561
+ function metadataBytes(metadata) {
562
+ return Object.entries(metadata).reduce((total, [name, value]) => total + Buffer.byteLength(name, 'utf8') + Buffer.byteLength(value, 'utf8'), 0);
563
+ }
564
+ const DEFAULT_MAX_ATTEMPTS = 3;
565
+ const BASE_DELAY_MS = 100;
566
+ /**
567
+ * Wait out one backoff window, cancellable via `release.signal`. Resolves `true`
568
+ * when the signal aborts the wait (so the caller stops retrying) and `false`
569
+ * otherwise — never rejects, preserving the best-effort, non-throwing contract.
570
+ */
571
+ async function backoffSleep(delayMs, release) {
572
+ try {
573
+ await (0, retry_1.sleep)((0, retry_1.fullJitter)(delayMs, release.rng), release.signal);
574
+ return false;
575
+ }
576
+ catch {
577
+ return true;
578
+ }
579
+ }
580
+ /** Drop every key outside the row's scope, reporting each one. */
581
+ function ownedOnly(offloader, keys, release, scope) {
582
+ return keys.filter((key) => {
583
+ if (offloader.ownsKey(key, scope))
584
+ return true;
585
+ (0, logger_1.absorbLoggerFailure)(() => release.logger.warn(`${release.operation}: refusing to delete an S3 object outside this row's scope`, {
586
+ key: (0, truncate_1.truncateForLog)(key),
587
+ }));
588
+ return false;
589
+ });
590
+ }
591
+ /** The non-empty keys, restricted to the row's scope when one is given. */
592
+ function selectOrphans(offloader, release) {
593
+ const present = release.keys.filter((key) => typeof key === 'string' && key.length > 0);
594
+ return release.scope ? ownedOnly(offloader, present, release, release.scope) : present;
595
+ }
596
+ /**
597
+ * Best-effort delete of S3 objects orphaned by a failed DynamoDB write.
598
+ *
599
+ * Accepts: `release.keys` — may hold `undefined` and empty entries, which are
600
+ * dropped; nothing left means no request. `release.operation` — names the
601
+ * operation in every log line. `release.scope` — the row's own leading key
602
+ * parts when `keys` came from a row: a key outside that path is reported and
603
+ * never deleted. Own uploads pass no scope. `release.maxAttempts` — attempts
604
+ * on a transient failure, default 3. `release.signal` — aborts the wait
605
+ * between attempts.
606
+ *
607
+ * Returns: nothing.
608
+ *
609
+ * Throws: **nothing**, ever. This is the library's sole non-throwing path: a
610
+ * cleanup failure must not mask the write failure that caused it. Every
611
+ * outcome that is not a clean delete is logged at `warn`, carrying counts and
612
+ * the failing error's *name* — never its message, which can hold a credential
613
+ * fragment. A `logger` that throws is one more thing this absorbs (see
614
+ * `absorbLoggerFailure`): the promise covers the caller's own code too,
615
+ * or every call site's `catch` would hand its caller the wrong error. Every
616
+ * logger reaching here through an adapter is already contained at
617
+ * `resolveLogger`; the guard stays because this promise is written without a
618
+ * precondition, and a `logger` is an argument.
619
+ *
620
+ * Guarantees: an object outside the row's scope is never deleted. Leftovers
621
+ * have no automatic backstop — an S3 lifecycle rule sweeps them only if one was
622
+ * provisioned through `ensureS3LifecycleRule()`, which is opt-in.
623
+ */
624
+ async function cleanUpS3Orphans(offloader, release) {
625
+ const orphans = selectOrphans(offloader, release);
626
+ if (orphans.length === 0)
627
+ return;
628
+ const maxAttempts = release.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
629
+ let delay = BASE_DELAY_MS;
630
+ let lastError = new Error('S3 orphan cleanup did not run');
631
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
632
+ try {
633
+ const failed = await offloader.deleteBatch(orphans);
634
+ if (failed.length === 0)
635
+ return;
636
+ // Absorbed here and not by the `catch` below, which would read a broken
637
+ // logger as a failed delete: the delete succeeded, and only part of it
638
+ // could be reported.
639
+ (0, logger_1.absorbLoggerFailure)(() => release.logger.warn(`Some orphaned S3 objects could not be deleted after ${release.operation}; a lifecycle rule from ensureS3LifecycleRule() would sweep them, otherwise clean up manually`, { failedCount: failed.length }));
640
+ return;
641
+ }
642
+ catch (error) {
643
+ lastError = error;
644
+ if (attempt >= maxAttempts || !(0, retry_1.isTransientS3Error)(lastError))
645
+ break;
646
+ if (await backoffSleep(delay, release))
647
+ break;
648
+ delay = (0, retry_1.nextBackoffDelay)(delay);
649
+ }
650
+ }
651
+ // The error's *name*, never its message: an underlying failure can carry a
652
+ // credential fragment in its text, and this package promises that its logs
653
+ // hold identifiers and counts only. Bounded all the same — a name is an
654
+ // identifier this package did not length-check, and `message` is bounded
655
+ // where `redactedMessage` relays it, so bounding one and relaying the other
656
+ // whole would split what is one value.
657
+ (0, logger_1.absorbLoggerFailure)(() => release.logger.warn(`Failed to clean up orphaned S3 objects after ${release.operation}; a lifecycle rule from ensureS3LifecycleRule() would sweep them, otherwise clean up manually`, { reason: (0, truncate_1.truncateForLog)((0, base_error_1.failureLabel)(lastError)) }));
658
+ }