@farukada/aws-langgraph-dynamodb-ts 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/README.md +52 -9
  2. package/dist/checkpointer/actions/delete-thread.d.ts +3 -4
  3. package/dist/checkpointer/actions/delete-thread.d.ts.map +1 -1
  4. package/dist/checkpointer/actions/delete-thread.js +21 -41
  5. package/dist/checkpointer/actions/delete-thread.js.map +1 -1
  6. package/dist/checkpointer/actions/list.d.ts.map +1 -1
  7. package/dist/checkpointer/actions/list.js +38 -4
  8. package/dist/checkpointer/actions/list.js.map +1 -1
  9. package/dist/checkpointer/actions/put-writes.d.ts.map +1 -1
  10. package/dist/checkpointer/actions/put-writes.js +15 -24
  11. package/dist/checkpointer/actions/put-writes.js.map +1 -1
  12. package/dist/checkpointer/internal/item-reader.d.ts +45 -0
  13. package/dist/checkpointer/internal/item-reader.d.ts.map +1 -1
  14. package/dist/checkpointer/internal/item-reader.js +63 -1
  15. package/dist/checkpointer/internal/item-reader.js.map +1 -1
  16. package/dist/checkpointer/internal/item-writer.d.ts +0 -8
  17. package/dist/checkpointer/internal/item-writer.d.ts.map +1 -1
  18. package/dist/checkpointer/internal/item-writer.js +16 -21
  19. package/dist/checkpointer/internal/item-writer.js.map +1 -1
  20. package/dist/checkpointer/internal/keys.d.ts +19 -3
  21. package/dist/checkpointer/internal/keys.d.ts.map +1 -1
  22. package/dist/checkpointer/internal/keys.js +41 -6
  23. package/dist/checkpointer/internal/keys.js.map +1 -1
  24. package/dist/checkpointer/internal/special-write-cas.d.ts +30 -0
  25. package/dist/checkpointer/internal/special-write-cas.d.ts.map +1 -0
  26. package/dist/checkpointer/internal/special-write-cas.js +104 -0
  27. package/dist/checkpointer/internal/special-write-cas.js.map +1 -0
  28. package/dist/checkpointer/internal/special-write-cleanup.d.ts +18 -12
  29. package/dist/checkpointer/internal/special-write-cleanup.d.ts.map +1 -1
  30. package/dist/checkpointer/internal/special-write-cleanup.js +31 -86
  31. package/dist/checkpointer/internal/special-write-cleanup.js.map +1 -1
  32. package/dist/checkpointer/internal/special-write-verify.d.ts +54 -0
  33. package/dist/checkpointer/internal/special-write-verify.d.ts.map +1 -0
  34. package/dist/checkpointer/internal/special-write-verify.js +65 -0
  35. package/dist/checkpointer/internal/special-write-verify.js.map +1 -0
  36. package/dist/checkpointer/internal/validation.d.ts +8 -4
  37. package/dist/checkpointer/internal/validation.d.ts.map +1 -1
  38. package/dist/checkpointer/internal/validation.js +12 -10
  39. package/dist/checkpointer/internal/validation.js.map +1 -1
  40. package/dist/checkpointer/internal/write-guard.d.ts +13 -0
  41. package/dist/checkpointer/internal/write-guard.d.ts.map +1 -0
  42. package/dist/checkpointer/internal/write-guard.js +39 -0
  43. package/dist/checkpointer/internal/write-guard.js.map +1 -0
  44. package/dist/checkpointer/internal/write-index.d.ts +37 -0
  45. package/dist/checkpointer/internal/write-index.d.ts.map +1 -0
  46. package/dist/checkpointer/internal/write-index.js +42 -0
  47. package/dist/checkpointer/internal/write-index.js.map +1 -0
  48. package/dist/checkpointer/types.d.ts +8 -0
  49. package/dist/checkpointer/types.d.ts.map +1 -1
  50. package/dist/history/actions/add-messages.d.ts.map +1 -1
  51. package/dist/history/actions/add-messages.js +24 -4
  52. package/dist/history/actions/add-messages.js.map +1 -1
  53. package/dist/history/actions/clear.d.ts +4 -6
  54. package/dist/history/actions/clear.d.ts.map +1 -1
  55. package/dist/history/actions/clear.js +18 -33
  56. package/dist/history/actions/clear.js.map +1 -1
  57. package/dist/history/actions/get-messages.d.ts +2 -1
  58. package/dist/history/actions/get-messages.d.ts.map +1 -1
  59. package/dist/history/actions/get-messages.js +28 -2
  60. package/dist/history/actions/get-messages.js.map +1 -1
  61. package/dist/history/actions/list-sessions.d.ts +2 -1
  62. package/dist/history/actions/list-sessions.d.ts.map +1 -1
  63. package/dist/history/actions/list-sessions.js +21 -2
  64. package/dist/history/actions/list-sessions.js.map +1 -1
  65. package/dist/history/actions/reconcile-count.d.ts.map +1 -1
  66. package/dist/history/actions/reconcile-count.js +2 -2
  67. package/dist/history/actions/reconcile-count.js.map +1 -1
  68. package/dist/history/internal/append-saga.d.ts.map +1 -1
  69. package/dist/history/internal/append-saga.js +2 -78
  70. package/dist/history/internal/append-saga.js.map +1 -1
  71. package/dist/history/internal/compensation.d.ts +21 -0
  72. package/dist/history/internal/compensation.d.ts.map +1 -0
  73. package/dist/history/internal/compensation.js +84 -0
  74. package/dist/history/internal/compensation.js.map +1 -0
  75. package/dist/history/internal/keys.d.ts +9 -1
  76. package/dist/history/internal/keys.d.ts.map +1 -1
  77. package/dist/history/internal/keys.js +21 -4
  78. package/dist/history/internal/keys.js.map +1 -1
  79. package/dist/history/internal/message-chunker.d.ts.map +1 -1
  80. package/dist/history/internal/message-chunker.js +13 -5
  81. package/dist/history/internal/message-chunker.js.map +1 -1
  82. package/dist/history/internal/session-count.d.ts +21 -0
  83. package/dist/history/internal/session-count.d.ts.map +1 -1
  84. package/dist/history/internal/session-count.js +51 -0
  85. package/dist/history/internal/session-count.js.map +1 -1
  86. package/dist/history/internal/session-title.d.ts +20 -0
  87. package/dist/history/internal/session-title.d.ts.map +1 -0
  88. package/dist/history/internal/session-title.js +44 -0
  89. package/dist/history/internal/session-title.js.map +1 -0
  90. package/dist/history/internal/setup.d.ts +2 -1
  91. package/dist/history/internal/setup.d.ts.map +1 -1
  92. package/dist/history/internal/setup.js +1 -0
  93. package/dist/history/internal/setup.js.map +1 -1
  94. package/dist/history/internal/title-generator.d.ts +8 -2
  95. package/dist/history/internal/title-generator.d.ts.map +1 -1
  96. package/dist/history/internal/title-generator.js +12 -3
  97. package/dist/history/internal/title-generator.js.map +1 -1
  98. package/dist/history/internal/validation.d.ts +9 -0
  99. package/dist/history/internal/validation.d.ts.map +1 -0
  100. package/dist/history/internal/validation.js +16 -0
  101. package/dist/history/internal/validation.js.map +1 -0
  102. package/dist/history/types.d.ts +12 -0
  103. package/dist/history/types.d.ts.map +1 -1
  104. package/dist/index.d.ts +1 -1
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js.map +1 -1
  107. package/dist/shared/codec/s3/offloader.d.ts +7 -1
  108. package/dist/shared/codec/s3/offloader.d.ts.map +1 -1
  109. package/dist/shared/codec/s3/offloader.js +14 -1
  110. package/dist/shared/codec/s3/offloader.js.map +1 -1
  111. package/dist/shared/constants.d.ts +15 -0
  112. package/dist/shared/constants.d.ts.map +1 -1
  113. package/dist/shared/constants.js +16 -1
  114. package/dist/shared/constants.js.map +1 -1
  115. package/dist/shared/dynamodb/conditional-put.d.ts +51 -0
  116. package/dist/shared/dynamodb/conditional-put.d.ts.map +1 -0
  117. package/dist/shared/dynamodb/conditional-put.js +59 -0
  118. package/dist/shared/dynamodb/conditional-put.js.map +1 -0
  119. package/dist/shared/dynamodb/partition-delete.d.ts +33 -0
  120. package/dist/shared/dynamodb/partition-delete.d.ts.map +1 -0
  121. package/dist/shared/dynamodb/partition-delete.js +70 -0
  122. package/dist/shared/dynamodb/partition-delete.js.map +1 -0
  123. package/dist/shared/dynamodb/retry-classifier.d.ts.map +1 -1
  124. package/dist/shared/dynamodb/retry-classifier.js +9 -1
  125. package/dist/shared/dynamodb/retry-classifier.js.map +1 -1
  126. package/dist/shared/logging/redaction-walk.d.ts +23 -0
  127. package/dist/shared/logging/redaction-walk.d.ts.map +1 -0
  128. package/dist/shared/logging/redaction-walk.js +92 -0
  129. package/dist/shared/logging/redaction-walk.js.map +1 -0
  130. package/dist/shared/logging/redaction.d.ts +26 -17
  131. package/dist/shared/logging/redaction.d.ts.map +1 -1
  132. package/dist/shared/logging/redaction.js +24 -48
  133. package/dist/shared/logging/redaction.js.map +1 -1
  134. package/dist/shared/logging/secret-patterns.d.ts +74 -0
  135. package/dist/shared/logging/secret-patterns.d.ts.map +1 -0
  136. package/dist/shared/logging/secret-patterns.js +107 -0
  137. package/dist/shared/logging/secret-patterns.js.map +1 -0
  138. package/dist/shared/validation/primitives.d.ts +9 -1
  139. package/dist/shared/validation/primitives.d.ts.map +1 -1
  140. package/dist/shared/validation/primitives.js +13 -0
  141. package/dist/shared/validation/primitives.js.map +1 -1
  142. package/dist/store/actions/get.d.ts.map +1 -1
  143. package/dist/store/actions/get.js +15 -1
  144. package/dist/store/actions/get.js.map +1 -1
  145. package/dist/store/actions/list-namespaces.d.ts.map +1 -1
  146. package/dist/store/actions/list-namespaces.js +1 -0
  147. package/dist/store/actions/list-namespaces.js.map +1 -1
  148. package/dist/store/actions/put.d.ts.map +1 -1
  149. package/dist/store/actions/put.js +24 -18
  150. package/dist/store/actions/put.js.map +1 -1
  151. package/dist/store/actions/search.d.ts.map +1 -1
  152. package/dist/store/actions/search.js +4 -35
  153. package/dist/store/actions/search.js.map +1 -1
  154. package/dist/store/internal/backend-search.d.ts +5 -0
  155. package/dist/store/internal/backend-search.d.ts.map +1 -0
  156. package/dist/store/internal/backend-search.js +68 -0
  157. package/dist/store/internal/backend-search.js.map +1 -0
  158. package/dist/store/internal/filter.d.ts.map +1 -1
  159. package/dist/store/internal/filter.js +46 -5
  160. package/dist/store/internal/filter.js.map +1 -1
  161. package/dist/store/internal/index-reconcile.d.ts.map +1 -1
  162. package/dist/store/internal/index-reconcile.js +41 -5
  163. package/dist/store/internal/index-reconcile.js.map +1 -1
  164. package/dist/store/internal/item-mapper.d.ts +1 -0
  165. package/dist/store/internal/item-mapper.d.ts.map +1 -1
  166. package/dist/store/internal/item-mapper.js +2 -0
  167. package/dist/store/internal/item-mapper.js.map +1 -1
  168. package/dist/store/internal/keys.d.ts +1 -1
  169. package/dist/store/internal/keys.d.ts.map +1 -1
  170. package/dist/store/internal/keys.js +8 -2
  171. package/dist/store/internal/keys.js.map +1 -1
  172. package/dist/store/internal/overwrite-swap.d.ts +33 -0
  173. package/dist/store/internal/overwrite-swap.d.ts.map +1 -0
  174. package/dist/store/internal/overwrite-swap.js +62 -0
  175. package/dist/store/internal/overwrite-swap.js.map +1 -0
  176. package/dist/store/internal/persist.d.ts +22 -6
  177. package/dist/store/internal/persist.d.ts.map +1 -1
  178. package/dist/store/internal/persist.js +41 -20
  179. package/dist/store/internal/persist.js.map +1 -1
  180. package/dist/store/internal/read-existing.d.ts +19 -0
  181. package/dist/store/internal/read-existing.d.ts.map +1 -0
  182. package/dist/store/internal/read-existing.js +29 -0
  183. package/dist/store/internal/read-existing.js.map +1 -0
  184. package/dist/store/internal/score-direction.d.ts +32 -0
  185. package/dist/store/internal/score-direction.d.ts.map +1 -0
  186. package/dist/store/internal/score-direction.js +39 -0
  187. package/dist/store/internal/score-direction.js.map +1 -0
  188. package/dist/store/internal/search-filter.d.ts +4 -0
  189. package/dist/store/internal/search-filter.d.ts.map +1 -0
  190. package/dist/store/internal/search-filter.js +11 -0
  191. package/dist/store/internal/search-filter.js.map +1 -0
  192. package/dist/store/internal/setup.d.ts +13 -1
  193. package/dist/store/internal/setup.d.ts.map +1 -1
  194. package/dist/store/internal/setup.js +57 -1
  195. package/dist/store/internal/setup.js.map +1 -1
  196. package/dist/store/internal/validation.d.ts +8 -2
  197. package/dist/store/internal/validation.d.ts.map +1 -1
  198. package/dist/store/internal/validation.js +15 -6
  199. package/dist/store/internal/validation.js.map +1 -1
  200. package/dist/store/internal/write-verify.d.ts +31 -6
  201. package/dist/store/internal/write-verify.d.ts.map +1 -1
  202. package/dist/store/internal/write-verify.js +48 -8
  203. package/dist/store/internal/write-verify.js.map +1 -1
  204. package/dist/store/types.d.ts +16 -0
  205. package/dist/store/types.d.ts.map +1 -1
  206. package/dist/store/vector-backend.d.ts +17 -0
  207. package/dist/store/vector-backend.d.ts.map +1 -1
  208. package/package.json +1 -1
