@zyraxon-ai/http-recorder 19.0.5

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 (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +217 -0
  3. package/dist/cassette.d.ts +36 -0
  4. package/dist/cassette.d.ts.map +1 -0
  5. package/dist/cassette.js +105 -0
  6. package/dist/cassette.js.map +1 -0
  7. package/dist/effect.d.ts +11 -0
  8. package/dist/effect.d.ts.map +1 -0
  9. package/dist/effect.js +18 -0
  10. package/dist/effect.js.map +1 -0
  11. package/dist/index.d.ts +18 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +5 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/internal-effect.d.ts +19 -0
  16. package/dist/internal-effect.d.ts.map +1 -0
  17. package/dist/internal-effect.js +105 -0
  18. package/dist/internal-effect.js.map +1 -0
  19. package/dist/internal.d.ts +9 -0
  20. package/dist/internal.d.ts.map +1 -0
  21. package/dist/internal.js +9 -0
  22. package/dist/internal.js.map +1 -0
  23. package/dist/matching.d.ts +14 -0
  24. package/dist/matching.d.ts.map +1 -0
  25. package/dist/matching.js +92 -0
  26. package/dist/matching.js.map +1 -0
  27. package/dist/recorder.d.ts +13 -0
  28. package/dist/recorder.d.ts.map +1 -0
  29. package/dist/recorder.js +33 -0
  30. package/dist/recorder.js.map +1 -0
  31. package/dist/redaction.d.ts +14 -0
  32. package/dist/redaction.d.ts.map +1 -0
  33. package/dist/redaction.js +100 -0
  34. package/dist/redaction.js.map +1 -0
  35. package/dist/redactor.d.ts +30 -0
  36. package/dist/redactor.d.ts.map +1 -0
  37. package/dist/redactor.js +87 -0
  38. package/dist/redactor.js.map +1 -0
  39. package/dist/schema.d.ts +330 -0
  40. package/dist/schema.d.ts.map +1 -0
  41. package/dist/schema.js +53 -0
  42. package/dist/schema.js.map +1 -0
  43. package/dist/socket.d.ts +16 -0
  44. package/dist/socket.d.ts.map +1 -0
  45. package/dist/socket.js +194 -0
  46. package/dist/socket.js.map +1 -0
  47. package/dist/types.d.ts +101 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +2 -0
  50. package/dist/types.js.map +1 -0
  51. package/dist/websocket.d.ts +29 -0
  52. package/dist/websocket.d.ts.map +1 -0
  53. package/dist/websocket.js +101 -0
  54. package/dist/websocket.js.map +1 -0
  55. package/package.json +47 -0
@@ -0,0 +1,9 @@
1
+ export { CassetteNotFoundError, hasCassetteSync, UnsafeCassetteError } from "./cassette.js";
2
+ export { cassetteLayer, recordingLayer } from "./internal-effect.js";
3
+ export { redactHeaders, redactUrl, secretFindings } from "./redaction.js";
4
+ export { socketLayer } from "./socket.js";
5
+ export { makeWebSocketExecutor, } from "./websocket.js";
6
+ export * as Cassette from "./cassette.js";
7
+ export * as Redactor from "./redactor.js";
8
+ export * as HttpRecorderInternal from "./internal.js";
9
+ //# sourceMappingURL=internal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal.js","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAC3F,OAAO,EAAE,aAAa,EAAE,cAAc,EAAmD,MAAM,sBAAsB,CAAA;AACrH,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,cAAc,EAAsB,MAAM,gBAAgB,CAAA;AAC7F,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EACL,qBAAqB,GAKtB,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AAEzC,OAAO,KAAK,oBAAoB,MAAM,eAAe,CAAA"}
@@ -0,0 +1,14 @@
1
+ import { Option } from "effect";
2
+ import type { HttpInteraction, RequestMatcher, RequestSnapshot } from "./types.js";
3
+ export declare const decodeJson: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => Option.Option<unknown>;
4
+ export declare const canonicalizeJson: (value: unknown) => unknown;
5
+ export type { RequestMatcher } from "./types.js";
6
+ export declare const canonicalSnapshot: (snapshot: RequestSnapshot) => string;
7
+ export declare const defaultMatcher: RequestMatcher;
8
+ export declare const safeText: (value: unknown) => string;
9
+ export declare const requestDiff: (expected: RequestSnapshot, received: RequestSnapshot) => ReadonlyArray<string>;
10
+ export declare const selectSequential: (interactions: ReadonlyArray<HttpInteraction>, incoming: RequestSnapshot, match: RequestMatcher, index: number) => {
11
+ readonly interaction: HttpInteraction | undefined;
12
+ readonly detail: string;
13
+ };
14
+ //# sourceMappingURL=matching.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matching.d.ts","sourceRoot":"","sources":["../src/matching.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAA;AAEvC,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAGlF,eAAO,MAAM,UAAU,+FAAwC,CAAA;AAK/D,eAAO,MAAM,gBAAgB,GAAI,OAAO,OAAO,KAAG,OAUjD,CAAA;AAED,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,eAAO,MAAM,iBAAiB,GAAI,UAAU,eAAe,KAAG,MAS1D,CAAA;AAEJ,eAAO,MAAM,cAAc,EAAE,cACgC,CAAA;AAE7D,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,WAMtC,CAAA;AA4BD,eAAO,MAAM,WAAW,GAAI,UAAU,eAAe,EAAE,UAAU,eAAe,KAAG,aAAa,CAAC,MAAM,CAoBtG,CAAA;AAED,eAAO,MAAM,gBAAgB,GAC3B,cAAc,aAAa,CAAC,eAAe,CAAC,EAC5C,UAAU,eAAe,EACzB,OAAO,cAAc,EACrB,OAAO,MAAM,KACZ;IAAE,QAAQ,CAAC,WAAW,EAAE,eAAe,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAM9E,CAAA"}
@@ -0,0 +1,92 @@
1
+ import { Option, Schema } from "effect";
2
+ import { REDACTED, secretFindings } from "./redaction.js";
3
+ const JsonValue = Schema.fromJsonString(Schema.Unknown);
4
+ export const decodeJson = Schema.decodeUnknownOption(JsonValue);
5
+ const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
6
+ export const canonicalizeJson = (value) => {
7
+ if (Array.isArray(value))
8
+ return value.map(canonicalizeJson);
9
+ if (isRecord(value)) {
10
+ return Object.fromEntries(Object.keys(value)
11
+ .toSorted()
12
+ .map((key) => [key, canonicalizeJson(value[key])]));
13
+ }
14
+ return value;
15
+ };
16
+ export const canonicalSnapshot = (snapshot) => JSON.stringify({
17
+ method: snapshot.method,
18
+ url: snapshot.url,
19
+ headers: canonicalizeJson(snapshot.headers),
20
+ body: Option.match(decodeJson(snapshot.body), {
21
+ onNone: () => snapshot.body,
22
+ onSome: canonicalizeJson,
23
+ }),
24
+ });
25
+ export const defaultMatcher = (incoming, recorded) => canonicalSnapshot(incoming) === canonicalSnapshot(recorded);
26
+ export const safeText = (value) => {
27
+ if (value === undefined)
28
+ return "undefined";
29
+ if (secretFindings(value).length > 0)
30
+ return JSON.stringify(REDACTED);
31
+ const text = JSON.stringify(value);
32
+ if (!text)
33
+ return typeof value;
34
+ return text.length > 300 ? `${text.slice(0, 300)}...` : text;
35
+ };
36
+ const jsonBody = (body) => Option.getOrUndefined(decodeJson(body));
37
+ const valueDiffs = (expected, received, base = "$", limit = 8) => {
38
+ if (Object.is(expected, received))
39
+ return [];
40
+ if (isRecord(expected) && isRecord(received)) {
41
+ return [...new Set([...Object.keys(expected), ...Object.keys(received)])]
42
+ .toSorted()
43
+ .flatMap((key) => valueDiffs(expected[key], received[key], `${base}.${key}`, limit))
44
+ .slice(0, limit);
45
+ }
46
+ if (Array.isArray(expected) && Array.isArray(received)) {
47
+ return Array.from({ length: Math.max(expected.length, received.length) }, (_, index) => index)
48
+ .flatMap((index) => valueDiffs(expected[index], received[index], `${base}[${index}]`, limit))
49
+ .slice(0, limit);
50
+ }
51
+ return [`${base} expected ${safeText(expected)}, received ${safeText(received)}`];
52
+ };
53
+ const headerDiffs = (expected, received) => [...new Set([...Object.keys(expected), ...Object.keys(received)])].toSorted().flatMap((key) => {
54
+ if (expected[key] === received[key])
55
+ return [];
56
+ if (expected[key] === undefined)
57
+ return [` ${key} unexpected ${safeText(received[key])}`];
58
+ if (received[key] === undefined)
59
+ return [` ${key} missing expected ${safeText(expected[key])}`];
60
+ return [` ${key} expected ${safeText(expected[key])}, received ${safeText(received[key])}`];
61
+ });
62
+ export const requestDiff = (expected, received) => {
63
+ const lines = [];
64
+ if (expected.method !== received.method) {
65
+ lines.push("method:", ` expected ${expected.method}, received ${received.method}`);
66
+ }
67
+ if (expected.url !== received.url) {
68
+ lines.push("url:", ` expected ${expected.url}`, ` received ${received.url}`);
69
+ }
70
+ const headers = headerDiffs(expected.headers, received.headers);
71
+ if (headers.length > 0)
72
+ lines.push("headers:", ...headers.slice(0, 8));
73
+ const expectedBody = jsonBody(expected.body);
74
+ const receivedBody = jsonBody(received.body);
75
+ const body = expectedBody !== undefined && receivedBody !== undefined
76
+ ? valueDiffs(expectedBody, receivedBody).map((line) => ` ${line}`)
77
+ : expected.body === received.body
78
+ ? []
79
+ : [` expected ${safeText(expected.body)}, received ${safeText(received.body)}`];
80
+ if (body.length > 0)
81
+ lines.push("body:", ...body);
82
+ return lines;
83
+ };
84
+ export const selectSequential = (interactions, incoming, match, index) => {
85
+ const interaction = interactions[index];
86
+ if (!interaction)
87
+ return { interaction, detail: `interaction ${index + 1} of ${interactions.length} not recorded` };
88
+ if (!match(incoming, interaction.request))
89
+ return { interaction: undefined, detail: requestDiff(interaction.request, incoming).join("\n") };
90
+ return { interaction, detail: "" };
91
+ };
92
+ //# sourceMappingURL=matching.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matching.js","sourceRoot":"","sources":["../src/matching.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { Effect, Scope } from "effect";
2
+ import type * as CassetteService from "./cassette.js";
3
+ import type { CassetteNotFoundError } from "./cassette.js";
4
+ import type { Interaction } from "./schema.js";
5
+ export declare const resolveAutoMode: (cassette: CassetteService.Interface, name: string) => Effect.Effect<"record" | "replay" | "passthrough">;
6
+ export interface ReplayState<T> {
7
+ readonly claim: <E>(validate: (interaction: T | undefined, index: number, interactions: ReadonlyArray<T>) => Effect.Effect<void, E>) => Effect.Effect<{
8
+ readonly interaction: T;
9
+ readonly index: number;
10
+ }, CassetteNotFoundError | E>;
11
+ }
12
+ export declare const makeReplayState: <T>(cassette: CassetteService.Interface, name: string, project: (interactions: ReadonlyArray<Interaction>) => ReadonlyArray<T>) => Effect.Effect<ReplayState<T>, never, Scope.Scope>;
13
+ //# sourceMappingURL=recorder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAmB,MAAM,QAAQ,CAAA;AACvD,OAAO,KAAK,KAAK,eAAe,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAO9C,eAAO,MAAM,eAAe,GAC1B,UAAU,eAAe,CAAC,SAAS,EACnC,MAAM,MAAM,KACX,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,QAAQ,GAAG,aAAa,CAIhD,CAAA;AAEJ,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,EAChB,QAAQ,EAAE,CAAC,WAAW,EAAE,CAAC,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,KAC5G,MAAM,CAAC,MAAM,CAAC;QAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,qBAAqB,GAAG,CAAC,CAAC,CAAA;CACnG;AAED,eAAO,MAAM,eAAe,GAAI,CAAC,EAC/B,UAAU,eAAe,CAAC,SAAS,EACnC,MAAM,MAAM,EACZ,SAAS,CAAC,YAAY,EAAE,aAAa,CAAC,WAAW,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC,KACtE,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAgC/C,CAAA"}
@@ -0,0 +1,33 @@
1
+ import { Effect, SynchronizedRef } from "effect";
2
+ const isCI = () => {
3
+ const value = process.env.CI;
4
+ return value !== undefined && value !== "" && value !== "false" && value !== "0";
5
+ };
6
+ export const resolveAutoMode = (cassette, name) => Effect.gen(function* () {
7
+ if (isCI())
8
+ return "replay";
9
+ return (yield* cassette.exists(name)) ? "replay" : "record";
10
+ });
11
+ export const makeReplayState = (cassette, name, project) => Effect.gen(function* () {
12
+ const load = yield* Effect.cached(cassette.read(name).pipe(Effect.map(project)));
13
+ const position = yield* SynchronizedRef.make(0);
14
+ yield* Effect.addFinalizer(() => Effect.gen(function* () {
15
+ const used = yield* SynchronizedRef.get(position);
16
+ if (used === 0)
17
+ return yield* Effect.void;
18
+ const interactions = yield* load.pipe(Effect.orDie);
19
+ if (used < interactions.length)
20
+ return yield* Effect.die(new Error(`Unused recorded interactions in ${name}: used ${used} of ${interactions.length}`));
21
+ return yield* Effect.void;
22
+ }));
23
+ return {
24
+ claim: (validate) => Effect.flatMap(load, (interactions) => SynchronizedRef.modifyEffect(position, (index) => Effect.gen(function* () {
25
+ const interaction = interactions[index];
26
+ yield* validate(interaction, index, interactions);
27
+ if (interaction === undefined)
28
+ return yield* Effect.die("Replay validation accepted a missing interaction");
29
+ return [{ interaction, index }, index + 1];
30
+ }))),
31
+ };
32
+ });
33
+ //# sourceMappingURL=recorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorder.js","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAS,eAAe,EAAE,MAAM,QAAQ,CAAA;AAKvD,MAAM,IAAI,GAAG,GAAG,EAAE;IAChB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAA;IAC5B,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,GAAG,CAAA;AAClF,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,QAAmC,EACnC,IAAY,EACwC,EAAE,CACtD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,IAAI,IAAI,EAAE;QAAE,OAAO,QAAQ,CAAA;IAC3B,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAA;AAC7D,CAAC,CAAC,CAAA;AAQJ,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,QAAmC,EACnC,IAAY,EACZ,OAAuE,EACpB,EAAE,CACrD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAChF,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAE/C,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAC9B,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACjD,IAAI,IAAI,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAA;QACzC,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACnD,IAAI,IAAI,GAAG,YAAY,CAAC,MAAM;YAC5B,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CACtB,IAAI,KAAK,CAAC,mCAAmC,IAAI,UAAU,IAAI,OAAO,YAAY,CAAC,MAAM,EAAE,CAAC,CAC7F,CAAA;QACH,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAA;IAC3B,CAAC,CAAC,CACH,CAAA;IAED,OAAO;QACL,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE,CAClB,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,YAAY,EAAE,EAAE,CACpC,eAAe,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAC/C,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,WAAW,GAAG,YAAY,CAAC,KAAK,CAAC,CAAA;YACvC,KAAK,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,EAAE,YAAY,CAAC,CAAA;YACjD,IAAI,WAAW,KAAK,SAAS;gBAC3B,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAA;YAC9E,OAAO,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,KAAK,GAAG,CAAC,CAAU,CAAA;QACrD,CAAC,CAAC,CACH,CACF;KACJ,CAAA;AACH,CAAC,CAAC,CAAA"}
@@ -0,0 +1,14 @@
1
+ import { Schema } from "effect";
2
+ export declare const REDACTED = "[REDACTED]";
3
+ export type UrlRedactor = (url: string) => string;
4
+ export declare const redactUrl: (raw: string, query?: ReadonlyArray<string>, urlRedactor?: UrlRedactor) => string;
5
+ export declare const redactHeaders: (headers: Record<string, string>, allow: ReadonlyArray<string>, redact?: ReadonlyArray<string>) => {
6
+ [k: string]: string;
7
+ };
8
+ export declare const SecretFindingSchema: Schema.Struct<{
9
+ readonly path: Schema.String;
10
+ readonly reason: Schema.String;
11
+ }>;
12
+ export type SecretFinding = Schema.Schema.Type<typeof SecretFindingSchema>;
13
+ export declare const secretFindings: (value: unknown) => ReadonlyArray<SecretFinding>;
14
+ //# sourceMappingURL=redaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE/B,eAAO,MAAM,QAAQ,eAAe,CAAA;AA+DpC,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;AAEjD,eAAO,MAAM,SAAS,GACpB,KAAK,MAAM,EACX,QAAO,aAAa,CAAC,MAAM,CAAwB,EACnD,cAAc,WAAW,WAW1B,CAAA;AAED,eAAO,MAAM,aAAa,GACxB,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,OAAO,aAAa,CAAC,MAAM,CAAC,EAC5B,SAAQ,aAAa,CAAC,MAAM,CAA0B;;CAWvD,CAAA;AAED,eAAO,MAAM,mBAAmB;;;EAG9B,CAAA;AACF,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAE1E,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,aAAa,CAAC,aAAa,CAW1E,CAAA"}
@@ -0,0 +1,100 @@
1
+ import { Schema } from "effect";
2
+ export const REDACTED = "[REDACTED]";
3
+ const DEFAULT_REDACT_HEADERS = [
4
+ "authorization",
5
+ "cookie",
6
+ "proxy-authorization",
7
+ "set-cookie",
8
+ "x-api-key",
9
+ "x-amz-security-token",
10
+ "x-goog-api-key",
11
+ ];
12
+ const DEFAULT_REDACT_QUERY = [
13
+ "access_token",
14
+ "api-key",
15
+ "api_key",
16
+ "apikey",
17
+ "code",
18
+ "key",
19
+ "signature",
20
+ "sig",
21
+ "token",
22
+ "x-amz-credential",
23
+ "x-amz-security-token",
24
+ "x-amz-signature",
25
+ ];
26
+ const SECRET_PATTERNS = [
27
+ { label: "bearer token", pattern: /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}\b/i },
28
+ { label: "API key", pattern: /\bsk-[A-Za-z0-9][A-Za-z0-9_-]{20,}\b/ },
29
+ { label: "Anthropic API key", pattern: /\bsk-ant-[A-Za-z0-9_-]{20,}\b/ },
30
+ { label: "Google API key", pattern: /\bAIza[0-9A-Za-z_-]{20,}\b/ },
31
+ { label: "AWS access key", pattern: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/ },
32
+ { label: "GitHub token", pattern: /\bgh[pousr]_[A-Za-z0-9_]{20,}\b/ },
33
+ { label: "private key", pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----/ },
34
+ ];
35
+ const ENV_SECRET_NAMES = /(?:API|AUTH|BEARER|CREDENTIAL|KEY|PASSWORD|SECRET|TOKEN)/i;
36
+ const SAFE_ENV_VALUES = new Set(["fixture", "test", "test-key"]);
37
+ const envSecrets = () => Object.entries(process.env).flatMap(([name, value]) => {
38
+ if (!value)
39
+ return [];
40
+ if (!ENV_SECRET_NAMES.test(name))
41
+ return [];
42
+ if (value.length < 12)
43
+ return [];
44
+ if (SAFE_ENV_VALUES.has(value.toLowerCase()))
45
+ return [];
46
+ return [{ name, value }];
47
+ });
48
+ const pathFor = (base, key) => (base ? `${base}.${key}` : key);
49
+ const stringEntries = (value, base = "") => {
50
+ if (typeof value === "string")
51
+ return [{ path: base, value }];
52
+ if (Array.isArray(value))
53
+ return value.flatMap((item, index) => stringEntries(item, `${base}[${index}]`));
54
+ if (value && typeof value === "object") {
55
+ return Object.entries(value).flatMap(([key, child]) => stringEntries(child, pathFor(base, key)));
56
+ }
57
+ return [];
58
+ };
59
+ const redactionSet = (values, defaults) => new Set([...defaults, ...(values ?? [])].map((value) => value.toLowerCase()));
60
+ export const redactUrl = (raw, query = DEFAULT_REDACT_QUERY, urlRedactor) => {
61
+ if (!URL.canParse(raw))
62
+ return urlRedactor?.(raw) ?? raw;
63
+ const url = new URL(raw);
64
+ if (url.username)
65
+ url.username = REDACTED;
66
+ if (url.password)
67
+ url.password = REDACTED;
68
+ const redacted = redactionSet(query, DEFAULT_REDACT_QUERY);
69
+ for (const key of url.searchParams.keys()) {
70
+ if (redacted.has(key.toLowerCase()))
71
+ url.searchParams.set(key, REDACTED);
72
+ }
73
+ return urlRedactor?.(url.toString()) ?? url.toString();
74
+ };
75
+ export const redactHeaders = (headers, allow, redact = DEFAULT_REDACT_HEADERS) => {
76
+ const allowed = new Set(allow.map((name) => name.toLowerCase()));
77
+ const redacted = redactionSet(redact, DEFAULT_REDACT_HEADERS);
78
+ return Object.fromEntries(Object.entries(headers)
79
+ .map(([name, value]) => [name.toLowerCase(), value])
80
+ .filter(([name]) => allowed.has(name))
81
+ .map(([name, value]) => [name, redacted.has(name) ? REDACTED : value])
82
+ .toSorted(([a], [b]) => a.localeCompare(b)));
83
+ };
84
+ export const SecretFindingSchema = Schema.Struct({
85
+ path: Schema.String,
86
+ reason: Schema.String,
87
+ });
88
+ export const secretFindings = (value) => {
89
+ const environment = envSecrets();
90
+ return stringEntries(value).flatMap((entry) => [
91
+ ...SECRET_PATTERNS.filter((item) => item.pattern.test(entry.value)).map((item) => ({
92
+ path: entry.path,
93
+ reason: item.label,
94
+ })),
95
+ ...environment
96
+ .filter((item) => entry.value.includes(item.value))
97
+ .map((item) => ({ path: entry.path, reason: `environment secret ${item.name}` })),
98
+ ]);
99
+ };
100
+ //# sourceMappingURL=redaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redaction.js","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { RedactOptions, RequestSnapshot, ResponseSnapshot } from "./types.js";
2
+ export type { RedactOptions } from "./types.js";
3
+ export declare const DEFAULT_REQUEST_HEADERS: ReadonlyArray<string>;
4
+ export declare const DEFAULT_RESPONSE_HEADERS: ReadonlyArray<string>;
5
+ export interface Redactor {
6
+ readonly request: (snapshot: RequestSnapshot) => RequestSnapshot;
7
+ readonly response: (snapshot: ResponseSnapshot) => ResponseSnapshot;
8
+ }
9
+ export declare const compose: (...redactors: ReadonlyArray<Partial<Redactor>>) => Redactor;
10
+ export interface HeaderOptions {
11
+ readonly allow?: ReadonlyArray<string>;
12
+ readonly redact?: ReadonlyArray<string>;
13
+ }
14
+ export declare const requestHeaders: (options?: HeaderOptions) => Partial<Redactor>;
15
+ export declare const responseHeaders: (options?: HeaderOptions) => Partial<Redactor>;
16
+ export interface UrlOptions {
17
+ readonly query?: ReadonlyArray<string>;
18
+ readonly transform?: (url: string) => string;
19
+ }
20
+ export declare const url: (options?: UrlOptions) => Partial<Redactor>;
21
+ export declare const body: (transform: (parsed: unknown) => unknown) => Partial<Redactor>;
22
+ export interface DefaultRedactorOverrides {
23
+ readonly requestHeaders?: HeaderOptions;
24
+ readonly responseHeaders?: HeaderOptions;
25
+ readonly url?: UrlOptions;
26
+ readonly body?: (parsed: unknown) => unknown;
27
+ }
28
+ export declare const make: (options?: RedactOptions) => Redactor;
29
+ export declare const defaults: (overrides?: DefaultRedactorOverrides) => Redactor;
30
+ //# sourceMappingURL=redactor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redactor.d.ts","sourceRoot":"","sources":["../src/redactor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElF,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,eAAO,MAAM,uBAAuB,EAAE,aAAa,CAAC,MAAM,CAA6C,CAAA;AACvG,eAAO,MAAM,wBAAwB,EAAE,aAAa,CAAC,MAAM,CAAoB,CAAA;AAI/E,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,eAAe,CAAA;IAChE,QAAQ,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,gBAAgB,CAAA;CACpE;AAED,eAAO,MAAM,OAAO,GAAI,GAAG,WAAW,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,KAAG,QAOxE,CAAA;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACxC;AAED,eAAO,MAAM,cAAc,GAAI,UAAS,aAAkB,KAAG,OAAO,CAAC,QAAQ,CAK3E,CAAA;AAEF,eAAO,MAAM,eAAe,GAAI,UAAS,aAAkB,KAAG,OAAO,CAAC,QAAQ,CAK5E,CAAA;AAEF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;CAC7C;AAED,eAAO,MAAM,GAAG,GAAI,UAAS,UAAe,KAAG,OAAO,CAAC,QAAQ,CAE7D,CAAA;AAEF,eAAO,MAAM,IAAI,GAAI,WAAW,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,KAAG,OAAO,CAAC,QAAQ,CAQ7E,CAAA;AAEF,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAA;IACvC,QAAQ,CAAC,eAAe,CAAC,EAAE,aAAa,CAAA;IACxC,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAA;CAC7C;AAkCD,eAAO,MAAM,IAAI,GAAI,UAAS,aAAkB,KAAG,QAuBlD,CAAA;AAED,eAAO,MAAM,QAAQ,GAAI,YAAW,wBAA6B,KAAG,QAMjE,CAAA"}
@@ -0,0 +1,87 @@
1
+ import { Option } from "effect";
2
+ import { decodeJson } from "./matching.js";
3
+ import { REDACTED, redactHeaders, redactUrl } from "./redaction.js";
4
+ export const DEFAULT_REQUEST_HEADERS = ["content-type", "accept", "openai-beta"];
5
+ export const DEFAULT_RESPONSE_HEADERS = ["content-type"];
6
+ const identity = (value) => value;
7
+ export const compose = (...redactors) => {
8
+ const requests = redactors.map((r) => r.request).filter((fn) => fn !== undefined);
9
+ const responses = redactors.map((r) => r.response).filter((fn) => fn !== undefined);
10
+ return {
11
+ request: requests.length === 0 ? identity : (snapshot) => requests.reduce((acc, fn) => fn(acc), snapshot),
12
+ response: responses.length === 0 ? identity : (snapshot) => responses.reduce((acc, fn) => fn(acc), snapshot),
13
+ };
14
+ };
15
+ export const requestHeaders = (options = {}) => ({
16
+ request: (snapshot) => ({
17
+ ...snapshot,
18
+ headers: redactHeaders(snapshot.headers, options.allow ?? DEFAULT_REQUEST_HEADERS, options.redact),
19
+ }),
20
+ });
21
+ export const responseHeaders = (options = {}) => ({
22
+ response: (snapshot) => ({
23
+ ...snapshot,
24
+ headers: redactHeaders(snapshot.headers, options.allow ?? DEFAULT_RESPONSE_HEADERS, options.redact),
25
+ }),
26
+ });
27
+ export const url = (options = {}) => ({
28
+ request: (snapshot) => ({ ...snapshot, url: redactUrl(snapshot.url, options.query, options.transform) }),
29
+ });
30
+ export const body = (transform) => ({
31
+ request: (snapshot) => ({
32
+ ...snapshot,
33
+ body: Option.match(decodeJson(snapshot.body), {
34
+ onNone: () => snapshot.body,
35
+ onSome: (parsed) => JSON.stringify(transform(parsed)),
36
+ }),
37
+ }),
38
+ });
39
+ const DEFAULT_REDACT_JSON_FIELDS = [
40
+ "access_token",
41
+ "api_key",
42
+ "apikey",
43
+ "client_secret",
44
+ "password",
45
+ "refresh_token",
46
+ "secret",
47
+ "token",
48
+ ];
49
+ const normalizeField = (field) => field.replace(/[^a-z0-9]/gi, "").toLowerCase();
50
+ const redactJsonFields = (value, fields) => {
51
+ if (Array.isArray(value))
52
+ return value.map((item) => redactJsonFields(item, fields));
53
+ if (!value || typeof value !== "object")
54
+ return value;
55
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [
56
+ key,
57
+ fields.has(normalizeField(key)) ? REDACTED : redactJsonFields(child, fields),
58
+ ]));
59
+ };
60
+ const redactBody = (value, fields, transform) => {
61
+ const redacted = Option.match(decodeJson(value), {
62
+ onNone: () => value,
63
+ onSome: (parsed) => JSON.stringify(redactJsonFields(parsed, fields)),
64
+ });
65
+ return transform?.(redacted) ?? redacted;
66
+ };
67
+ export const make = (options = {}) => {
68
+ const fields = new Set([...DEFAULT_REDACT_JSON_FIELDS, ...(options.jsonFields ?? [])].map(normalizeField));
69
+ return compose(requestHeaders({
70
+ allow: [...DEFAULT_REQUEST_HEADERS, ...(options.allowRequestHeaders ?? []), ...(options.headers ?? [])],
71
+ redact: options.headers,
72
+ }), responseHeaders({
73
+ allow: [...DEFAULT_RESPONSE_HEADERS, ...(options.allowResponseHeaders ?? []), ...(options.headers ?? [])],
74
+ redact: options.headers,
75
+ }), url({ query: options.queryParameters, transform: options.url }), {
76
+ request: (snapshot) => ({
77
+ ...snapshot,
78
+ body: redactBody(snapshot.body, fields, options.body),
79
+ }),
80
+ response: (snapshot) => ({
81
+ ...snapshot,
82
+ body: redactBody(snapshot.body, fields, options.body),
83
+ }),
84
+ });
85
+ };
86
+ export const defaults = (overrides = {}) => compose(requestHeaders(overrides.requestHeaders), responseHeaders(overrides.responseHeaders), url(overrides.url), ...(overrides.body ? [body(overrides.body)] : []));
87
+ //# sourceMappingURL=redactor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redactor.js","sourceRoot":"","sources":["../src/redactor.ts"],"names":[],"mappings":"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"}