@farukada/aws-langgraph-dynamodb-ts 0.8.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 (95) hide show
  1. package/README.md +1 -1
  2. package/dist/checkpointer/actions/list.d.ts.map +1 -1
  3. package/dist/checkpointer/actions/list.js +7 -0
  4. package/dist/checkpointer/actions/list.js.map +1 -1
  5. package/dist/checkpointer/actions/put-writes.d.ts.map +1 -1
  6. package/dist/checkpointer/actions/put-writes.js +2 -1
  7. package/dist/checkpointer/actions/put-writes.js.map +1 -1
  8. package/dist/checkpointer/internal/item-reader.d.ts +14 -0
  9. package/dist/checkpointer/internal/item-reader.d.ts.map +1 -1
  10. package/dist/checkpointer/internal/item-reader.js +15 -1
  11. package/dist/checkpointer/internal/item-reader.js.map +1 -1
  12. package/dist/checkpointer/internal/item-writer.d.ts.map +1 -1
  13. package/dist/checkpointer/internal/item-writer.js +2 -1
  14. package/dist/checkpointer/internal/item-writer.js.map +1 -1
  15. package/dist/checkpointer/internal/special-write-cas.d.ts +30 -0
  16. package/dist/checkpointer/internal/special-write-cas.d.ts.map +1 -0
  17. package/dist/checkpointer/internal/special-write-cas.js +104 -0
  18. package/dist/checkpointer/internal/special-write-cas.js.map +1 -0
  19. package/dist/checkpointer/internal/special-write-cleanup.d.ts +18 -12
  20. package/dist/checkpointer/internal/special-write-cleanup.d.ts.map +1 -1
  21. package/dist/checkpointer/internal/special-write-cleanup.js +31 -86
  22. package/dist/checkpointer/internal/special-write-cleanup.js.map +1 -1
  23. package/dist/checkpointer/internal/special-write-verify.d.ts +54 -0
  24. package/dist/checkpointer/internal/special-write-verify.d.ts.map +1 -0
  25. package/dist/checkpointer/internal/special-write-verify.js +65 -0
  26. package/dist/checkpointer/internal/special-write-verify.js.map +1 -0
  27. package/dist/checkpointer/internal/write-guard.d.ts +0 -8
  28. package/dist/checkpointer/internal/write-guard.d.ts.map +1 -1
  29. package/dist/checkpointer/internal/write-guard.js +0 -9
  30. package/dist/checkpointer/internal/write-guard.js.map +1 -1
  31. package/dist/checkpointer/internal/write-index.d.ts +8 -0
  32. package/dist/checkpointer/internal/write-index.d.ts.map +1 -1
  33. package/dist/checkpointer/internal/write-index.js +6 -2
  34. package/dist/checkpointer/internal/write-index.js.map +1 -1
  35. package/dist/checkpointer/types.d.ts +6 -0
  36. package/dist/checkpointer/types.d.ts.map +1 -1
  37. package/dist/shared/constants.d.ts +15 -0
  38. package/dist/shared/constants.d.ts.map +1 -1
  39. package/dist/shared/constants.js +16 -1
  40. package/dist/shared/constants.js.map +1 -1
  41. package/dist/shared/dynamodb/conditional-put.d.ts +51 -0
  42. package/dist/shared/dynamodb/conditional-put.d.ts.map +1 -0
  43. package/dist/shared/dynamodb/conditional-put.js +59 -0
  44. package/dist/shared/dynamodb/conditional-put.js.map +1 -0
  45. package/dist/shared/logging/redaction.d.ts +5 -1
  46. package/dist/shared/logging/redaction.d.ts.map +1 -1
  47. package/dist/shared/logging/redaction.js.map +1 -1
  48. package/dist/shared/logging/secret-patterns.d.ts +33 -0
  49. package/dist/shared/logging/secret-patterns.d.ts.map +1 -1
  50. package/dist/shared/logging/secret-patterns.js +35 -2
  51. package/dist/shared/logging/secret-patterns.js.map +1 -1
  52. package/dist/store/actions/put.d.ts.map +1 -1
  53. package/dist/store/actions/put.js +4 -16
  54. package/dist/store/actions/put.js.map +1 -1
  55. package/dist/store/internal/backend-search.d.ts.map +1 -1
  56. package/dist/store/internal/backend-search.js +2 -1
  57. package/dist/store/internal/backend-search.js.map +1 -1
  58. package/dist/store/internal/filter.d.ts.map +1 -1
  59. package/dist/store/internal/filter.js +25 -5
  60. package/dist/store/internal/filter.js.map +1 -1
  61. package/dist/store/internal/index-reconcile.d.ts.map +1 -1
  62. package/dist/store/internal/index-reconcile.js +8 -2
  63. package/dist/store/internal/index-reconcile.js.map +1 -1
  64. package/dist/store/internal/item-mapper.d.ts +1 -0
  65. package/dist/store/internal/item-mapper.d.ts.map +1 -1
  66. package/dist/store/internal/item-mapper.js +2 -0
  67. package/dist/store/internal/item-mapper.js.map +1 -1
  68. package/dist/store/internal/overwrite-swap.d.ts +33 -0
  69. package/dist/store/internal/overwrite-swap.d.ts.map +1 -0
  70. package/dist/store/internal/overwrite-swap.js +62 -0
  71. package/dist/store/internal/overwrite-swap.js.map +1 -0
  72. package/dist/store/internal/persist.d.ts +22 -6
  73. package/dist/store/internal/persist.d.ts.map +1 -1
  74. package/dist/store/internal/persist.js +42 -17
  75. package/dist/store/internal/persist.js.map +1 -1
  76. package/dist/store/internal/read-existing.d.ts +19 -0
  77. package/dist/store/internal/read-existing.d.ts.map +1 -0
  78. package/dist/store/internal/read-existing.js +29 -0
  79. package/dist/store/internal/read-existing.js.map +1 -0
  80. package/dist/store/internal/score-direction.d.ts +32 -0
  81. package/dist/store/internal/score-direction.d.ts.map +1 -0
  82. package/dist/store/internal/score-direction.js +39 -0
  83. package/dist/store/internal/score-direction.js.map +1 -0
  84. package/dist/store/internal/setup.d.ts +2 -0
  85. package/dist/store/internal/setup.d.ts.map +1 -1
  86. package/dist/store/internal/setup.js +41 -1
  87. package/dist/store/internal/setup.js.map +1 -1
  88. package/dist/store/internal/write-verify.d.ts +20 -7
  89. package/dist/store/internal/write-verify.d.ts.map +1 -1
  90. package/dist/store/internal/write-verify.js +22 -9
  91. package/dist/store/internal/write-verify.js.map +1 -1
  92. package/dist/store/types.d.ts +16 -0
  93. package/dist/store/types.d.ts.map +1 -1
  94. package/dist/store/vector-backend.d.ts +6 -6
  95. package/package.json +1 -1