@@ -0,0 +1,33 @@
1
+ import type { StoreItemRecord } from '../types';
2
+ import { type ExistingRecordMeta } from './read-existing';
3
+ import type { StoreContext } from './setup';
4
+ /**
5
+ * Commit `record`, re-reading and retrying while another writer holds the row,
6
+ * and return the state this write actually superseded — the only descriptor
7
+ * safe to delete afterwards.
8
+ *
9
+ * Without the swap both racers read the same previous descriptor, both commit,
10
+ * and both delete it, orphaning the loser's own upload. Retrying against the
11
+ * *re-read* state is what makes each writer supersede exactly one payload.
12
+ *
13
+ * A rejection is not proof a competitor won: `withDynamoDBRetry` retries
14
+ * transient errors, so an attempt can commit server-side, its response can be
15
+ * lost, and the retried put can hit the row it just wrote and fail the same
16
+ * guard — indistinguishable from a competitor's win by the rejection alone.
17
+ * Each attempt's pinned observation is captured in `attempted` before the
18
+ * put, so that when a re-read finds the row already holding *this call's
19
+ * own* `rev`, the swap returns whatever `attempted` held — never this
20
+ * record's own just-committed value, which would strand the live row
21
+ * pointing at a deleted object. That comparison is guarded on `rev` being
22
+ * set: `rev` is optional on the record type, and an unnonced record against a
23
+ * pre-0.9.0 revision-less row would otherwise match `undefined === undefined`
24
+ * and claim a race it never entered.
25
+ *
26
+ * On exhaustion the write proceeds unconditionally and warns. That is
27
+ * deliberate: the fallback is precisely the pre-0.9.0 behaviour — one possible
28
+ * orphan, reclaimed by a lifecycle rule — so pathological contention degrades
29
+ * instead of turning a working put into an error. `createdAt` is refreshed from
30
+ * each re-read so a row created by whoever won keeps its true creation time.
31
+ */
32
+ export declare function putWithRevisionSwap(context: StoreContext, record: StoreItemRecord, existing: ExistingRecordMeta): Promise<ExistingRecordMeta>;
33
+ //# sourceMappingURL=overwrite-swap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overwrite-swap.d.ts","sourceRoot":"","sources":["../../../src/store/internal/overwrite-swap.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,KAAK,kBAAkB,EAAgB,MAAM,iBAAiB,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAc5C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAqB7B"}
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.putWithRevisionSwap = putWithRevisionSwap;
4
+ const conditional_put_1 = require("../../shared/dynamodb/conditional-put");
5
+ const retry_1 = require("../../shared/dynamodb/retry");
6
+ const read_existing_1 = require("./read-existing");
7
+ /** Put the record, optionally pinned to the revision the caller observed. */
8
+ async function put(context, record, observed) {
9
+ const guard = observed ? (0, conditional_put_1.revisionGuard)(conditional_put_1.REVISION_ATTRIBUTE, observed) : {};
10
+ await (0, retry_1.withDynamoDBRetry)(() => context.client.put({ TableName: context.tableName, Item: record, ...guard }));
11
+ }
12
+ /**
13
+ * Commit `record`, re-reading and retrying while another writer holds the row,
14
+ * and return the state this write actually superseded — the only descriptor
15
+ * safe to delete afterwards.
16
+ *
17
+ * Without the swap both racers read the same previous descriptor, both commit,
18
+ * and both delete it, orphaning the loser's own upload. Retrying against the
19
+ * *re-read* state is what makes each writer supersede exactly one payload.
20
+ *
21
+ * A rejection is not proof a competitor won: `withDynamoDBRetry` retries
22
+ * transient errors, so an attempt can commit server-side, its response can be
23
+ * lost, and the retried put can hit the row it just wrote and fail the same
24
+ * guard — indistinguishable from a competitor's win by the rejection alone.
25
+ * Each attempt's pinned observation is captured in `attempted` before the
26
+ * put, so that when a re-read finds the row already holding *this call's
27
+ * own* `rev`, the swap returns whatever `attempted` held — never this
28
+ * record's own just-committed value, which would strand the live row
29
+ * pointing at a deleted object. That comparison is guarded on `rev` being
30
+ * set: `rev` is optional on the record type, and an unnonced record against a
31
+ * pre-0.9.0 revision-less row would otherwise match `undefined === undefined`
32
+ * and claim a race it never entered.
33
+ *
34
+ * On exhaustion the write proceeds unconditionally and warns. That is
35
+ * deliberate: the fallback is precisely the pre-0.9.0 behaviour — one possible
36
+ * orphan, reclaimed by a lifecycle rule — so pathological contention degrades
37
+ * instead of turning a working put into an error. `createdAt` is refreshed from
38
+ * each re-read so a row created by whoever won keeps its true creation time.
39
+ */
40
+ async function putWithRevisionSwap(context, record, existing) {
41
+ let observed = existing;
42
+ for (let attempt = 1; attempt <= conditional_put_1.OVERWRITE_CAS_MAX_ATTEMPTS; attempt++) {
43
+ const attempted = observed;
44
+ try {
45
+ await put(context, record, attempted);
46
+ return attempted;
47
+ }
48
+ catch (error) {
49
+ if (!(0, conditional_put_1.isConditionalCheckFailed)(error))
50
+ throw error;
51
+ observed = await (0, read_existing_1.readExisting)(context, record.PK, record.SK);
52
+ if (record.rev !== undefined && observed.revision === record.rev)
53
+ return attempted;
54
+ record.createdAt = observed.createdAt ?? record.createdAt;
55
+ }
56
+ }
57
+ context.logger.warn('store.put: compare-and-swap exhausted; overwriting unconditionally, which can orphan one ' +
58
+ 'S3 object under a concurrent put (reclaimed by ensureS3LifecycleRule)', { namespace: record.namespace, key: record.key, attempts: conditional_put_1.OVERWRITE_CAS_MAX_ATTEMPTS });
59
+ await put(context, record);
60
+ return observed;
61
+ }
62
+ //# sourceMappingURL=overwrite-swap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overwrite-swap.js","sourceRoot":"","sources":["../../../src/store/internal/overwrite-swap.ts"],"names":[],"mappings":";;;AAAA,2EAK+C;AAC/C,uDAAgE;AAEhE,mDAAwE;AAGxE,6EAA6E;AAC7E,KAAK,UAAU,GAAG,CAChB,OAAqB,EACrB,MAAuB,EACvB,QAA6B;IAE7B,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAA,+BAAa,EAAC,oCAAkB,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,MAAM,IAAA,yBAAiB,EAAC,GAAG,EAAE,CAC3B,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC,CAC7E,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACI,KAAK,8BACV,OAAqB,EACrB,MAAuB,EACvB,QAA4B;IAE5B,IAAI,QAAQ,GAAG,QAAQ,CAAC;IACxB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,4CAA0B,EAAE,OAAO,EAAE,EAAE,CAAC;QACvE,MAAM,SAAS,GAAG,QAAQ,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;YACtC,OAAO,SAAS,CAAC;QACnB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,IAAA,0CAAwB,EAAC,KAA0B,CAAC;gBAAE,MAAM,KAAK,CAAC;YACvE,QAAQ,GAAG,MAAM,IAAA,4BAAY,EAAC,OAAO,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;YAC7D,IAAI,MAAM,CAAC,GAAG,KAAK,SAAS,IAAI,QAAQ,CAAC,QAAQ,KAAK,MAAM,CAAC,GAAG;gBAAE,OAAO,SAAS,CAAC;YACnF,MAAM,CAAC,SAAS,GAAG,QAAQ,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC;QAC5D,CAAC;IACH,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,IAAI,CACjB,2FAA2F;QACzF,uEAAuE,EACzE,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,QAAQ,EAAE,4CAA0B,EAAE,CACvF,CAAC;IACF,MAAM,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC3B,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -1,11 +1,27 @@
1
- import { type PayloadDescriptor } from '../../shared/codec/codec';
2
1
  import type { StoreItemRecord } from '../types';
