@loopingai/core 0.2.0 → 0.3.1

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 CHANGED
@@ -198,7 +198,7 @@ not drag in the A2A adapter, and the test harness cannot reach a production bund
198
198
  | `@loopingai/core/subagent` | `RecipeSubagentBase`, resumable runs, fingerprinting, workspace |
199
199
  | `@loopingai/core/db` | `AgentDB`, `notify_tasks` + `subtasks` schema, migrations, `PluginStore` |
200
200
  | `@loopingai/core/testing` | VCR, `FakeSession`, `mockModel`, DO helpers, JWK fixtures — _workerd realm_ |
201
- | `@loopingai/core/testing/node` | the VCR recorder — _Node realm, never import from a spec_ |
201
+ | `@loopingai/core/testing/node` | the VCR recorder + cassette store — _Node realm, never import from a spec_ |
202
202
  | `@loopingai/core/eslint` | the `no-deprecated-object-properties` rule |
203
203
 
204
204
  `/testing*` and `/eslint` are structurally incapable of entering a runtime graph, and
@@ -363,8 +363,27 @@ await withDb("accepts a turn once", async (db) => {
363
363
  ```
364
364
 
365
365
  - **VCR** — record/replay real HTTP against on-disk cassettes, split across the Node
366
- and workerd realms because specs run in workerd, which has no filesystem. Point
367
- vitest's `globalSetup` at `@loopingai/core/testing/vcr-global-setup`.
366
+ and workerd realms because specs run in workerd, which has no filesystem. The
367
+ recorder is a Miniflare `outboundService`, so it works on any
368
+ `@cloudflare/vitest-pool-workers` from 0.18 up and needs no `undici`:
369
+
370
+ ```ts
371
+ // vitest.config.ts
372
+ const vcr = createVcr({
373
+ snapshotsDir: path.resolve(import.meta.dirname, "test/snapshots"),
374
+ record: recordFromEnv(), // RECORD=1
375
+ excludeHeaders: ["authorization", "x-api-key"] // never written to a cassette
376
+ });
377
+
378
+ cloudflareTest({ miniflare: { outboundService: vcr.outboundService } });
379
+ ```
380
+
381
+ Then `setupRecording()` at the top of a spec gives every `it` its own cassette,
382
+ auto-named from the file + describe + test names. Cassettes match on method, URL
383
+ and body — never on headers, so a runtime upgrade cannot invalidate them — and a
384
+ request with no active cassette is blocked rather than reaching the network.
385
+ Point vitest's `globalSetup` at `@loopingai/core/testing/vcr-global-setup`.
386
+
368
387
  - **Fakes** — `FakeSession` (a `SessionLike` reference implementation) and `mockModel`
369
388
  (a scripted `LanguageModel`), so a loop can be driven with no model call at all.
370
389
  - **Fixtures** — Ed25519 keypairs and a gateway-JWT signer, so the zero-trust path is
@@ -7,10 +7,10 @@
7
7
  * does not exist in a deployed Worker.
8
8
  *
9
9
  * **This barrel is the workerd half** — safe to import from a spec. The VCR
10
- * recorder needs `undici` and `node:fs`, so it lives behind
11
- * `@loopingai/core/testing/node` and must not be re-exported here: pulling it into
12
- * this graph would drag Node builtins into every spec that wanted a fixture.
13
- * `vcr-shared.ts` is the seam both realms may load.
10
+ * recorder needs `node:fs`, so it lives behind `@loopingai/core/testing/node`
11
+ * and must not be re-exported here: pulling it into this graph would drag Node
12
+ * builtins into every spec that wanted a fixture. `vcr-shared.ts` is the seam
13
+ * both realms may load.
14
14
  *
15
15
  * Three things live here:
16
16
  *
@@ -21,8 +21,8 @@
21
21
  * - **Fixtures** — Ed25519 keypairs and a gateway-JWT signer, so the zero-trust
22
22
  * path can be exercised end to end without a real gateway.
23
23
  */
24
- export { setupRecording, cassetteNameFor } from "./vcr-spec.js";
25
- export { VCR_CONTROL_ORIGIN, CASSETTE_NAME_RE } from "./vcr-shared.js";
24
+ export { setupRecording, cassetteNameFor, type SetupRecordingOptions } from "./vcr-spec.js";
25
+ export { VCR_CONTROL_ORIGIN, VCR_MARKER_HEADER, CASSETTE_NAME_RE, type VcrReleaseResult } from "./vcr-shared.js";
26
26
  export { FakeSession } from "./fake-session.js";
27
27
  export { mockModel, finalReply, type MockStep } from "./mock-model.js";
28
28
  export { makeGatewayToken, type GatewayTokenOptions } from "./auth.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEvE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEvE,OAAO,EAAE,gBAAgB,EAAE,KAAK,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EACL,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,wBAAwB,EACxB,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,QAAQ,EACT,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EACL,cAAc,EACd,eAAe,EACf,KAAK,qBAAqB,EAC3B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,gBAAgB,EACtB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEvE,OAAO,EAAE,gBAAgB,EAAE,KAAK,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EACL,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,wBAAwB,EACxB,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,QAAQ,EACT,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC"}
@@ -7,10 +7,10 @@
7
7
  * does not exist in a deployed Worker.
8
8
  *
9
9
  * **This barrel is the workerd half** — safe to import from a spec. The VCR
10
- * recorder needs `undici` and `node:fs`, so it lives behind
11
- * `@loopingai/core/testing/node` and must not be re-exported here: pulling it into
12
- * this graph would drag Node builtins into every spec that wanted a fixture.
13
- * `vcr-shared.ts` is the seam both realms may load.
10
+ * recorder needs `node:fs`, so it lives behind `@loopingai/core/testing/node`
11
+ * and must not be re-exported here: pulling it into this graph would drag Node
12
+ * builtins into every spec that wanted a fixture. `vcr-shared.ts` is the seam
13
+ * both realms may load.
14
14
  *
15
15
  * Three things live here:
16
16
  *
@@ -22,7 +22,7 @@
22
22
  * path can be exercised end to end without a real gateway.
23
23
  */
24
24
  export { setupRecording, cassetteNameFor } from "./vcr-spec.js";
25
- export { VCR_CONTROL_ORIGIN, CASSETTE_NAME_RE } from "./vcr-shared.js";
25
+ export { VCR_CONTROL_ORIGIN, VCR_MARKER_HEADER, CASSETTE_NAME_RE } from "./vcr-shared.js";
26
26
  export { FakeSession } from "./fake-session.js";
27
27
  export { mockModel, finalReply } from "./mock-model.js";
28
28
  export { makeGatewayToken } from "./auth.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEvE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAiB,MAAM,iBAAiB,CAAC;AAEvE,OAAO,EAAE,gBAAgB,EAA4B,MAAM,WAAW,CAAC;AACvE,OAAO,EACL,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,wBAAwB,EACxB,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,QAAQ,EACT,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAsB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EACL,cAAc,EACd,eAAe,EAEhB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAiB,MAAM,iBAAiB,CAAC;AAEvE,OAAO,EAAE,gBAAgB,EAA4B,MAAM,WAAW,CAAC;AACvE,OAAO,EACL,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,wBAAwB,EACxB,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,QAAQ,EACT,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAsB,MAAM,SAAS,CAAC"}
@@ -2,46 +2,56 @@
2
2
  * `@loopingai/core/testing/node` — the **Node-realm** half of the VCR harness.
3
3
  *
4
4
  * Separate from `@loopingai/core/testing` because the two halves cannot share a
5
- * module graph. This side imports `undici` and `node:fs`; the other side runs
6
- * inside workerd, which has neither. `vcr-shared.ts` is the only thing both may
7
- * touch, and it is deliberately dependency-free so it can load in either realm.
5
+ * module graph. This side reaches `node:fs`; the other side runs inside workerd,
6
+ * which has no filesystem. `vcr-shared.ts` is the only thing both may touch, and
7
+ * it is deliberately dependency-free so it can load in either realm.
8
8
  *
9
9
  * Import this from a Vitest **config** (or anything else running in Node), never
10
10
  * from a spec:
11
11
  *
12
12
  * ```ts
13
13
  * // vitest.config.ts
14
- * import { createVcrAgent } from "@loopingai/core/testing/node";
14
+ * import { defineConfig } from "vitest/config";
15
+ * import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
16
+ * import path from "node:path";
17
+ * import { createVcr, recordFromEnv } from "@loopingai/core/testing/node";
18
+ *
19
+ * const vcr = createVcr({
20
+ * snapshotsDir: path.resolve(import.meta.dirname, "test/snapshots"),
21
+ * record: recordFromEnv(),
22
+ * // Why a committed cassette is safe, and why playback needs no credentials.
23
+ * excludeHeaders: ["x-api-key", "authorization", "cookie", "set-cookie"]
24
+ * });
15
25
  *
16
26
  * export default defineConfig({
17
- * plugins: [cloudflareTest({ miniflare: { fetchMock: createVcrAgent({ … }) } })],
27
+ * plugins: [
28
+ * cloudflareTest({
29
+ * wrangler: { configPath: "./wrangler.jsonc" },
30
+ * miniflare: { outboundService: vcr.outboundService }
31
+ * })
32
+ * ],
18
33
  * test: { globalSetup: ["@loopingai/core/testing/vcr-global-setup"] }
19
34
  * });
20
35
  * ```
21
36
  *
22
- * ## Two version constraints, both of which fail unreadably
23
- *
24
- * **1. `@cloudflare/vitest-pool-workers` must be `^0.18`.** The recorder is
25
- * installed as Miniflare's `fetchMock`, and that option **does not exist in
26
- * Miniflare 5**, which pool `0.19`+ depends on — only `outboundService` remains.
27
- * On a newer pool the option is silently ignored rather than rejected, so
28
- * `disableNetConnect()` never takes effect, every request goes to the real
29
- * network, and each one fails as `internal error; reference = …` naming nothing.
30
- * That is why the peer range is pinned rather than open.
31
- *
32
- * **2. Your `undici` must be the copy Miniflare uses.** `fetchMock` is an undici
33
- * `MockAgent` and Miniflare validates it against *its own* undici, so two copies
34
- * in one `node_modules` fail with `Input not instance of MockAgent`. Miniflare
35
- * pins an exact version (7.28.0 at the time of writing) and this package's
36
- * `undici` peer range tracks it. Check with
37
- * `ls node_modules/miniflare/node_modules/undici` anything there means a
38
- * second copy, and the fix is to align yours rather than to debug the message.
39
- *
40
- * For the common case — wiring cassette flush/teardown — you do not need this
41
- * subpath at all. Point `globalSetup` at
42
- * `@loopingai/core/testing/vcr-global-setup`, which is this module's `setup`/
43
- * `teardown` pair already packaged for Vitest.
37
+ * `RECORD=1` captures live traffic into every cassette a run activates; anything
38
+ * else replays. A host with no active cassette is **blocked**, so a test that
39
+ * was never wired for recording cannot reach the network by accident.
40
+ *
41
+ * ## Version constraints: there are none
42
+ *
43
+ * This used to carry two, both of which failed unreadably, and both of which
44
+ * were consequences of installing the recorder as Miniflare's `fetchMock`:
45
+ * the pool had to be `^0.18` (Miniflare 5 dropped the option, and pool 0.20
46
+ * removed it from its overrides), and your `undici` had to be byte-identical to
47
+ * Miniflare's (`fetchMock` was validated with `instanceof MockAgent`).
48
+ *
49
+ * The recorder is now a plain `outboundService` function the hook `fetchMock`
50
+ * was sugar over in the first place. It is present and identical in Miniflare 4
51
+ * and 5, it type-checks against both, and it involves no `undici` at all. Core
52
+ * declares no `undici` peer and the pool peer is open.
44
53
  */
45
- export { createVcrAgent, closeVcr, VcrAgent, type CreateVcrAgentOptions } from "./vcr.js";
46
- export { VCR_CONTROL_ORIGIN, CASSETTE_NAME_RE } from "./vcr-shared.js";
54
+ export { createVcr, recordFromEnv, closeVcr, type Vcr, type VcrOptions, type VcrRequest, type VcrRequestHeaders, type VcrOutboundService } from "./vcr.js";
55
+ export { Cassette, requestKey, replayable, type CassetteEntry, type CassetteOptions, type RecordedRequest, type RecordedResponse, type ReplayableResponse } from "./vcr-store.js";
56
+ export { VCR_CONTROL_ORIGIN, VCR_MARKER_HEADER, CASSETTE_NAME_RE, type VcrReleaseResult } from "./vcr-shared.js";
47
57
  //# sourceMappingURL=node.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/testing/node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,EACL,cAAc,EACd,QAAQ,EACR,QAAQ,EACR,KAAK,qBAAqB,EAC3B,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/testing/node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AAEH,OAAO,EACL,SAAS,EACT,aAAa,EACb,QAAQ,EACR,KAAK,GAAG,EACR,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACxB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,QAAQ,EACR,UAAU,EACV,UAAU,EACV,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,gBAAgB,EACtB,MAAM,iBAAiB,CAAC"}
@@ -2,46 +2,56 @@
2
2
  * `@loopingai/core/testing/node` — the **Node-realm** half of the VCR harness.
3
3
  *
4
4
  * Separate from `@loopingai/core/testing` because the two halves cannot share a
5
- * module graph. This side imports `undici` and `node:fs`; the other side runs
6
- * inside workerd, which has neither. `vcr-shared.ts` is the only thing both may
7
- * touch, and it is deliberately dependency-free so it can load in either realm.
5
+ * module graph. This side reaches `node:fs`; the other side runs inside workerd,
6
+ * which has no filesystem. `vcr-shared.ts` is the only thing both may touch, and
7
+ * it is deliberately dependency-free so it can load in either realm.
8
8
  *
9
9
  * Import this from a Vitest **config** (or anything else running in Node), never
10
10
  * from a spec:
11
11
  *
12
12
  * ```ts
13
13
  * // vitest.config.ts
14
- * import { createVcrAgent } from "@loopingai/core/testing/node";
14
+ * import { defineConfig } from "vitest/config";
15
+ * import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
16
+ * import path from "node:path";
17
+ * import { createVcr, recordFromEnv } from "@loopingai/core/testing/node";
18
+ *
19
+ * const vcr = createVcr({
20
+ * snapshotsDir: path.resolve(import.meta.dirname, "test/snapshots"),
21
+ * record: recordFromEnv(),
22
+ * // Why a committed cassette is safe, and why playback needs no credentials.
23
+ * excludeHeaders: ["x-api-key", "authorization", "cookie", "set-cookie"]
24
+ * });
15
25
  *
16
26
  * export default defineConfig({
17
- * plugins: [cloudflareTest({ miniflare: { fetchMock: createVcrAgent({ … }) } })],
27
+ * plugins: [
28
+ * cloudflareTest({
29
+ * wrangler: { configPath: "./wrangler.jsonc" },
30
+ * miniflare: { outboundService: vcr.outboundService }
31
+ * })
32
+ * ],
18
33
  * test: { globalSetup: ["@loopingai/core/testing/vcr-global-setup"] }
19
34
  * });
20
35
  * ```
21
36
  *
22
- * ## Two version constraints, both of which fail unreadably
23
- *
24
- * **1. `@cloudflare/vitest-pool-workers` must be `^0.18`.** The recorder is
25
- * installed as Miniflare's `fetchMock`, and that option **does not exist in
26
- * Miniflare 5**, which pool `0.19`+ depends on — only `outboundService` remains.
27
- * On a newer pool the option is silently ignored rather than rejected, so
28
- * `disableNetConnect()` never takes effect, every request goes to the real
29
- * network, and each one fails as `internal error; reference = …` naming nothing.
30
- * That is why the peer range is pinned rather than open.
31
- *
32
- * **2. Your `undici` must be the copy Miniflare uses.** `fetchMock` is an undici
33
- * `MockAgent` and Miniflare validates it against *its own* undici, so two copies
34
- * in one `node_modules` fail with `Input not instance of MockAgent`. Miniflare
35
- * pins an exact version (7.28.0 at the time of writing) and this package's
36
- * `undici` peer range tracks it. Check with
37
- * `ls node_modules/miniflare/node_modules/undici` anything there means a
38
- * second copy, and the fix is to align yours rather than to debug the message.
39
- *
40
- * For the common case — wiring cassette flush/teardown — you do not need this
41
- * subpath at all. Point `globalSetup` at
42
- * `@loopingai/core/testing/vcr-global-setup`, which is this module's `setup`/
43
- * `teardown` pair already packaged for Vitest.
37
+ * `RECORD=1` captures live traffic into every cassette a run activates; anything
38
+ * else replays. A host with no active cassette is **blocked**, so a test that
39
+ * was never wired for recording cannot reach the network by accident.
40
+ *
41
+ * ## Version constraints: there are none
42
+ *
43
+ * This used to carry two, both of which failed unreadably, and both of which
44
+ * were consequences of installing the recorder as Miniflare's `fetchMock`:
45
+ * the pool had to be `^0.18` (Miniflare 5 dropped the option, and pool 0.20
46
+ * removed it from its overrides), and your `undici` had to be byte-identical to
47
+ * Miniflare's (`fetchMock` was validated with `instanceof MockAgent`).
48
+ *
49
+ * The recorder is now a plain `outboundService` function the hook `fetchMock`
50
+ * was sugar over in the first place. It is present and identical in Miniflare 4
51
+ * and 5, it type-checks against both, and it involves no `undici` at all. Core
52
+ * declares no `undici` peer and the pool peer is open.
44
53
  */
45
- export { createVcrAgent, closeVcr, VcrAgent } from "./vcr.js";
46
- export { VCR_CONTROL_ORIGIN, CASSETTE_NAME_RE } from "./vcr-shared.js";
54
+ export { createVcr, recordFromEnv, closeVcr } from "./vcr.js";
55
+ export { Cassette, requestKey, replayable } from "./vcr-store.js";
56
+ export { VCR_CONTROL_ORIGIN, VCR_MARKER_HEADER, CASSETTE_NAME_RE } from "./vcr-shared.js";
47
57
  //# sourceMappingURL=node.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/testing/node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,EACL,cAAc,EACd,QAAQ,EACR,QAAQ,EAET,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/testing/node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AAEH,OAAO,EACL,SAAS,EACT,aAAa,EACb,QAAQ,EAMT,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,QAAQ,EACR,UAAU,EACV,UAAU,EAMX,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,iBAAiB,CAAC"}
@@ -1,3 +1,15 @@
1
+ /**
2
+ * Vitest `globalSetup`: the exported teardown runs after all tests but before
3
+ * Vite tears down its server, so it is the last point at which a cassette can
4
+ * still be written.
5
+ *
6
+ * Belt and braces rather than load-bearing — each cassette is already flushed
7
+ * when its test releases it, so a run that ends normally has nothing left to do
8
+ * here, and a run that dies mid-test keeps everything recorded up to that point.
9
+ * (Under the old undici recorder this step was mandatory: it held real sockets
10
+ * and a self-refreshing auto-flush timer open, and a `RECORD=1` run hung on
11
+ * "close timed out" without it. Neither exists now.)
12
+ */
1
13
  export declare function setup(): void;
2
14
  export declare function teardown(): Promise<void>;
3
15
  //# sourceMappingURL=vcr-global-setup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vcr-global-setup.d.ts","sourceRoot":"","sources":["../../src/testing/vcr-global-setup.ts"],"names":[],"mappings":"AAKA,wBAAgB,KAAK,IAAI,IAAI,CAAG;AAEhC,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAE9C"}
1
+ {"version":3,"file":"vcr-global-setup.d.ts","sourceRoot":"","sources":["../../src/testing/vcr-global-setup.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,wBAAgB,KAAK,IAAI,IAAI,CAAG;AAEhC,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAE9C"}
@@ -1,7 +1,16 @@
1
1
  import { closeVcr } from "./vcr.js";
2
- // Vitest globalSetup: the exported teardown runs after all tests but before Vite
3
- // tears down its server, so closing the VCR agent here (flush cassette, stop the
4
- // recorder timer, close real sockets) prevents the record-run hang.
2
+ /**
3
+ * Vitest `globalSetup`: the exported teardown runs after all tests but before
4
+ * Vite tears down its server, so it is the last point at which a cassette can
5
+ * still be written.
6
+ *
7
+ * Belt and braces rather than load-bearing — each cassette is already flushed
8
+ * when its test releases it, so a run that ends normally has nothing left to do
9
+ * here, and a run that dies mid-test keeps everything recorded up to that point.
10
+ * (Under the old undici recorder this step was mandatory: it held real sockets
11
+ * and a self-refreshing auto-flush timer open, and a `RECORD=1` run hung on
12
+ * "close timed out" without it. Neither exists now.)
13
+ */
5
14
  export function setup() { }
6
15
  export async function teardown() {
7
16
  await closeVcr();
@@ -1 +1 @@
1
- {"version":3,"file":"vcr-global-setup.js","sourceRoot":"","sources":["../../src/testing/vcr-global-setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpC,iFAAiF;AACjF,iFAAiF;AACjF,oEAAoE;AACpE,MAAM,UAAU,KAAK,KAAU,CAAC;AAEhC,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,MAAM,QAAQ,EAAE,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"vcr-global-setup.js","sourceRoot":"","sources":["../../src/testing/vcr-global-setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,KAAK,KAAU,CAAC;AAEhC,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,MAAM,QAAQ,EAAE,CAAC;AACnB,CAAC"}
@@ -1,16 +1,34 @@
1
1
  /**
2
2
  * Constants shared between the two sides of the VCR harness that must NOT import
3
- * each other: the Node-side agent (`vcr.ts`, imports `undici`) and the
4
- * worker-side spec helper (`vcr-spec.ts`, runs in workerd). Keep this file
5
- * dependency-free so both realms can load it.
3
+ * each other: the Node-side recorder (`vcr.ts` / `vcr-store.ts`, which reach
4
+ * `node:fs`) and the worker-side spec helper (`vcr-spec.ts`, which runs in
5
+ * workerd). Keep this file dependency-free so both realms can load it.
6
6
  */
7
7
  /**
8
8
  * Reserved origin for the in-band control channel. A recorded spec `fetch`es
9
- * here (via the single Miniflare `fetchMock`) to tell the Node-side agent which
10
- * cassette is active for the current test see `setupRecording` (vcr-spec.ts)
11
- * and `VcrAgent` (vcr.ts). Never a real network host.
9
+ * here through the same Miniflare `outboundService` every worker fetch already
10
+ * flows through to tell the Node-side recorder which cassette is active for
11
+ * the current test. See `setupRecording` (vcr-spec.ts) and `createVcr` (vcr.ts).
12
+ * Never a real network host.
12
13
  */
13
14
  export declare const VCR_CONTROL_ORIGIN = "https://vcr.internal";
15
+ /**
16
+ * Stamped on every response the recorder produces.
17
+ *
18
+ * The point is to prove the recorder is *installed*. A Miniflare option that no
19
+ * longer exists is silently ignored rather than rejected — which is exactly how
20
+ * a harness still wired to `fetchMock` on pool 0.20 failed: nothing intercepted
21
+ * anything, every request escaped to the real network, and each one died as
22
+ * `internal error; reference = …` naming nothing. Checking for this header on
23
+ * the control response turns that whole class of failure into one named error
24
+ * before any test runs.
25
+ */
26
+ export declare const VCR_MARKER_HEADER = "x-vcr";
27
+ /** Body of a successful `POST /release`: what the recorder could not serve. */
28
+ export interface VcrReleaseResult {
29
+ /** `"<METHOD> <url>"` for each blocked or unmatched request in the test. */
30
+ misses: string[];
31
+ }
14
32
  /**
15
33
  * Cassette filenames are derived from kebab-cased file + describe + test names
16
34
  * (see `cassetteNameFor`), so they are always lowercase `a-z0-9-` plus the
@@ -1 +1 @@
1
- {"version":3,"file":"vcr-shared.d.ts","sourceRoot":"","sources":["../../src/testing/vcr-shared.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,yBAAyB,CAAC;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,QAAiC,CAAC"}
1
+ {"version":3,"file":"vcr-shared.d.ts","sourceRoot":"","sources":["../../src/testing/vcr-shared.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,yBAAyB,CAAC;AAEzD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,UAAU,CAAC;AAEzC,+EAA+E;AAC/E,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,QAAiC,CAAC"}
@@ -1,16 +1,29 @@
1
1
  /**
2
2
  * Constants shared between the two sides of the VCR harness that must NOT import
3
- * each other: the Node-side agent (`vcr.ts`, imports `undici`) and the
4
- * worker-side spec helper (`vcr-spec.ts`, runs in workerd). Keep this file
5
- * dependency-free so both realms can load it.
3
+ * each other: the Node-side recorder (`vcr.ts` / `vcr-store.ts`, which reach
4
+ * `node:fs`) and the worker-side spec helper (`vcr-spec.ts`, which runs in
5
+ * workerd). Keep this file dependency-free so both realms can load it.
6
6
  */
7
7
  /**
8
8
  * Reserved origin for the in-band control channel. A recorded spec `fetch`es
9
- * here (via the single Miniflare `fetchMock`) to tell the Node-side agent which
10
- * cassette is active for the current test see `setupRecording` (vcr-spec.ts)
11
- * and `VcrAgent` (vcr.ts). Never a real network host.
9
+ * here through the same Miniflare `outboundService` every worker fetch already
10
+ * flows through to tell the Node-side recorder which cassette is active for
11
+ * the current test. See `setupRecording` (vcr-spec.ts) and `createVcr` (vcr.ts).
12
+ * Never a real network host.
12
13
  */
13
14
  export const VCR_CONTROL_ORIGIN = "https://vcr.internal";
15
+ /**
16
+ * Stamped on every response the recorder produces.
17
+ *
18
+ * The point is to prove the recorder is *installed*. A Miniflare option that no
19
+ * longer exists is silently ignored rather than rejected — which is exactly how
20
+ * a harness still wired to `fetchMock` on pool 0.20 failed: nothing intercepted
21
+ * anything, every request escaped to the real network, and each one died as
22
+ * `internal error; reference = …` naming nothing. Checking for this header on
23
+ * the control response turns that whole class of failure into one named error
24
+ * before any test runs.
25
+ */
26
+ export const VCR_MARKER_HEADER = "x-vcr";
14
27
  /**
15
28
  * Cassette filenames are derived from kebab-cased file + describe + test names
16
29
  * (see `cassetteNameFor`), so they are always lowercase `a-z0-9-` plus the
@@ -1 +1 @@
1
- {"version":3,"file":"vcr-shared.js","sourceRoot":"","sources":["../../src/testing/vcr-shared.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,sBAAsB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,8BAA8B,CAAC"}
1
+ {"version":3,"file":"vcr-shared.js","sourceRoot":"","sources":["../../src/testing/vcr-shared.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,sBAAsB,CAAC;AAEzD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAC;AAQzC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,8BAA8B,CAAC"}
@@ -7,11 +7,20 @@ import type { RunnerTestCase } from "vitest";
7
7
  * Exported for debugging / the cassette-rename step.
8
8
  */
9
9
  export declare function cassetteNameFor(task: RunnerTestCase): string;
10
+ export interface SetupRecordingOptions {
11
+ /**
12
+ * Let a test finish even though the recorder could not serve one of its
13
+ * requests. Only for a spec that deliberately exercises a failing `fetch`;
14
+ * the default is to fail, because a silent miss reads as a broken assertion
15
+ * several frames from its cause.
16
+ */
17
+ allowMisses?: boolean;
18
+ }
10
19
  /**
11
20
  * Call once at the top of a recorded spec file (outside any `describe`). Adds
12
21
  * per-test hooks that activate the test's cassette before it runs and release
13
22
  * it after. A missing cassette **fails** the test with instructions to record
14
23
  * (never skips — CI must go red so the gap is visible).
15
24
  */
16
- export declare function setupRecording(): void;
25
+ export declare function setupRecording(options?: SetupRecordingOptions): void;
17
26
  //# sourceMappingURL=vcr-spec.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vcr-spec.d.ts","sourceRoot":"","sources":["../../src/testing/vcr-spec.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAmB,MAAM,QAAQ,CAAC;AAkD9D;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAW5D;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,IAAI,IAAI,CA8BrC"}
1
+ {"version":3,"file":"vcr-spec.d.ts","sourceRoot":"","sources":["../../src/testing/vcr-spec.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAmB,MAAM,QAAQ,CAAC;AAsD9D;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAW5D;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAgBD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,qBAA0B,GAAG,IAAI,CAkDxE"}
@@ -1,15 +1,15 @@
1
1
  import { beforeEach, afterEach } from "vitest";
2
- import { VCR_CONTROL_ORIGIN } from "./vcr-shared.js";
2
+ import { VCR_CONTROL_ORIGIN, VCR_MARKER_HEADER } from "./vcr-shared.js";
3
3
  /**
4
4
  * Worker-side half of the VCR harness. `setupRecording()` is the *only* thing a
5
5
  * recorded spec wires up: one call at the top of the file gives every `it` its
6
6
  * own cassette, auto-named from the file + describe + test names, recorded under
7
7
  * `RECORD=1` and replayed otherwise. No per-recipe config, no registry.
8
8
  *
9
- * How it reaches the Node-side agent: specs run in workerd (no filesystem), so
10
- * the active cassette is announced over an in-band control channel — a `fetch`
11
- * to {@link VCR_CONTROL_ORIGIN} that `VcrAgent` (test/helpers/vcr.ts) answers
12
- * from the same Miniflare `fetchMock` every worker fetch already flows through.
9
+ * How it reaches the Node-side recorder: specs run in workerd (no filesystem),
10
+ * so the active cassette is announced over an in-band control channel — a
11
+ * `fetch` to {@link VCR_CONTROL_ORIGIN} that `createVcr` (vcr.ts) answers from
12
+ * the same Miniflare `outboundService` every worker fetch already flows through.
13
13
  */
14
14
  const kebab = (s) => s
15
15
  .toLowerCase()
@@ -60,18 +60,35 @@ export function cassetteNameFor(task) {
60
60
  }
61
61
  return [rel, ...suites, task.name].map(kebab).join("--") + ".snapshot.json";
62
62
  }
63
+ /**
64
+ * The recorder answers every control request with {@link VCR_MARKER_HEADER}, so
65
+ * its absence — or a `fetch` that rejects outright, which is what a request to
66
+ * a host that does not resolve does — means nothing is intercepting outbound
67
+ * traffic at all.
68
+ */
69
+ const NOT_INSTALLED = `The VCR recorder is not installed, so ${VCR_CONTROL_ORIGIN} went to the ` +
70
+ `real network. Wire it into your vitest config:\n\n` +
71
+ ` const vcr = createVcr({ snapshotsDir, record: recordFromEnv() });\n` +
72
+ ` cloudflareTest({ miniflare: { outboundService: vcr.outboundService } })\n\n` +
73
+ `(\`fetchMock\` is not it: pool 0.20 removed the option, and an unknown key ` +
74
+ `in \`miniflare\` is ignored rather than rejected.)`;
63
75
  /**
64
76
  * Call once at the top of a recorded spec file (outside any `describe`). Adds
65
77
  * per-test hooks that activate the test's cassette before it runs and release
66
78
  * it after. A missing cassette **fails** the test with instructions to record
67
79
  * (never skips — CI must go red so the gap is visible).
68
80
  */
69
- export function setupRecording() {
81
+ export function setupRecording(options = {}) {
70
82
  beforeEach(async (ctx) => {
71
83
  const cassette = cassetteNameFor(ctx.task);
72
84
  const res = await fetch(`${VCR_CONTROL_ORIGIN}/use?cassette=${cassette}`, {
73
85
  method: "POST"
86
+ }).catch(() => {
87
+ throw new Error(NOT_INSTALLED);
74
88
  });
89
+ if (res.headers.get(VCR_MARKER_HEADER) === null) {
90
+ throw new Error(NOT_INSTALLED);
91
+ }
75
92
  if (res.status === 404) {
76
93
  throw new Error(`No VCR cassette "${cassette}". Record it with \`RECORD=1\` ` +
77
94
  `(add \`-t "${ctx.task.name}"\` to record only this test), which needs ` +
@@ -79,7 +96,7 @@ export function setupRecording() {
79
96
  }
80
97
  if (res.status === 409) {
81
98
  throw new Error(`VCR cassette "${cassette}" could not activate: another recorded test is ` +
82
- `already using the shared agent. Recorded specs must not run in ` +
99
+ `already using the shared recorder. Recorded specs must not run in ` +
83
100
  `parallel — keep them sequential.`);
84
101
  }
85
102
  if (!res.ok) {
@@ -87,7 +104,22 @@ export function setupRecording() {
87
104
  }
88
105
  });
89
106
  afterEach(async () => {
90
- await fetch(`${VCR_CONTROL_ORIGIN}/release`, { method: "POST" });
107
+ const res = await fetch(`${VCR_CONTROL_ORIGIN}/release`, {
108
+ method: "POST"
109
+ });
110
+ if (options.allowMisses === true || !res.ok)
111
+ return;
112
+ const { misses } = (await res.json());
113
+ if (misses.length === 0)
114
+ return;
115
+ // A miss cannot reject the Worker's own `fetch()` — Miniflare turns anything
116
+ // an outbound service throws into a 500 — so the request under test sees a
117
+ // 500 body and fails somewhere downstream, if at all. Reporting it here is
118
+ // what makes the *cause* the failure.
119
+ throw new Error(`VCR could not serve ${misses.length} request(s) in this test:\n` +
120
+ misses.map((m) => ` • ${m}`).join("\n") +
121
+ `\nRe-record with \`RECORD=1\`, or pass ` +
122
+ `\`setupRecording({ allowMisses: true })\` if the failure is the point.`);
91
123
  });
92
124
  }
93
125
  //# sourceMappingURL=vcr-spec.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"vcr-spec.js","sourceRoot":"","sources":["../../src/testing/vcr-spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAE/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD;;;;;;;;;;GAUG;AAEH,MAAM,KAAK,GAAG,CAAC,CAAS,EAAU,EAAE,CAClC,CAAC;KACE,WAAW,EAAE;KACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;KAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AAE7B,kFAAkF;AAClF,SAAS,UAAU,CAAC,KAAsB;IACxC,OAAO,OAAQ,KAA+B,CAAC,QAAQ,KAAK,QAAQ,CAAC;AACvE,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAS,gBAAgB,CAAC,IAA4B;IACpD,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IACxE,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC;AAC7D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,IAAoB;IAClD,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAEnE,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,KAAK,GAAgC,IAAI,CAAC,KAAK,CAAC;IACpD,OAAO,KAAK,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IACtB,CAAC;IAED,OAAO,CAAC,GAAG,EAAE,GAAG,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC;AAC9E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc;IAC5B,UAAU,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACvB,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,kBAAkB,iBAAiB,QAAQ,EAAE,EAAE;YACxE,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACb,oBAAoB,QAAQ,iCAAiC;gBAC3D,cAAc,GAAG,CAAC,IAAI,CAAC,IAAI,6CAA6C;gBACxE,2DAA2D,CAC9D,CAAC;QACJ,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACb,iBAAiB,QAAQ,iDAAiD;gBACxE,iEAAiE;gBACjE,kCAAkC,CACrC,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,mCAAmC,GAAG,CAAC,MAAM,iBAAiB,QAAQ,IAAI,CAC3E,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,KAAK,CAAC,GAAG,kBAAkB,UAAU,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"vcr-spec.js","sourceRoot":"","sources":["../../src/testing/vcr-spec.ts"],"names":[],"mappings":"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"}