@@ -0,0 +1,54 @@
1
+ import type { PayloadDescriptor } from '../../shared/codec/codec';
2
+ import type { CheckpointWriteItem } from '../types';
3
+ import type { CheckpointerContext } from './setup';
4
+ /**
5
+ * A special row already carries a per-call ULID in `writeGroup`, so it needs no
6
+ * extra revision attribute to compare and swap on.
7
+ */
8
+ export declare const SPECIAL_REVISION_ATTRIBUTE = "writeGroup";
9
+ /** What a special item's row held before this call tried to overwrite it. */
10
+ export interface SpecialRowState {
11
+ exists: boolean;
12
+ value?: PayloadDescriptor;
13
+ revision?: string;
14
+ }
15
+ /** Outcome of one special item's conditional write. Never thrown, always returned. */
16
+ export interface SpecialWriteOutcome {
17
+ committed: boolean;
18
+ superseded?: PayloadDescriptor;
19
+ error?: Error;
20
+ }
21
+ /** What a post-failure verification read established about the attempt. */
22
+ export interface VerifiedFailure {
23
+ outcome: SpecialWriteOutcome;
24
+ /** Present only when the row was read and holds some other writer's group. */
25
+ observed?: SpecialRowState;
26
+ }
27
+ /** Read a special row's current descriptor and the writeGroup guarding it. */
28
+ export declare function readSpecialRow(context: CheckpointerContext, item: CheckpointWriteItem): Promise<SpecialRowState>;
29
+ /**
30
+ * Read the row back after a put failed, and report what that failure actually
31
+ * did — never assuming it did nothing.
32
+ *
33
+ * No rejection is proof of a non-commit. `withDynamoDBRetry` re-issues a put
34
+ * whose response was lost, and those re-issues can time out at the transport
35
+ * without ever reaching DynamoDB, so the budget is spent on a
36
+ * `RetryExhaustedError` and never on a `ConditionalCheckFailedException` — yet
37
+ * the row is committed. Reporting that as a confirmed non-commit let
38
+ * `writeSpecialItemsWithCleanup` delete the S3 object the live row points at,
39
+ * so every later `getTuple()` on that checkpoint failed with `NoSuchKey`,
40
+ * permanently.
41
+ *
42
+ * Three answers are possible:
43
+ * - the row holds this item's own `writeGroup`: the write landed, and the
44
+ * descriptor this attempt pinned is the dead one.
45
+ * - the row holds some other group: the write is confirmed not to be what is
46
+ * live, so this item's own upload is the dead one. `observed` is returned so
47
+ * a rejected compare-and-swap can re-pin and try again.
48
+ * - the read itself fails: nothing is confirmed, so the outcome still reports a
49
+ * commit and keeps the originating error. That leaks one S3 object at worst
50
+ * (reclaimed by `ensureS3LifecycleRule`) where the alternative strands a live
51
+ * row — the same trade `store/internal/persist.ts` makes.
52
+ */
53
+ export declare function verifyAfterFailure(context: CheckpointerContext, item: CheckpointWriteItem, attempted: SpecialRowState, error: Error): Promise<VerifiedFailure>;
54
+ //# sourceMappingURL=special-write-verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"special-write-verify.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/special-write-verify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD;;;GAGG;AACH,eAAO,MAAM,0BAA0B,eAAe,CAAC;AAEvD,6EAA6E;AAC7E,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,sFAAsF;AACtF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,8EAA8E;AAC9E,wBAAsB,cAAc,CAClC,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,eAAe,CAAC,CAgB1B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,mBAAmB,EACzB,SAAS,EAAE,eAAe,EAC1B,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,eAAe,CAAC,CAU1B"}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SPECIAL_REVISION_ATTRIBUTE = void 0;
4
+ exports.readSpecialRow = readSpecialRow;
5
+ exports.verifyAfterFailure = verifyAfterFailure;
6
+ const retry_1 = require("../../shared/dynamodb/retry");
7
+ /**
8
+ * A special row already carries a per-call ULID in `writeGroup`, so it needs no
9
+ * extra revision attribute to compare and swap on.
10
+ */
11
+ exports.SPECIAL_REVISION_ATTRIBUTE = 'writeGroup';
12
+ /** Read a special row's current descriptor and the writeGroup guarding it. */
13
+ async function readSpecialRow(context, item) {
14
+ const result = await (0, retry_1.withDynamoDBRetry)(() => context.client.get({
15
+ TableName: context.tableName,
16
+ Key: { PK: item.PK, SK: item.SK },
17
+ ConsistentRead: true,
18
+ ProjectionExpression: '#v, #g',
19
+ ExpressionAttributeNames: { '#v': 'value', '#g': exports.SPECIAL_REVISION_ATTRIBUTE },
20
+ }));
21
+ if (!result.Item)
22
+ return { exists: false };
23
+ return {
24
+ exists: true,
25
+ value: result.Item.value,
26
+ revision: result.Item[exports.SPECIAL_REVISION_ATTRIBUTE],
27
+ };
28
+ }
29
+ /**
30
+ * Read the row back after a put failed, and report what that failure actually
31
+ * did — never assuming it did nothing.
32
+ *
33
+ * No rejection is proof of a non-commit. `withDynamoDBRetry` re-issues a put
34
+ * whose response was lost, and those re-issues can time out at the transport
35
+ * without ever reaching DynamoDB, so the budget is spent on a
36
+ * `RetryExhaustedError` and never on a `ConditionalCheckFailedException` — yet
37
+ * the row is committed. Reporting that as a confirmed non-commit let
38
+ * `writeSpecialItemsWithCleanup` delete the S3 object the live row points at,
39
+ * so every later `getTuple()` on that checkpoint failed with `NoSuchKey`,
40
+ * permanently.
41
+ *
42
+ * Three answers are possible:
43
+ * - the row holds this item's own `writeGroup`: the write landed, and the
44
+ * descriptor this attempt pinned is the dead one.
45
+ * - the row holds some other group: the write is confirmed not to be what is
46
+ * live, so this item's own upload is the dead one. `observed` is returned so
47
+ * a rejected compare-and-swap can re-pin and try again.
48
+ * - the read itself fails: nothing is confirmed, so the outcome still reports a
49
+ * commit and keeps the originating error. That leaks one S3 object at worst
50
+ * (reclaimed by `ensureS3LifecycleRule`) where the alternative strands a live
51
+ * row — the same trade `store/internal/persist.ts` makes.
52
+ */
53
+ async function verifyAfterFailure(context, item, attempted, error) {
54
+ try {
55
+ const observed = await readSpecialRow(context, item);
56
+ if (observed.revision === item.writeGroup) {
57
+ return { outcome: { committed: true, superseded: attempted.value } };
58
+ }
59
+ return { outcome: { committed: false, error }, observed };
60
+ }
61
+ catch {
62
+ return { outcome: { committed: true, error } };
63
+ }
64
+ }
65
+ //# sourceMappingURL=special-write-verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"special-write-verify.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/special-write-verify.ts"],"names":[],"mappings":";;;;;AACA,uDAAgE;AAIhE;;;GAGG;AACU,QAAA,0BAA0B,GAAG,YAAY,CAAC;AAuBvD,8EAA8E;AACvE,KAAK,yBACV,OAA4B,EAC5B,IAAyB;IAEzB,MAAM,MAAM,GAAG,MAAM,IAAA,yBAAiB,EAAC,GAAG,EAAE,CAC1C,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC;QACjB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE;QACjC,cAAc,EAAE,IAAI;QACpB,oBAAoB,EAAE,QAAQ;QAC9B,wBAAwB,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,QAAA,0BAA0B,EAAE;KAC9E,CAAC,CACH,CAAC;IACF,IAAI,CAAC,MAAM,CAAC,IAAI;QAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC3C,OAAO;QACL,MAAM,EAAE,IAAI;QACZ,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAsC;QACzD,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,QAAA,0BAA0B,CAAuB;KACxE,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACI,KAAK,6BACV,OAA4B,EAC5B,IAAyB,EACzB,SAA0B,EAC1B,KAAY;IAEZ,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACrD,IAAI,QAAQ,CAAC,QAAQ,KAAK,IAAI,CAAC,UAAU,EAAE,CAAC;YAC1C,OAAO,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;QACvE,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;IACjD,CAAC;AACH,CAAC"}
@@ -1,13 +1,5 @@
1
1
  import type { CheckpointWriteItem } from '../types';
