@fuzdev/fuz_app 0.65.0 → 0.67.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 (159) hide show
  1. package/dist/actions/CLAUDE.md +65 -86
  2. package/dist/actions/action_codegen.d.ts +1 -1
  3. package/dist/actions/action_codegen.js +1 -1
  4. package/dist/actions/action_event_data.d.ts +1 -1
  5. package/dist/auth/CLAUDE.md +83 -104
  6. package/dist/auth/audit_log_schema.js +2 -2
  7. package/dist/auth/daemon_token_middleware.d.ts +15 -5
  8. package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
  9. package/dist/auth/daemon_token_middleware.js +24 -15
  10. package/dist/auth/invite_queries.d.ts +17 -7
  11. package/dist/auth/invite_queries.d.ts.map +1 -1
  12. package/dist/auth/invite_queries.js +19 -8
  13. package/dist/auth/signup_routes.d.ts +47 -1
  14. package/dist/auth/signup_routes.d.ts.map +1 -1
  15. package/dist/auth/signup_routes.js +103 -52
  16. package/dist/env/resolve.d.ts +44 -7
  17. package/dist/env/resolve.d.ts.map +1 -1
  18. package/dist/env/resolve.js +94 -27
  19. package/dist/http/CLAUDE.md +47 -52
  20. package/dist/http/jsonrpc.d.ts +23 -7
  21. package/dist/http/jsonrpc.d.ts.map +1 -1
  22. package/dist/http/jsonrpc.js +19 -3
  23. package/dist/http/surface.d.ts +9 -2
  24. package/dist/http/surface.d.ts.map +1 -1
  25. package/dist/runtime/mock.d.ts +1 -1
  26. package/dist/runtime/mock.js +1 -1
  27. package/dist/testing/CLAUDE.md +659 -511
  28. package/dist/testing/admin_integration.d.ts +5 -5
  29. package/dist/testing/admin_integration.d.ts.map +1 -1
  30. package/dist/testing/admin_integration.js +95 -39
  31. package/dist/testing/app_server.d.ts +16 -1
  32. package/dist/testing/app_server.d.ts.map +1 -1
  33. package/dist/testing/app_server.js +18 -3
  34. package/dist/testing/audit_completeness.d.ts +7 -5
  35. package/dist/testing/audit_completeness.d.ts.map +1 -1
  36. package/dist/testing/audit_completeness.js +5 -9
  37. package/dist/testing/bootstrap_success.js +2 -2
  38. package/dist/testing/cross_backend/backend_config.d.ts +113 -0
  39. package/dist/testing/cross_backend/backend_config.d.ts.map +1 -0
  40. package/dist/testing/cross_backend/backend_config.js +1 -0
  41. package/dist/testing/cross_backend/bench/bench_report.d.ts +46 -0
  42. package/dist/testing/cross_backend/bench/bench_report.d.ts.map +1 -0
  43. package/dist/testing/cross_backend/bench/bench_report.js +83 -0
  44. package/dist/testing/cross_backend/bench/run_cross_impl_bench.d.ts +44 -0
  45. package/dist/testing/cross_backend/bench/run_cross_impl_bench.d.ts.map +1 -0
  46. package/dist/testing/cross_backend/bench/run_cross_impl_bench.js +38 -0
  47. package/dist/testing/cross_backend/bench/scenario.d.ts +57 -0
  48. package/dist/testing/cross_backend/bench/scenario.d.ts.map +1 -0
  49. package/dist/testing/cross_backend/bench/scenario.js +28 -0
  50. package/dist/testing/cross_backend/bootstrap_backend.d.ts +41 -0
  51. package/dist/testing/cross_backend/bootstrap_backend.d.ts.map +1 -0
  52. package/dist/testing/cross_backend/bootstrap_backend.js +34 -0
  53. package/dist/testing/cross_backend/build_test_backend_paths.d.ts +24 -0
  54. package/dist/testing/cross_backend/build_test_backend_paths.d.ts.map +1 -0
  55. package/dist/testing/cross_backend/build_test_backend_paths.js +33 -0
  56. package/dist/testing/cross_backend/capabilities.d.ts +3 -2
  57. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  58. package/dist/testing/cross_backend/default_backend_configs.d.ts +122 -0
  59. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -0
  60. package/dist/testing/cross_backend/default_backend_configs.js +111 -0
  61. package/dist/testing/cross_backend/default_secrets.d.ts +40 -0
  62. package/dist/testing/cross_backend/default_secrets.d.ts.map +1 -0
  63. package/dist/testing/cross_backend/default_secrets.js +39 -0
  64. package/dist/testing/cross_backend/default_spine_surface.d.ts +64 -0
  65. package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -0
  66. package/dist/testing/cross_backend/default_spine_surface.js +121 -0
  67. package/dist/testing/cross_backend/setup.d.ts +270 -34
  68. package/dist/testing/cross_backend/setup.d.ts.map +1 -1
  69. package/dist/testing/cross_backend/setup.js +495 -15
  70. package/dist/testing/cross_backend/spawn_backend.d.ts +58 -0
  71. package/dist/testing/cross_backend/spawn_backend.d.ts.map +1 -0
  72. package/dist/testing/cross_backend/spawn_backend.js +229 -0
  73. package/dist/testing/cross_backend/spine_stub_backend_config.d.ts +66 -0
  74. package/dist/testing/cross_backend/spine_stub_backend_config.d.ts.map +1 -0
  75. package/dist/testing/cross_backend/spine_stub_backend_config.js +49 -0
  76. package/dist/testing/cross_backend/sse_round_trip.d.ts +37 -0
  77. package/dist/testing/cross_backend/sse_round_trip.d.ts.map +1 -0
  78. package/dist/testing/cross_backend/sse_round_trip.js +137 -0
  79. package/dist/testing/cross_backend/standard.d.ts +96 -0
  80. package/dist/testing/cross_backend/standard.d.ts.map +1 -0
  81. package/dist/testing/cross_backend/standard.js +49 -0
  82. package/dist/testing/cross_backend/testing_reset_actions.d.ts +171 -0
  83. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -0
  84. package/dist/testing/cross_backend/testing_reset_actions.js +213 -0
  85. package/dist/testing/cross_backend/testing_server_bun.d.ts +5 -0
  86. package/dist/testing/cross_backend/testing_server_bun.d.ts.map +1 -0
  87. package/dist/testing/cross_backend/testing_server_bun.js +59 -0
  88. package/dist/testing/cross_backend/testing_server_core.d.ts +140 -0
  89. package/dist/testing/cross_backend/testing_server_core.d.ts.map +1 -0
  90. package/dist/testing/cross_backend/testing_server_core.js +68 -0
  91. package/dist/testing/cross_backend/testing_server_deno.d.ts +5 -0
  92. package/dist/testing/cross_backend/testing_server_deno.d.ts.map +1 -0
  93. package/dist/testing/cross_backend/testing_server_deno.js +37 -0
  94. package/dist/testing/cross_backend/testing_server_node.d.ts +5 -0
  95. package/dist/testing/cross_backend/testing_server_node.d.ts.map +1 -0
  96. package/dist/testing/cross_backend/testing_server_node.js +50 -0
  97. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +72 -0
  98. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -0
  99. package/dist/testing/cross_backend/ts_spine_backend_config.js +112 -0
  100. package/dist/testing/cross_backend/ws_round_trip.d.ts +35 -0
  101. package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -0
  102. package/dist/testing/cross_backend/ws_round_trip.js +113 -0
  103. package/dist/testing/data_exposure.d.ts +4 -6
  104. package/dist/testing/data_exposure.d.ts.map +1 -1
  105. package/dist/testing/data_exposure.js +1 -5
  106. package/dist/testing/db_entities.d.ts +18 -7
  107. package/dist/testing/db_entities.d.ts.map +1 -1
  108. package/dist/testing/db_entities.js +18 -7
  109. package/dist/testing/integration.d.ts +27 -6
  110. package/dist/testing/integration.d.ts.map +1 -1
  111. package/dist/testing/integration.js +93 -58
  112. package/dist/testing/round_trip.d.ts +4 -5
  113. package/dist/testing/round_trip.d.ts.map +1 -1
  114. package/dist/testing/round_trip.js +1 -5
  115. package/dist/testing/rpc_helpers.d.ts +10 -4
  116. package/dist/testing/rpc_helpers.d.ts.map +1 -1
  117. package/dist/testing/rpc_helpers.js +1 -1
  118. package/dist/testing/rpc_round_trip.d.ts +5 -5
  119. package/dist/testing/rpc_round_trip.d.ts.map +1 -1
  120. package/dist/testing/rpc_round_trip.js +1 -5
  121. package/dist/testing/sse_round_trip.d.ts.map +1 -1
  122. package/dist/testing/sse_round_trip.js +1 -68
  123. package/dist/testing/standard.d.ts +4 -5
  124. package/dist/testing/standard.d.ts.map +1 -1
  125. package/dist/testing/stubs.d.ts +10 -3
  126. package/dist/testing/stubs.d.ts.map +1 -1
  127. package/dist/testing/stubs.js +9 -2
  128. package/dist/testing/testing_rate_limiter.d.ts +59 -0
  129. package/dist/testing/testing_rate_limiter.d.ts.map +1 -0
  130. package/dist/testing/testing_rate_limiter.js +74 -0
  131. package/dist/testing/transports/bootstrap.d.ts +52 -0
  132. package/dist/testing/transports/bootstrap.d.ts.map +1 -0
  133. package/dist/testing/transports/bootstrap.js +70 -0
  134. package/dist/testing/transports/fetch_transport.d.ts +81 -0
  135. package/dist/testing/transports/fetch_transport.d.ts.map +1 -0
  136. package/dist/testing/transports/fetch_transport.js +74 -0
  137. package/dist/testing/transports/sse_frame_reader.d.ts +41 -0
  138. package/dist/testing/transports/sse_frame_reader.d.ts.map +1 -0
  139. package/dist/testing/transports/sse_frame_reader.js +84 -0
  140. package/dist/testing/transports/sse_transport.d.ts +54 -0
  141. package/dist/testing/transports/sse_transport.d.ts.map +1 -0
  142. package/dist/testing/transports/sse_transport.js +51 -0
  143. package/dist/testing/transports/ws_client.d.ts +108 -0
  144. package/dist/testing/transports/ws_client.d.ts.map +1 -0
  145. package/dist/testing/transports/ws_client.js +56 -0
  146. package/dist/testing/transports/ws_transport.d.ts +43 -0
  147. package/dist/testing/transports/ws_transport.d.ts.map +1 -0
  148. package/dist/testing/transports/ws_transport.js +169 -0
  149. package/dist/testing/ws_round_trip.d.ts +21 -103
  150. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  151. package/dist/testing/ws_round_trip.js +42 -40
  152. package/dist/ui/CLAUDE.md +5 -3
  153. package/dist/ui/MenuLink.svelte +16 -16
  154. package/dist/ui/MenuLink.svelte.d.ts +13 -4
  155. package/dist/ui/MenuLink.svelte.d.ts.map +1 -1
  156. package/package.json +20 -3
  157. package/dist/testing/transports/surface_source.d.ts +0 -51
  158. package/dist/testing/transports/surface_source.d.ts.map +0 -1
  159. 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;AAM1B,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;AAyHD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,mBAAmB,KAAG,IAgIvE,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 { SurfaceSource } from './transports/surface_source.js';
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
- * Source of the app surface. Currently requires `kind: 'inline'`
36
- * the cross-process snapshot variant lands alongside the spawned-backend
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: SurfaceSource;
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,aAAa,EAAC,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,iBAAiB,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,2CAA2C;IAC3C,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,cAAc,EAAE,aAAa,CAAC;IAC9B,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"}
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"}
@@ -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., tx's `extend_middleware_for_tx_binary`). */
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 attack surface testing.
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 snapshot and adversarial testing
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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"}
@@ -206,7 +206,14 @@ export const create_stub_app_server_context = (session_options) => {
206
206
  };
207
207
  };
208
208
  /**
209
- * Create an `AppSurfaceSpec` for attack surface testing.
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 snapshot and adversarial testing
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
+ };