@fuzdev/fuz_app 0.65.0 → 0.66.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/dist/actions/CLAUDE.md +65 -86
- package/dist/actions/action_codegen.d.ts +1 -1
- package/dist/actions/action_codegen.js +1 -1
- package/dist/actions/action_event_data.d.ts +1 -1
- package/dist/auth/CLAUDE.md +83 -104
- package/dist/auth/audit_log_schema.js +2 -2
- package/dist/auth/daemon_token_middleware.d.ts +15 -5
- package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
- package/dist/auth/daemon_token_middleware.js +24 -15
- package/dist/auth/invite_queries.d.ts +17 -7
- package/dist/auth/invite_queries.d.ts.map +1 -1
- package/dist/auth/invite_queries.js +19 -8
- package/dist/auth/signup_routes.d.ts +47 -1
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +103 -52
- package/dist/env/resolve.d.ts +44 -7
- package/dist/env/resolve.d.ts.map +1 -1
- package/dist/env/resolve.js +94 -27
- package/dist/http/CLAUDE.md +47 -52
- package/dist/http/jsonrpc.d.ts +23 -7
- package/dist/http/jsonrpc.d.ts.map +1 -1
- package/dist/http/jsonrpc.js +19 -3
- package/dist/http/surface.d.ts +9 -2
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/runtime/mock.d.ts +1 -1
- package/dist/runtime/mock.js +1 -1
- package/dist/testing/CLAUDE.md +659 -511
- package/dist/testing/admin_integration.d.ts +5 -5
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +95 -39
- package/dist/testing/app_server.d.ts +16 -1
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +18 -3
- package/dist/testing/audit_completeness.d.ts +7 -5
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +5 -9
- package/dist/testing/bootstrap_success.js +2 -2
- package/dist/testing/cross_backend/backend_config.d.ts +113 -0
- package/dist/testing/cross_backend/backend_config.d.ts.map +1 -0
- package/dist/testing/cross_backend/backend_config.js +1 -0
- package/dist/testing/cross_backend/bench/bench_report.d.ts +46 -0
- package/dist/testing/cross_backend/bench/bench_report.d.ts.map +1 -0
- package/dist/testing/cross_backend/bench/bench_report.js +83 -0
- package/dist/testing/cross_backend/bench/run_cross_impl_bench.d.ts +44 -0
- package/dist/testing/cross_backend/bench/run_cross_impl_bench.d.ts.map +1 -0
- package/dist/testing/cross_backend/bench/run_cross_impl_bench.js +38 -0
- package/dist/testing/cross_backend/bench/scenario.d.ts +57 -0
- package/dist/testing/cross_backend/bench/scenario.d.ts.map +1 -0
- package/dist/testing/cross_backend/bench/scenario.js +28 -0
- package/dist/testing/cross_backend/bootstrap_backend.d.ts +41 -0
- package/dist/testing/cross_backend/bootstrap_backend.d.ts.map +1 -0
- package/dist/testing/cross_backend/bootstrap_backend.js +34 -0
- package/dist/testing/cross_backend/build_test_backend_paths.d.ts +24 -0
- package/dist/testing/cross_backend/build_test_backend_paths.d.ts.map +1 -0
- package/dist/testing/cross_backend/build_test_backend_paths.js +33 -0
- package/dist/testing/cross_backend/capabilities.d.ts +3 -2
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.d.ts +122 -0
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -0
- package/dist/testing/cross_backend/default_backend_configs.js +111 -0
- package/dist/testing/cross_backend/default_secrets.d.ts +40 -0
- package/dist/testing/cross_backend/default_secrets.d.ts.map +1 -0
- package/dist/testing/cross_backend/default_secrets.js +39 -0
- package/dist/testing/cross_backend/default_spine_surface.d.ts +64 -0
- package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -0
- package/dist/testing/cross_backend/default_spine_surface.js +121 -0
- package/dist/testing/cross_backend/setup.d.ts +270 -34
- package/dist/testing/cross_backend/setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/setup.js +495 -15
- package/dist/testing/cross_backend/spawn_backend.d.ts +58 -0
- package/dist/testing/cross_backend/spawn_backend.d.ts.map +1 -0
- package/dist/testing/cross_backend/spawn_backend.js +229 -0
- package/dist/testing/cross_backend/spine_stub_backend_config.d.ts +66 -0
- package/dist/testing/cross_backend/spine_stub_backend_config.d.ts.map +1 -0
- package/dist/testing/cross_backend/spine_stub_backend_config.js +49 -0
- package/dist/testing/cross_backend/sse_round_trip.d.ts +37 -0
- package/dist/testing/cross_backend/sse_round_trip.d.ts.map +1 -0
- package/dist/testing/cross_backend/sse_round_trip.js +137 -0
- package/dist/testing/cross_backend/standard.d.ts +96 -0
- package/dist/testing/cross_backend/standard.d.ts.map +1 -0
- package/dist/testing/cross_backend/standard.js +49 -0
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +171 -0
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -0
- package/dist/testing/cross_backend/testing_reset_actions.js +213 -0
- package/dist/testing/cross_backend/testing_server_bun.d.ts +5 -0
- package/dist/testing/cross_backend/testing_server_bun.d.ts.map +1 -0
- package/dist/testing/cross_backend/testing_server_bun.js +59 -0
- package/dist/testing/cross_backend/testing_server_core.d.ts +140 -0
- package/dist/testing/cross_backend/testing_server_core.d.ts.map +1 -0
- package/dist/testing/cross_backend/testing_server_core.js +68 -0
- package/dist/testing/cross_backend/testing_server_deno.d.ts +5 -0
- package/dist/testing/cross_backend/testing_server_deno.d.ts.map +1 -0
- package/dist/testing/cross_backend/testing_server_deno.js +37 -0
- package/dist/testing/cross_backend/testing_server_node.d.ts +5 -0
- package/dist/testing/cross_backend/testing_server_node.d.ts.map +1 -0
- package/dist/testing/cross_backend/testing_server_node.js +50 -0
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +72 -0
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -0
- package/dist/testing/cross_backend/ts_spine_backend_config.js +112 -0
- package/dist/testing/cross_backend/ws_round_trip.d.ts +35 -0
- package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -0
- package/dist/testing/cross_backend/ws_round_trip.js +113 -0
- package/dist/testing/data_exposure.d.ts +4 -6
- package/dist/testing/data_exposure.d.ts.map +1 -1
- package/dist/testing/data_exposure.js +1 -5
- package/dist/testing/db_entities.d.ts +18 -7
- package/dist/testing/db_entities.d.ts.map +1 -1
- package/dist/testing/db_entities.js +18 -7
- package/dist/testing/integration.d.ts +27 -6
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +93 -58
- package/dist/testing/round_trip.d.ts +4 -5
- package/dist/testing/round_trip.d.ts.map +1 -1
- package/dist/testing/round_trip.js +1 -5
- package/dist/testing/rpc_helpers.d.ts +10 -4
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_helpers.js +1 -1
- package/dist/testing/rpc_round_trip.d.ts +5 -5
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +1 -5
- package/dist/testing/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/sse_round_trip.js +1 -68
- package/dist/testing/standard.d.ts +4 -5
- package/dist/testing/standard.d.ts.map +1 -1
- package/dist/testing/stubs.d.ts +10 -3
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +9 -2
- package/dist/testing/testing_rate_limiter.d.ts +59 -0
- package/dist/testing/testing_rate_limiter.d.ts.map +1 -0
- package/dist/testing/testing_rate_limiter.js +74 -0
- package/dist/testing/transports/bootstrap.d.ts +52 -0
- package/dist/testing/transports/bootstrap.d.ts.map +1 -0
- package/dist/testing/transports/bootstrap.js +70 -0
- package/dist/testing/transports/fetch_transport.d.ts +81 -0
- package/dist/testing/transports/fetch_transport.d.ts.map +1 -0
- package/dist/testing/transports/fetch_transport.js +74 -0
- package/dist/testing/transports/sse_frame_reader.d.ts +41 -0
- package/dist/testing/transports/sse_frame_reader.d.ts.map +1 -0
- package/dist/testing/transports/sse_frame_reader.js +84 -0
- package/dist/testing/transports/sse_transport.d.ts +54 -0
- package/dist/testing/transports/sse_transport.d.ts.map +1 -0
- package/dist/testing/transports/sse_transport.js +51 -0
- package/dist/testing/transports/ws_client.d.ts +108 -0
- package/dist/testing/transports/ws_client.d.ts.map +1 -0
- package/dist/testing/transports/ws_client.js +56 -0
- package/dist/testing/transports/ws_transport.d.ts +43 -0
- package/dist/testing/transports/ws_transport.d.ts.map +1 -0
- package/dist/testing/transports/ws_transport.js +169 -0
- package/dist/testing/ws_round_trip.d.ts +21 -103
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +42 -40
- package/dist/ui/CLAUDE.md +5 -3
- package/dist/ui/MenuLink.svelte +16 -16
- package/dist/ui/MenuLink.svelte.d.ts +13 -4
- package/dist/ui/MenuLink.svelte.d.ts.map +1 -1
- package/package.json +7 -1
- package/dist/testing/transports/surface_source.d.ts +0 -51
- package/dist/testing/transports/surface_source.d.ts.map +0 -1
- package/dist/testing/transports/surface_source.js +0 -19
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/sse_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAmB7B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAwB,KAAK,SAAS,EAAuB,MAAM,oBAAoB,CAAC;AAE/F,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAG/D,OAAO,EAEN,KAAK,eAAe,EACpB,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAwB,KAAK,SAAS,EAAC,MAAM,SAAS,CAAC;AAE9D,OAAO,EAIN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"sse_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/sse_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAmB7B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAwB,KAAK,SAAS,EAAuB,MAAM,oBAAoB,CAAC;AAE/F,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAG/D,OAAO,EAEN,KAAK,eAAe,EACpB,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAwB,KAAK,SAAS,EAAC,MAAM,SAAS,CAAC;AAE9D,OAAO,EAIN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAO1B,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAChC,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,OAAO,EAAE,CAAC,GAAG,EAAE;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E;;;OAGG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/B;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IACnC,4CAA4C;IAC5C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,qDAAqD;IACrD,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE,iDAAiD;IACjD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAChC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAChD;;;;;;;;;;;;OAYG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC,8BAA8B;IAC9B,MAAM,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC;CAChC;AA+CD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,mBAAmB,KAAG,IAgIvE,CAAC"}
|
|
@@ -24,74 +24,7 @@ import { rpc_call, require_rpc_endpoint_path, resolve_rpc_endpoints_for_setup, }
|
|
|
24
24
|
import { run_migrations } from '../db/migrate.js';
|
|
25
25
|
import { auth_migration_ns } from '../auth/migrations.js';
|
|
26
26
|
import { account_session_revoke_all_action_spec } from '../auth/account_action_specs.js';
|
|
27
|
-
|
|
28
|
-
* Read one complete SSE frame (up to `\n\n`) from a stream reader.
|
|
29
|
-
*
|
|
30
|
-
* Returns the frame without the trailing `\n\n`. Throws on premature close.
|
|
31
|
-
* Preserves any bytes past the terminator in the shared buffer for the next call.
|
|
32
|
-
*/
|
|
33
|
-
const create_sse_frame_reader = (reader) => {
|
|
34
|
-
const decoder = new TextDecoder();
|
|
35
|
-
let buffer = '';
|
|
36
|
-
let closed = false;
|
|
37
|
-
const pump_once = async (timeout_ms) => {
|
|
38
|
-
// Race the read against a timeout — vitest will otherwise hang on a misbehaving stream.
|
|
39
|
-
const timeout = new Promise((resolve) => {
|
|
40
|
-
setTimeout(() => resolve({ done: true, value: undefined, timed_out: true }), timeout_ms);
|
|
41
|
-
});
|
|
42
|
-
const result = (await Promise.race([reader.read(), timeout]));
|
|
43
|
-
if ('timed_out' in result) {
|
|
44
|
-
throw new Error(`SSE read timed out after ${timeout_ms}ms`);
|
|
45
|
-
}
|
|
46
|
-
if (result.done) {
|
|
47
|
-
closed = true;
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
buffer += decoder.decode(result.value, { stream: true });
|
|
51
|
-
return true;
|
|
52
|
-
};
|
|
53
|
-
return {
|
|
54
|
-
read_frame: async (timeout_ms = 2000) => {
|
|
55
|
-
// SSE frames end with a blank line — the canonical terminator is `\n\n`.
|
|
56
|
-
while (true) {
|
|
57
|
-
const idx = buffer.indexOf('\n\n');
|
|
58
|
-
if (idx >= 0) {
|
|
59
|
-
const frame = buffer.slice(0, idx);
|
|
60
|
-
buffer = buffer.slice(idx + 2);
|
|
61
|
-
return frame;
|
|
62
|
-
}
|
|
63
|
-
const cont = await pump_once(timeout_ms);
|
|
64
|
-
if (!cont)
|
|
65
|
-
throw new Error('SSE stream ended before a frame was received');
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
wait_for_close: async (timeout_ms) => {
|
|
69
|
-
// Drain until the server closes the stream (pump_once returns false) or timeout expires.
|
|
70
|
-
const deadline = Date.now() + timeout_ms;
|
|
71
|
-
for (;;) {
|
|
72
|
-
if (closed)
|
|
73
|
-
return true;
|
|
74
|
-
const remaining = deadline - Date.now();
|
|
75
|
-
if (remaining <= 0)
|
|
76
|
-
return false;
|
|
77
|
-
try {
|
|
78
|
-
await pump_once(Math.min(remaining, timeout_ms));
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
cancel: async () => {
|
|
86
|
-
try {
|
|
87
|
-
await reader.cancel();
|
|
88
|
-
}
|
|
89
|
-
catch {
|
|
90
|
-
// already closed
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
};
|
|
94
|
-
};
|
|
27
|
+
import { create_sse_frame_reader } from './transports/sse_frame_reader.js';
|
|
95
28
|
/**
|
|
96
29
|
* Validate a decoded SSE `data:` frame as a JSON-RPC-style `{method, params}` payload.
|
|
97
30
|
*/
|
|
@@ -23,7 +23,7 @@ import type { RouteSpec } from '../http/route_spec.js';
|
|
|
23
23
|
import type { RpcEndpointsSuiteOption } from './rpc_helpers.js';
|
|
24
24
|
import type { BackendCapabilities } from './cross_backend/capabilities.js';
|
|
25
25
|
import type { SetupTest } from './cross_backend/setup.js';
|
|
26
|
-
import type {
|
|
26
|
+
import type { AppSurfaceSpec } from '../http/surface.js';
|
|
27
27
|
import type { SuiteAppOptions } from './app_server.js';
|
|
28
28
|
/**
|
|
29
29
|
* Configuration for `describe_standard_tests`.
|
|
@@ -32,11 +32,10 @@ export interface StandardTestOptions {
|
|
|
32
32
|
/** Per-test fixture-producing function. */
|
|
33
33
|
setup_test: SetupTest;
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* transport plumbing.
|
|
35
|
+
* App surface. Constructed in TS by the consumer; same shape for
|
|
36
|
+
* in-process and cross-process tests.
|
|
38
37
|
*/
|
|
39
|
-
surface_source:
|
|
38
|
+
surface_source: AppSurfaceSpec;
|
|
40
39
|
/** Backend capability declarations. */
|
|
41
40
|
capabilities: BackendCapabilities;
|
|
42
41
|
/** Session config — needed for cookie_name + factory-form rpc_endpoints resolution. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/standard.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAC,gBAAgB,EAAE,sBAAsB,EAAC,MAAM,yBAAyB,CAAC;AACtF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AASrD,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,kBAAkB,CAAC;AAC9D,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,0BAA0B,CAAC;AACxD,OAAO,KAAK,EAAC,
|
|
1
|
+
{"version":3,"file":"standard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/standard.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAC,gBAAgB,EAAE,sBAAsB,EAAC,MAAM,yBAAyB,CAAC;AACtF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AASrD,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,kBAAkB,CAAC;AAC9D,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,0BAA0B,CAAC;AACxD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,iBAAiB,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,2CAA2C;IAC3C,UAAU,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,uFAAuF;IACvF,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;OAKG;IACH,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE;;;;;OAKG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;OAGG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,sBAAsB,CAAC;IACnC,sEAAsE;IACtE,yBAAyB,CAAC,EAAE,eAAe,CAAC;IAC5C;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,8DAA8D;IAC9D,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAAI,SAAS,mBAAmB,KAAG,IA2DtE,CAAC"}
|
package/dist/testing/stubs.d.ts
CHANGED
|
@@ -127,7 +127,7 @@ export interface CreateTestAppSurfaceSpecOptions {
|
|
|
127
127
|
* only, never mounts.
|
|
128
128
|
*/
|
|
129
129
|
ws_endpoints?: ReadonlyArray<WsEndpointSpec> | ((ctx: AppServerContext) => ReadonlyArray<WsEndpointSpec>);
|
|
130
|
-
/** Transform middleware array (e.g.,
|
|
130
|
+
/** Transform middleware array (e.g., zap's `extend_middleware_for_zap_binary`). */
|
|
131
131
|
transform_middleware?: (specs: Array<MiddlewareSpec>) => Array<MiddlewareSpec>;
|
|
132
132
|
/**
|
|
133
133
|
* Bootstrap config — symmetric with `AppServerOptions.bootstrap`. Discriminated
|
|
@@ -141,7 +141,14 @@ export interface CreateTestAppSurfaceSpecOptions {
|
|
|
141
141
|
bootstrap?: BootstrapServerOptions;
|
|
142
142
|
}
|
|
143
143
|
/**
|
|
144
|
-
* Create an `AppSurfaceSpec` for
|
|
144
|
+
* Create an `AppSurfaceSpec` for the standard testing suites.
|
|
145
|
+
*
|
|
146
|
+
* Used by both in-process and cross-process tests as the schema source —
|
|
147
|
+
* the cross-process-ness lives in the transport + per-test fixture, not
|
|
148
|
+
* here. The on-disk `*_attack_surface.json` snapshot is observability
|
|
149
|
+
* (gen-time drift detection via `assert_surface_matches_snapshot`); the
|
|
150
|
+
* suites consume the spec object this function returns, not the JSON
|
|
151
|
+
* file.
|
|
145
152
|
*
|
|
146
153
|
* Mirrors `create_app_server`'s route assembly: consumer routes +
|
|
147
154
|
* factory-managed bootstrap routes + surface generation. If
|
|
@@ -149,7 +156,7 @@ export interface CreateTestAppSurfaceSpecOptions {
|
|
|
149
156
|
* to stay in sync (single source of truth for all consumers).
|
|
150
157
|
*
|
|
151
158
|
* @param options - surface spec options
|
|
152
|
-
* @returns the surface spec for
|
|
159
|
+
* @returns the surface spec for the standard suites
|
|
153
160
|
*/
|
|
154
161
|
export declare const create_test_app_surface_spec: (options: CreateTestAppSurfaceSpecOptions) => AppSurfaceSpec;
|
|
155
162
|
//# sourceMappingURL=stubs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stubs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/stubs.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"stubs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/stubs.ts"],"names":[],"mappings":"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"}
|
package/dist/testing/stubs.js
CHANGED
|
@@ -206,7 +206,14 @@ export const create_stub_app_server_context = (session_options) => {
|
|
|
206
206
|
};
|
|
207
207
|
};
|
|
208
208
|
/**
|
|
209
|
-
* Create an `AppSurfaceSpec` for
|
|
209
|
+
* Create an `AppSurfaceSpec` for the standard testing suites.
|
|
210
|
+
*
|
|
211
|
+
* Used by both in-process and cross-process tests as the schema source —
|
|
212
|
+
* the cross-process-ness lives in the transport + per-test fixture, not
|
|
213
|
+
* here. The on-disk `*_attack_surface.json` snapshot is observability
|
|
214
|
+
* (gen-time drift detection via `assert_surface_matches_snapshot`); the
|
|
215
|
+
* suites consume the spec object this function returns, not the JSON
|
|
216
|
+
* file.
|
|
210
217
|
*
|
|
211
218
|
* Mirrors `create_app_server`'s route assembly: consumer routes +
|
|
212
219
|
* factory-managed bootstrap routes + surface generation. If
|
|
@@ -214,7 +221,7 @@ export const create_stub_app_server_context = (session_options) => {
|
|
|
214
221
|
* to stay in sync (single source of truth for all consumers).
|
|
215
222
|
*
|
|
216
223
|
* @param options - surface spec options
|
|
217
|
-
* @returns the surface spec for
|
|
224
|
+
* @returns the surface spec for the standard suites
|
|
218
225
|
*/
|
|
219
226
|
export const create_test_app_surface_spec = (options) => {
|
|
220
227
|
const ctx = create_stub_app_server_context(options.session_options);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import './assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Test-only `RateLimiter` subclass with bucket tracking + reset_all.
|
|
4
|
+
*
|
|
5
|
+
* Production code never instantiates this; test binaries swap it in for
|
|
6
|
+
* production `RateLimiter` so `_testing_reset` can clear every bucket
|
|
7
|
+
* between cases. Mirrors the `TestingArgon2idHasher` pattern from the
|
|
8
|
+
* Rust spine — same call surface as the production class, plus test-only
|
|
9
|
+
* knobs (`reset_all`, `tracked_keys`).
|
|
10
|
+
*
|
|
11
|
+
* Constructor + every overridden method preserve production semantics
|
|
12
|
+
* by delegating to `super.*` after tracking; `reset_all` walks the
|
|
13
|
+
* tracked-keys set and calls `super.reset` per entry. Tests that depend
|
|
14
|
+
* on burst behavior get identical results between production and test
|
|
15
|
+
* instantiations.
|
|
16
|
+
*
|
|
17
|
+
* Usage in a test binary:
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* import {TestingRateLimiter} from '@fuzdev/fuz_app/testing/testing_rate_limiter.js';
|
|
21
|
+
*
|
|
22
|
+
* const limiter = new TestingRateLimiter(default_login_ip_rate_limit);
|
|
23
|
+
* await create_app_server({backend, ip_rate_limiter: limiter, ...});
|
|
24
|
+
*
|
|
25
|
+
* // Inside the `_testing_reset` handler's `reset_state`:
|
|
26
|
+
* limiter.reset_all();
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @module
|
|
30
|
+
*/
|
|
31
|
+
import { RateLimiter, type RateLimitResult } from '../rate_limiter.js';
|
|
32
|
+
/**
|
|
33
|
+
* `RateLimiter` plus bucket tracking. Every `check`/`record` call adds
|
|
34
|
+
* its key to `#seen_keys`; `reset` removes it; `reset_all` clears every
|
|
35
|
+
* tracked bucket. Drop-in replacement anywhere a `RateLimiter` is
|
|
36
|
+
* expected — the type is nominally compatible via subclassing.
|
|
37
|
+
*/
|
|
38
|
+
export declare class TestingRateLimiter extends RateLimiter {
|
|
39
|
+
#private;
|
|
40
|
+
check(key: string, now?: number): RateLimitResult;
|
|
41
|
+
record(key: string, now?: number): RateLimitResult;
|
|
42
|
+
reset(key: string): void;
|
|
43
|
+
/**
|
|
44
|
+
* Clear every bucket this limiter has been asked about. Idempotent;
|
|
45
|
+
* safe to call before any check/record activity. Designed to be invoked
|
|
46
|
+
* from a `_testing_reset` handler's `reset_state` callback so the test
|
|
47
|
+
* binary's rate-limit buckets don't leak across test cases.
|
|
48
|
+
*/
|
|
49
|
+
reset_all(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Snapshot of every bucket key this limiter has observed via
|
|
52
|
+
* `check`/`record`. Doesn't reflect post-cleanup pruning — keys that
|
|
53
|
+
* `cleanup()` removed remain in `tracked_keys` until `reset`/`reset_all`
|
|
54
|
+
* runs (or the limiter is disposed). Useful for assertions like
|
|
55
|
+
* "limiter saw exactly N IPs" in tests.
|
|
56
|
+
*/
|
|
57
|
+
get tracked_keys(): ReadonlySet<string>;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=testing_rate_limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing_rate_limiter.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/testing_rate_limiter.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAC,WAAW,EAAE,KAAK,eAAe,EAAC,MAAM,oBAAoB,CAAC;AAErE;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,WAAW;;IAGzC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,eAAe;IAKjD,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,eAAe;IAKlD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAKjC;;;;;OAKG;IACH,SAAS,IAAI,IAAI;IAOjB;;;;;;OAMG;IACH,IAAI,YAAY,IAAI,WAAW,CAAC,MAAM,CAAC,CAEtC;CACD"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import './assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Test-only `RateLimiter` subclass with bucket tracking + reset_all.
|
|
4
|
+
*
|
|
5
|
+
* Production code never instantiates this; test binaries swap it in for
|
|
6
|
+
* production `RateLimiter` so `_testing_reset` can clear every bucket
|
|
7
|
+
* between cases. Mirrors the `TestingArgon2idHasher` pattern from the
|
|
8
|
+
* Rust spine — same call surface as the production class, plus test-only
|
|
9
|
+
* knobs (`reset_all`, `tracked_keys`).
|
|
10
|
+
*
|
|
11
|
+
* Constructor + every overridden method preserve production semantics
|
|
12
|
+
* by delegating to `super.*` after tracking; `reset_all` walks the
|
|
13
|
+
* tracked-keys set and calls `super.reset` per entry. Tests that depend
|
|
14
|
+
* on burst behavior get identical results between production and test
|
|
15
|
+
* instantiations.
|
|
16
|
+
*
|
|
17
|
+
* Usage in a test binary:
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* import {TestingRateLimiter} from '@fuzdev/fuz_app/testing/testing_rate_limiter.js';
|
|
21
|
+
*
|
|
22
|
+
* const limiter = new TestingRateLimiter(default_login_ip_rate_limit);
|
|
23
|
+
* await create_app_server({backend, ip_rate_limiter: limiter, ...});
|
|
24
|
+
*
|
|
25
|
+
* // Inside the `_testing_reset` handler's `reset_state`:
|
|
26
|
+
* limiter.reset_all();
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @module
|
|
30
|
+
*/
|
|
31
|
+
import { RateLimiter } from '../rate_limiter.js';
|
|
32
|
+
/**
|
|
33
|
+
* `RateLimiter` plus bucket tracking. Every `check`/`record` call adds
|
|
34
|
+
* its key to `#seen_keys`; `reset` removes it; `reset_all` clears every
|
|
35
|
+
* tracked bucket. Drop-in replacement anywhere a `RateLimiter` is
|
|
36
|
+
* expected — the type is nominally compatible via subclassing.
|
|
37
|
+
*/
|
|
38
|
+
export class TestingRateLimiter extends RateLimiter {
|
|
39
|
+
#seen_keys = new Set();
|
|
40
|
+
check(key, now) {
|
|
41
|
+
this.#seen_keys.add(key);
|
|
42
|
+
return super.check(key, now);
|
|
43
|
+
}
|
|
44
|
+
record(key, now) {
|
|
45
|
+
this.#seen_keys.add(key);
|
|
46
|
+
return super.record(key, now);
|
|
47
|
+
}
|
|
48
|
+
reset(key) {
|
|
49
|
+
this.#seen_keys.delete(key);
|
|
50
|
+
super.reset(key);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Clear every bucket this limiter has been asked about. Idempotent;
|
|
54
|
+
* safe to call before any check/record activity. Designed to be invoked
|
|
55
|
+
* from a `_testing_reset` handler's `reset_state` callback so the test
|
|
56
|
+
* binary's rate-limit buckets don't leak across test cases.
|
|
57
|
+
*/
|
|
58
|
+
reset_all() {
|
|
59
|
+
for (const key of this.#seen_keys) {
|
|
60
|
+
super.reset(key);
|
|
61
|
+
}
|
|
62
|
+
this.#seen_keys.clear();
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Snapshot of every bucket key this limiter has observed via
|
|
66
|
+
* `check`/`record`. Doesn't reflect post-cleanup pruning — keys that
|
|
67
|
+
* `cleanup()` removed remain in `tracked_keys` until `reset`/`reset_all`
|
|
68
|
+
* runs (or the limiter is disposed). Useful for assertions like
|
|
69
|
+
* "limiter saw exactly N IPs" in tests.
|
|
70
|
+
*/
|
|
71
|
+
get tracked_keys() {
|
|
72
|
+
return this.#seen_keys;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
3
|
+
import type { BackendConfig } from '../cross_backend/backend_config.js';
|
|
4
|
+
import type { FetchTransport } from './fetch_transport.js';
|
|
5
|
+
/** Input for `bootstrap()`. */
|
|
6
|
+
export interface BootstrapOptions {
|
|
7
|
+
/**
|
|
8
|
+
* The cookie-threading HTTP transport pointed at the binary. After
|
|
9
|
+
* `bootstrap()` resolves, the transport carries the keeper session
|
|
10
|
+
* cookie — every later call against it is authenticated as keeper.
|
|
11
|
+
*/
|
|
12
|
+
readonly transport: FetchTransport;
|
|
13
|
+
/**
|
|
14
|
+
* Backend config — used for `bootstrap_path` plus the
|
|
15
|
+
* `bootstrap.username` / `bootstrap.password` / `bootstrap.token`
|
|
16
|
+
* credentials. The runner already wrote `bootstrap.token` to
|
|
17
|
+
* `bootstrap.token_path` before spawning, so the binary picks the
|
|
18
|
+
* token up at startup.
|
|
19
|
+
*/
|
|
20
|
+
readonly config: BackendConfig;
|
|
21
|
+
}
|
|
22
|
+
/** The keeper credentials captured from `POST /api/account/bootstrap`. */
|
|
23
|
+
export interface BootstrapResult {
|
|
24
|
+
/**
|
|
25
|
+
* Same transport that came in, now carrying the keeper session
|
|
26
|
+
* cookie in its jar. Returned for call-site clarity (callers don't
|
|
27
|
+
* have to remember the mutation happens in place).
|
|
28
|
+
*/
|
|
29
|
+
readonly transport: FetchTransport;
|
|
30
|
+
/** Account JSON returned by `POST /bootstrap`. */
|
|
31
|
+
readonly account: {
|
|
32
|
+
readonly id: Uuid;
|
|
33
|
+
readonly username: string;
|
|
34
|
+
};
|
|
35
|
+
/** Actor JSON returned by `POST /bootstrap`. */
|
|
36
|
+
readonly actor: {
|
|
37
|
+
readonly id: Uuid;
|
|
38
|
+
};
|
|
39
|
+
/** Raw `Set-Cookie` values for threading into a WS transport. */
|
|
40
|
+
readonly cookies: ReadonlyArray<string>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Fire `POST {config.bootstrap_path}` and capture the keeper session.
|
|
44
|
+
*
|
|
45
|
+
* @throws Error when the binary refuses bootstrap (non-2xx response) or
|
|
46
|
+
* the body fails to parse as the expected `{account, actor}` envelope.
|
|
47
|
+
* The error carries the status + raw body so a mistyped token /
|
|
48
|
+
* username collision / boot-time DB drift surfaces with enough
|
|
49
|
+
* context to debug.
|
|
50
|
+
*/
|
|
51
|
+
export declare const bootstrap: (options: BootstrapOptions) => Promise<BootstrapResult>;
|
|
52
|
+
//# sourceMappingURL=bootstrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/transports/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAuB9B,OAAO,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAE5C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,oCAAoC,CAAC;AACtE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,sBAAsB,CAAC;AAezD,+BAA+B;AAC/B,MAAM,WAAW,gBAAgB;IAChC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;CAC/B;AAED,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE;QAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;KAAC,CAAC;IACjE,gDAAgD;IAChD,QAAQ,CAAC,KAAK,EAAE;QAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAA;KAAC,CAAC;IACpC,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACxC;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,GAAU,SAAS,gBAAgB,KAAG,OAAO,CAAC,eAAe,CA4BlF,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Stateless cross-process bootstrap.
|
|
4
|
+
*
|
|
5
|
+
* POSTs `{bootstrap_path}` against the running test binary with the
|
|
6
|
+
* preconfigured token + username + password, parses the
|
|
7
|
+
* `BootstrapOutput` envelope, captures the session `Set-Cookie` onto
|
|
8
|
+
* the supplied `FetchTransport` (which carries it on every subsequent
|
|
9
|
+
* call), and returns the keeper credentials.
|
|
10
|
+
*
|
|
11
|
+
* Fires exactly once per backend lifetime — `spawn_backend` calls it
|
|
12
|
+
* inside vitest's `globalSetup`. Per-test fixtures re-use the captured
|
|
13
|
+
* keeper credentials; fresh per-test accounts come from
|
|
14
|
+
* `fixture.create_account()` (signup+login through production RPC),
|
|
15
|
+
* not re-bootstrap. The hybrid reset model in `default_cross_process_setup`
|
|
16
|
+
* depends on this — re-bootstrap would race the bootstrap lock and
|
|
17
|
+
* in-memory caches.
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
import { z } from 'zod';
|
|
22
|
+
import { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
23
|
+
/**
|
|
24
|
+
* The `BootstrapOutput` envelope shape the cross-process bootstrap call
|
|
25
|
+
* cares about. Looser than the full `BootstrapOutput` Zod schema in
|
|
26
|
+
* `auth/bootstrap_account.ts` — that schema is the canonical wire shape
|
|
27
|
+
* on the server side, and this one is a structural subset the runner
|
|
28
|
+
* uses to extract the keeper identity. Kept local so cross-process
|
|
29
|
+
* testing doesn't pull the full auth-domain schema into its dep graph.
|
|
30
|
+
*/
|
|
31
|
+
const BootstrapResponse = z.object({
|
|
32
|
+
account: z.object({ id: Uuid, username: z.string() }),
|
|
33
|
+
actor: z.object({ id: Uuid }),
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* Fire `POST {config.bootstrap_path}` and capture the keeper session.
|
|
37
|
+
*
|
|
38
|
+
* @throws Error when the binary refuses bootstrap (non-2xx response) or
|
|
39
|
+
* the body fails to parse as the expected `{account, actor}` envelope.
|
|
40
|
+
* The error carries the status + raw body so a mistyped token /
|
|
41
|
+
* username collision / boot-time DB drift surfaces with enough
|
|
42
|
+
* context to debug.
|
|
43
|
+
*/
|
|
44
|
+
export const bootstrap = async (options) => {
|
|
45
|
+
const { transport, config } = options;
|
|
46
|
+
const response = await transport(config.bootstrap_path, {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
headers: { 'Content-Type': 'application/json' },
|
|
49
|
+
body: JSON.stringify({
|
|
50
|
+
token: config.bootstrap.token,
|
|
51
|
+
username: config.bootstrap.username,
|
|
52
|
+
password: config.bootstrap.password,
|
|
53
|
+
}),
|
|
54
|
+
});
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
const body = await response.text().catch(() => '<unreadable>');
|
|
57
|
+
throw new Error(`bootstrap(${config.name}) failed: status=${response.status} body=${body}`);
|
|
58
|
+
}
|
|
59
|
+
const raw = await response.json();
|
|
60
|
+
const parsed = BootstrapResponse.safeParse(raw);
|
|
61
|
+
if (!parsed.success) {
|
|
62
|
+
throw new Error(`bootstrap(${config.name}) returned unexpected body: ${JSON.stringify(raw)} (${parsed.error.message})`);
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
transport,
|
|
66
|
+
account: parsed.data.account,
|
|
67
|
+
actor: parsed.data.actor,
|
|
68
|
+
cookies: transport.cookies(),
|
|
69
|
+
};
|
|
70
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cookie-threading HTTP transport for cross-process tests.
|
|
4
|
+
*
|
|
5
|
+
* Wraps the global `fetch` against a base URL, carries cookies across
|
|
6
|
+
* requests in a `Map`-backed jar so the session cookie set on bootstrap
|
|
7
|
+
* is re-sent on every subsequent call. Satisfies the `RpcTestTransport`
|
|
8
|
+
* shape `Hono.request` already does — so every suite body that takes
|
|
9
|
+
* `transport: RpcTestTransport` works against a cross-process binary
|
|
10
|
+
* unchanged.
|
|
11
|
+
*
|
|
12
|
+
* The `Origin` header is threaded onto every request because the
|
|
13
|
+
* backend's allowlist (`{ZZZ,ZAP,FUZ}_ALLOWED_ORIGINS`) rejects mutations
|
|
14
|
+
* without an `Origin`. Cross-process tests run with
|
|
15
|
+
* `ALLOWED_ORIGINS=http://localhost:*`, so defaulting `origin` to the
|
|
16
|
+
* configured `base_url` is safe.
|
|
17
|
+
*
|
|
18
|
+
* The cookie jar is intentionally simple — it does not honour `Domain`,
|
|
19
|
+
* `Path`, `Expires`, or `SameSite` attributes. Cross-process tests
|
|
20
|
+
* always hit a single host:port, so name-keyed last-write-wins matches
|
|
21
|
+
* the behaviour real browsers exhibit against the same surface.
|
|
22
|
+
*
|
|
23
|
+
* @module
|
|
24
|
+
*/
|
|
25
|
+
import type { RpcTestTransport } from '../rpc_helpers.js';
|
|
26
|
+
/** Construction options for `create_fetch_transport`. */
|
|
27
|
+
export interface FetchTransportOptions {
|
|
28
|
+
/** Base URL the binary is reachable at — e.g. `http://localhost:8788`. */
|
|
29
|
+
readonly base_url: string;
|
|
30
|
+
/**
|
|
31
|
+
* Initial cookie values to seed the jar. Pass the `Set-Cookie` values
|
|
32
|
+
* captured from a prior `bootstrap()` call to keep the keeper session
|
|
33
|
+
* across a transport-recreation boundary. Each entry is a full
|
|
34
|
+
* `Set-Cookie` value (the same string `Headers.getSetCookie()` returns).
|
|
35
|
+
*/
|
|
36
|
+
readonly initial_cookies?: ReadonlyArray<string>;
|
|
37
|
+
/**
|
|
38
|
+
* Origin header threaded onto every request when the caller hasn't set
|
|
39
|
+
* one. Defaults to `base_url`; backends running with
|
|
40
|
+
* `ALLOWED_ORIGINS=http://localhost:*` accept `http://localhost:<port>`
|
|
41
|
+
* matching the spawned binary.
|
|
42
|
+
*
|
|
43
|
+
* Pass `null` to disable the default — useful for bearer-only probes
|
|
44
|
+
* that must not look like browser-context requests (the auth middleware
|
|
45
|
+
* silently discards bearer credentials when `Origin`/`Referer` is
|
|
46
|
+
* present). Callers can still set `Origin` per-call via `init.headers`.
|
|
47
|
+
*/
|
|
48
|
+
readonly origin?: string | null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The transport shape: callable as `RpcTestTransport` plus a `cookies()`
|
|
52
|
+
* accessor that returns the current jar state. The accessor exists so
|
|
53
|
+
* `ws_transport` can thread the session cookie onto the WS upgrade
|
|
54
|
+
* without an HTTP round trip.
|
|
55
|
+
*/
|
|
56
|
+
export interface FetchTransport extends RpcTestTransport {
|
|
57
|
+
/**
|
|
58
|
+
* Snapshot of every cookie currently in the jar, formatted as full
|
|
59
|
+
* `Set-Cookie` values (`name=value`). Used by `ws_transport` to
|
|
60
|
+
* compose the `Cookie` header on the upgrade request.
|
|
61
|
+
*/
|
|
62
|
+
readonly cookies: () => ReadonlyArray<string>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Build a cookie-threading transport pinned to `options.base_url`. The
|
|
66
|
+
* returned function carries a private `Map<name, cookie-head>` jar that
|
|
67
|
+
* updates on every response's `Set-Cookie` and re-sends on every
|
|
68
|
+
* subsequent request.
|
|
69
|
+
*
|
|
70
|
+
* Request rewriting:
|
|
71
|
+
*
|
|
72
|
+
* - Absolute URLs (`http://other.example/...`) pass through verbatim —
|
|
73
|
+
* handy for cross-origin negative tests that target a deliberately
|
|
74
|
+
* different host.
|
|
75
|
+
* - Relative URLs are resolved against `base_url`.
|
|
76
|
+
* - `Origin` is set to `options.origin ?? base_url` unless the caller
|
|
77
|
+
* already provided one.
|
|
78
|
+
* - `Cookie` is set from the jar unless the caller already provided one.
|
|
79
|
+
*/
|
|
80
|
+
export declare const create_fetch_transport: (options: FetchTransportOptions) => FetchTransport;
|
|
81
|
+
//# sourceMappingURL=fetch_transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch_transport.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/transports/fetch_transport.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,mBAAmB,CAAC;AAExD,yDAAyD;AACzD,MAAM,WAAW,qBAAqB;IACrC,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACjD;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACvD;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;CAC9C;AAkBD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,qBAAqB,KAAG,cAyCvE,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parse the `name=value` head of a `Set-Cookie` value. Returns `null`
|
|
4
|
+
* for malformed inputs (missing `=`, empty name). Drops every attribute
|
|
5
|
+
* after the first `;` — the jar is name-keyed and the lifetime
|
|
6
|
+
* attributes (`Expires`, `Max-Age`, `Path`, `Domain`, `SameSite`)
|
|
7
|
+
* don't affect cross-process test plumbing.
|
|
8
|
+
*/
|
|
9
|
+
const parse_set_cookie = (value) => {
|
|
10
|
+
const head = value.split(';', 1)[0].trim();
|
|
11
|
+
const eq = head.indexOf('=');
|
|
12
|
+
if (eq <= 0)
|
|
13
|
+
return null;
|
|
14
|
+
const name = head.slice(0, eq).trim();
|
|
15
|
+
if (!name)
|
|
16
|
+
return null;
|
|
17
|
+
return { name, cookie: head };
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Build a cookie-threading transport pinned to `options.base_url`. The
|
|
21
|
+
* returned function carries a private `Map<name, cookie-head>` jar that
|
|
22
|
+
* updates on every response's `Set-Cookie` and re-sends on every
|
|
23
|
+
* subsequent request.
|
|
24
|
+
*
|
|
25
|
+
* Request rewriting:
|
|
26
|
+
*
|
|
27
|
+
* - Absolute URLs (`http://other.example/...`) pass through verbatim —
|
|
28
|
+
* handy for cross-origin negative tests that target a deliberately
|
|
29
|
+
* different host.
|
|
30
|
+
* - Relative URLs are resolved against `base_url`.
|
|
31
|
+
* - `Origin` is set to `options.origin ?? base_url` unless the caller
|
|
32
|
+
* already provided one.
|
|
33
|
+
* - `Cookie` is set from the jar unless the caller already provided one.
|
|
34
|
+
*/
|
|
35
|
+
export const create_fetch_transport = (options) => {
|
|
36
|
+
const { base_url, initial_cookies, origin } = options;
|
|
37
|
+
const jar = new Map();
|
|
38
|
+
if (initial_cookies) {
|
|
39
|
+
for (const raw of initial_cookies) {
|
|
40
|
+
const parsed = parse_set_cookie(raw);
|
|
41
|
+
if (parsed)
|
|
42
|
+
jar.set(parsed.name, parsed.cookie);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const default_origin = origin === null ? null : (origin ?? base_url);
|
|
46
|
+
const cookies = () => Array.from(jar.values());
|
|
47
|
+
const transport = (async (url, init) => {
|
|
48
|
+
const target = /^https?:\/\//i.test(url) ? url : `${base_url}${url}`;
|
|
49
|
+
const headers = new Headers(init.headers);
|
|
50
|
+
if (default_origin !== null && !headers.has('Origin')) {
|
|
51
|
+
headers.set('Origin', default_origin);
|
|
52
|
+
}
|
|
53
|
+
if (!headers.has('Cookie') && jar.size > 0) {
|
|
54
|
+
headers.set('Cookie', Array.from(jar.values()).join('; '));
|
|
55
|
+
}
|
|
56
|
+
const response = await fetch(target, { ...init, headers });
|
|
57
|
+
// `Headers.getSetCookie()` returns each `Set-Cookie` value as a
|
|
58
|
+
// separate string — the only way to read multiple cookies set in
|
|
59
|
+
// one response (Headers.get() collapses to a single comma-joined
|
|
60
|
+
// string). Node 19.7+ ships it; vitest's runtime supports it.
|
|
61
|
+
const set_cookies = response.headers.getSetCookie();
|
|
62
|
+
for (const raw of set_cookies) {
|
|
63
|
+
const parsed = parse_set_cookie(raw);
|
|
64
|
+
if (parsed)
|
|
65
|
+
jar.set(parsed.name, parsed.cookie);
|
|
66
|
+
}
|
|
67
|
+
return response;
|
|
68
|
+
});
|
|
69
|
+
// Attach the cookies() accessor onto the callable. Using a property
|
|
70
|
+
// definition keeps it non-enumerable-ish and avoids polluting `.bind`
|
|
71
|
+
// / `.call` reflection on the function.
|
|
72
|
+
Object.defineProperty(transport, 'cookies', { value: cookies, enumerable: false });
|
|
73
|
+
return transport;
|
|
74
|
+
};
|