2
2
  import type { CheckpointerContext } from './setup';
3
- /**
4
- * True when the guard rejected a write — NOT evidence a competitor won: a
5
- * `PutCommand` retried after its response was lost can re-hit its own
6
- * committed row and fail identically; the two cases are indistinguishable.
7
- */
8
- export declare function isConditionalCheckFailed(error: {
9
- name?: string;
10
- }): boolean;
11
3
  /**
12
4
  * Report a guard rejection. Sort keys carry their channel, so a rejection
13
5
  * normally means this exact (task, channel, occurrence) row is already
@@ -1 +1 @@
1
- {"version":3,"file":"write-guard.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/write-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAE1E;AAcD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE,KAAK,GACX,IAAI,CAcN"}
1
+ {"version":3,"file":"write-guard.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/write-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAcnD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE,KAAK,GACX,IAAI,CAcN"}
@@ -1,15 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isConditionalCheckFailed = isConditionalCheckFailed;
4
3
  exports.reportGuardRejection = reportGuardRejection;
5
- /**
6
- * True when the guard rejected a write — NOT evidence a competitor won: a
7
- * `PutCommand` retried after its response was lost can re-hit its own
8
- * committed row and fail identically; the two cases are indistinguishable.
9
- */
10
- function isConditionalCheckFailed(error) {
11
- return error.name === 'ConditionalCheckFailedException';
12
- }
13
4
  /**
14
5
  * The channel recorded on the row that turned a write away, or undefined when
15
6
  * the service returned no attributes. `ReturnValuesOnConditionCheckFailure:
@@ -1 +1 @@
1
- {"version":3,"file":"write-guard.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/write-guard.ts"],"names":[],"mappings":";;;;AAGA;;;;GAIG;AACH,kCAAyC,KAAwB;IAC/D,OAAO,KAAK,CAAC,IAAI,KAAK,iCAAiC,CAAC;AAC1D,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,eAAe,CAAC,KAAY;IACnC,OAAQ,KAAiD,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;;;;GAQG;AACH,8BACE,OAA4B,EAC5B,IAAyB,EACzB,KAAY;IAEZ,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;QAClD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,yEAAyE,EAAE;YAC7F,OAAO,EAAE,IAAI,CAAC,EAAE;YAChB,QAAQ,EAAE,IAAI,CAAC,OAAO;YACtB,KAAK;SACN,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wEAAwE,EAAE;QAC7F,OAAO,EAAE,IAAI,CAAC,EAAE;QAChB,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"write-guard.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/write-guard.ts"],"names":[],"mappings":";;;AAGA;;;;;;;GAOG;AACH,SAAS,eAAe,CAAC,KAAY;IACnC,OAAQ,KAAiD,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;;;;GAQG;AACH,8BACE,OAA4B,EAC5B,IAAyB,EACzB,KAAY;IAEZ,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;QAClD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,yEAAyE,EAAE;YAC7F,OAAO,EAAE,IAAI,CAAC,EAAE;YAChB,QAAQ,EAAE,IAAI,CAAC,OAAO;YACtB,KAAK;SACN,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wEAAwE,EAAE;QAC7F,OAAO,EAAE,IAAI,CAAC,EAAE;QAChB,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAC,CAAC;AACL,CAAC"}
@@ -4,6 +4,14 @@ export interface ResolvedWrite {
4
4
  channel: string;
5
5
  value: PendingWriteValue;
6
6
  index: number;
7
+ /**
8
+ * How many earlier writes in this same call already used this channel. A
9
+ * retry that emits a channel *more* often than the original call produces a
10
+ * row at an occurrence no earlier call ever wrote — it collides with nothing
11
+ * and commits cleanly, so the read-side dedup must not mistake it for a
12
+ * superseding duplicate.
13
+ */
14
+ occurrence: number;
7
15
  }
8
16
  /**
9
17
  * Assign every write in one `putWrites` call its sort-key index, in a single
@@ -1 +1 @@
1
- {"version":3,"file":"write-index.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/write-index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAGvF,+DAA+D;AAC/D,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,iBAAiB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,aAAa,EAAE,CAY3E"}
1
+ {"version":3,"file":"write-index.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/write-index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAGvF,+DAA+D;AAC/D,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,iBAAiB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,aAAa,EAAE,CAgB3E"}
@@ -25,13 +25,17 @@ const langgraph_checkpoint_1 = require("@langchain/langgraph-checkpoint");
25
25
  function resolveWriteIndices(writes) {
26
26
  const bySpecialIndex = new Map();
27
27
  const regular = [];
28
+ const occurrences = new Map();
28
29
  writes.forEach(([channel, value], positional) => {
29
30
  if (Object.hasOwn(langgraph_checkpoint_1.WRITES_IDX_MAP, channel)) {
30
31
  const index = langgraph_checkpoint_1.WRITES_IDX_MAP[channel];
31
- bySpecialIndex.set(index, { channel, value, index });
32
+ /** Last write wins per special channel, so a call holds exactly one. */
33
+ bySpecialIndex.set(index, { channel, value, index, occurrence: 0 });
32
34
  return;
33
35
  }
34
- regular.push({ channel, value, index: positional });
36
+ const occurrence = occurrences.get(channel) ?? 0;
37
+ occurrences.set(channel, occurrence + 1);
38
+ regular.push({ channel, value, index: positional, occurrence });
35
39
  });
36
40
  return [...bySpecialIndex.values(), ...regular];