2
+ import type { ExistingRecordMeta } from './read-existing';
3
3
  import type { StoreContext } from './setup';
4
4
  /**
5
- * Put the record: on a definite failure, clean up *this* record's nonced S3
6
- * object; on an ambiguous retry-exhaustion failure, verify via
7
- * `writeLandedAt` before deleting anything — if it landed, clean up the
8
- * *previous* row's object instead, like the ordinary success path.
5
+ * Put the record and clean up whichever side is now dead.
6
+ *
7
+ * The compare-and-swap path runs **only when an offloader is configured**:
8
+ * without one there is no S3 object to orphan, so a plain last-write-wins put
9
+ * stays correct and costs no extra write capacity (DynamoDB charges for a
10
+ * failed conditional write too). With one, the swap is what lets this call
11
+ * delete exactly the payload it superseded rather than a descriptor a racer may
12
+ * already have replaced.
13
+ *
14
+ * Every failure reaching the catch arrives after at least one put was issued —
15
+ * `putWithRevisionSwap` only re-reads from inside its own catch — so none of
16
+ * them proves a non-commit on its own: a put can commit server-side and lose
17
+ * its response, and a `ConditionalCheckFailedException` is as consistent with
18
+ * hitting the row this call just wrote as with a competitor's win. The row is
19
+ * therefore read back (`verifyWriteLanded`) before anything is deleted. Only a
20
+ * confirmed `'not-landed'` deletes this record's own nonced object; a confirmed
21
+ * `'landed'` cleans up the previous object like the success path and swallows
22
+ * the error, and an `'unverified'` read deletes nothing and rethrows — leaking
23
+ * one object at worst rather than stranding a live row pointing at a deleted
24
+ * one. An inline payload has no object to leak, so it needs no read.
9
25
  */
