@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.
- package/README.md +1 -1
- package/dist/checkpointer/actions/list.d.ts.map +1 -1
- package/dist/checkpointer/actions/list.js +7 -0
- package/dist/checkpointer/actions/list.js.map +1 -1
- package/dist/checkpointer/actions/put-writes.d.ts.map +1 -1
- package/dist/checkpointer/actions/put-writes.js +2 -1
- package/dist/checkpointer/actions/put-writes.js.map +1 -1
- package/dist/checkpointer/internal/item-reader.d.ts +14 -0
- package/dist/checkpointer/internal/item-reader.d.ts.map +1 -1
- package/dist/checkpointer/internal/item-reader.js +15 -1
- package/dist/checkpointer/internal/item-reader.js.map +1 -1
- package/dist/checkpointer/internal/item-writer.d.ts.map +1 -1
- package/dist/checkpointer/internal/item-writer.js +2 -1
- package/dist/checkpointer/internal/item-writer.js.map +1 -1
- package/dist/checkpointer/internal/special-write-cas.d.ts +30 -0
- package/dist/checkpointer/internal/special-write-cas.d.ts.map +1 -0
- package/dist/checkpointer/internal/special-write-cas.js +104 -0
- package/dist/checkpointer/internal/special-write-cas.js.map +1 -0
- package/dist/checkpointer/internal/special-write-cleanup.d.ts +18 -12
- package/dist/checkpointer/internal/special-write-cleanup.d.ts.map +1 -1
- package/dist/checkpointer/internal/special-write-cleanup.js +31 -86
- package/dist/checkpointer/internal/special-write-cleanup.js.map +1 -1
- package/dist/checkpointer/internal/special-write-verify.d.ts +54 -0
- package/dist/checkpointer/internal/special-write-verify.d.ts.map +1 -0
- package/dist/checkpointer/internal/special-write-verify.js +65 -0
- package/dist/checkpointer/internal/special-write-verify.js.map +1 -0
- package/dist/checkpointer/internal/write-guard.d.ts +0 -8
- package/dist/checkpointer/internal/write-guard.d.ts.map +1 -1
- package/dist/checkpointer/internal/write-guard.js +0 -9
- package/dist/checkpointer/internal/write-guard.js.map +1 -1
- package/dist/checkpointer/internal/write-index.d.ts +8 -0
- package/dist/checkpointer/internal/write-index.d.ts.map +1 -1
- package/dist/checkpointer/internal/write-index.js +6 -2
- package/dist/checkpointer/internal/write-index.js.map +1 -1
- package/dist/checkpointer/types.d.ts +6 -0
- package/dist/checkpointer/types.d.ts.map +1 -1
- package/dist/shared/constants.d.ts +15 -0
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/constants.js +16 -1
- package/dist/shared/constants.js.map +1 -1
- package/dist/shared/dynamodb/conditional-put.d.ts +51 -0
- package/dist/shared/dynamodb/conditional-put.d.ts.map +1 -0
- package/dist/shared/dynamodb/conditional-put.js +59 -0
- package/dist/shared/dynamodb/conditional-put.js.map +1 -0
- package/dist/shared/logging/redaction.d.ts +5 -1
- package/dist/shared/logging/redaction.d.ts.map +1 -1
- package/dist/shared/logging/redaction.js.map +1 -1
- package/dist/shared/logging/secret-patterns.d.ts +33 -0
- package/dist/shared/logging/secret-patterns.d.ts.map +1 -1
- package/dist/shared/logging/secret-patterns.js +35 -2
- package/dist/shared/logging/secret-patterns.js.map +1 -1
- package/dist/store/actions/put.d.ts.map +1 -1
- package/dist/store/actions/put.js +4 -16
- package/dist/store/actions/put.js.map +1 -1
- package/dist/store/internal/backend-search.d.ts.map +1 -1
- package/dist/store/internal/backend-search.js +2 -1
- package/dist/store/internal/backend-search.js.map +1 -1
- package/dist/store/internal/filter.d.ts.map +1 -1
- package/dist/store/internal/filter.js +25 -5
- package/dist/store/internal/filter.js.map +1 -1
- package/dist/store/internal/index-reconcile.d.ts.map +1 -1
- package/dist/store/internal/index-reconcile.js +8 -2
- package/dist/store/internal/index-reconcile.js.map +1 -1
- package/dist/store/internal/item-mapper.d.ts +1 -0
- package/dist/store/internal/item-mapper.d.ts.map +1 -1
- package/dist/store/internal/item-mapper.js +2 -0
- package/dist/store/internal/item-mapper.js.map +1 -1
- package/dist/store/internal/overwrite-swap.d.ts +33 -0
- package/dist/store/internal/overwrite-swap.d.ts.map +1 -0
- package/dist/store/internal/overwrite-swap.js +62 -0
- package/dist/store/internal/overwrite-swap.js.map +1 -0
- package/dist/store/internal/persist.d.ts +22 -6
- package/dist/store/internal/persist.d.ts.map +1 -1
- package/dist/store/internal/persist.js +42 -17
- package/dist/store/internal/persist.js.map +1 -1
- package/dist/store/internal/read-existing.d.ts +19 -0
- package/dist/store/internal/read-existing.d.ts.map +1 -0
- package/dist/store/internal/read-existing.js +29 -0
- package/dist/store/internal/read-existing.js.map +1 -0
- package/dist/store/internal/score-direction.d.ts +32 -0
- package/dist/store/internal/score-direction.d.ts.map +1 -0
- package/dist/store/internal/score-direction.js +39 -0
- package/dist/store/internal/score-direction.js.map +1 -0
- package/dist/store/internal/setup.d.ts +2 -0
- package/dist/store/internal/setup.d.ts.map +1 -1
- package/dist/store/internal/setup.js +41 -1
- package/dist/store/internal/setup.js.map +1 -1
- package/dist/store/internal/write-verify.d.ts +20 -7
- package/dist/store/internal/write-verify.d.ts.map +1 -1
- package/dist/store/internal/write-verify.js +22 -9
- package/dist/store/internal/write-verify.js.map +1 -1
- package/dist/store/types.d.ts +16 -0
- package/dist/store/types.d.ts.map +1 -1
- package/dist/store/vector-backend.d.ts +6 -6
- 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;
|
|
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":"
|
|
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;
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
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"}
|
package/dist/shared/constants.js
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
|
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;
|
|
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
|
|
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)
|
|
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
|
|
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;
|
|
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"}
|