37
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"write-index.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/write-index.ts"],"names":[],"mappings":";;;AACA,0EAAiE;AASjE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,6BAAoC,MAAsB;IACxD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAyB,CAAC;IACxD,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,UAAU,EAAE,EAAE;QAC9C,IAAI,MAAM,CAAC,MAAM,CAAC,qCAAc,EAAE,OAAO,CAAC,EAAE,CAAC;YAC3C,MAAM,KAAK,GAAG,qCAAc,CAAC,OAAO,CAAC,CAAC;YACtC,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACrD,OAAO;QACT,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,CAAC;AAClD,CAAC"}
1
+ {"version":3,"file":"write-index.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/write-index.ts"],"names":[],"mappings":";;;AACA,0EAAiE;AAiBjE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,6BAAoC,MAAsB;IACxD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAyB,CAAC;IACxD,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC9C,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,UAAU,EAAE,EAAE;QAC9C,IAAI,MAAM,CAAC,MAAM,CAAC,qCAAc,EAAE,OAAO,CAAC,EAAE,CAAC;YAC3C,MAAM,KAAK,GAAG,qCAAc,CAAC,OAAO,CAAC,CAAC;YACtC,wEAAwE;YACxE,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;YACpE,OAAO;QACT,CAAC;QACD,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACjD,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC;QACzC,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,CAAC;AAClD,CAAC"}
@@ -39,6 +39,12 @@ export interface CheckpointWriteItem {
39
39
  channel: string;
40
40
  /** Identifies the `putWrites` call that produced this row (see item-writer). */
41
41
  writeGroup: string;
42
+ /**
43
+ * How many earlier writes in the same call already used this channel.
44
+ * Optional: rows written before 0.9.0 carry none and read back as 0, which
45
+ * is exactly the identity they were stored under.
46
+ */
47
+ occurrence?: number;
42
48
  value: PayloadDescriptor;
43
49
  ttl?: number;
44
50
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/checkpointer/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAE1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1E,yCAAyC;AACzC,MAAM,MAAM,oBAAoB,GAAG,kBAAkB,GACnD,YAAY,GAAG;IACb,8EAA8E;IAC9E,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC5B,CAAC;AAEJ,2EAA2E;AAC3E,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,6EAA6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,4DAA4D;AAC5D,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,iBAAiB,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,gEAAgE;AAChE,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,iBAAiB,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/checkpointer/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAE1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1E,yCAAyC;AACzC,MAAM,MAAM,oBAAoB,GAAG,kBAAkB,GACnD,YAAY,GAAG;IACb,8EAA8E;IAC9E,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC5B,CAAC;AAEJ,2EAA2E;AAC3E,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,6EAA6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,4DAA4D;AAC5D,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,iBAAiB,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,gEAAgE;AAChE,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,iBAAiB,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd"}
@@ -46,4 +46,19 @@ export declare const DEFAULT_RETRY_MAX_ATTEMPTS = 5;
46
46
  export declare const MESSAGE_APPEND_RETRY_MAX_ATTEMPTS = 18;
47
47
  /** Default cap on candidates the in-DB semantic ranker will score. */
48
48
  export declare const DEFAULT_MAX_SEARCH_CANDIDATES = 1000;
49
+ /**
50
+ * Raw rows a single `listCheckpoints` call may pull before it warns. The read
51
+ * itself is deliberately unbounded — capping it counted raw rows rather than
52
+ * filter-matched ones, which turned a caller asking for a handful of rare
53
+ * matches over a large thread into a hard error instead of the true answer.
54
+ * The warning restores the operational signal without restoring the wrong
55
+ * error.
56
+ *
57
+ * Its own literal, deliberately: this is the point at which a scan is worth
58
+ * telling an operator about, which is independent of
59
+ * {@link MAX_TOTAL_ITEMS_IN_MEMORY}'s hard collection cap. Aliasing the two
60
+ * meant retuning the memory cap silently moved the warning as well, and it
61
+ * left the pair reported as a duplicate export.
62
+ */
63
+ export declare const LIST_SCAN_WARN_THRESHOLD = 10000;
49
64
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/shared/constants.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,eAAO,MAAM,YAAY,QAAU,CAAC;AAEpC,oDAAoD;AACpD,eAAO,MAAM,eAAe,QAA2B,CAAC;AAExD,yEAAyE;AACzE,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAExC,wEAAwE;AACxE,eAAO,MAAM,yBAAyB,QAAQ,CAAC;AAE/C,yDAAyD;AACzD,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC,mDAAmD;AACnD,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC,mEAAmE;AACnE,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,6CAA6C;AAC7C,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,iDAAiD;AACjD,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAExC,8FAA8F;AAC9F,eAAO,MAAM,0BAA0B,QAAa,CAAC;AAErD,oDAAoD;AACpD,eAAO,MAAM,qBAAqB,2BAA2B,CAAC;AAE9D,mDAAmD;AACnD,eAAO,MAAM,cAAc,WAAW,CAAC;AAEvC,yEAAyE;AACzE,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAElD,6DAA6D;AAC7D,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C,qEAAqE;AACrE,eAAO,MAAM,8BAA8B,QAAmB,CAAC;AAE/D,4DAA4D;AAC5D,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iCAAiC,KAAK,CAAC;AAEpD,sEAAsE;AACtE,eAAO,MAAM,6BAA6B,OAAO,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/shared/constants.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,eAAO,MAAM,YAAY,QAAU,CAAC;AAEpC,oDAAoD;AACpD,eAAO,MAAM,eAAe,QAA2B,CAAC;AAExD,yEAAyE;AACzE,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAExC,wEAAwE;AACxE,eAAO,MAAM,yBAAyB,QAAQ,CAAC;AAE/C,yDAAyD;AACzD,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC,mDAAmD;AACnD,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC,mEAAmE;AACnE,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,6CAA6C;AAC7C,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,iDAAiD;AACjD,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAExC,8FAA8F;AAC9F,eAAO,MAAM,0BAA0B,QAAa,CAAC;AAErD,oDAAoD;AACpD,eAAO,MAAM,qBAAqB,2BAA2B,CAAC;AAE9D,mDAAmD;AACnD,eAAO,MAAM,cAAc,WAAW,CAAC;AAEvC,yEAAyE;AACzE,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAElD,6DAA6D;AAC7D,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C,qEAAqE;AACrE,eAAO,MAAM,8BAA8B,QAAmB,CAAC;AAE/D,4DAA4D;AAC5D,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iCAAiC,KAAK,CAAC;AAEpD,sEAAsE;AACtE,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAElD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,QAAQ,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_MAX_SEARCH_CANDIDATES = exports.MESSAGE_APPEND_RETRY_MAX_ATTEMPTS = exports.DEFAULT_RETRY_MAX_ATTEMPTS = exports.DEFAULT_MAX_DECOMPRESSED_BYTES = exports.DEFAULT_COMPRESSION_LEVEL = exports.DEFAULT_COMPRESSION_MIN_BYTES = exports.DEFAULT_S3_SSE = exports.DEFAULT_S3_KEY_PREFIX = exports.DEFAULT_S3_THRESHOLD_BYTES = exports.S3_DELETE_BATCH_MAX = exports.MAX_BACKOFF_DELAY_MS = exports.INITIAL_BACKOFF_DELAY_MS = exports.MAX_UNPROCESSED_RETRIES = exports.BATCH_GET_MAX = exports.BATCH_WRITE_MAX = exports.MAX_TOTAL_ITEMS_IN_MEMORY = exports.MAX_LOOP_ITERATIONS = exports.MAX_TTL_SECONDS = exports.MAX_TTL_DAYS = void 0;
3
+ exports.LIST_SCAN_WARN_THRESHOLD = exports.DEFAULT_MAX_SEARCH_CANDIDATES = exports.MESSAGE_APPEND_RETRY_MAX_ATTEMPTS = exports.DEFAULT_RETRY_MAX_ATTEMPTS = exports.DEFAULT_MAX_DECOMPRESSED_BYTES = exports.DEFAULT_COMPRESSION_LEVEL = exports.DEFAULT_COMPRESSION_MIN_BYTES = exports.DEFAULT_S3_SSE = exports.DEFAULT_S3_KEY_PREFIX = exports.DEFAULT_S3_THRESHOLD_BYTES = exports.S3_DELETE_BATCH_MAX = exports.MAX_BACKOFF_DELAY_MS = exports.INITIAL_BACKOFF_DELAY_MS = exports.MAX_UNPROCESSED_RETRIES = exports.BATCH_GET_MAX = exports.BATCH_WRITE_MAX = exports.MAX_TOTAL_ITEMS_IN_MEMORY = exports.MAX_LOOP_ITERATIONS = exports.MAX_TTL_SECONDS = exports.MAX_TTL_DAYS = void 0;
4
4
  /** Maximum TTL expressed in days (5 years). */
5
5
  exports.MAX_TTL_DAYS = 365 * 5;
6
6
  /** Maximum TTL expressed in seconds (100 years). */
@@ -49,4 +49,19 @@ exports.DEFAULT_RETRY_MAX_ATTEMPTS = 5;
49
49
  exports.MESSAGE_APPEND_RETRY_MAX_ATTEMPTS = 18;
50
50
  /** Default cap on candidates the in-DB semantic ranker will score. */
51
51
  exports.DEFAULT_MAX_SEARCH_CANDIDATES = 1000;
52
+ /**
53
+ * Raw rows a single `listCheckpoints` call may pull before it warns. The read
54
+ * itself is deliberately unbounded — capping it counted raw rows rather than
55
+ * filter-matched ones, which turned a caller asking for a handful of rare
56
+ * matches over a large thread into a hard error instead of the true answer.
57
+ * The warning restores the operational signal without restoring the wrong
58
+ * error.
59
+ *
60
+ * Its own literal, deliberately: this is the point at which a scan is worth
61
+ * telling an operator about, which is independent of
62
+ * {@link MAX_TOTAL_ITEMS_IN_MEMORY}'s hard collection cap. Aliasing the two
63
+ * meant retuning the memory cap silently moved the warning as well, and it
64
+ * left the pair reported as a duplicate export.
65
+ */
66
+ exports.LIST_SCAN_WARN_THRESHOLD = 10000;
52
67
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/shared/constants.ts"],"names":[],"mappings":";;;AAAA,+CAA+C;AAClC,QAAA,YAAY,GAAG,GAAG,GAAG,CAAC,CAAC;AAEpC,oDAAoD;AACvC,QAAA,eAAe,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAExD,yEAAyE;AAC5D,QAAA,mBAAmB,GAAG,IAAI,CAAC;AAExC,wEAAwE;AAC3D,QAAA,yBAAyB,GAAG,KAAK,CAAC;AAE/C,yDAAyD;AAC5C,QAAA,eAAe,GAAG,EAAE,CAAC;AAElC,mDAAmD;AACtC,QAAA,aAAa,GAAG,GAAG,CAAC;AAEjC,mEAAmE;AACtD,QAAA,uBAAuB,GAAG,EAAE,CAAC;AAE1C,2EAA2E;AAC9D,QAAA,wBAAwB,GAAG,GAAG,CAAC;AAE5C,6CAA6C;AAChC,QAAA,oBAAoB,GAAG,IAAI,CAAC;AAEzC,iDAAiD;AACpC,QAAA,mBAAmB,GAAG,IAAI,CAAC;AAExC,8FAA8F;AACjF,QAAA,0BAA0B,GAAG,GAAG,GAAG,IAAI,CAAC;AAErD,oDAAoD;AACvC,QAAA,qBAAqB,GAAG,wBAAwB,CAAC;AAE9D,mDAAmD;AACtC,QAAA,cAAc,GAAG,QAAQ,CAAC;AAEvC,yEAAyE;AAC5D,QAAA,6BAA6B,GAAG,IAAI,CAAC;AAElD,6DAA6D;AAChD,QAAA,yBAAyB,GAAG,CAAC,CAAC;AAE3C,qEAAqE;AACxD,QAAA,8BAA8B,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE/D,4DAA4D;AAC/C,QAAA,0BAA0B,GAAG,CAAC,CAAC;AAE5C;;;;;;;;;;GAUG;AACU,QAAA,iCAAiC,GAAG,EAAE,CAAC;AAEpD,sEAAsE;AACzD,QAAA,6BAA6B,GAAG,IAAI,CAAC"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/shared/constants.ts"],"names":[],"mappings":";;;AAAA,+CAA+C;AAClC,QAAA,YAAY,GAAG,GAAG,GAAG,CAAC,CAAC;AAEpC,oDAAoD;AACvC,QAAA,eAAe,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAExD,yEAAyE;AAC5D,QAAA,mBAAmB,GAAG,IAAI,CAAC;AAExC,wEAAwE;AAC3D,QAAA,yBAAyB,GAAG,KAAK,CAAC;AAE/C,yDAAyD;AAC5C,QAAA,eAAe,GAAG,EAAE,CAAC;AAElC,mDAAmD;AACtC,QAAA,aAAa,GAAG,GAAG,CAAC;AAEjC,mEAAmE;AACtD,QAAA,uBAAuB,GAAG,EAAE,CAAC;AAE1C,2EAA2E;AAC9D,QAAA,wBAAwB,GAAG,GAAG,CAAC;AAE5C,6CAA6C;AAChC,QAAA,oBAAoB,GAAG,IAAI,CAAC;AAEzC,iDAAiD;AACpC,QAAA,mBAAmB,GAAG,IAAI,CAAC;AAExC,8FAA8F;AACjF,QAAA,0BAA0B,GAAG,GAAG,GAAG,IAAI,CAAC;AAErD,oDAAoD;AACvC,QAAA,qBAAqB,GAAG,wBAAwB,CAAC;AAE9D,mDAAmD;AACtC,QAAA,cAAc,GAAG,QAAQ,CAAC;AAEvC,yEAAyE;AAC5D,QAAA,6BAA6B,GAAG,IAAI,CAAC;AAElD,6DAA6D;AAChD,QAAA,yBAAyB,GAAG,CAAC,CAAC;AAE3C,qEAAqE;AACxD,QAAA,8BAA8B,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE/D,4DAA4D;AAC/C,QAAA,0BAA0B,GAAG,CAAC,CAAC;AAE5C;;;;;;;;;;GAUG;AACU,QAAA,iCAAiC,GAAG,EAAE,CAAC;AAEpD,sEAAsE;AACzD,QAAA,6BAA6B,GAAG,IAAI,CAAC;AAElD;;;;;;;;;;;;;GAaG;AACU,QAAA,wBAAwB,GAAG,KAAK,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Attribute holding a row's revision token on adapters that need one. The
3
+ * checkpointer's special writes reuse their existing per-call `writeGroup`
4
+ * instead, so `revisionGuard` takes the attribute name rather than assuming it.
5
+ */
6
+ export declare const REVISION_ATTRIBUTE = "rev";
7
+ /**
8
+ * Compare-and-swap attempts before a caller gives up and overwrites
9
+ * unconditionally. Kept small on purpose: DynamoDB charges write capacity for a
10
+ * *failed* conditional write too, sized on the existing item, so an aggressive
11
+ * loop turns contention into cost. Three attempts settle every realistic race,
12
+ * and the fallback is exactly the pre-0.9.0 behaviour rather than an error.
13
+ */
14
+ export declare const OVERWRITE_CAS_MAX_ATTEMPTS = 3;
15
+ /** What a caller saw at the row before it tried to overwrite it. */
16
+ export interface ObservedRow {
17
+ exists: boolean;
18
+ revision?: string;
19
+ }
20
+ /** Condition fragments to spread into a `PutCommand` input. */
21
+ export interface RevisionGuard {
22
+ ConditionExpression: string;
23
+ ExpressionAttributeNames?: Record<string, string>;
24
+ ExpressionAttributeValues?: Record<string, string>;
25
+ }
26
+ /**
27
+ * Build the condition admitting a write only while the row still holds the
28
+ * revision this caller observed.
29
+ *
30
+ * Without it, two concurrent overwrites both read the same previous payload
31
+ * descriptor, both commit their own nonced upload, and both delete that same
32
+ * previous object — leaving the loser's upload orphaned with nothing left
33
+ * recording that it ever existed. A post-commit read-back cannot repair that,
34
+ * because neither writer can learn of an object it never saw; only refusing the
35
+ * second write until it re-reads can.
36
+ *
37
+ * A row with no revision attribute was written before 0.9.0. Pinning its
38
+ * *absence* is what makes the swap correct across an upgrade: the first writer
39
+ * to touch such a row stamps one, and any racer still holding the pre-upgrade
40
+ * observation is turned away.
41
+ */
42
+ export declare function revisionGuard(attribute: string, observed: ObservedRow): RevisionGuard;
43
+ /**
44
+ * True when the guard rejected a write — NOT evidence a competitor won: a
45
+ * `PutCommand` retried after its response was lost can re-hit its own
46
+ * committed row and fail identically; the two cases are indistinguishable.
47
+ */
48
+ export declare function isConditionalCheckFailed(error: {
49
+ name?: string;
50
+ }): boolean;
51
+ //# sourceMappingURL=conditional-put.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conditional-put.d.ts","sourceRoot":"","sources":["../../../src/shared/dynamodb/conditional-put.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAExC;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,+DAA+D;AAC/D,MAAM,WAAW,aAAa;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,wBAAwB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClD,yBAAyB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,aAAa,CAarF;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAE1E"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OVERWRITE_CAS_MAX_ATTEMPTS = exports.REVISION_ATTRIBUTE = void 0;
4
+ exports.revisionGuard = revisionGuard;
5
+ exports.isConditionalCheckFailed = isConditionalCheckFailed;
6
+ /**
7
+ * Attribute holding a row's revision token on adapters that need one. The
8
+ * checkpointer's special writes reuse their existing per-call `writeGroup`
9
+ * instead, so `revisionGuard` takes the attribute name rather than assuming it.
10
+ */
11
+ exports.REVISION_ATTRIBUTE = 'rev';
12
+ /**
13
+ * Compare-and-swap attempts before a caller gives up and overwrites
14
+ * unconditionally. Kept small on purpose: DynamoDB charges write capacity for a
15
+ * *failed* conditional write too, sized on the existing item, so an aggressive
16
+ * loop turns contention into cost. Three attempts settle every realistic race,
17
+ * and the fallback is exactly the pre-0.9.0 behaviour rather than an error.
18
+ */
19
+ exports.OVERWRITE_CAS_MAX_ATTEMPTS = 3;
20
+ /**
21
+ * Build the condition admitting a write only while the row still holds the
22
+ * revision this caller observed.
23
+ *
24
+ * Without it, two concurrent overwrites both read the same previous payload
25
+ * descriptor, both commit their own nonced upload, and both delete that same
26
+ * previous object — leaving the loser's upload orphaned with nothing left
27
+ * recording that it ever existed. A post-commit read-back cannot repair that,
28
+ * because neither writer can learn of an object it never saw; only refusing the
29
+ * second write until it re-reads can.
30
+ *
31
+ * A row with no revision attribute was written before 0.9.0. Pinning its
32
+ * *absence* is what makes the swap correct across an upgrade: the first writer
33
+ * to touch such a row stamps one, and any racer still holding the pre-upgrade
34
+ * observation is turned away.
35
+ */
36
+ function revisionGuard(attribute, observed) {
37
+ if (!observed.exists)
38
+ return { ConditionExpression: 'attribute_not_exists(PK)' };
39
+ if (observed.revision === undefined) {
40
+ return {
41
+ ConditionExpression: 'attribute_not_exists(#rev)',
42
+ ExpressionAttributeNames: { '#rev': attribute },
43
+ };
44
+ }
45
+ return {
46
+ ConditionExpression: '#rev = :rev',
47
+ ExpressionAttributeNames: { '#rev': attribute },
48
+ ExpressionAttributeValues: { ':rev': observed.revision },
49
+ };
50
+ }
51
+ /**
52
+ * True when the guard rejected a write — NOT evidence a competitor won: a
53
+ * `PutCommand` retried after its response was lost can re-hit its own
54
+ * committed row and fail identically; the two cases are indistinguishable.
55
+ */
56
+ function isConditionalCheckFailed(error) {
57
+ return error.name === 'ConditionalCheckFailedException';
58
+ }
59
+ //# sourceMappingURL=conditional-put.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conditional-put.js","sourceRoot":"","sources":["../../../src/shared/dynamodb/conditional-put.ts"],"names":[],"mappings":";;;;;AAAA;;;;GAIG;AACU,QAAA,kBAAkB,GAAG,KAAK,CAAC;AAExC;;;;;;GAMG;AACU,QAAA,0BAA0B,GAAG,CAAC,CAAC;AAe5C;;;;;;;;;;;;;;;GAeG;AACH,uBAA8B,SAAiB,EAAE,QAAqB;IACpE,IAAI,CAAC,QAAQ,CAAC,MAAM;QAAE,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,CAAC;IACjF,IAAI,QAAQ,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACpC,OAAO;YACL,mBAAmB,EAAE,4BAA4B;YACjD,wBAAwB,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;SAChD,CAAC;IACJ,CAAC;IACD,OAAO;QACL,mBAAmB,EAAE,aAAa;QAClC,wBAAwB,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;QAC/C,yBAAyB,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,QAAQ,EAAE;KACzD,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,kCAAyC,KAAwB;IAC/D,OAAO,KAAK,CAAC,IAAI,KAAK,iCAAiC,CAAC;AAC1D,CAAC"}
@@ -20,7 +20,11 @@ export declare function redactSecrets(value: Redactable, patterns?: readonly str
20
20
  export interface RedactLoggerOptions {
21
21
  /** Additional key names (matched case-insensitively as substrings) to redact. */
22
22
  extraKeys?: readonly string[];
23
- /** Additional secret shapes to redact wherever they appear inside a string. */
23
+ /**
24
+ * Additional secret shapes to redact wherever they appear inside a string.
25
+ * A pattern's first capture group, if it has one, is preserved verbatim and
26
+ * only the remainder of the match is replaced.
27
+ */
24
28
  extraValuePatterns?: readonly RegExp[];
25
29
  }
26
30
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../../../src/shared/logging/redaction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,MAAM,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,KAAK,UAAU,EAAc,MAAM,kBAAkB,CAAC;AAO/D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,UAAU,EACjB,QAAQ,GAAE,SAAS,MAAM,EAAgC,EACzD,aAAa,GAAE,SAAS,MAAM,EAAkC,GAC/D,UAAU,CAcZ;AAED,gDAAgD;AAChD,MAAM,WAAW,mBAAmB;IAClC,iFAAiF;IACjF,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,+EAA+E;IAC/E,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAerF"}
1
+ {"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../../../src/shared/logging/redaction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,MAAM,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,KAAK,UAAU,EAAc,MAAM,kBAAkB,CAAC;AAO/D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,UAAU,EACjB,QAAQ,GAAE,SAAS,MAAM,EAAgC,EACzD,aAAa,GAAE,SAAS,MAAM,EAAkC,GAC/D,UAAU,CAcZ;AAED,gDAAgD;AAChD,MAAM,WAAW,mBAAmB;IAClC,iFAAiF;IACjF,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAerF"}
@@ -1 +1 @@
1
- {"version":3,"file":"redaction.js","sourceRoot":"","sources":["../../../src/shared/logging/redaction.ts"],"names":[],"mappings":";;;;AACA,qDAA+D;AAC/D,uDAI2B;AAE3B;;;;;;;;;;;;;;GAcG;AACH,uBACE,KAAiB,EACjB,QAAQ,GAAsB,6CAA2B,EACzD,aAAa,GAAsB,+CAA6B;IAEhE,MAAM,IAAI,GAAG,IAAI,OAAO,EAAU,CAAC;IACnC,MAAM,IAAI,GAAG,CAAC,OAAmB,EAAc,EAAE;QAC/C,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,IAAA,4BAAU,EAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QAC3E,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,OAAO,CAAC;QACpE,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO,YAAY,CAAC;QAC3C,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAClB,IAAI,CAAC;YACH,OAAO,IAAA,2BAAU,EAAC,OAAO,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7E,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC;IACF,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;AACrB,CAAC;AAUD;;;GAGG;AACH,sBAA6B,KAAa,EAAE,OAAO,GAAwB,EAAE;IAC3E,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS;QAChC,CAAC,CAAC,CAAC,GAAG,6CAA2B,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;QACxF,CAAC,CAAC,6CAA2B,CAAC;IAChC,MAAM,aAAa,GAAG,OAAO,CAAC,kBAAkB;QAC9C,CAAC,CAAC,CAAC,GAAG,+CAA6B,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC;QACnE,CAAC,CAAC,+CAA6B,CAAC;IAClC,MAAM,IAAI,GAAG,CAAC,IAAmB,EAAiB,EAAE,CAClD,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,GAAiB,EAAE,QAAQ,EAAE,aAAa,CAAgB,CAAC,CAAC;IAC9F,OAAO;QACL,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,KAAK,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;QAChE,KAAK,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;KACjE,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"redaction.js","sourceRoot":"","sources":["../../../src/shared/logging/redaction.ts"],"names":[],"mappings":";;;;AACA,qDAA+D;AAC/D,uDAI2B;AAE3B;;;;;;;;;;;;;;GAcG;AACH,uBACE,KAAiB,EACjB,QAAQ,GAAsB,6CAA2B,EACzD,aAAa,GAAsB,+CAA6B;IAEhE,MAAM,IAAI,GAAG,IAAI,OAAO,EAAU,CAAC;IACnC,MAAM,IAAI,GAAG,CAAC,OAAmB,EAAc,EAAE;QAC/C,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,IAAA,4BAAU,EAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QAC3E,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,OAAO,CAAC;QACpE,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO,YAAY,CAAC;QAC3C,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAClB,IAAI,CAAC;YACH,OAAO,IAAA,2BAAU,EAAC,OAAO,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7E,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC;IACF,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;AACrB,CAAC;AAcD;;;GAGG;AACH,sBAA6B,KAAa,EAAE,OAAO,GAAwB,EAAE;IAC3E,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS;QAChC,CAAC,CAAC,CAAC,GAAG,6CAA2B,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;QACxF,CAAC,CAAC,6CAA2B,CAAC;IAChC,MAAM,aAAa,GAAG,OAAO,CAAC,kBAAkB;QAC9C,CAAC,CAAC,CAAC,GAAG,+CAA6B,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC;QACnE,CAAC,CAAC,+CAA6B,CAAC;IAClC,MAAM,IAAI,GAAG,CAAC,IAAmB,EAAiB,EAAE,CAClD,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,GAAiB,EAAE,QAAQ,EAAE,aAAa,CAAgB,CAAC,CAAC;IAC9F,OAAO;QACL,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,KAAK,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;QAChE,KAAK,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;KACjE,CAAC;AACJ,CAAC"}
@@ -8,6 +8,31 @@ export declare const DEFAULT_SECRET_KEY_PATTERNS: readonly string[];
8
8
  * high-confidence — each pattern describes a credential *format* rather than a
9
9
  * word that merely sounds sensitive — so ordinary operational text survives
10
10
  * redaction unchanged.
11
+ *
12
+ * The credential-pair pattern (last) tolerates a **closing quote after the
13
+ * keyword**, because that is what `JSON.stringify` produces
14
+ * (`{"password":"…"}`) and requiring the separator to follow the bare keyword
15
+ * matched nothing at all there — a silent, complete bypass on the single most
16
+ * common shape a downstream HTTP error arrives in. Its group 1 is the keyword
17
+ * and separator, preserved by {@link redactText}.
18
+ *
19
+ * Its value side tries three shapes, and their order is load-bearing:
20
+ * 1. a fully-quoted span that consumes escapes, so `{"password":"a\"b"}` is
21
+ * redacted whole. Ending the span at the escaped quote stopped the
22
+ * redaction short and printed the rest of the secret verbatim.
23
+ * 2. an unquoted JSON scalar — number, `true`, `false`, `null` — required to
24
+ * end at a real delimiter — `,`, `}`, `]` or end of input, optionally
25
+ * preceded by whitespace. Without this alternative,
26
+ * `{"apiKey":123,"region":"us-east-1"}` fell through to the fallback below,
27
+ * which then destroyed every sibling field after the secret. The lookahead
28
+ * is what stops the alternative truncating a value it does not fully
29
+ * describe, such as `token=123abc`, and leaking the tail it left behind.
30
+ * Whitespace alone does not end a scalar: treating a space as a delimiter
31
+ * made `api_key: 5 items` redact to `api_key: [REDACTED] items`, which
32
+ * reads as a partial redaction of a value the pattern never described.
33
+ * 3. the rest of the line, so a multi-word secret is redacted whole instead of
34
+ * up to its first space. Trying the two precise shapes first is what keeps
35
+ * this fallback from over-redacting sibling JSON fields.
11
36
  */
12
37
  export declare const DEFAULT_SECRET_VALUE_PATTERNS: readonly RegExp[];
13
38
  /** True when `key` matches any secret-key pattern. */
@@ -17,6 +42,14 @@ export declare function isSecretKey(key: string, patterns: readonly string[]): b
17
42
  * leaving the surrounding text intact so a redacted message stays readable.
18
43
  * Each pattern is rebuilt per call so a `g` flag's `lastIndex` never leaks
19
44
  * between invocations.
45
+ *
46
+ * A pattern may capture a leading group it wants **preserved**: only the rest
47
+ * of the match is replaced, which is what keeps `apiKey=[REDACTED]` saying
48
+ * which field was redacted instead of collapsing to a bare marker. A pattern
49
+ * with no group is replaced whole, as before. `String.prototype.replace`
50
+ * passes the match *offset* — a number — as the second callback argument when
51
+ * the pattern has no group, hence the `typeof` test rather than an
52
+ * `undefined` check.
20
53
  */
21
54
  export declare function redactText(value: string, patterns: readonly RegExp[]): string;
22
55
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"secret-patterns.d.ts","sourceRoot":"","sources":["../../../src/shared/logging/secret-patterns.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,eAAO,MAAM,QAAQ,eAAe,CAAC;AAErC,oFAAoF;AACpF,eAAO,MAAM,2BAA2B,EAAE,SAAS,MAAM,EAYxD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,EAAE,SAAS,MAAM,EAK1D,CAAC;AAEF,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAG7E;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAK7E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAE1D;AAED,kFAAkF;AAClF,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,iBAAiB,CAS5F"}
1
+ {"version":3,"file":"secret-patterns.d.ts","sourceRoot":"","sources":["../../../src/shared/logging/secret-patterns.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,eAAO,MAAM,QAAQ,eAAe,CAAC;AAErC,oFAAoF;AACpF,eAAO,MAAM,2BAA2B,EAAE,SAAS,MAAM,EAYxD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,6BAA6B,EAAE,SAAS,MAAM,EAK1D,CAAC;AAEF,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAG7E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAQ7E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAE1D;AAED,kFAAkF;AAClF,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,iBAAiB,CAS5F"}
@@ -27,12 +27,37 @@ exports.DEFAULT_SECRET_KEY_PATTERNS = [
27
27
  * high-confidence — each pattern describes a credential *format* rather than a
28
28
  * word that merely sounds sensitive — so ordinary operational text survives
29
29
  * redaction unchanged.
30
+ *
31
+ * The credential-pair pattern (last) tolerates a **closing quote after the
32
+ * keyword**, because that is what `JSON.stringify` produces
33
+ * (`{"password":"…"}`) and requiring the separator to follow the bare keyword
34
+ * matched nothing at all there — a silent, complete bypass on the single most
35
+ * common shape a downstream HTTP error arrives in. Its group 1 is the keyword
36
+ * and separator, preserved by {@link redactText}.
37
+ *
38
+ * Its value side tries three shapes, and their order is load-bearing:
39
+ * 1. a fully-quoted span that consumes escapes, so `{"password":"a\"b"}` is
40
+ * redacted whole. Ending the span at the escaped quote stopped the
41
+ * redaction short and printed the rest of the secret verbatim.
42
+ * 2. an unquoted JSON scalar — number, `true`, `false`, `null` — required to
43
+ * end at a real delimiter — `,`, `}`, `]` or end of input, optionally
44
+ * preceded by whitespace. Without this alternative,
45
+ * `{"apiKey":123,"region":"us-east-1"}` fell through to the fallback below,
46
+ * which then destroyed every sibling field after the secret. The lookahead
47
+ * is what stops the alternative truncating a value it does not fully
48
+ * describe, such as `token=123abc`, and leaking the tail it left behind.
49
+ * Whitespace alone does not end a scalar: treating a space as a delimiter
50
+ * made `api_key: 5 items` redact to `api_key: [REDACTED] items`, which
51
+ * reads as a partial redaction of a value the pattern never described.
52
+ * 3. the rest of the line, so a multi-word secret is redacted whole instead of
53
+ * up to its first space. Trying the two precise shapes first is what keeps
54
+ * this fallback from over-redacting sibling JSON fields.
30
55
  */
31
56
  exports.DEFAULT_SECRET_VALUE_PATTERNS = [
32
57
  /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g,
33
58
  /\bBearer\s+[A-Za-z0-9._~+/-]+=*/gi,
34
59
  /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g,
35
- /(?:aws_)?(?:secret_access_key|secretaccesskey|password|passwd|api_?key|token)\s*[=:]\s*\S+/gi,
60
+ /((?:aws_)?(?:secret_access_key|secretaccesskey|password|passwd|api_?key|token)["']?\s*[=:]\s*)(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|(?:-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)(?=\s*[,}\]]|\s*$)|[^\r\n]+)/gi,
36
61
  ];
37
62
  /** True when `key` matches any secret-key pattern. */
38
63
  function isSecretKey(key, patterns) {
@@ -44,9 +69,17 @@ function isSecretKey(key, patterns) {
44
69
  * leaving the surrounding text intact so a redacted message stays readable.
45
70
  * Each pattern is rebuilt per call so a `g` flag's `lastIndex` never leaks
46
71
  * between invocations.
72
+ *
73
+ * A pattern may capture a leading group it wants **preserved**: only the rest
74
+ * of the match is replaced, which is what keeps `apiKey=[REDACTED]` saying
75
+ * which field was redacted instead of collapsing to a bare marker. A pattern
76
+ * with no group is replaced whole, as before. `String.prototype.replace`
77
+ * passes the match *offset* — a number — as the second callback argument when
78
+ * the pattern has no group, hence the `typeof` test rather than an
79
+ * `undefined` check.
47
80
  */
48
81
  function redactText(value, patterns) {
49
- return patterns.reduce((text, pattern) => text.replace(new RegExp(pattern.source, pattern.flags), exports.REDACTED), value);
82
+ return patterns.reduce((text, pattern) => text.replace(new RegExp(pattern.source, pattern.flags), (_match, prefix) => typeof prefix === 'string' ? `${prefix}${exports.REDACTED}` : exports.REDACTED), value);
50
83
  }
51
84
  /**
52
85
  * A short label for a binary view. Recursing one would explode it into a
@@ -1 +1 @@
1
- {"version":3,"file":"secret-patterns.js","sourceRoot":"","sources":["../../../src/shared/logging/secret-patterns.ts"],"names":[],"mappings":";;;;;;;AAAA,4DAA4D;AAC/C,QAAA,QAAQ,GAAG,YAAY,CAAC;AAErC,oFAAoF;AACvE,QAAA,2BAA2B,GAAsB;IAC5D,WAAW;IACX,WAAW;IACX,QAAQ;IACR,cAAc;IACd,eAAe;IACf,eAAe;IACf,UAAU;IACV,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,YAAY;CACb,CAAC;AAEF;;;;;;GAMG;AACU,QAAA,6BAA6B,GAAsB;IAC9D,gCAAgC;IAChC,mCAAmC;IACnC,wDAAwD;IACxD,8FAA8F;CAC/F,CAAC;AAEF,sDAAsD;AACtD,qBAA4B,GAAW,EAAE,QAA2B;IAClE,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAChC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;GAKG;AACH,oBAA2B,KAAa,EAAE,QAA2B;IACnE,OAAO,QAAQ,CAAC,MAAM,CACpB,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,QAAA,QAAQ,CAAC,EACpF,KAAK,CACN,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,qBAA4B,KAAsB;IAChD,OAAO,IAAI,KAAK,CAAC,WAAW,CAAC,IAAI,IAAI,KAAK,CAAC,UAAU,IAAI,CAAC;AAC5D,CAAC;AAUD;;;;;GAKG;AACH,yBAAgC,KAAY,EAAE,QAA2B;IACvE,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACxF,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO;QACP,KAAK;QACL,OAAO,EAAE,OAAO,KAAK,KAAK,CAAC,OAAO,IAAI,KAAK,KAAK,KAAK,CAAC,KAAK;KAC5D,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"secret-patterns.js","sourceRoot":"","sources":["../../../src/shared/logging/secret-patterns.ts"],"names":[],"mappings":";;;;;;;AAAA,4DAA4D;AAC/C,QAAA,QAAQ,GAAG,YAAY,CAAC;AAErC,oFAAoF;AACvE,QAAA,2BAA2B,GAAsB;IAC5D,WAAW;IACX,WAAW;IACX,QAAQ;IACR,cAAc;IACd,eAAe;IACf,eAAe;IACf,UAAU;IACV,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,YAAY;CACb,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACU,QAAA,6BAA6B,GAAsB;IAC9D,gCAAgC;IAChC,mCAAmC;IACnC,wDAAwD;IACxD,yNAAyN;CAC1N,CAAC;AAEF,sDAAsD;AACtD,qBAA4B,GAAW,EAAE,QAA2B;IAClE,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAChC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,oBAA2B,KAAa,EAAE,QAA2B;IACnE,OAAO,QAAQ,CAAC,MAAM,CACpB,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAChB,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAuB,EAAE,EAAE,CAC1F,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,QAAA,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAA,QAAQ,CAC/D,EACH,KAAK,CACN,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,qBAA4B,KAAsB;IAChD,OAAO,IAAI,KAAK,CAAC,WAAW,CAAC,IAAI,IAAI,KAAK,CAAC,UAAU,IAAI,CAAC;AAC5D,CAAC;AAUD;;;;;GAKG;AACH,yBAAgC,KAAY,EAAE,QAA2B;IACvE,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACxF,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO;QACP,KAAK;QACL,OAAO,EAAE,OAAO,KAAK,KAAK,CAAC,OAAO,IAAI,KAAK,KAAK,KAAK,CAAC,KAAK;KAC5D,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"put.d.ts","sourceRoot":"","sources":["../../../src/store/actions/put.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAcpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAgFtD;;;;;;GAMG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBpF"}
1
+ {"version":3,"file":"put.d.ts","sourceRoot":"","sources":["../../../src/store/actions/put.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAcpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAsDtD;;;;;;GAMG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBpF"}