10
- export declare function persistRecord(context: StoreContext, record: StoreItemRecord, previousValue: PayloadDescriptor | undefined): Promise<void>;
26
+ export declare function persistRecord(context: StoreContext, record: StoreItemRecord, existing: ExistingRecordMeta): Promise<void>;
11
27
  //# sourceMappingURL=persist.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../../src/store/internal/persist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAmB,MAAM,0BAA0B,CAAC;AAInF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAQ5C;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,aAAa,EAAE,iBAAiB,GAAG,SAAS,GAC3C,OAAO,CAAC,IAAI,CAAC,CA8Bf"}
1
+ {"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../../src/store/internal/persist.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAa5C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAmBf"}
@@ -5,34 +5,55 @@ const codec_1 = require("../../shared/codec/codec");
5
5
  const descriptor_keys_1 = require("../../shared/codec/descriptor-keys");
6
6
  const orphans_1 = require("../../shared/codec/s3/orphans");
7
7
  const retry_1 = require("../../shared/dynamodb/retry");
8
+ const overwrite_swap_1 = require("./overwrite-swap");
8
9
  const write_verify_1 = require("./write-verify");
9
- /** True when `error` is a {@link RetryExhaustedError} — checked by name, not `instanceof` (banned repo-wide). */
10
- function isRetryExhausted(error) {
11
- return error.name === 'RetryExhaustedError';
10
+ /** Best-effort delete of one descriptor's S3 object. */
11
+ async function cleanUp(context, descriptor, label) {
12
+ if (!context.offloader || !descriptor)
13
+ return;
14
+ await (0, orphans_1.cleanUpS3Orphans)(context.offloader, (0, descriptor_keys_1.collectS3Keys)([descriptor]), label, context.logger);
12
15
  }
13
16
  /**
14
- * Put the record: on a definite failure, clean up *this* record's nonced S3
15
- * object; on an ambiguous retry-exhaustion failure, verify via
16
- * `writeLandedAt` before deleting anything — if it landed, clean up the
17
- * *previous* row's object instead, like the ordinary success path.
17
+ * Put the record and clean up whichever side is now dead.
18
+ *
19
+ * The compare-and-swap path runs **only when an offloader is configured**:
20
+ * without one there is no S3 object to orphan, so a plain last-write-wins put
21
+ * stays correct and costs no extra write capacity (DynamoDB charges for a
22
+ * failed conditional write too). With one, the swap is what lets this call
23
+ * delete exactly the payload it superseded rather than a descriptor a racer may
24
+ * already have replaced.
25
+ *
26
+ * Every failure reaching the catch arrives after at least one put was issued —
27
+ * `putWithRevisionSwap` only re-reads from inside its own catch — so none of
28
+ * them proves a non-commit on its own: a put can commit server-side and lose
29
+ * its response, and a `ConditionalCheckFailedException` is as consistent with
30
+ * hitting the row this call just wrote as with a competitor's win. The row is
31
+ * therefore read back (`verifyWriteLanded`) before anything is deleted. Only a
32
+ * confirmed `'not-landed'` deletes this record's own nonced object; a confirmed
33
+ * `'landed'` cleans up the previous object like the success path and swallows
34
+ * the error, and an `'unverified'` read deletes nothing and rethrows — leaking
35
+ * one object at worst rather than stranding a live row pointing at a deleted
36
+ * one. An inline payload has no object to leak, so it needs no read.
18
37
  */
19
- async function persistRecord(context, record, previousValue) {
38
+ async function persistRecord(context, record, existing) {
39
+ let superseded = existing;
20
40
  try {
21
- await (0, retry_1.withDynamoDBRetry)(() => context.client.put({ TableName: context.tableName, Item: record }));
41
+ if (context.offloader) {
42
+ superseded = await (0, overwrite_swap_1.putWithRevisionSwap)(context, record, existing);
43
+ }
44
+ else {
45
+ await (0, retry_1.withDynamoDBRetry)(() => context.client.put({ TableName: context.tableName, Item: record }));
46
+ }
22
47
  }
23
48
  catch (error) {
24
- const landed = isRetryExhausted(error) &&
25
- record.value.location === codec_1.PayloadLocation.S3 &&
26
- (await (0, write_verify_1.writeLandedAt)(context, record, record.value.s3Key));
27
- if (!landed) {
28
- if (context.offloader) {
29
- await (0, orphans_1.cleanUpS3Orphans)(context.offloader, (0, descriptor_keys_1.collectS3Keys)([record.value]), 'store.put', context.logger);
30
- }
49
+ const verdict = record.value.location === codec_1.PayloadLocation.S3
50
+ ? await (0, write_verify_1.verifyWriteLanded)(context, record, record.value.s3Key)
51
+ : 'not-landed';
52
+ if (verdict === 'not-landed')
53
+ await cleanUp(context, record.value, 'store.put');
54
+ if (verdict !== 'landed')
31
55
  throw error;
32
- }
33
- }
34
- if (context.offloader && previousValue) {
35
- await (0, orphans_1.cleanUpS3Orphans)(context.offloader, (0, descriptor_keys_1.collectS3Keys)([previousValue]), 'store.put.overwrite', context.logger);
36
56
  }
57
+ await cleanUp(context, superseded.value, 'store.put.overwrite');
37
58
  }
38
59
  //# sourceMappingURL=persist.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"persist.js","sourceRoot":"","sources":["../../../src/store/internal/persist.ts"],"names":[],"mappings":";;;AAAA,oDAAmF;AACnF,wEAAmE;AACnE,2DAAiE;AACjE,uDAAgE;AAGhE,iDAA+C;AAE/C,iHAAiH;AACjH,SAAS,gBAAgB,CAAC,KAAY;IACpC,OAAO,KAAK,CAAC,IAAI,KAAK,qBAAqB,CAAC;AAC9C,CAAC;AAED;;;;;GAKG;AACI,KAAK,wBACV,OAAqB,EACrB,MAAuB,EACvB,aAA4C;IAE5C,IAAI,CAAC;QACH,MAAM,IAAA,yBAAiB,EAAC,GAAG,EAAE,CAC3B,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CACnE,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GACV,gBAAgB,CAAC,KAAc,CAAC;YAChC,MAAM,CAAC,KAAK,CAAC,QAAQ,KAAK,uBAAe,CAAC,EAAE;YAC5C,CAAC,MAAM,IAAA,4BAAa,EAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7D,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;gBACtB,MAAM,IAAA,0BAAgB,EACpB,OAAO,CAAC,SAAS,EACjB,IAAA,+BAAa,EAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAC7B,WAAW,EACX,OAAO,CAAC,MAAM,CACf,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,SAAS,IAAI,aAAa,EAAE,CAAC;QACvC,MAAM,IAAA,0BAAgB,EACpB,OAAO,CAAC,SAAS,EACjB,IAAA,+BAAa,EAAC,CAAC,aAAa,CAAC,CAAC,EAC9B,qBAAqB,EACrB,OAAO,CAAC,MAAM,CACf,CAAC;IACJ,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"persist.js","sourceRoot":"","sources":["../../../src/store/internal/persist.ts"],"names":[],"mappings":";;;AAAA,oDAAmF;AACnF,wEAAmE;AACnE,2DAAiE;AACjE,uDAAgE;AAEhE,qDAAuD;AAGvD,iDAAsE;AAEtE,wDAAwD;AACxD,KAAK,UAAU,OAAO,CACpB,OAAqB,EACrB,UAAyC,EACzC,KAAa;IAEb,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,UAAU;QAAE,OAAO;IAC9C,MAAM,IAAA,0BAAgB,EAAC,OAAO,CAAC,SAAS,EAAE,IAAA,+BAAa,EAAC,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;AAChG,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACI,KAAK,wBACV,OAAqB,EACrB,MAAuB,EACvB,QAA4B;IAE5B,IAAI,UAAU,GAAG,QAAQ,CAAC;IAC1B,IAAI,CAAC;QACH,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,UAAU,GAAG,MAAM,IAAA,oCAAmB,EAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QACpE,CAAC;aAAM,CAAC;YACN,MAAM,IAAA,yBAAiB,EAAC,GAAG,EAAE,CAC3B,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CACnE,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GACX,MAAM,CAAC,KAAK,CAAC,QAAQ,KAAK,uBAAe,CAAC,EAAE;YAC1C,CAAC,CAAC,MAAM,IAAA,gCAAiB,EAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;YAC9D,CAAC,CAAC,YAAY,CAAC;QACnB,IAAI,OAAO,KAAK,YAAY;YAAE,MAAM,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QAChF,IAAI,OAAO,KAAK,QAAQ;YAAE,MAAM,KAAK,CAAC;IACxC,CAAC;IACD,MAAM,OAAO,CAAC,OAAO,EAAE,UAAU,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { PayloadDescriptor } from '../../shared/codec/codec';
2
+ import type { StoreContext } from './setup';
3
+ /** The previous row's createdAt, payload descriptor and revision. */
4
+ export interface ExistingRecordMeta {
5
+ exists: boolean;
6
+ createdAt?: string;
7
+ value?: PayloadDescriptor;
8
+ revision?: string;
9
+ }
10
+ /**
11
+ * Read the fields a put needs from the row it is about to replace, in one
12
+ * strongly-consistent projection: `createdAt` to preserve, `value` to clean up
13
+ * afterwards, and the revision the compare-and-swap pins.
14
+ *
15
+ * Lives apart from `actions/put.ts` so `persist.ts` can re-read on a lost swap
16
+ * without importing its own caller.
17
+ */
18
+ export declare function readExisting(context: StoreContext, pk: string, sk: string): Promise<ExistingRecordMeta>;
19
+ //# sourceMappingURL=read-existing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-existing.d.ts","sourceRoot":"","sources":["../../../src/store/internal/read-existing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,qEAAqE;AACrE,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,YAAY,EACrB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,kBAAkB,CAAC,CAgB7B"}
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readExisting = readExisting;
4
+ const conditional_put_1 = require("../../shared/dynamodb/conditional-put");
5
+ const retry_1 = require("../../shared/dynamodb/retry");
6
+ /**
7
+ * Read the fields a put needs from the row it is about to replace, in one
8
+ * strongly-consistent projection: `createdAt` to preserve, `value` to clean up
9
+ * afterwards, and the revision the compare-and-swap pins.
10
+ *
11
+ * Lives apart from `actions/put.ts` so `persist.ts` can re-read on a lost swap
12
+ * without importing its own caller.
13
+ */
14
+ async function readExisting(context, pk, sk) {
15
+ const existing = await (0, retry_1.withDynamoDBRetry)(() => context.client.get({
16
+ TableName: context.tableName,
17
+ Key: { PK: pk, SK: sk },
18
+ ConsistentRead: true,
19
+ ProjectionExpression: '#c, #v, #r',
20
+ ExpressionAttributeNames: { '#c': 'createdAt', '#v': 'value', '#r': conditional_put_1.REVISION_ATTRIBUTE },
21
+ }));
22
+ return {
23
+ exists: existing.Item !== undefined,
24
+ createdAt: existing.Item?.createdAt,
25
+ value: existing.Item?.value,
26
+ revision: existing.Item?.[conditional_put_1.REVISION_ATTRIBUTE],
27
+ };
28
+ }
29
+ //# sourceMappingURL=read-existing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-existing.js","sourceRoot":"","sources":["../../../src/store/internal/read-existing.ts"],"names":[],"mappings":";;;AACA,2EAA2E;AAC3E,uDAAgE;AAWhE;;;;;;;GAOG;AACI,KAAK,uBACV,OAAqB,EACrB,EAAU,EACV,EAAU;IAEV,MAAM,QAAQ,GAAG,MAAM,IAAA,yBAAiB,EAAC,GAAG,EAAE,CAC5C,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC;QACjB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QACvB,cAAc,EAAE,IAAI;QACpB,oBAAoB,EAAE,YAAY;QAClC,wBAAwB,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,oCAAkB,EAAE;KACzF,CAAC,CACH,CAAC;IACF,OAAO;QACL,MAAM,EAAE,QAAQ,CAAC,IAAI,KAAK,SAAS;QACnC,SAAS,EAAE,QAAQ,CAAC,IAAI,EAAE,SAA+B;QACzD,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE,KAAsC;QAC5D,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,oCAAkB,CAAuB;KACpE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { VectorMatch } from '../vector-backend';
2
+ /** Which direction a {@link VectorBackend} reports its score in. */
3
+ export type VectorScoreDirection = 'relevance' | 'distance';
4
+ /** Every direction {@link toRelevanceScores} recognises, for validating input. */
5
+ export declare const VECTOR_SCORE_DIRECTIONS: readonly VectorScoreDirection[];
6
+ /**
7
+ * Normalise a backend's matches to the relevance direction upstream
8
+ * `SearchItem.score` documents — "higher scores indicate better matches",
9
+ * typically a cosine similarity between -1 and 1.
10
+ *
11
+ * A distance is converted by **negation** rather than `1 / (1 + d)`: negation
12
+ * is monotone over the whole real line, needs no non-negativity precondition,
13
+ * and is exactly invertible, so the original distance is simply `-score`. A
14
+ * reciprocal would silently imply a different `(0, 1]` scale and is undefined
15
+ * at `d === -1`. Negative scores are already in range for this contract, so
16
+ * nothing is lost by producing them.
17
+ *
18
+ * Results are re-sorted after conversion so a backend that returns its matches
19
+ * in some other order still ranks correctly. Anything that is not `'distance'`
20
+ * is returned untouched — its own order stays authoritative, exactly as before.
21
+ *
22
+ * The test is for `'distance'` rather than against `'relevance'` on purpose:
23
+ * converting is the destructive branch, so only the exact value that asks for
24
+ * it may reach it. Testing the other way round made every unrecognised
25
+ * string — `'Distance'`, a typo, a value read from a config file — reverse the
26
+ * ranking silently, with no error and no warning (the ascending-score warning
27
+ * lives downstream of this conversion, so it could never fire). `setUpStore`
28
+ * rejects such a value outright; this keeps the failure harmless for any
29
+ * caller that reaches the function some other way.
30
+ */
31
+ export declare function toRelevanceScores(matches: VectorMatch[], direction: VectorScoreDirection): VectorMatch[];
32
+ //# sourceMappingURL=score-direction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"score-direction.d.ts","sourceRoot":"","sources":["../../../src/store/internal/score-direction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,oEAAoE;AACpE,MAAM,MAAM,oBAAoB,GAAG,WAAW,GAAG,UAAU,CAAC;AAE5D,kFAAkF;AAClF,eAAO,MAAM,uBAAuB,EAAE,SAAS,oBAAoB,EAA8B,CAAC;AAElG;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,WAAW,EAAE,EACtB,SAAS,EAAE,oBAAoB,GAC9B,WAAW,EAAE,CAKf"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VECTOR_SCORE_DIRECTIONS = void 0;
4
+ exports.toRelevanceScores = toRelevanceScores;
5
+ /** Every direction {@link toRelevanceScores} recognises, for validating input. */
6
+ exports.VECTOR_SCORE_DIRECTIONS = ['relevance', 'distance'];
7
+ /**
8
+ * Normalise a backend's matches to the relevance direction upstream
9
+ * `SearchItem.score` documents — "higher scores indicate better matches",
10
+ * typically a cosine similarity between -1 and 1.
11
+ *
12
+ * A distance is converted by **negation** rather than `1 / (1 + d)`: negation
13
+ * is monotone over the whole real line, needs no non-negativity precondition,
14
+ * and is exactly invertible, so the original distance is simply `-score`. A
15
+ * reciprocal would silently imply a different `(0, 1]` scale and is undefined
16
+ * at `d === -1`. Negative scores are already in range for this contract, so
17
+ * nothing is lost by producing them.
18
+ *
19
+ * Results are re-sorted after conversion so a backend that returns its matches
20
+ * in some other order still ranks correctly. Anything that is not `'distance'`
21
+ * is returned untouched — its own order stays authoritative, exactly as before.
22
+ *
23
+ * The test is for `'distance'` rather than against `'relevance'` on purpose:
24
+ * converting is the destructive branch, so only the exact value that asks for
25
+ * it may reach it. Testing the other way round made every unrecognised
26
+ * string — `'Distance'`, a typo, a value read from a config file — reverse the
27
+ * ranking silently, with no error and no warning (the ascending-score warning
28
+ * lives downstream of this conversion, so it could never fire). `setUpStore`
29
+ * rejects such a value outright; this keeps the failure harmless for any
30
+ * caller that reaches the function some other way.
31
+ */
32
+ function toRelevanceScores(matches, direction) {
33
+ if (direction !== 'distance')
34
+ return matches;
35
+ return matches
36
+ .map((match) => ({ ...match, score: -match.score }))
37
+ .sort((left, right) => right.score - left.score);
38
+ }
39
+ //# sourceMappingURL=score-direction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"score-direction.js","sourceRoot":"","sources":["../../../src/store/internal/score-direction.ts"],"names":[],"mappings":";;;;AAKA,kFAAkF;AACrE,QAAA,uBAAuB,GAAoC,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;AAElG;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,2BACE,OAAsB,EACtB,SAA+B;IAE/B,IAAI,SAAS,KAAK,UAAU;QAAE,OAAO,OAAO,CAAC;IAC7C,OAAO,OAAO;SACX,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;SACnD,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;AACrD,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Item, SearchOperation } from '@langchain/langgraph-checkpoint';
2
+ /** True when `item` satisfies the search operation's optional metadata filter. */
3
+ export declare function passesFilter(item: Item, op: SearchOperation): boolean;
4
+ //# sourceMappingURL=search-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-filter.d.ts","sourceRoot":"","sources":["../../../src/store/internal/search-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAI7E,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,eAAe,GAAG,OAAO,CAMrE"}
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.passesFilter = passesFilter;
4
+ const filter_1 = require("./filter");
5
+ /** True when `item` satisfies the search operation's optional metadata filter. */
6
+ function passesFilter(item, op) {
7
+ if (!op.filter)
8
+ return true;
9
+ return (0, filter_1.matchesStoreFilter)(item.value, op.filter);
10
+ }
11
+ //# sourceMappingURL=search-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-filter.js","sourceRoot":"","sources":["../../../src/store/internal/search-filter.ts"],"names":[],"mappings":";;;AAEA,qCAA8D;AAE9D,kFAAkF;AAClF,sBAA6B,IAAU,EAAE,EAAmB;IAC1D,IAAI,CAAC,EAAE,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAC5B,OAAO,IAAA,2BAAkB,EACvB,IAAI,CAAC,KAAkC,EACvC,EAAE,CAAC,MAAmC,CACvC,CAAC;AACJ,CAAC"}
@@ -7,6 +7,7 @@ import { type Logger } from '../../shared/logging/logger';
7
7
  import type { TtlOption } from '../../shared/validation/ttl';
8
8
  import type { DynamoDBStoreOptions } from '../types';
9
9
  import type { VectorBackend } from '../vector-backend';
10
+ import { type VectorScoreDirection } from './score-direction';
10
11
  /** Resolved collaborators shared by every store action. */
11
12
  export interface StoreContext {
12
13
  client: DynamoDBDocument;
@@ -18,6 +19,7 @@ export interface StoreContext {
18
19
  logger: Logger;
19
20
  index?: IndexConfig;
20
21
  vectorBackend?: VectorBackend;
22
+ vectorScoreDirection: VectorScoreDirection;
21
23
  maxSearchCandidates: number;
22
24
  maxScanItems: number;
23
25
  }
@@ -27,6 +29,16 @@ export interface StoreSetup {
27
29
  ddbClient: DynamoDBClient | undefined;
28
30
  ownsClient: boolean;
29
31
  }
30
- /** Resolve the client, optional S3 offloader, serializer, and index config. */
32
+ /**
33
+ * Resolve the client, optional S3 offloader, serializer, and index config.
34
+ *
35
+ * A `vectorBackend` without an `index` is rejected outright rather than
36
+ * silently degrading: with no embeddings configured, every `put` would compute
37
+ * no vector and instruct the backend to *delete* the item's entry instead of
38
+ * indexing it, and `search()` would fall through to an unranked scan-order
39
+ * listing with no `.score` field and no error — a semantic query returning a
40
+ * normal-looking but meaningless response. `reconcileVectorIndex` already
41
+ * refused this exact misconfiguration.
42
+ */
31
43
  export declare function setUpStore(options: DynamoDBStoreOptions): StoreSetup;
32
44
  //# sourceMappingURL=setup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/store/internal/setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAEvF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAGxE,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAO9D,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,6BAA6B,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,2DAA2D;AAC3D,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,oDAAoD;AACpD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,cAAc,GAAG,SAAS,CAAC;IACtC,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,OAAO,EAAE,oBAAoB,GAAG,UAAU,CAyBpE"}
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/store/internal/setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAEvF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAGxE,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAQ9D,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,6BAA6B,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAA2B,KAAK,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEvF,2DAA2D;AAC3D,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,oDAAoD;AACpD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,cAAc,GAAG,SAAS,CAAC;IACtC,UAAU,EAAE,OAAO,CAAC;CACrB;AA4CD;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,oBAAoB,GAAG,UAAU,CAoCpE"}
@@ -6,9 +6,64 @@ const config_1 = require("../../shared/codec/s3/config");
6
6
  const offloader_1 = require("../../shared/codec/s3/offloader");
7
7
  const constants_1 = require("../../shared/constants");
8
8
  const client_1 = require("../../shared/dynamodb/client");
9
+ const errors_1 = require("../../shared/errors/errors");
9
10
  const logger_1 = require("../../shared/logging/logger");
10
- /** Resolve the client, optional S3 offloader, serializer, and index config. */
11
+ const score_direction_1 = require("./score-direction");
12
+ /**
13
+ * Reject an `index` that cannot actually embed. `IndexConfig` mandates
14
+ * `embeddings`, but a JavaScript caller can omit it or pass the wrong shape,
15
+ * and the failure then surfaced as a raw `TypeError` deep inside the first
16
+ * `put()`/`search()` rather than this library's typed error at construction.
17
+ *
18
+ * Only `embeddings` is checked. `dims` is part of the upstream type but is
19
+ * never read anywhere in this package, so rejecting a configuration over it
20
+ * would break working callers for no benefit.
21
+ */
22
+ function assertUsableIndex(index) {
23
+ if (!index)
24
+ return;
25
+ const embeddings = index.embeddings;
26
+ if (typeof embeddings?.embedQuery !== 'function') {
27
+ throw new errors_1.ValidationError('`index.embeddings` must be an Embeddings implementation exposing embedQuery(); ' +
28
+ 'without one no embedding can be computed for put() or search()', 'index');
29
+ }
30
+ }
31
+ /**
32
+ * Reject a `vectorScoreDirection` outside the declared union.
33
+ *
34
+ * {@link toRelevanceScores} treats anything it does not recognise as a no-op —
35
+ * the only safe default, since guessing would invert a ranking — so a mistyped
36
+ * or config-file-sourced value would otherwise leave a distance backend ranked
37
+ * backwards with no error and no warning anywhere. Same premise as
38
+ * {@link assertUsableIndex}: a JavaScript caller can pass a string the type
39
+ * never admits.
40
+ */
41
+ function assertScoreDirection(direction) {
42
+ if (direction === undefined || score_direction_1.VECTOR_SCORE_DIRECTIONS.includes(direction))
43
+ return;
44
+ throw new errors_1.ValidationError(`vectorScoreDirection must be one of ${score_direction_1.VECTOR_SCORE_DIRECTIONS.join(' | ')}; received ` +
45
+ `${JSON.stringify(direction)}, which would be left in the backend's own direction and ` +
46
+ 'could rank a distance backend backwards', 'vectorScoreDirection');
47
+ }
48
+ /**
49
+ * Resolve the client, optional S3 offloader, serializer, and index config.
50
+ *
51
+ * A `vectorBackend` without an `index` is rejected outright rather than
52
+ * silently degrading: with no embeddings configured, every `put` would compute
53
+ * no vector and instruct the backend to *delete* the item's entry instead of
54
+ * indexing it, and `search()` would fall through to an unranked scan-order
55
+ * listing with no `.score` field and no error — a semantic query returning a
56
+ * normal-looking but meaningless response. `reconcileVectorIndex` already
57
+ * refused this exact misconfiguration.
58
+ */
11
59
  function setUpStore(options) {
60
+ if (options.vectorBackend && !options.index) {
61
+ throw new errors_1.ValidationError('vectorBackend requires a configured `index` (embeddings); without one no embedding ' +
62
+ 'is computed, every put would clear the item vector, and search would silently return ' +
63
+ 'unranked, score-less results', 'vectorBackend');
64
+ }
65
+ assertUsableIndex(options.index);
66
+ assertScoreDirection(options.vectorScoreDirection);
12
67
  const resolved = (0, client_1.resolveDynamoDBClient)(options);
13
68
  return {
14
69
  context: {
@@ -26,6 +81,7 @@ function setUpStore(options) {
26
81
  logger: (0, logger_1.resolveLogger)(options.logger),
27
82
  index: options.index,
28
83
  vectorBackend: options.vectorBackend,
84
+ vectorScoreDirection: options.vectorScoreDirection ?? 'relevance',
29
85
  maxSearchCandidates: options.maxSearchCandidates ?? constants_1.DEFAULT_MAX_SEARCH_CANDIDATES,
30
86
  maxScanItems: options.maxScanItems ?? constants_1.MAX_TOTAL_ITEMS_IN_MEMORY,
31
87
  },
@@ -1 +1 @@
1
- {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../src/store/internal/setup.ts"],"names":[],"mappings":";;;AAKA,8DAA2D;AAC3D,yDAAuE;AACvE,+DAA8D;AAC9D,sDAIgC;AAChC,yDAAqE;AACrE,wDAAyE;AA2BzE,+EAA+E;AAC/E,oBAA2B,OAA6B;IACtD,MAAM,QAAQ,GAAG,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAChD,OAAO;QACL,OAAO,EAAE;YACP,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,uBAAU;YAClC,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,SAAS,EAAE,OAAO,CAAC,EAAE;gBACnB,CAAC,CAAC,IAAI,uBAAW,CAAC;oBACd,GAAG,OAAO,CAAC,EAAE;oBACb,SAAS,EACP,OAAO,CAAC,EAAE,CAAC,SAAS,IAAI,IAAA,gCAAuB,EAAC,iCAAqB,EAAE,OAAO,CAAC;iBAClF,CAAC;gBACJ,CAAC,CAAC,SAAS;YACb,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,MAAM,EAAE,IAAA,sBAAa,EAAC,OAAO,CAAC,MAAM,CAAC;YACrC,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,mBAAmB,EAAE,OAAO,CAAC,mBAAmB,IAAI,yCAA6B;YACjF,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,qCAAyB;SAChE;QACD,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,UAAU,EAAE,QAAQ,CAAC,UAAU;KAChC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../src/store/internal/setup.ts"],"names":[],"mappings":";;;AAMA,8DAA2D;AAC3D,yDAAuE;AACvE,+DAA8D;AAC9D,sDAIgC;AAChC,yDAAqE;AACrE,uDAA6D;AAC7D,wDAAyE;AAIzE,uDAAuF;AAyBvF;;;;;;;;;GASG;AACH,SAAS,iBAAiB,CAAC,KAAmB;IAC5C,IAAI,CAAC,KAAK;QAAE,OAAO;IACnB,MAAM,UAAU,GAAoC,KAAK,CAAC,UAAU,CAAC;IACrE,IAAI,OAAO,UAAU,EAAE,UAAU,KAAK,UAAU,EAAE,CAAC;QACjD,MAAM,IAAI,wBAAe,CACvB,iFAAiF;YAC/E,gEAAgE,EAClE,OAAO,CACR,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,oBAAoB,CAAC,SAAgC;IAC5D,IAAI,SAAS,KAAK,SAAS,IAAI,yCAAuB,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO;IACnF,MAAM,IAAI,wBAAe,CACvB,uCAAuC,yCAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa;QACrF,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,2DAA2D;QACvF,yCAAyC,EAC3C,sBAAsB,CACvB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,oBAA2B,OAA6B;IACtD,IAAI,OAAO,CAAC,aAAa,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAC5C,MAAM,IAAI,wBAAe,CACvB,qFAAqF;YACnF,uFAAuF;YACvF,8BAA8B,EAChC,eAAe,CAChB,CAAC;IACJ,CAAC;IACD,iBAAiB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACjC,oBAAoB,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAChD,OAAO;QACL,OAAO,EAAE;YACP,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,uBAAU;YAClC,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,SAAS,EAAE,OAAO,CAAC,EAAE;gBACnB,CAAC,CAAC,IAAI,uBAAW,CAAC;oBACd,GAAG,OAAO,CAAC,EAAE;oBACb,SAAS,EACP,OAAO,CAAC,EAAE,CAAC,SAAS,IAAI,IAAA,gCAAuB,EAAC,iCAAqB,EAAE,OAAO,CAAC;iBAClF,CAAC;gBACJ,CAAC,CAAC,SAAS;YACb,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,MAAM,EAAE,IAAA,sBAAa,EAAC,OAAO,CAAC,MAAM,CAAC;YACrC,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,oBAAoB,EAAE,OAAO,CAAC,oBAAoB,IAAI,WAAW;YACjE,mBAAmB,EAAE,OAAO,CAAC,mBAAmB,IAAI,yCAA6B;YACjF,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,qCAAyB;SAChE;QACD,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,UAAU,EAAE,QAAQ,CAAC,UAAU;KAChC,CAAC;AACJ,CAAC"}
@@ -1,7 +1,13 @@
1
1
  /** Validate search/listNamespaces paging is non-negative integers. */
2
2
  export declare function validatePaging(offset: number, limit: number): void;
3
- /** Validate the namespace is non-empty and each element is separator-free. */
3
+ /** Validate the namespace is non-empty and each element is a valid identifier. */
4
4
  export declare function validateNamespace(namespace: string[]): void;
5
- /** Validate an item key is a non-empty, separator-free string. */
5
+ /** Validate an item key is a non-empty, separator- and control-char-free string. */
6
6
  export declare function validateKey(key: string): void;
7
+ /**
8
+ * Validate an optional namespace depth cap. Left unchecked, a negative value
9
+ * silently inverted truncation via `Array.prototype.slice(0, -n)`, which drops
10
+ * the *last* n elements rather than erroring.
11
+ */
12
+ export declare function validateMaxDepth(maxDepth?: number): void;
7
13
  //# sourceMappingURL=validation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/store/internal/validation.ts"],"names":[],"mappings":"AAQA,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAGlE;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAM3D;AAED,kEAAkE;AAClE,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAG7C"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/store/internal/validation.ts"],"names":[],"mappings":"AAOA,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAGlE;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAK3D;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAGxD"}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validatePaging = validatePaging;
4
4
  exports.validateNamespace = validateNamespace;
5
5
  exports.validateKey = validateKey;
6
+ exports.validateMaxDepth = validateMaxDepth;
6
7
  const primitives_1 = require("../../shared/validation/primitives");
7
8
  const keys_1 = require("./keys");
8
9
  /** Validate search/listNamespaces paging is non-negative integers. */
@@ -10,17 +11,25 @@ function validatePaging(offset, limit) {
10
11
  (0, primitives_1.validateInteger)(offset, 'offset', { min: 0 });
11
12
  (0, primitives_1.validateInteger)(limit, 'limit', { min: 0 });
12
13
  }
13
- /** Validate the namespace is non-empty and each element is separator-free. */
14
+ /** Validate the namespace is non-empty and each element is a valid identifier. */
14
15
  function validateNamespace(namespace) {
15
16
  (0, primitives_1.validateNonEmptyArray)(namespace, 'namespace');
16
17
  for (const element of namespace) {
17
- (0, primitives_1.validateNonEmptyString)(element, 'namespace element');
18
- (0, primitives_1.assertNoSeparator)(element, keys_1.NAMESPACE_SEPARATOR, 'namespace element');
18
+ (0, primitives_1.validateIdentifier)(element, keys_1.NAMESPACE_SEPARATOR, 'namespace element');
19
19
  }
20
20
  }
21
- /** Validate an item key is a non-empty, separator-free string. */
21
+ /** Validate an item key is a non-empty, separator- and control-char-free string. */
22
22
  function validateKey(key) {
23
- (0, primitives_1.validateNonEmptyString)(key, 'key');
24
- (0, primitives_1.assertNoSeparator)(key, keys_1.NAMESPACE_SEPARATOR, 'key');
23
+ (0, primitives_1.validateIdentifier)(key, keys_1.NAMESPACE_SEPARATOR, 'key');
24
+ }
25
+ /**
26
+ * Validate an optional namespace depth cap. Left unchecked, a negative value
27
+ * silently inverted truncation via `Array.prototype.slice(0, -n)`, which drops
28
+ * the *last* n elements rather than erroring.
29
+ */
30
+ function validateMaxDepth(maxDepth) {
31
+ if (maxDepth === undefined)
32
+ return;
33
+ (0, primitives_1.validateInteger)(maxDepth, 'maxDepth', { min: 1 });
25
34
  }
26
35
  //# sourceMappingURL=validation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../../src/store/internal/validation.ts"],"names":[],"mappings":";;;;;AAAA,mEAK4C;AAC5C,iCAA6C;AAE7C,sEAAsE;AACtE,wBAA+B,MAAc,EAAE,KAAa;IAC1D,IAAA,4BAAe,EAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAC9C,IAAA,4BAAe,EAAC,KAAK,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,8EAA8E;AAC9E,2BAAkC,SAAmB;IACnD,IAAA,kCAAqB,EAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAC9C,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE,CAAC;QAChC,IAAA,mCAAsB,EAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;QACrD,IAAA,8BAAiB,EAAC,OAAO,EAAE,0BAAmB,EAAE,mBAAmB,CAAC,CAAC;IACvE,CAAC;AACH,CAAC;AAED,kEAAkE;AAClE,qBAA4B,GAAW;IACrC,IAAA,mCAAsB,EAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACnC,IAAA,8BAAiB,EAAC,GAAG,EAAE,0BAAmB,EAAE,KAAK,CAAC,CAAC;AACrD,CAAC"}
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../../src/store/internal/validation.ts"],"names":[],"mappings":";;;;;;AAAA,mEAI4C;AAC5C,iCAA6C;AAE7C,sEAAsE;AACtE,wBAA+B,MAAc,EAAE,KAAa;IAC1D,IAAA,4BAAe,EAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAC9C,IAAA,4BAAe,EAAC,KAAK,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,kFAAkF;AAClF,2BAAkC,SAAmB;IACnD,IAAA,kCAAqB,EAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAC9C,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE,CAAC;QAChC,IAAA,+BAAkB,EAAC,OAAO,EAAE,0BAAmB,EAAE,mBAAmB,CAAC,CAAC;IACxE,CAAC;AACH,CAAC;AAED,oFAAoF;AACpF,qBAA4B,GAAW;IACrC,IAAA,+BAAkB,EAAC,GAAG,EAAE,0BAAmB,EAAE,KAAK,CAAC,CAAC;AACtD,CAAC;AAED;;;;GAIG;AACH,0BAAiC,QAAiB;IAChD,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO;IACnC,IAAA,4BAAe,EAAC,QAAQ,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;AACpD,CAAC"}