cassetter 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +169 -0
  3. package/dist/binding.d.ts +60 -0
  4. package/dist/binding.d.ts.map +1 -0
  5. package/dist/binding.js +12 -0
  6. package/dist/binding.js.map +1 -0
  7. package/dist/cassette.d.ts +122 -0
  8. package/dist/cassette.d.ts.map +1 -0
  9. package/dist/cassette.js +377 -0
  10. package/dist/cassette.js.map +1 -0
  11. package/dist/context.d.ts +22 -0
  12. package/dist/context.d.ts.map +1 -0
  13. package/dist/context.js +70 -0
  14. package/dist/context.js.map +1 -0
  15. package/dist/index.d.ts +29 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +26 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/intercept/base.d.ts +11 -0
  20. package/dist/intercept/base.d.ts.map +1 -0
  21. package/dist/intercept/base.js +20 -0
  22. package/dist/intercept/base.js.map +1 -0
  23. package/dist/intercept/fetch.d.ts +13 -0
  24. package/dist/intercept/fetch.d.ts.map +1 -0
  25. package/dist/intercept/fetch.js +104 -0
  26. package/dist/intercept/fetch.js.map +1 -0
  27. package/dist/intercept/index.d.ts +4 -0
  28. package/dist/intercept/index.d.ts.map +1 -0
  29. package/dist/intercept/index.js +3 -0
  30. package/dist/intercept/index.js.map +1 -0
  31. package/dist/recording.d.ts +22 -0
  32. package/dist/recording.d.ts.map +1 -0
  33. package/dist/recording.js +51 -0
  34. package/dist/recording.js.map +1 -0
  35. package/dist/types.d.ts +96 -0
  36. package/dist/types.d.ts.map +1 -0
  37. package/dist/types.js +35 -0
  38. package/dist/types.js.map +1 -0
  39. package/native/cassetter.darwin-arm64.node +0 -0
  40. package/native/cassetter.darwin-x64.node +0 -0
  41. package/native/cassetter.linux-arm64-gnu.node +0 -0
  42. package/native/cassetter.linux-arm64-musl.node +0 -0
  43. package/native/cassetter.linux-x64-gnu.node +0 -0
  44. package/native/cassetter.linux-x64-musl.node +0 -0
  45. package/native/cassetter.win32-arm64-msvc.node +0 -0
  46. package/native/cassetter.win32-x64-msvc.node +0 -0
  47. package/native/index.js +326 -0
  48. package/native/package.json +3 -0
  49. package/package.json +74 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Marcelo Trylesinski
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # cassetter (Node)
2
+
3
+ HTTP cassette recorder for Node.js tests. Safe by default.
4
+
5
+ This is the Node binding. The cassette format, request matching, security
6
+ filtering, and body processing are implemented in Rust
7
+ ([`crates/cassetter-core`](../crates/cassetter-core)) and shared with the Python
8
+ package, so cassettes are interchangeable between them.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install --save-dev cassetter
14
+ ```
15
+
16
+ The package includes native addons for macOS and Windows on x64 and ARM64. It
17
+ also includes Linux x64 and ARM64 addons for glibc and musl.
18
+
19
+ ## Quick start
20
+
21
+ ```ts
22
+ import { useCassette } from "cassetter";
23
+
24
+ await useCassette("tests/cassettes/users.yaml", async () => {
25
+ const res = await fetch("https://api.example.com/users");
26
+ console.log(res.status); // 200
27
+ });
28
+ ```
29
+
30
+ The first run records real traffic. Later runs replay from the file - no network.
31
+
32
+ With vitest:
33
+
34
+ ```ts
35
+ import { expect, it } from "vitest";
36
+ import { useCassette } from "cassetter";
37
+
38
+ it("fetches users", async () => {
39
+ await useCassette("tests/cassettes/users.yaml", async () => {
40
+ const res = await fetch("https://api.example.com/users");
41
+ expect(res.status).toBe(200);
42
+ });
43
+ });
44
+ ```
45
+
46
+ The callback receives the cassette if you want to inspect what was recorded:
47
+
48
+ ```ts
49
+ await useCassette("cassette.yaml", async (cassette) => {
50
+ await fetch("https://api.example.com/users");
51
+ expect(cassette.interactions).toHaveLength(1);
52
+ });
53
+ ```
54
+
55
+ ## Record modes
56
+
57
+ | Mode | Behavior |
58
+ | --- | --- |
59
+ | `none` | Replay only. Throws `NoMatchError` if nothing matches. |
60
+ | `once` | Record if the cassette doesn't exist, otherwise replay. (default) |
61
+ | `new_episodes` | Replay what exists, record what doesn't. |
62
+ | `all` | Record everything, overwriting the cassette. |
63
+ | `rewrite` | Delete the cassette, then record everything. |
64
+
65
+ ```ts
66
+ await useCassette("cassette.yaml", { recordMode: "none" }, async () => { ... });
67
+ ```
68
+
69
+ Under `once`, an existing cassette replays only: an unmatched request throws
70
+ rather than silently reaching the network and appending to the file.
71
+
72
+ ## Safe by default
73
+
74
+ Secrets are filtered **at write time**, so they never reach disk. The defaults
75
+ come from the Rust core - the same lists the Python package uses:
76
+
77
+ - **Headers**: `authorization`, `cookie`, `set-cookie`, `x-api-key`, `api-key`,
78
+ `x-auth-token`, `proxy-authorization`, `www-authenticate`, `x-goog-api-key`,
79
+ `x-amz-security-token`
80
+ - **Query params**: `api_key`, `apikey`, `token`, `access_token`, `client_secret`
81
+ - **JSON body fields**: `access_token`, `refresh_token`, `client_secret`, `password`
82
+
83
+ ```ts
84
+ await useCassette("cassette.yaml", {
85
+ filterHeaders: ["x-custom-secret"],
86
+ bodyScrubPatterns: ["my_secret_field"],
87
+ replacement: "***REDACTED***",
88
+ }, async () => { ... });
89
+ ```
90
+
91
+ These **add to** the built-in lists rather than standing in for them, so naming
92
+ one more header to scrub never starts recording the ones above. Read the
93
+ built-ins with `defaultFilterHeaders()` and friends if you need them directly.
94
+
95
+ Filtering applies to every protocol: HTTP headers, query params, and bodies;
96
+ gRPC metadata and the `jsonDebug` payload; WebSocket handshake headers and
97
+ text/JSON frame bodies. Binary protobuf bodies are stored as-is - they cannot
98
+ be pattern-scrubbed.
99
+
100
+ ## Request matching
101
+
102
+ Defaults to method + URI:
103
+
104
+ ```ts
105
+ await useCassette("cassette.yaml", {
106
+ matchOn: ["method", "uri", "json_body"],
107
+ ignoreJsonPaths: ["requestId", "timestamp"],
108
+ }, async () => { ... });
109
+ ```
110
+
111
+ Available matchers: `method`, `uri`, `headers`, `body`, `json_body`. An unknown
112
+ matcher is rejected rather than silently matching everything.
113
+
114
+ ## Cassette format
115
+
116
+ YAML by default; use a `.toml` extension for TOML. TOML loads faster and
117
+ produces smaller files, but cannot store gRPC or WebSocket interactions.
118
+
119
+ ```ts
120
+ await useCassette("cassette.toml", async () => { ... });
121
+ ```
122
+
123
+ ## Cassette expiry
124
+
125
+ ```ts
126
+ await useCassette("cassette.yaml", {
127
+ maxAge: "30d",
128
+ onExpiry: "rerecord",
129
+ }, async () => { ... });
130
+ ```
131
+
132
+ `maxAge` accepts `"24h"`, `"7d"`, `"4w"`. `onExpiry` is `warn` (default),
133
+ `fail`, or `rerecord`.
134
+
135
+ ## Working with bodies
136
+
137
+ Bodies are `{ type, content }`, matching the cassette file. Binary content is
138
+ hex, so helpers are provided:
139
+
140
+ ```ts
141
+ import { bodyToBuffer, binaryBody, binaryBodyBytes } from "cassetter";
142
+
143
+ bodyToBuffer({ type: "text", content: "hi" });
144
+ binaryBody(Buffer.from("hi"));
145
+ binaryBodyBytes({ type: "binary", content: "6869" });
146
+ ```
147
+
148
+ ## Interception
149
+
150
+ The global `fetch` is intercepted, which covers `fetch`, `undici`, and
151
+ libraries built on them. `ignoreLocalhost: true` lets localhost traffic through
152
+ untouched.
153
+
154
+ ## Development
155
+
156
+ Requires a Rust toolchain and Node 18+.
157
+
158
+ ```bash
159
+ npm install
160
+ npm run build:native # compile the addon into native/
161
+ npm test
162
+ npm run typecheck
163
+ ```
164
+
165
+ `npm run build` does the native build plus `tsc`.
166
+
167
+ Changes to matching, security, the cassette format, or body handling belong in
168
+ `crates/cassetter-core` so the Python binding gets them too - see
169
+ [`conformance/`](../conformance) for the suite that enforces this.
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Loader and type surface for the native `cassetter-node` addon.
3
+ *
4
+ * Everything below is implemented in Rust by `cassetter-core` and shared with
5
+ * the Python binding. Structured values cross the boundary shaped exactly like
6
+ * the cassette file format.
7
+ */
8
+ import type { Body, GrpcInteraction, HttpInteraction, HttpRequest, MatchConfig, SecurityConfig, WsInteraction } from "./types.js";
9
+ export interface MatchHit<T> {
10
+ index: number;
11
+ interaction: T;
12
+ }
13
+ export interface NativeCassette {
14
+ readonly version: number;
15
+ readonly length: number;
16
+ save(path: string, order?: number[], mode?: number): void;
17
+ toYaml(order?: number[]): string;
18
+ toToml(order?: number[]): string;
19
+ outputOrder(sortConfig?: MatchConfig, recordOrder?: number[]): number[];
20
+ readonly interactions: HttpInteraction[];
21
+ readonly playedIndices: boolean[];
22
+ readonly unplayedCount: number;
23
+ addInteraction(interaction: HttpInteraction): void;
24
+ markPlayed(index: number): void;
25
+ /** Match and mark played in one atomic step. */
26
+ takeMatch(request: HttpRequest, config?: MatchConfig): MatchHit<HttpInteraction> | null;
27
+ readonly grpcInteractions: GrpcInteraction[];
28
+ readonly grpcPlayed: boolean[];
29
+ addGrpcInteraction(interaction: GrpcInteraction): void;
30
+ markGrpcPlayed(index: number): void;
31
+ takeGrpcMatch(method: string): MatchHit<GrpcInteraction> | null;
32
+ readonly wsInteractions: WsInteraction[];
33
+ readonly wsPlayed: boolean[];
34
+ addWsInteraction(interaction: WsInteraction): void;
35
+ markWsPlayed(index: number): void;
36
+ takeWsMatch(uri: string): MatchHit<WsInteraction> | null;
37
+ }
38
+ interface NativeBinding {
39
+ Cassette: {
40
+ new (): NativeCassette;
41
+ load(path: string): NativeCassette;
42
+ fromYaml(content: string): NativeCassette;
43
+ fromToml(content: string): NativeCassette;
44
+ };
45
+ processBody(rawBytes: Buffer, contentType?: string | null, contentEncoding?: string | null, maxDecompressed?: number | null): Body;
46
+ scrubInteraction(interaction: HttpInteraction, config?: SecurityConfig): HttpInteraction;
47
+ scrubGrpcInteraction(interaction: GrpcInteraction, config?: SecurityConfig): GrpcInteraction;
48
+ scrubWsInteraction(interaction: WsInteraction, config?: SecurityConfig): WsInteraction;
49
+ defaultFilterHeaders(): string[];
50
+ defaultFilterQueryParameters(): string[];
51
+ defaultBodyScrubPatterns(): string[];
52
+ defaultMatchOn(): string[];
53
+ knownMatchers(): string[];
54
+ defaultReplacement(): string;
55
+ formatVersion(): number;
56
+ }
57
+ export declare const native: NativeBinding;
58
+ export declare const processBody: (rawBytes: Buffer, contentType?: string | null, contentEncoding?: string | null, maxDecompressed?: number | null) => Body, scrubInteraction: (interaction: HttpInteraction, config?: SecurityConfig) => HttpInteraction, scrubGrpcInteraction: (interaction: GrpcInteraction, config?: SecurityConfig) => GrpcInteraction, scrubWsInteraction: (interaction: WsInteraction, config?: SecurityConfig) => WsInteraction, defaultFilterHeaders: () => string[], defaultFilterQueryParameters: () => string[], defaultBodyScrubPatterns: () => string[], defaultMatchOn: () => string[], knownMatchers: () => string[], defaultReplacement: () => string, formatVersion: () => number;
59
+ export {};
60
+ //# sourceMappingURL=binding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EACV,IAAI,EACJ,eAAe,EACf,eAAe,EACf,WAAW,EACX,WAAW,EACX,cAAc,EACd,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,CAAC,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1D,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IACjC,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IACjC,WAAW,CAAC,UAAU,CAAC,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IAExE,QAAQ,CAAC,YAAY,EAAE,eAAe,EAAE,CAAC;IACzC,QAAQ,CAAC,aAAa,EAAE,OAAO,EAAE,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,cAAc,CAAC,WAAW,EAAE,eAAe,GAAG,IAAI,CAAC;IACnD,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,gDAAgD;IAChD,SAAS,CACP,OAAO,EAAE,WAAW,EACpB,MAAM,CAAC,EAAE,WAAW,GACnB,QAAQ,CAAC,eAAe,CAAC,GAAG,IAAI,CAAC;IAEpC,QAAQ,CAAC,gBAAgB,EAAE,eAAe,EAAE,CAAC;IAC7C,QAAQ,CAAC,UAAU,EAAE,OAAO,EAAE,CAAC;IAC/B,kBAAkB,CAAC,WAAW,EAAE,eAAe,GAAG,IAAI,CAAC;IACvD,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC,eAAe,CAAC,GAAG,IAAI,CAAC;IAEhE,QAAQ,CAAC,cAAc,EAAE,aAAa,EAAE,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC;IAC7B,gBAAgB,CAAC,WAAW,EAAE,aAAa,GAAG,IAAI,CAAC;IACnD,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC;CAC1D;AAED,UAAU,aAAa;IACrB,QAAQ,EAAE;QACR,QAAQ,cAAc,CAAC;QACvB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAAC;QACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAAC;QAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAAC;KAC3C,CAAC;IACF,WAAW,CACT,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,EAC3B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,EAC/B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,GAC9B,IAAI,CAAC;IACR,gBAAgB,CACd,WAAW,EAAE,eAAe,EAC5B,MAAM,CAAC,EAAE,cAAc,GACtB,eAAe,CAAC;IACnB,oBAAoB,CAClB,WAAW,EAAE,eAAe,EAC5B,MAAM,CAAC,EAAE,cAAc,GACtB,eAAe,CAAC;IACnB,kBAAkB,CAChB,WAAW,EAAE,aAAa,EAC1B,MAAM,CAAC,EAAE,cAAc,GACtB,aAAa,CAAC;IACjB,oBAAoB,IAAI,MAAM,EAAE,CAAC;IACjC,4BAA4B,IAAI,MAAM,EAAE,CAAC;IACzC,wBAAwB,IAAI,MAAM,EAAE,CAAC;IACrC,cAAc,IAAI,MAAM,EAAE,CAAC;IAC3B,aAAa,IAAI,MAAM,EAAE,CAAC;IAC1B,kBAAkB,IAAI,MAAM,CAAC;IAC7B,aAAa,IAAI,MAAM,CAAC;CACzB;AAID,eAAO,MAAM,MAAM,EAAE,aAA8D,CAAC;AAEpF,eAAO,MACL,WAAW,aA/BC,MAAM,gBACF,MAAM,GAAG,IAAI,oBACT,MAAM,GAAG,IAAI,oBACb,MAAM,GAAG,IAAI,KAC9B,IAAI,EA4BP,gBAAgB,gBA1BD,eAAe,WACnB,cAAc,KACtB,eAAe,EAyBlB,oBAAoB,gBAvBL,eAAe,WACnB,cAAc,KACtB,eAAe,EAsBlB,kBAAkB,gBApBH,aAAa,WACjB,cAAc,KACtB,aAAa,EAmBhB,oBAAoB,QAlBI,MAAM,EAAE,EAmBhC,4BAA4B,QAlBI,MAAM,EAAE,EAmBxC,wBAAwB,QAlBI,MAAM,EAAE,EAmBpC,cAAc,QAlBI,MAAM,EAAE,EAmB1B,aAAa,QAlBI,MAAM,EAAE,EAmBzB,kBAAkB,QAlBI,MAAM,EAmB5B,aAAa,QAlBI,MAmBT,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Loader and type surface for the native `cassetter-node` addon.
3
+ *
4
+ * Everything below is implemented in Rust by `cassetter-core` and shared with
5
+ * the Python binding. Structured values cross the boundary shaped exactly like
6
+ * the cassette file format.
7
+ */
8
+ import { createRequire } from "node:module";
9
+ const require = createRequire(import.meta.url);
10
+ export const native = require("../native/index.js");
11
+ export const { processBody, scrubInteraction, scrubGrpcInteraction, scrubWsInteraction, defaultFilterHeaders, defaultFilterQueryParameters, defaultBodyScrubPatterns, defaultMatchOn, knownMatchers, defaultReplacement, formatVersion, } = native;
12
+ //# sourceMappingURL=binding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding.js","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAoF5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE/C,MAAM,CAAC,MAAM,MAAM,GAAkB,OAAO,CAAC,oBAAoB,CAAkB,CAAC;AAEpF,MAAM,CAAC,MAAM,EACX,WAAW,EACX,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,oBAAoB,EACpB,4BAA4B,EAC5B,wBAAwB,EACxB,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,aAAa,GACd,GAAG,MAAM,CAAC"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Record/replay orchestration over the native cassette core.
3
+ *
4
+ * The cassette format, request matching, security filtering, and body
5
+ * processing all live in Rust (`cassetter-core`). This file is the thin,
6
+ * idiomatic layer on top - the same role `src/cassetter/cassette.py` plays for
7
+ * the Python binding.
8
+ */
9
+ import { RecordMode } from "./recording.js";
10
+ import type { Body, GrpcInteraction, GrpcResponse, HeaderMap, HttpInteraction, HttpResponse, MatchConfig, SecurityConfig, WsFrame, WsInteraction } from "./types.js";
11
+ /** Raised when a cassette file is required but absent. */
12
+ export declare class CassetteNotFoundError extends Error {
13
+ /** Configure a cassette. Nothing is read until `load()`. */
14
+ constructor(message: string);
15
+ }
16
+ /** Raised when a cassette exists but cannot be parsed. */
17
+ export declare class CassetteLoadError extends Error {
18
+ /** Configure a cassette. Nothing is read until `load()`. */
19
+ constructor(message: string);
20
+ }
21
+ /** Raised when a cassette is older than `maxAge` and `onExpiry` is `fail`. */
22
+ export declare class CassetteExpiredError extends Error {
23
+ /** Configure a cassette. Nothing is read until `load()`. */
24
+ constructor(message: string);
25
+ }
26
+ /** Raised when no recorded interaction matches, and none may be recorded. */
27
+ export declare class NoMatchError extends Error {
28
+ /** Configure a cassette. Nothing is read until `load()`. */
29
+ constructor(message: string);
30
+ }
31
+ export interface CassetteOptions {
32
+ recordMode?: RecordMode;
33
+ matchConfig?: MatchConfig;
34
+ securityConfig?: SecurityConfig;
35
+ maxAge?: string;
36
+ onExpiry?: "warn" | "fail" | "rerecord";
37
+ ignoreLocalhost?: boolean;
38
+ }
39
+ export declare class Cassette {
40
+ private readonly _path;
41
+ private readonly _recordMode;
42
+ private readonly _matchConfig;
43
+ private readonly _securityConfig;
44
+ private readonly _maxAge;
45
+ private readonly _onExpiry;
46
+ private readonly _ignoreLocalhost;
47
+ private _inner;
48
+ private _dirty;
49
+ /** Order interactions were recorded in, to break ties in the output order. */
50
+ private _recordOrders;
51
+ /** Next position `reserveRecordOrder` will hand out. */
52
+ private _nextRecordOrder;
53
+ /** `once` replays without recording when the cassette already existed. */
54
+ private _onceReplayOnly;
55
+ /** Mode of the cassette `rewrite` deleted, to put back on its replacement. */
56
+ private _rewrittenFileMode;
57
+ /** Configure a cassette. Nothing is read until `load()`. */
58
+ constructor(path: string, options?: CassetteOptions);
59
+ /** Where this cassette is read from and written to. */
60
+ get path(): string;
61
+ /** The record mode in force. */
62
+ get recordMode(): RecordMode;
63
+ /** Whether localhost traffic bypasses the cassette entirely. */
64
+ get ignoreLocalhost(): boolean;
65
+ /** The recorded HTTP interactions, empty before `load()`. */
66
+ get interactions(): HttpInteraction[];
67
+ /** The recorded gRPC interactions, empty before `load()`. */
68
+ get grpcInteractions(): GrpcInteraction[];
69
+ /** The recorded WebSocket interactions, empty before `load()`. */
70
+ get wsInteractions(): WsInteraction[];
71
+ /** Whether this mode may replay an existing interaction. */
72
+ get canReplay(): boolean;
73
+ /** Whether an unmatched request may go to the network and be recorded. */
74
+ get canRecord(): boolean;
75
+ /** Load from disk, or start an empty cassette based on the record mode. */
76
+ load(): void;
77
+ /**
78
+ * Write to disk if modified.
79
+ *
80
+ * An empty cassette is written only when a file already exists, so a
81
+ * re-record that captured nothing truncates the stale file instead of
82
+ * leaving it behind. Interactions go out in a canonical order rather than
83
+ * the order their responses arrived in, so a concurrent suite produces the
84
+ * same file every run.
85
+ */
86
+ save(): void;
87
+ /** Serialize to YAML without touching the filesystem. */
88
+ toYaml(): string;
89
+ /** Serialize to TOML without touching the filesystem. */
90
+ toToml(): string;
91
+ /**
92
+ * Claim this interaction's position before its request is issued.
93
+ *
94
+ * Interceptors record once the response is back, so under concurrency the
95
+ * cassette would otherwise be written in whatever order responses arrived
96
+ * in - different on every run.
97
+ */
98
+ reserveRecordOrder(): number;
99
+ play(method: string, uri: string, headers: HeaderMap, body: Buffer | null): HttpResponse;
100
+ record(method: string, uri: string, requestHeaders: HeaderMap, requestBody: Buffer | null, status: number, responseHeaders: HeaderMap, responseBody: Buffer | null, order?: number): HttpResponse;
101
+ /** Replay a gRPC response for `method`, or throw `NoMatchError`. */
102
+ playGrpc(method: string): GrpcResponse;
103
+ recordGrpc(method: string, metadata: HeaderMap, requestBody: Body, responseBody: Body, options?: {
104
+ statusCode?: number;
105
+ statusMessage?: string;
106
+ responseMetadata?: HeaderMap;
107
+ jsonDebug?: unknown;
108
+ }): GrpcResponse;
109
+ /** Replay a WebSocket interaction for `uri`, or throw `NoMatchError`. */
110
+ playWs(uri: string): WsInteraction;
111
+ /** Record a WebSocket connection and its frames, scrubbed. */
112
+ recordWs(uri: string, headers: HeaderMap, frames: WsFrame[]): void;
113
+ /** The native cassette, created on first use if `load()` never ran. */
114
+ private _ensureInner;
115
+ /** Apply `onExpiry` when the newest recording predates `maxAge`. */
116
+ private _checkExpiry;
117
+ /** The most recent `recordedAt` across every protocol, if any. */
118
+ private _newestRecordedAt;
119
+ }
120
+ /** Case-insensitive header lookup returning the first value. */
121
+ export declare function getHeader(headers: HeaderMap, name: string): string | null;
122
+ //# sourceMappingURL=cassette.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cassette.d.ts","sourceRoot":"","sources":["../src/cassette.ts"],"names":[],"mappings":"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"}