@qitqode/qitqore 0.1.0 → 0.3.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/LICENSE +21 -0
- package/README.md +165 -0
- package/dist/decision-policy.d.ts +132 -0
- package/dist/decision-policy.d.ts.map +1 -0
- package/dist/decision-policy.js +308 -0
- package/dist/decision-policy.js.map +1 -0
- package/dist/device-authorization.d.ts +67 -0
- package/dist/device-authorization.d.ts.map +1 -0
- package/dist/device-authorization.js +122 -0
- package/dist/device-authorization.js.map +1 -0
- package/dist/errors.d.ts +36 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +108 -17
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/recovery-capabilities.d.ts +26 -0
- package/dist/recovery-capabilities.d.ts.map +1 -0
- package/dist/recovery-capabilities.js +113 -0
- package/dist/recovery-capabilities.js.map +1 -0
- package/dist/recovery-directive.d.ts +20 -0
- package/dist/recovery-directive.d.ts.map +1 -0
- package/dist/recovery-directive.js +116 -0
- package/dist/recovery-directive.js.map +1 -0
- package/dist/recovery-lease.d.ts +80 -0
- package/dist/recovery-lease.d.ts.map +1 -0
- package/dist/recovery-lease.js +257 -0
- package/dist/recovery-lease.js.map +1 -0
- package/dist/recovery-status.d.ts +22 -0
- package/dist/recovery-status.d.ts.map +1 -0
- package/dist/recovery-status.js +113 -0
- package/dist/recovery-status.js.map +1 -0
- package/dist/recovery.d.ts +13 -0
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +94 -55
- package/dist/recovery.js.map +1 -1
- package/dist/redaction.d.ts +64 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/redaction.js +154 -0
- package/dist/redaction.js.map +1 -0
- package/dist/route-contract.d.ts +36 -0
- package/dist/route-contract.d.ts.map +1 -0
- package/dist/route-contract.js +89 -0
- package/dist/route-contract.js.map +1 -0
- package/dist/sse.d.ts +4 -0
- package/dist/sse.d.ts.map +1 -1
- package/dist/sse.js +16 -1
- package/dist/sse.js.map +1 -1
- package/dist/stream-payload.d.ts +40 -0
- package/dist/stream-payload.d.ts.map +1 -0
- package/dist/stream-payload.js +96 -0
- package/dist/stream-payload.js.map +1 -0
- package/dist/transport.d.ts +10 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +28 -0
- package/dist/transport.js.map +1 -1
- package/package.json +12 -2
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type QortexRecoveryLeaseUsage } from "./recovery-lease.js";
|
|
2
|
+
export type QortexExecutionState = "not_started" | "running" | "succeeded" | "failed" | "unknown";
|
|
3
|
+
export type QortexResultRecovery = "replayable" | "pending" | "expired" | "unsupported";
|
|
4
|
+
export interface QortexRecoveryStatus {
|
|
5
|
+
contractVersion: 1;
|
|
6
|
+
requestID: string;
|
|
7
|
+
scope: "qortex_route";
|
|
8
|
+
executionState: QortexExecutionState;
|
|
9
|
+
resultRecovery: QortexResultRecovery;
|
|
10
|
+
replayUntil?: number;
|
|
11
|
+
routeID?: string;
|
|
12
|
+
/** Qortex's usage report for a lease it issued; it must name this request and route. */
|
|
13
|
+
recoveryLeaseUsage?: QortexRecoveryLeaseUsage;
|
|
14
|
+
lastUpdatedAt: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Parse Qortex's owner-scoped recovery projection. The parser accepts either
|
|
18
|
+
* the route's top-level success envelope or the standard `{ success, data }`
|
|
19
|
+
* wrapper, but it never forwards unrecognized fields to callers.
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseRecoveryStatus(value: unknown, expectedRequestID?: string): QortexRecoveryStatus;
|
|
22
|
+
//# sourceMappingURL=recovery-status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery-status.d.ts","sourceRoot":"","sources":["../src/recovery-status.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE7F,MAAM,MAAM,oBAAoB,GAC5B,aAAa,GACb,SAAS,GACT,WAAW,GACX,QAAQ,GACR,SAAS,CAAC;AAEd,MAAM,MAAM,oBAAoB,GAC5B,YAAY,GACZ,SAAS,GACT,SAAS,GACT,aAAa,CAAC;AAElB,MAAM,WAAW,oBAAoB;IACnC,eAAe,EAAE,CAAC,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,cAAc,CAAC;IACtB,cAAc,EAAE,oBAAoB,CAAC;IACrC,cAAc,EAAE,oBAAoB,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wFAAwF;IACxF,kBAAkB,CAAC,EAAE,wBAAwB,CAAC;IAC9C,aAAa,EAAE,MAAM,CAAC;CACvB;AA8CD;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,EACd,iBAAiB,CAAC,EAAE,MAAM,GACzB,oBAAoB,CAyEtB"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { parseRecoveryLeaseUsage } from "./recovery-lease.js";
|
|
2
|
+
const EXECUTION_STATES = new Set([
|
|
3
|
+
"not_started",
|
|
4
|
+
"running",
|
|
5
|
+
"succeeded",
|
|
6
|
+
"failed",
|
|
7
|
+
"unknown",
|
|
8
|
+
]);
|
|
9
|
+
const RESULT_RECOVERY_STATES = new Set([
|
|
10
|
+
"replayable",
|
|
11
|
+
"pending",
|
|
12
|
+
"expired",
|
|
13
|
+
"unsupported",
|
|
14
|
+
]);
|
|
15
|
+
const ALLOWED_FIELDS = new Set([
|
|
16
|
+
"contractVersion",
|
|
17
|
+
"requestID",
|
|
18
|
+
"scope",
|
|
19
|
+
"executionState",
|
|
20
|
+
"resultRecovery",
|
|
21
|
+
"replayUntil",
|
|
22
|
+
"routeID",
|
|
23
|
+
"recoveryLeaseUsage",
|
|
24
|
+
"lastUpdatedAt",
|
|
25
|
+
"success",
|
|
26
|
+
]);
|
|
27
|
+
function isRecord(value) {
|
|
28
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
29
|
+
}
|
|
30
|
+
function boundedString(value, field, maxLength) {
|
|
31
|
+
if (typeof value !== "string" || value.length === 0 || value.length > maxLength) {
|
|
32
|
+
throw new TypeError(`Qortex recovery status has an invalid ${field}`);
|
|
33
|
+
}
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
function epoch(value, field) {
|
|
37
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
38
|
+
throw new TypeError(`Qortex recovery status has an invalid ${field}`);
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Parse Qortex's owner-scoped recovery projection. The parser accepts either
|
|
44
|
+
* the route's top-level success envelope or the standard `{ success, data }`
|
|
45
|
+
* wrapper, but it never forwards unrecognized fields to callers.
|
|
46
|
+
*/
|
|
47
|
+
export function parseRecoveryStatus(value, expectedRequestID) {
|
|
48
|
+
if (!isRecord(value)) {
|
|
49
|
+
throw new TypeError("Qortex recovery status must be an object");
|
|
50
|
+
}
|
|
51
|
+
let candidate = value;
|
|
52
|
+
if (value.success === true && Object.hasOwn(value, "data")) {
|
|
53
|
+
candidate = value.data;
|
|
54
|
+
}
|
|
55
|
+
if (!isRecord(candidate)) {
|
|
56
|
+
throw new TypeError("Qortex recovery status data must be an object");
|
|
57
|
+
}
|
|
58
|
+
for (const key of Object.keys(candidate)) {
|
|
59
|
+
if (!ALLOWED_FIELDS.has(key)) {
|
|
60
|
+
throw new TypeError("Qortex recovery status contains an unsupported field");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (candidate.success !== undefined && candidate.success !== true) {
|
|
64
|
+
throw new TypeError("Qortex recovery status has an invalid success marker");
|
|
65
|
+
}
|
|
66
|
+
if (candidate.contractVersion !== 1) {
|
|
67
|
+
throw new TypeError("Qortex recovery status uses an unsupported contract version");
|
|
68
|
+
}
|
|
69
|
+
const requestID = boundedString(candidate.requestID, "requestID", 256);
|
|
70
|
+
if (expectedRequestID !== undefined && requestID !== expectedRequestID) {
|
|
71
|
+
throw new TypeError("Qortex recovery status requestID does not match the lookup");
|
|
72
|
+
}
|
|
73
|
+
if (candidate.scope !== "qortex_route") {
|
|
74
|
+
throw new TypeError("Qortex recovery status has an unsupported scope");
|
|
75
|
+
}
|
|
76
|
+
if (typeof candidate.executionState !== "string" || !EXECUTION_STATES.has(candidate.executionState)) {
|
|
77
|
+
throw new TypeError("Qortex recovery status has an invalid executionState");
|
|
78
|
+
}
|
|
79
|
+
if (typeof candidate.resultRecovery !== "string" || !RESULT_RECOVERY_STATES.has(candidate.resultRecovery)) {
|
|
80
|
+
throw new TypeError("Qortex recovery status has an invalid resultRecovery");
|
|
81
|
+
}
|
|
82
|
+
const replayUntil = candidate.replayUntil === undefined
|
|
83
|
+
? undefined
|
|
84
|
+
: epoch(candidate.replayUntil, "replayUntil");
|
|
85
|
+
const routeID = candidate.routeID === undefined
|
|
86
|
+
? undefined
|
|
87
|
+
: boundedString(candidate.routeID, "routeID", 256);
|
|
88
|
+
const recoveryLeaseUsage = candidate.recoveryLeaseUsage === undefined
|
|
89
|
+
? undefined
|
|
90
|
+
: parseRecoveryLeaseUsage(candidate.recoveryLeaseUsage);
|
|
91
|
+
if (recoveryLeaseUsage !== undefined) {
|
|
92
|
+
// The request must always agree. Qortex's public schema lets the status omit its
|
|
93
|
+
// route, so the route is compared only when the status names one.
|
|
94
|
+
if (recoveryLeaseUsage.requestID !== requestID) {
|
|
95
|
+
throw new TypeError("Qortex recovery status lease usage does not match the request");
|
|
96
|
+
}
|
|
97
|
+
if (routeID !== undefined && recoveryLeaseUsage.routeID !== routeID) {
|
|
98
|
+
throw new TypeError("Qortex recovery status lease usage does not match the route");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
contractVersion: 1,
|
|
103
|
+
requestID,
|
|
104
|
+
scope: "qortex_route",
|
|
105
|
+
executionState: candidate.executionState,
|
|
106
|
+
resultRecovery: candidate.resultRecovery,
|
|
107
|
+
...(replayUntil === undefined ? {} : { replayUntil }),
|
|
108
|
+
...(routeID === undefined ? {} : { routeID }),
|
|
109
|
+
...(recoveryLeaseUsage === undefined ? {} : { recoveryLeaseUsage }),
|
|
110
|
+
lastUpdatedAt: epoch(candidate.lastUpdatedAt, "lastUpdatedAt"),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=recovery-status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery-status.js","sourceRoot":"","sources":["../src/recovery-status.ts"],"names":[],"mappings":"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"}
|
package/dist/recovery.d.ts
CHANGED
|
@@ -21,13 +21,26 @@ export interface ReplayAuthorizationContext {
|
|
|
21
21
|
readonly outcome: Readonly<AttemptOutcome>;
|
|
22
22
|
readonly signal?: AbortSignal;
|
|
23
23
|
}
|
|
24
|
+
export interface RecoveryAttemptOutcomeContext {
|
|
25
|
+
readonly operationId: string;
|
|
26
|
+
readonly attempt: number;
|
|
27
|
+
readonly outcome: Readonly<AttemptOutcome>;
|
|
28
|
+
readonly signal?: AbortSignal;
|
|
29
|
+
readonly error?: unknown;
|
|
30
|
+
}
|
|
24
31
|
export interface RecoveryOptions<T> {
|
|
25
32
|
operationId: string;
|
|
26
33
|
maxAttempts: number;
|
|
27
34
|
signal?: AbortSignal;
|
|
35
|
+
/** Absolute epoch deadline for the whole logical operation. */
|
|
36
|
+
deadlineAt?: number;
|
|
28
37
|
execute(context: QortexAttemptContext): Promise<T>;
|
|
29
38
|
shouldRetry(error: unknown, context: ReplayAuthorizationContext): boolean;
|
|
30
39
|
authorizeReplay(context: ReplayAuthorizationContext): boolean | Promise<boolean>;
|
|
40
|
+
/** Called immediately before an attempt is dispatched. */
|
|
41
|
+
onAttemptStart?: (context: QortexAttemptContext) => void | Promise<void>;
|
|
42
|
+
/** Called after an attempt settles, including failed and partially observed attempts. */
|
|
43
|
+
onAttemptOutcome?: (context: RecoveryAttemptOutcomeContext) => void | Promise<void>;
|
|
31
44
|
backoffMs?: number | ((nextAttempt: number, error: unknown) => number);
|
|
32
45
|
}
|
|
33
46
|
export declare function abortableDelay(ms: number, signal?: AbortSignal): Promise<void>;
|
package/dist/recovery.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,cAAc,GAAG,UAAU,CAAC;AAEvE,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,gBAAgB,CAAC;IAC7B,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,eAAe,IAAI,IAAI,CAAC;IACxB,YAAY,IAAI,IAAI,CAAC;IACrB,iBAAiB,IAAI,IAAI,CAAC;IAC1B,qBAAqB,IAAI,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACnD,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC;IAC1E,eAAe,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACjF,SAAS,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC,CAAC;CACxE;AAMD,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB9E;AAMD,wBAAsB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,cAAc,GAAG,UAAU,CAAC;AAEvE,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,gBAAgB,CAAC;IAC7B,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,eAAe,IAAI,IAAI,CAAC;IACxB,YAAY,IAAI,IAAI,CAAC;IACrB,iBAAiB,IAAI,IAAI,CAAC;IAC1B,qBAAqB,IAAI,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACnD,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC;IAC1E,eAAe,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACjF,0DAA0D;IAC1D,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,yFAAyF;IACzF,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,6BAA6B,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpF,SAAS,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC,CAAC;CACxE;AAMD,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB9E;AAMD,wBAAsB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAiHhF"}
|
package/dist/recovery.js
CHANGED
|
@@ -41,66 +41,105 @@ export async function runWithRecovery(options) {
|
|
|
41
41
|
if (!Number.isInteger(options.maxAttempts) || options.maxAttempts < 1 || options.maxAttempts > 5) {
|
|
42
42
|
throw new RangeError("maxAttempts must be an integer between 1 and 5");
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
outcome.sideEffectStarted = true;
|
|
68
|
-
},
|
|
69
|
-
};
|
|
70
|
-
try {
|
|
71
|
-
return await options.execute(context);
|
|
72
|
-
}
|
|
73
|
-
catch (error) {
|
|
74
|
-
if (options.signal?.aborted)
|
|
75
|
-
throw abortReason(options.signal);
|
|
76
|
-
if (attempt >= options.maxAttempts)
|
|
77
|
-
throw error;
|
|
78
|
-
const replayContext = Object.freeze({
|
|
44
|
+
if (options.deadlineAt !== undefined && (!Number.isFinite(options.deadlineAt) || options.deadlineAt <= 0)) {
|
|
45
|
+
throw new RangeError("deadlineAt must be a positive finite epoch timestamp");
|
|
46
|
+
}
|
|
47
|
+
if (options.deadlineAt !== undefined && options.deadlineAt <= Date.now()) {
|
|
48
|
+
throw new Error("The recovery operation deadline expired");
|
|
49
|
+
}
|
|
50
|
+
const deadlineController = options.deadlineAt === undefined ? undefined : new AbortController();
|
|
51
|
+
const deadlineTimer = deadlineController
|
|
52
|
+
? setTimeout(() => deadlineController.abort(new Error("The recovery operation deadline expired")), Math.max(0, options.deadlineAt - Date.now()))
|
|
53
|
+
: undefined;
|
|
54
|
+
const signal = deadlineController
|
|
55
|
+
? AbortSignal.any([...(options.signal ? [options.signal] : []), deadlineController.signal])
|
|
56
|
+
: options.signal;
|
|
57
|
+
try {
|
|
58
|
+
for (let attempt = 1; attempt <= options.maxAttempts; attempt += 1) {
|
|
59
|
+
if (signal?.aborted)
|
|
60
|
+
throw abortReason(signal);
|
|
61
|
+
const outcome = {
|
|
62
|
+
acceptance: "unknown",
|
|
63
|
+
partialOutput: false,
|
|
64
|
+
sideEffectStarted: false,
|
|
65
|
+
};
|
|
66
|
+
const context = {
|
|
79
67
|
operationId: options.operationId,
|
|
80
68
|
attempt,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
69
|
+
...(signal ? { signal } : {}),
|
|
70
|
+
markNotAccepted() {
|
|
71
|
+
if (outcome.acceptance !== "accepted")
|
|
72
|
+
outcome.acceptance = "not-accepted";
|
|
73
|
+
},
|
|
74
|
+
markAccepted() {
|
|
75
|
+
outcome.acceptance = "accepted";
|
|
76
|
+
},
|
|
77
|
+
markPartialOutput() {
|
|
78
|
+
outcome.partialOutput = true;
|
|
79
|
+
},
|
|
80
|
+
markSideEffectStarted() {
|
|
81
|
+
outcome.sideEffectStarted = true;
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
try {
|
|
85
|
+
if (options.onAttemptStart)
|
|
86
|
+
await options.onAttemptStart(context);
|
|
87
|
+
const result = await options.execute(context);
|
|
88
|
+
if (options.onAttemptOutcome) {
|
|
89
|
+
await options.onAttemptOutcome(Object.freeze({
|
|
90
|
+
operationId: options.operationId,
|
|
91
|
+
attempt,
|
|
92
|
+
outcome: snapshot(outcome),
|
|
93
|
+
...(signal ? { signal } : {}),
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
if (options.onAttemptOutcome) {
|
|
100
|
+
await options.onAttemptOutcome(Object.freeze({
|
|
101
|
+
operationId: options.operationId,
|
|
102
|
+
attempt,
|
|
103
|
+
outcome: snapshot(outcome),
|
|
104
|
+
...(signal ? { signal } : {}),
|
|
105
|
+
error,
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
if (signal?.aborted)
|
|
109
|
+
throw abortReason(signal);
|
|
110
|
+
if (attempt >= options.maxAttempts)
|
|
111
|
+
throw error;
|
|
112
|
+
const replayContext = Object.freeze({
|
|
113
|
+
operationId: options.operationId,
|
|
114
|
+
attempt,
|
|
115
|
+
nextAttempt: attempt + 1,
|
|
116
|
+
error,
|
|
117
|
+
outcome: snapshot(outcome),
|
|
118
|
+
...(signal ? { signal } : {}),
|
|
119
|
+
});
|
|
120
|
+
if (options.shouldRetry(error, replayContext) !== true)
|
|
121
|
+
throw error;
|
|
122
|
+
if (outcome.acceptance === "accepted" ||
|
|
123
|
+
outcome.partialOutput ||
|
|
124
|
+
outcome.sideEffectStarted) {
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
if ((await options.authorizeReplay(replayContext)) !== true)
|
|
128
|
+
throw error;
|
|
129
|
+
if (signal?.aborted)
|
|
130
|
+
throw abortReason(signal);
|
|
131
|
+
const configuredDelay = typeof options.backoffMs === "function"
|
|
132
|
+
? options.backoffMs(attempt + 1, error)
|
|
133
|
+
: options.backoffMs ?? 0;
|
|
134
|
+
const delay = Math.min(30_000, Math.max(0, configuredDelay));
|
|
135
|
+
await abortableDelay(delay, signal);
|
|
92
136
|
}
|
|
93
|
-
if ((await options.authorizeReplay(replayContext)) !== true)
|
|
94
|
-
throw error;
|
|
95
|
-
if (options.signal?.aborted)
|
|
96
|
-
throw abortReason(options.signal);
|
|
97
|
-
const configuredDelay = typeof options.backoffMs === "function"
|
|
98
|
-
? options.backoffMs(attempt + 1, error)
|
|
99
|
-
: options.backoffMs ?? 0;
|
|
100
|
-
const delay = Math.min(30_000, Math.max(0, configuredDelay));
|
|
101
|
-
await abortableDelay(delay, options.signal);
|
|
102
137
|
}
|
|
103
138
|
}
|
|
139
|
+
finally {
|
|
140
|
+
if (deadlineTimer !== undefined)
|
|
141
|
+
clearTimeout(deadlineTimer);
|
|
142
|
+
}
|
|
104
143
|
throw new Error("Unreachable recovery state");
|
|
105
144
|
}
|
|
106
145
|
//# sourceMappingURL=recovery.js.map
|
package/dist/recovery.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, opt-in secret redaction. The caller supplies every secret value and
|
|
3
|
+
* sensitive key name; QitQore never reads the environment, a credential store,
|
|
4
|
+
* or any global registry, and it keeps no state between calls except the
|
|
5
|
+
* configuration captured by `createSecretRedactor`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEFAULT_REDACTION_MARKER = "[redacted]";
|
|
8
|
+
export declare const TRUNCATION_MARKER = "[truncated]";
|
|
9
|
+
/**
|
|
10
|
+
* Replace credential shapes in free text: authorization headers, bearer or basic
|
|
11
|
+
* tokens, secret-looking `key=value` pairs, query-string secrets, and JWT-shaped
|
|
12
|
+
* values. These are heuristics, so `createSecretRedactor` can disable them for
|
|
13
|
+
* model output where an ordinary word such as `token: 5` must survive.
|
|
14
|
+
*/
|
|
15
|
+
export declare function redactCredentialShapes(text: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Bounded, log-safe text: credential shapes are redacted, control characters
|
|
18
|
+
* are replaced, surrounding whitespace is trimmed, and the result is capped.
|
|
19
|
+
*/
|
|
20
|
+
export declare function redactSensitiveText(text: string, maxLength: number): string;
|
|
21
|
+
/**
|
|
22
|
+
* Encodings in which a secret can legitimately appear in text: the raw value,
|
|
23
|
+
* URL-encoded, JSON-escaped, and base64. Hosts use this to count registrations
|
|
24
|
+
* against their own limits, so the encoding rule has one implementation.
|
|
25
|
+
*/
|
|
26
|
+
export declare function secretEncodings(value: string): string[];
|
|
27
|
+
export interface SecretRedactorOptions {
|
|
28
|
+
/** Exact secret values supplied by the host. Shorter values are ignored. */
|
|
29
|
+
secrets?: readonly string[];
|
|
30
|
+
/** Shortest configured value that is redacted. Defaults to 8 characters. */
|
|
31
|
+
minSecretLength?: number;
|
|
32
|
+
/** Key names whose values are replaced wholesale. Compared after normalization. */
|
|
33
|
+
sensitiveKeys?: readonly string[];
|
|
34
|
+
/** Additional key test over the normalized key, for host-specific naming rules. */
|
|
35
|
+
isSensitiveKey?: (normalized: string) => boolean;
|
|
36
|
+
/** Replacement text for redacted values. Must be deterministic for the caller. */
|
|
37
|
+
marker?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Apply credential-shape heuristics in addition to exact values. Defaults to
|
|
40
|
+
* true. Disable it for model output, where a shape match can alter ordinary prose.
|
|
41
|
+
*/
|
|
42
|
+
patterns?: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface SecretRedactionStream {
|
|
45
|
+
/** Redact a chunk. Text that could still be the start of a secret is held back. */
|
|
46
|
+
push(chunk: string): string;
|
|
47
|
+
/** Release everything still held back. Call once the stream has ended. */
|
|
48
|
+
finish(): string;
|
|
49
|
+
}
|
|
50
|
+
export interface SecretRedactor {
|
|
51
|
+
/** Replace configured values and their encodings, then common credential shapes. */
|
|
52
|
+
redact(text: string): string;
|
|
53
|
+
/** Create a stateful, chunk-safe redactor for one streamed output. */
|
|
54
|
+
stream(): SecretRedactionStream;
|
|
55
|
+
/** Recursively redact a structured value with bounded depth, entries, and string size. */
|
|
56
|
+
redactStructure(value: unknown): unknown;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Build a redactor from explicit configuration. Each call returns an independent
|
|
60
|
+
* object, so hosts that rotate secrets create a new redactor rather than mutating
|
|
61
|
+
* a shared one.
|
|
62
|
+
*/
|
|
63
|
+
export declare function createSecretRedactor(options?: SecretRedactorOptions): SecretRedactor;
|
|
64
|
+
//# sourceMappingURL=redaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,eAAO,MAAM,wBAAwB,eAAe,CAAC;AACrD,eAAO,MAAM,iBAAiB,gBAAgB,CAAC;AAS/C;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO3D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE3E;AAQD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAQvD;AAUD,MAAM,WAAW,qBAAqB;IACpC,4EAA4E;IAC5E,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mFAAmF;IACnF,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,mFAAmF;IACnF,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,mFAAmF;IACnF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,0EAA0E;IAC1E,MAAM,IAAI,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,oFAAoF;IACpF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,sEAAsE;IACtE,MAAM,IAAI,qBAAqB,CAAC;IAChC,0FAA0F;IAC1F,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;CAC1C;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,qBAA0B,GAAG,cAAc,CA0FxF"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, opt-in secret redaction. The caller supplies every secret value and
|
|
3
|
+
* sensitive key name; QitQore never reads the environment, a credential store,
|
|
4
|
+
* or any global registry, and it keeps no state between calls except the
|
|
5
|
+
* configuration captured by `createSecretRedactor`.
|
|
6
|
+
*/
|
|
7
|
+
const AUTH_HEADER = /\b(authorization)\s*[:=]\s*(?:bearer|basic)\s+\S+/gi;
|
|
8
|
+
const SECRET_VALUE = /\b(api[-_ ]?key|access[-_ ]?token|refresh[-_ ]?token|token|password|secret)\s*[:=]\s*["']?[^\s,;"'&]+/gi;
|
|
9
|
+
const BEARER_TOKEN = /\b(bearer|basic)\s+[A-Za-z0-9._~+/-]+=*/gi;
|
|
10
|
+
const URL_SECRET = /([?&](?:api[-_]?key|access[-_]?token|refresh[-_]?token|token|secret)=)[^&\s]+/gi;
|
|
11
|
+
const JWT = /\beyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g;
|
|
12
|
+
const CONTROL_CHARACTERS = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g;
|
|
13
|
+
export const DEFAULT_REDACTION_MARKER = "[redacted]";
|
|
14
|
+
export const TRUNCATION_MARKER = "[truncated]";
|
|
15
|
+
const DEFAULT_MIN_SECRET_LENGTH = 8;
|
|
16
|
+
const MAX_STRUCTURE_DEPTH = 8;
|
|
17
|
+
const MAX_STRUCTURE_ENTRIES = 256;
|
|
18
|
+
const MAX_STRING_LENGTH = 16_384;
|
|
19
|
+
/** Longest non-whitespace run a stream holds back, so a split token is never emitted early. */
|
|
20
|
+
const MAX_STREAM_TOKEN_HOLD = 4_096;
|
|
21
|
+
/**
|
|
22
|
+
* Replace credential shapes in free text: authorization headers, bearer or basic
|
|
23
|
+
* tokens, secret-looking `key=value` pairs, query-string secrets, and JWT-shaped
|
|
24
|
+
* values. These are heuristics, so `createSecretRedactor` can disable them for
|
|
25
|
+
* model output where an ordinary word such as `token: 5` must survive.
|
|
26
|
+
*/
|
|
27
|
+
export function redactCredentialShapes(text) {
|
|
28
|
+
return text
|
|
29
|
+
.replace(AUTH_HEADER, "$1: [redacted]")
|
|
30
|
+
.replace(SECRET_VALUE, "$1=[redacted]")
|
|
31
|
+
.replace(BEARER_TOKEN, "$1 [redacted]")
|
|
32
|
+
.replace(URL_SECRET, "$1[redacted]")
|
|
33
|
+
.replace(JWT, "[redacted-token]");
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Bounded, log-safe text: credential shapes are redacted, control characters
|
|
37
|
+
* are replaced, surrounding whitespace is trimmed, and the result is capped.
|
|
38
|
+
*/
|
|
39
|
+
export function redactSensitiveText(text, maxLength) {
|
|
40
|
+
return redactCredentialShapes(text).replace(CONTROL_CHARACTERS, " ").trim().slice(0, maxLength);
|
|
41
|
+
}
|
|
42
|
+
function utf8Base64(value) {
|
|
43
|
+
let binary = "";
|
|
44
|
+
for (const byte of new TextEncoder().encode(value))
|
|
45
|
+
binary += String.fromCharCode(byte);
|
|
46
|
+
return btoa(binary);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Encodings in which a secret can legitimately appear in text: the raw value,
|
|
50
|
+
* URL-encoded, JSON-escaped, and base64. Hosts use this to count registrations
|
|
51
|
+
* against their own limits, so the encoding rule has one implementation.
|
|
52
|
+
*/
|
|
53
|
+
export function secretEncodings(value) {
|
|
54
|
+
const variants = [
|
|
55
|
+
value,
|
|
56
|
+
encodeURIComponent(value),
|
|
57
|
+
JSON.stringify(value).slice(1, -1),
|
|
58
|
+
utf8Base64(value),
|
|
59
|
+
];
|
|
60
|
+
return [...new Set(variants)].filter((item) => item.length > 0);
|
|
61
|
+
}
|
|
62
|
+
/** Normalize a key so `accessToken`, `access-token`, and `access_token` compare equal. */
|
|
63
|
+
function normalizedKey(key) {
|
|
64
|
+
return key
|
|
65
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1_$2")
|
|
66
|
+
.replace(/-/g, "_")
|
|
67
|
+
.toLowerCase();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Build a redactor from explicit configuration. Each call returns an independent
|
|
71
|
+
* object, so hosts that rotate secrets create a new redactor rather than mutating
|
|
72
|
+
* a shared one.
|
|
73
|
+
*/
|
|
74
|
+
export function createSecretRedactor(options = {}) {
|
|
75
|
+
const minSecretLength = options.minSecretLength ?? DEFAULT_MIN_SECRET_LENGTH;
|
|
76
|
+
const marker = options.marker ?? DEFAULT_REDACTION_MARKER;
|
|
77
|
+
const variants = [
|
|
78
|
+
...new Set((options.secrets ?? [])
|
|
79
|
+
.filter((value) => typeof value === "string" && value.length >= minSecretLength)
|
|
80
|
+
.flatMap(secretEncodings)),
|
|
81
|
+
].sort((a, b) => b.length - a.length);
|
|
82
|
+
const longest = variants.length === 0 ? 0 : variants[0].length;
|
|
83
|
+
const sensitiveKeys = new Set((options.sensitiveKeys ?? []).map(normalizedKey));
|
|
84
|
+
const isSensitiveKey = (key) => sensitiveKeys.has(normalizedKey(key)) || (options.isSensitiveKey?.(normalizedKey(key)) ?? false);
|
|
85
|
+
const patterns = options.patterns ?? true;
|
|
86
|
+
// Exact values come first so a secret is never partially rewritten by a shape rule.
|
|
87
|
+
// Whitespace and control characters are preserved: streamed text must reassemble unchanged.
|
|
88
|
+
const redact = (text) => {
|
|
89
|
+
const exact = variants.length === 0 || text.length === 0
|
|
90
|
+
? text
|
|
91
|
+
: variants.reduce((current, item) => current.split(item).join(marker), text);
|
|
92
|
+
return patterns ? redactCredentialShapes(exact) : exact;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Earliest index where a still-incomplete secret could begin. Everything from
|
|
96
|
+
* that index on is held, so a value split across chunks is never emitted in part.
|
|
97
|
+
*/
|
|
98
|
+
const partialSecretStart = (text) => {
|
|
99
|
+
for (let start = Math.max(0, text.length - longest + 1); start < text.length; start += 1) {
|
|
100
|
+
const suffix = text.slice(start);
|
|
101
|
+
if (variants.some((variant) => variant.length > suffix.length && variant.startsWith(suffix)))
|
|
102
|
+
return start;
|
|
103
|
+
}
|
|
104
|
+
return text.length;
|
|
105
|
+
};
|
|
106
|
+
/** Credential shapes span non-whitespace runs, so the trailing run is held as well. */
|
|
107
|
+
const credentialTailStart = (text) => {
|
|
108
|
+
const tail = /\S*$/.exec(text)?.[0].length ?? 0;
|
|
109
|
+
return text.length - Math.min(tail, MAX_STREAM_TOKEN_HOLD);
|
|
110
|
+
};
|
|
111
|
+
const stream = () => {
|
|
112
|
+
let pending = "";
|
|
113
|
+
return {
|
|
114
|
+
push(chunk) {
|
|
115
|
+
pending += chunk;
|
|
116
|
+
let boundary = Math.min(partialSecretStart(pending), patterns ? credentialTailStart(pending) : pending.length);
|
|
117
|
+
if (boundary > 0 && boundary < pending.length) {
|
|
118
|
+
const before = pending.charCodeAt(boundary - 1);
|
|
119
|
+
const after = pending.charCodeAt(boundary);
|
|
120
|
+
if (before >= 0xd800 && before <= 0xdbff && after >= 0xdc00 && after <= 0xdfff)
|
|
121
|
+
boundary -= 1;
|
|
122
|
+
}
|
|
123
|
+
const safe = pending.slice(0, boundary);
|
|
124
|
+
pending = pending.slice(boundary);
|
|
125
|
+
return redact(safe);
|
|
126
|
+
},
|
|
127
|
+
finish() {
|
|
128
|
+
const final = redact(pending);
|
|
129
|
+
pending = "";
|
|
130
|
+
return final;
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
const redactStructure = (value, depth = 0) => {
|
|
135
|
+
if (typeof value === "string") {
|
|
136
|
+
const scrubbed = redact(value);
|
|
137
|
+
return scrubbed.length > MAX_STRING_LENGTH
|
|
138
|
+
? `${scrubbed.slice(0, MAX_STRING_LENGTH)}${TRUNCATION_MARKER}`
|
|
139
|
+
: scrubbed;
|
|
140
|
+
}
|
|
141
|
+
if (value === null || typeof value !== "object")
|
|
142
|
+
return value;
|
|
143
|
+
if (depth >= MAX_STRUCTURE_DEPTH)
|
|
144
|
+
return TRUNCATION_MARKER;
|
|
145
|
+
if (Array.isArray(value)) {
|
|
146
|
+
return value.slice(0, MAX_STRUCTURE_ENTRIES).map((item) => redactStructure(item, depth + 1));
|
|
147
|
+
}
|
|
148
|
+
return Object.fromEntries(Object.entries(value)
|
|
149
|
+
.slice(0, MAX_STRUCTURE_ENTRIES)
|
|
150
|
+
.map(([key, item]) => [key, isSensitiveKey(key) ? marker : redactStructure(item, depth + 1)]));
|
|
151
|
+
};
|
|
152
|
+
return { redact, stream, redactStructure };
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=redaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.js","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"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"}
|