@fuzdev/fuz_app 0.126.0 → 0.128.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 (77) hide show
  1. package/dist/actions/CLAUDE.md +37 -21
  2. package/dist/actions/connection_closer.d.ts +16 -4
  3. package/dist/actions/connection_closer.d.ts.map +1 -1
  4. package/dist/actions/connection_closer.js +20 -4
  5. package/dist/actions/register_action_ws.d.ts +10 -2
  6. package/dist/actions/register_action_ws.d.ts.map +1 -1
  7. package/dist/actions/register_action_ws.js +6 -3
  8. package/dist/actions/register_ws_endpoint.d.ts +3 -1
  9. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  10. package/dist/actions/register_ws_endpoint.js +3 -1
  11. package/dist/auth/CLAUDE.md +13 -9
  12. package/dist/auth/audit_emitter.d.ts +59 -12
  13. package/dist/auth/audit_emitter.d.ts.map +1 -1
  14. package/dist/auth/audit_emitter.js +53 -15
  15. package/dist/auth/middleware.d.ts +30 -0
  16. package/dist/auth/middleware.d.ts.map +1 -1
  17. package/dist/auth/middleware.js +59 -0
  18. package/dist/http/CLAUDE.md +3 -3
  19. package/dist/http/surface.d.ts +12 -11
  20. package/dist/http/surface.d.ts.map +1 -1
  21. package/dist/http/surface.js +1 -15
  22. package/dist/rate_limiter.d.ts +19 -1
  23. package/dist/rate_limiter.d.ts.map +1 -1
  24. package/dist/rate_limiter.js +23 -0
  25. package/dist/realtime/sse_auth_guard.d.ts +3 -1
  26. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  27. package/dist/server/app_server.d.ts +41 -13
  28. package/dist/server/app_server.d.ts.map +1 -1
  29. package/dist/server/app_server.js +105 -50
  30. package/dist/testing/CLAUDE.md +51 -38
  31. package/dist/testing/app_server.d.ts +1 -1
  32. package/dist/testing/app_server.d.ts.map +1 -1
  33. package/dist/testing/app_server.js +15 -14
  34. package/dist/testing/attack_surface.d.ts +1 -1
  35. package/dist/testing/attack_surface.d.ts.map +1 -1
  36. package/dist/testing/audit_drift_guard.d.ts +4 -2
  37. package/dist/testing/audit_drift_guard.d.ts.map +1 -1
  38. package/dist/testing/audit_drift_guard.js +14 -4
  39. package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
  40. package/dist/testing/cross_backend/account_lifecycle.js +1 -2
  41. package/dist/testing/cross_backend/capabilities.d.ts +3 -1
  42. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  43. package/dist/testing/cross_backend/default_backend_configs.d.ts +3 -34
  44. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  45. package/dist/testing/cross_backend/default_backend_configs.js +1 -31
  46. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +1 -1
  47. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
  48. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +3 -3
  49. package/dist/testing/cross_backend/spine_surface_constants.d.ts +43 -3
  50. package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
  51. package/dist/testing/cross_backend/spine_surface_constants.js +43 -3
  52. package/dist/testing/cross_backend/testing_backdoor.d.ts +15 -1
  53. package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
  54. package/dist/testing/cross_backend/testing_backdoor.js +49 -5
  55. package/dist/testing/cross_backend/testing_reset_actions.d.ts +27 -18
  56. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  57. package/dist/testing/cross_backend/testing_reset_actions.js +30 -19
  58. package/dist/testing/cross_backend/testing_server_core.d.ts +11 -11
  59. package/dist/testing/cross_backend/testing_server_core.d.ts.map +1 -1
  60. package/dist/testing/cross_backend/testing_server_core.js +2 -2
  61. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +0 -2
  62. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
  63. package/dist/testing/cross_backend/ts_spine_backend_config.js +2 -4
  64. package/dist/testing/cross_backend/ws_action_rate_limit.d.ts +1 -1
  65. package/dist/testing/cross_backend/ws_action_rate_limit.d.ts.map +1 -1
  66. package/dist/testing/cross_backend/ws_round_trip.d.ts +2 -1
  67. package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
  68. package/dist/testing/cross_backend/ws_round_trip.js +40 -4
  69. package/dist/testing/middleware.d.ts.map +1 -1
  70. package/dist/testing/middleware.js +4 -3
  71. package/dist/testing/stubs.d.ts +26 -6
  72. package/dist/testing/stubs.d.ts.map +1 -1
  73. package/dist/testing/stubs.js +56 -16
  74. package/dist/testing/ws_round_trip.d.ts +7 -31
  75. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  76. package/dist/testing/ws_round_trip.js +0 -38
  77. package/package.json +1 -1
@@ -168,16 +168,21 @@ export const testing_reset_action_spec = {
168
168
  * the cross-backend conformance suite uses in place of a poll/sleep before
169
169
  * asserting on audit rows.
170
170
  *
171
- * On the TS spine the barrier is **satisfied by construction**: the test
172
- * binary runs `await_pending_effects: true`, so every **HTTP** mutation's
173
- * fire-and-forget audit emits are awaited before its response returns — by
174
- * the time a later drain call runs, prior emits are already durable. A
175
- * WS-dispatched mutation replies before its effects flush, so on TS this
176
- * action is not a barrier for WS-originated emits; issue audit-asserting
177
- * mutations over HTTP. The action still
178
- * exists so the cross-backend test body calls the same method on every
179
- * backend; the Rust spine (whose audit writes are detached tokio tasks)
180
- * does the real await in `AuditEmitter::drain_inflight`.
171
+ * The handler awaits `AuditEmitter.drain_inflight`, which waits until no
172
+ * tracked `emit` write is outstanding — so the binary's emitter must be built
173
+ * with `track_inflight: true`, or the drain resolves at once and is no barrier.
174
+ * Tracking is what makes it a barrier whatever the transport: an **HTTP**
175
+ * mutation on a binary running `await_pending_effects: true` has its writes
176
+ * settled by the time it responds, but a **WS** mutation is answered before
177
+ * its effects flush (reply-then-close for a self-revocation), so its audit
178
+ * write can still be in flight when the caller reads the reply. The Rust
179
+ * spine does the same await in `AuditEmitter::drain_inflight`, over an
180
+ * emitter built with `new_with_inflight_tracking`.
181
+ *
182
+ * Covered: every `emit` that ran before the drain was dispatched, and any
183
+ * started while it waits. A write that starts after the drain resolves is
184
+ * not — nor are eager writes outside `emit` (session touch, token usage),
185
+ * which only the dispatching transport's own flush awaits.
181
186
  *
182
187
  * `auth` gates on the daemon-token credential, matching `_testing_reset`.
183
188
  */
@@ -246,13 +251,18 @@ export const testing_mint_session_action_spec = {
246
251
  'signed cookie value (exercises the authoritative server-side DB-row expiry gate).'
247
252
  };
248
253
  /**
249
- * Build the standalone `_testing_drain_effects` action. No deps — on TS the
250
- * barrier is satisfied by `await_pending_effects` (see the spec doc), so the
251
- * handler just returns `{ok: true}`. Mount it on any test endpoint whose
252
- * suite asserts on audit rows (the spine binary bundles it via
254
+ * Build the standalone `_testing_drain_effects` action over `audit` — the
255
+ * emitter the backend's handlers emit through (`deps.audit`), built with
256
+ * `track_inflight: true` (see the spec doc). Mount it on any test endpoint
257
+ * whose suite asserts on audit rows (the spine binary bundles it via
253
258
  * `create_testing_actions`; in-process suites mount it directly).
259
+ *
260
+ * @param audit - the backend's emitter, whose in-flight writes the action awaits
254
261
  */
255
- export const create_testing_drain_effects_action = () => rpc_action(testing_drain_effects_action_spec, async () => ({ ok: true }));
262
+ export const create_testing_drain_effects_action = (audit) => rpc_action(testing_drain_effects_action_spec, async () => {
263
+ await audit.drain_inflight();
264
+ return { ok: true };
265
+ });
256
266
  /**
257
267
  * `_testing_put_fact` — seed an **embedded** fact (`fact.bytes`) for the
258
268
  * cross-process fact-serving suite, which drives over real HTTP and has no
@@ -405,9 +415,10 @@ export const create_testing_action_manifest_action = (mounted) => {
405
415
  /**
406
416
  * Build the testing RPC actions for a test binary's registry.
407
417
  *
408
- * Returns `_testing_reset` — the single privileged action test binaries
409
- * register. The test binary calls this at server-assembly time and
410
- * registers the result on its dispatcher.
418
+ * Returns the bundled `_testing_*` actions (module doc). The test binary
419
+ * calls this at server-assembly time and registers the result on its
420
+ * dispatcher. `_testing_drain_effects` drains `deps.audit`, so the binary's
421
+ * `audit_factory` builds the emitter with `track_inflight: true`.
411
422
  *
412
423
  * The reset action's table-wipe list mirrors
413
424
  * `auth_integration_truncate_tables` from `testing/db.ts` — the
@@ -533,7 +544,7 @@ export const create_testing_actions = (deps, options) => {
533
544
  });
534
545
  return { hash };
535
546
  }),
536
- create_testing_drain_effects_action(),
547
+ create_testing_drain_effects_action(deps.audit),
537
548
  create_testing_schema_snapshot_action(),
538
549
  create_testing_migration_tracker_action()
539
550
  ];
@@ -9,13 +9,13 @@ import '../assert_dev_env.js';
9
9
  * orchestration — stale-daemon check, daemon-info write, serve, post-serve
10
10
  * WS attach, graceful shutdown — and delegates the runtime-boundary
11
11
  * primitives (HTTP serve, WS upgrade construction, signals, pid, exit) to a
12
- * {@link TestingServerAdapter}. The shipped adapters are
12
+ * `TestingServerAdapter`. The shipped adapters are
13
13
  * `testing/cross_backend/testing_server_node.ts` (`@hono/node-server` + `@hono/node-ws`),
14
14
  * `testing/cross_backend/testing_server_deno.ts` (`Deno.serve` + `@hono/deno`), and
15
15
  * `testing/cross_backend/testing_server_bun.ts` (`Bun.serve` + `@hono/bun`).
16
16
  *
17
17
  * The app itself — routes, RPC, DB, `_testing_reset`, WS endpoints — is the
18
- * caller's {@link StartTestingServerOptions.build_app} seam, so this core
18
+ * caller's `StartTestingServerOptions.build_app` seam, so this core
19
19
  * stays domain-free. WebSocket endpoints mount through `create_app_server`'s
20
20
  * `ws_endpoints`: `build_app` asks its context to prepare the runtime's
21
21
  * upgrade helper and passes the resulting factory as
@@ -49,7 +49,7 @@ export interface ServeHandle {
49
49
  /** Adapter-specific server ref for post-serve hooks. Type-erased at the seam. */
50
50
  native?: unknown;
51
51
  }
52
- /** Options for {@link TestingServerAdapter.prepare_websocket}. */
52
+ /** Options for `TestingServerAdapter.prepare_websocket`. */
53
53
  export interface PrepareWebsocketOptions {
54
54
  /**
55
55
  * Largest inbound WebSocket message, in bytes — pass the largest
@@ -70,7 +70,7 @@ export interface PrepareWebsocketOptions {
70
70
  *
71
71
  * `create_upgrade_websocket` is the factory a build passes to
72
72
  * `create_app_server` as `AppServerOptions.create_upgrade_websocket`.
73
- * `attach_to_server` runs after `serve()` returns a {@link ServeHandle} —
73
+ * `attach_to_server` runs after `serve()` returns a `ServeHandle` —
74
74
  * Node uses it for `injectWebSocket(server)`, and it does nothing when the
75
75
  * factory was never called (no `ws_endpoints`); Deno and Bun leave it
76
76
  * undefined.
@@ -83,7 +83,7 @@ export interface PreparedWebsocket {
83
83
  * Runtime adapter contract for the test-binary entry. Each adapter
84
84
  * (`testing/cross_backend/testing_server_node.ts`, `testing/cross_backend/testing_server_deno.ts`,
85
85
  * `testing/cross_backend/testing_server_bun.ts`) implements this and hands the shape to
86
- * {@link start_testing_server}.
86
+ * `start_testing_server`.
87
87
  */
88
88
  export interface TestingServerAdapter {
89
89
  /** Human-readable runtime label for log output (e.g. `"Node"`, `"Deno"`). */
@@ -94,7 +94,7 @@ export interface TestingServerAdapter {
94
94
  get_connection_ip: (c: Context) => string | undefined;
95
95
  /** Prepare the runtime's WS upgrade helper and its post-serve attach step. */
96
96
  prepare_websocket: (options?: PrepareWebsocketOptions) => PreparedWebsocket;
97
- /** Bind `app.fetch` to `port` on `hostname`; return a {@link ServeHandle}. */
97
+ /** Bind `app.fetch` to `port` on `hostname`; return a `ServeHandle`. */
98
98
  serve: (options: {
99
99
  fetch: Hono['fetch'];
100
100
  port: number;
@@ -107,7 +107,7 @@ export interface TestingServerAdapter {
107
107
  /** Forceful exit on graceful-shutdown completion or fatal error. */
108
108
  exit: (code: number) => never;
109
109
  }
110
- /** What the core hands a {@link StartTestingServerOptions.build_app} seam. */
110
+ /** What the core hands a `StartTestingServerOptions.build_app` seam. */
111
111
  export interface BuildTestingAppContext {
112
112
  /**
113
113
  * Prepare the runtime adapter's WS upgrade helper and return the factory
@@ -120,7 +120,7 @@ export interface BuildTestingAppContext {
120
120
  */
121
121
  prepare_websocket: (options?: PrepareWebsocketOptions) => (app: Hono) => UpgradeWebSocket;
122
122
  }
123
- /** The assembled app a {@link StartTestingServerOptions.build_app} seam returns. */
123
+ /** The assembled app a `StartTestingServerOptions.build_app` seam returns. */
124
124
  export interface BuiltTestingApp {
125
125
  /** The assembled Hono app (HTTP routes, RPC, and WS endpoints already mounted). */
126
126
  app: Hono;
@@ -131,7 +131,7 @@ export interface BuiltTestingApp {
131
131
  */
132
132
  close: () => Promise<void>;
133
133
  }
134
- /** Options for {@link start_testing_server}. */
134
+ /** Options for `start_testing_server`. */
135
135
  export interface StartTestingServerOptions {
136
136
  /** Runtime-boundary adapter (Node or Deno). */
137
137
  adapter: TestingServerAdapter;
@@ -173,8 +173,8 @@ export declare const is_loopback_host: (host: string) => boolean;
173
173
  *
174
174
  * Mirrors a production `start_server` at the surface level — stale-daemon
175
175
  * check, daemon-info write, bind, graceful shutdown — but the app is the
176
- * caller's no-domain (or domain) {@link StartTestingServerOptions.build_app}
177
- * and the runtime boundary is the {@link TestingServerAdapter}. Refuses any
176
+ * caller's no-domain (or domain) `StartTestingServerOptions.build_app`
177
+ * and the runtime boundary is the `TestingServerAdapter`. Refuses any
178
178
  * non-loopback bind host (the test binary must stay on loopback — see
179
179
  * `is_loopback_host`).
180
180
  */
@@ -1 +1 @@
1
- {"version":3,"file":"testing_server_core.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_server_core.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"testing_server_core.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_server_core.ts"],"names":[],"mappings":"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"}
@@ -19,8 +19,8 @@ export const is_loopback_host = (host) => {
19
19
  *
20
20
  * Mirrors a production `start_server` at the surface level — stale-daemon
21
21
  * check, daemon-info write, bind, graceful shutdown — but the app is the
22
- * caller's no-domain (or domain) {@link StartTestingServerOptions.build_app}
23
- * and the runtime boundary is the {@link TestingServerAdapter}. Refuses any
22
+ * caller's no-domain (or domain) `StartTestingServerOptions.build_app`
23
+ * and the runtime boundary is the `TestingServerAdapter`. Refuses any
24
24
  * non-loopback bind host (the test binary must stay on loopback — see
25
25
  * `is_loopback_host`).
26
26
  */
@@ -17,8 +17,6 @@ import '../assert_dev_env.js';
17
17
  * @module
18
18
  */
19
19
  import type { BackendConfig } from './backend_config.js';
20
- /** Env var naming the backend root dir; `{dir}/run/daemon_token` must match `bootstrap.daemon_token_path`. */
21
- export declare const TS_SPINE_DIR_ENV = "FUZ_TESTING_TS_SPINE_DIR";
22
20
  /** Default port for the Node TS spine binary — slots beside the Rust `spine_stub` (1177). */
23
21
  export declare const TS_SPINE_NODE_DEFAULT_PORT = 1178;
24
22
  /** Default port for the Deno TS spine binary. */
@@ -1 +1 @@
1
- {"version":3,"file":"ts_spine_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ts_spine_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAUzD,8GAA8G;AAC9G,eAAO,MAAM,gBAAgB,6BAA6B,CAAC;AA2C3D,6FAA6F;AAC7F,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,iDAAiD;AACjD,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,gDAAgD;AAChD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,uDAAuD,CAAC;AAEvF,MAAM,WAAW,2BAA2B;IAC3C,mFAAmF;IACnF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;;;;OASG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,8BAA8B,CAAC,EAAE,MAAM,CAAC;CACjD;AAWD;;;GAGG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAoBF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GACvC,UAAS,2BAAgC,KACvC,aAoBF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAiCF,CAAC"}
1
+ {"version":3,"file":"ts_spine_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ts_spine_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAsDzD,6FAA6F;AAC7F,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,iDAAiD;AACjD,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,gDAAgD;AAChD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,uDAAuD,CAAC;AAEvF,MAAM,WAAW,2BAA2B;IAC3C,mFAAmF;IACnF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;;;;OASG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,8BAA8B,CAAC,EAAE,MAAM,CAAC;CACjD;AAWD;;;GAGG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAoBF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GACvC,UAAS,2BAAgC,KACvC,aAoBF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAiCF,CAAC"}
@@ -1,9 +1,7 @@
1
1
  import '../assert_dev_env.js';
2
2
  import { build_test_backend_paths } from './build_test_backend_paths.js';
3
- import { LOGIN_RATE_LIMIT_ENABLED_ENV, action_rate_limit_env, make_default_ts_backend_config, ts_default_capabilities } from './default_backend_configs.js';
4
- import { SPINE_SSE_PATH } from './spine_surface_constants.js';
5
- /** Env var naming the backend root dir; `{dir}/run/daemon_token` must match `bootstrap.daemon_token_path`. */
6
- export const TS_SPINE_DIR_ENV = 'FUZ_TESTING_TS_SPINE_DIR';
3
+ import { action_rate_limit_env, make_default_ts_backend_config, ts_default_capabilities } from './default_backend_configs.js';
4
+ import { LOGIN_RATE_LIMIT_ENABLED_ENV, SPINE_SSE_PATH, TS_SPINE_DIR_ENV } from './spine_surface_constants.js';
7
5
  /**
8
6
  * Capabilities for the TS spine binary — `ts_default_capabilities` plus `sse`
9
7
  * (the binary wires `audit_log_sse`), `ready` (the binary live-mounts the
@@ -1,6 +1,6 @@
1
1
  import '../assert_dev_env.js';
2
2
  import type { SetupTest } from './setup.js';
3
- /** Options for {@link describe_ws_action_rate_limit_cross_tests}. */
3
+ /** Options for `describe_ws_action_rate_limit_cross_tests`. */
4
4
  export interface WsActionRateLimitCrossTestOptions {
5
5
  /** Per-test fixture producer (cross-process only — see the module doc). */
6
6
  readonly setup_test: SetupTest;
@@ -1 +1 @@
1
- {"version":3,"file":"ws_action_rate_limit.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ws_action_rate_limit.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmE9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAyC5C,qEAAqE;AACrE,MAAM,WAAW,iCAAiC;IACjD,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC9B;AAkBD;;;;GAIG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,iCAAiC,KACxC,IAqRF,CAAC"}
1
+ {"version":3,"file":"ws_action_rate_limit.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ws_action_rate_limit.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmE9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAyC5C,+DAA+D;AAC/D,MAAM,WAAW,iCAAiC;IACjD,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC9B;AAkBD;;;;GAIG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,iCAAiC,KACxC,IAqRF,CAAC"}
@@ -38,7 +38,8 @@ export interface CrossProcessWsTestOptions {
38
38
  * oversized message closing with `WS_CLOSE_MESSAGE_TOO_BIG`, anonymous-upgrade
39
39
  * refusal, disallowed-origin refusal, and — when `rpc_path` is supplied —
40
40
  * session revocation and account deletion closing the live socket, plus a
41
- * self-revoking socket reading its reply before its close.
41
+ * self-revoking socket reading its reply before its close, and a WS mutation's
42
+ * audit row read back after `_testing_drain_effects`.
42
43
  */
43
44
  export declare const describe_cross_process_ws_tests: (options: CrossProcessWsTestOptions) => void;
44
45
  //# sourceMappingURL=ws_round_trip.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ws_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ws_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA6F9B,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAK5C,iEAAiE;AACjE,MAAM,WAAW,yBAAyB;IACzC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,GAAI,SAAS,yBAAyB,KAAG,IA+RpF,CAAC"}
1
+ {"version":3,"file":"ws_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ws_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAwG9B,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,mBAAmB,CAAC;AAEtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAK5C,iEAAiE;AACjE,MAAM,WAAW,yBAAyB;IACzC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,+BAA+B,GAAI,SAAS,yBAAyB,KAAG,IAkVpF,CAAC"}
@@ -55,7 +55,11 @@ import '../assert_dev_env.js';
55
55
  * `capabilities.ws_self_revocation_reply` — reads the reply to its request
56
56
  * first. The TS spine queues the close behind the commit and sends the reply
57
57
  * ahead of it, so the caller is never left without an answer; the Rust spine
58
- * does not answer yet, which that flag records.
58
+ * does not answer yet, which that flag records. Under the same gate (and
59
+ * `rpc_path`), a socket mints an API token over WS and the token's
60
+ * `token_create` audit row is read back after `_testing_drain_effects` — the
61
+ * barrier a WS mutation needs, since a WS reply can precede its audit write.
62
+ * The binary's `create_testing_actions` mount provides the drain.
59
63
  *
60
64
  * Gated on `capabilities.ws` — backends without an end-to-end WS transport
61
65
  * skip (the cases still surface as `.skip` in the report). Cross-process
@@ -68,14 +72,15 @@ import { assert, describe } from 'vitest';
68
72
  import { assert_rejects } from '@fuzdev/fuz_util/testing.ts';
69
73
  import { heartbeat_action_spec } from '../../actions/heartbeat.js';
70
74
  import { DEFAULT_WS_MAX_MESSAGE_BYTES, WS_CLOSE_MESSAGE_TOO_BIG, WS_CLOSE_SESSION_REVOKED } from '../../actions/transports.js';
71
- import { account_session_revoke_all_action_spec } from '../../auth/account_action_specs.js';
72
- import { account_delete_action_spec } from '../../auth/admin_action_specs.js';
75
+ import { account_session_revoke_all_action_spec, account_token_create_action_spec } from '../../auth/account_action_specs.js';
76
+ import { account_delete_action_spec, audit_log_list_action_spec, AuditLogListOutput } from '../../auth/admin_action_specs.js';
73
77
  import { JSONRPC_ERROR_CODES } from '../../http/jsonrpc_errors.js';
74
78
  import { is_response_for } from '../transports/ws_client.js';
75
79
  import { create_ws_transport } from '../transports/ws_transport.js';
76
80
  import { connect_raw_ws, is_raw_ws_close, RAW_WS_OPCODE_TEXT } from '../transports/ws_raw_client.js';
77
81
  import { create_rpc_post_init } from '../rpc_helpers.js';
78
82
  import { test_if } from './capabilities.js';
83
+ import { cross_rpc_call, expect_output } from './cell_cross_helpers.js';
79
84
  /** Origin guaranteed to fail the `http://localhost:*` allowlist the test backends run with. */
80
85
  const DISALLOWED_ORIGIN = 'http://disallowed.example';
81
86
  /**
@@ -84,7 +89,8 @@ const DISALLOWED_ORIGIN = 'http://disallowed.example';
84
89
  * oversized message closing with `WS_CLOSE_MESSAGE_TOO_BIG`, anonymous-upgrade
85
90
  * refusal, disallowed-origin refusal, and — when `rpc_path` is supplied —
86
91
  * session revocation and account deletion closing the live socket, plus a
87
- * self-revoking socket reading its reply before its close.
92
+ * self-revoking socket reading its reply before its close, and a WS mutation's
93
+ * audit row read back after `_testing_drain_effects`.
88
94
  */
89
95
  export const describe_cross_process_ws_tests = (options) => {
90
96
  const { setup_test, capabilities, base_url, ws_path, origin, rpc_path, max_message_bytes = DEFAULT_WS_MAX_MESSAGE_BYTES } = options;
@@ -308,5 +314,35 @@ export const describe_cross_process_ws_tests = (options) => {
308
314
  await client.close();
309
315
  }
310
316
  });
317
+ // A WS mutation is answered before its effects flush, so its audit row
318
+ // can land after the reply. `_testing_drain_effects` is the barrier: once
319
+ // it returns, the row is visible on either spine.
320
+ test_if(capabilities.ws && capabilities.ws_account_actions && rpc_path !== undefined, "a WS mutation's audit row is visible after _testing_drain_effects", async () => {
321
+ const fixture = await setup_test();
322
+ const client = await create_ws_transport({
323
+ base_url,
324
+ ws_path,
325
+ cookies: fixture.transport.cookies(),
326
+ origin
327
+ });
328
+ try {
329
+ await client.request(1, account_token_create_action_spec.method, {
330
+ name: 'ws drain probe',
331
+ scope: { kind: 'full' },
332
+ lifetime: { kind: 'eternal' }
333
+ });
334
+ }
335
+ finally {
336
+ await client.close();
337
+ }
338
+ const drained = await cross_rpc_call(fixture.fresh_transport({ origin: null }), rpc_path, '_testing_drain_effects', undefined, fixture.create_daemon_token_headers());
339
+ assert.ok(drained.ok, `_testing_drain_effects failed: ${JSON.stringify(drained.error)}`);
340
+ // `_testing_reset` wiped audit_log at setup, so the mint is the only
341
+ // token_create event
342
+ const listed = expect_output(await cross_rpc_call(fixture.transport, rpc_path, audit_log_list_action_spec.method, { event_type: 'token_create' }, fixture.create_session_headers()), AuditLogListOutput);
343
+ assert.strictEqual(listed.events.length, 1, JSON.stringify(listed.events));
344
+ assert.strictEqual(listed.events[0].outcome, 'success');
345
+ assert.strictEqual(listed.events[0].account_id, fixture.account.id);
346
+ });
311
347
  });
312
348
  };
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/middleware.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/middleware.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,7 @@ import { vi, test, assert, describe } from 'vitest';
12
12
  import { Hono } from 'hono';
13
13
  import { Logger } from '@fuzdev/fuz_util/log.ts';
14
14
  import { create_bearer_auth_middleware } from '../auth/bearer_auth.js';
15
+ import { AUTH_MIDDLEWARE_PATH } from '../auth/middleware.js';
15
16
  import { query_validate_api_token } from '../auth/api_token_queries.js';
16
17
  import { query_account_by_id, query_actor_by_id, query_active_actors_by_account } from '../auth/account_queries.js';
17
18
  import { query_role_grant_find_active_for_actor } from '../auth/role_grant_queries.js';
@@ -117,7 +118,7 @@ export const create_bearer_auth_test_app = (tc) => {
117
118
  c.set('client_ip', TEST_CLIENT_IP);
118
119
  await next();
119
120
  });
120
- app.use('/api/*', bearer_middleware);
121
+ app.use(AUTH_MIDDLEWARE_PATH, bearer_middleware);
121
122
  // route handler echoes the account-grain identity the middleware writes
122
123
  // (bearer auth sets `ACCOUNT_ID_KEY` + `CREDENTIAL_TYPE_KEY` +
123
124
  // `AUTH_API_TOKEN_ID_KEY`; it never builds the full request context —
@@ -230,8 +231,8 @@ export const create_test_middleware_stack_app = (options) => {
230
231
  const bearer_mw = create_bearer_auth_middleware(STUB_DEPS, new Logger('test', { level: 'off' }));
231
232
  const app = new Hono();
232
233
  app.use('*', proxy_mw);
233
- app.use('/api/*', origin_mw);
234
- app.use('/api/*', bearer_mw);
234
+ app.use(AUTH_MIDDLEWARE_PATH, origin_mw);
235
+ app.use(AUTH_MIDDLEWARE_PATH, bearer_mw);
235
236
  // echo route for assertions — exposes the account-grain identity bearer
236
237
  // auth writes (`ACCOUNT_ID_KEY`); the full request context is the
237
238
  // dispatcher's authorization phase concern, not middleware.
@@ -62,7 +62,8 @@ export declare const stub_mw: (_c: any, next: any) => Promise<void>;
62
62
  * immediately; `notify` is a no-op; `add_listener` throws, so a test that
63
63
  * wires a listener fails loudly instead of silently never firing
64
64
  * (`create_recording_audit_emitter` accepts listeners); `listener_count`
65
- * returns 0. Tests asserting on real audit-row persistence (or on listener
65
+ * returns 0; `drain_inflight` resolves immediately, since nothing is ever
66
+ * written. Tests asserting on real audit-row persistence (or on listener
66
67
  * fan-out) build a real emitter via `create_audit_emitter` against a stub or
67
68
  * real DB — `create_test_app` already does this on the test backend.
68
69
  */
@@ -84,7 +85,11 @@ export declare const stub_app_deps: AppDeps;
84
85
  * Create no-op `AppDeps` for auth surface testing.
85
86
  */
86
87
  export declare const create_stub_app_deps: () => AppDeps;
87
- /** Create the API middleware stub array matching `create_auth_middleware_specs` output. */
88
+ /**
89
+ * Create the API middleware stub array matching `create_auth_middleware_specs`
90
+ * output. Like the real factory's, each spec gets its own pass-through handler,
91
+ * so `assert_middleware_stack_mounted` can tell the layers apart by identity.
92
+ */
88
93
  export declare const create_stub_api_middleware: (options?: {
89
94
  /** Include the daemon_token middleware layer. */
90
95
  include_daemon_token?: boolean;
@@ -132,10 +137,19 @@ export interface CreateTestAppSurfaceSpecOptions {
132
137
  * The server's origin allowlist — `AppServerOptions.allowed_origins`.
133
138
  * Read only as the default for a `ws_endpoints` spec that declares no
134
139
  * `allowed_origins`, as `create_app_server` does; required when any spec
135
- * omits its own, since surface generation throws on an unresolved one.
140
+ * omits its own — without it, such a spec throws, since the surface records
141
+ * the exact patterns the upgrade gate matches.
136
142
  */
137
143
  allowed_origins?: ReadonlyArray<RegExp>;
138
- /** Transform middleware array (e.g., zap's `extend_middleware_for_zap_binary`). */
144
+ /**
145
+ * Transform middleware array — `AppServerOptions.transform_middleware`.
146
+ * Receives shallow copies of a stub `trusted_proxy` spec (at `'*'`)
147
+ * followed by the stub auth stack, in a fresh array, as
148
+ * `create_app_server` passes the real ones. Throws, like
149
+ * `create_app_server`, if the result doesn't keep each of those specs
150
+ * mounted once at its original path in its original order
151
+ * (`assert_middleware_stack_mounted`).
152
+ */
139
153
  transform_middleware?: (specs: Array<MiddlewareSpec>) => Array<MiddlewareSpec>;
140
154
  /**
141
155
  * Bootstrap config — symmetric with `AppServerOptions.bootstrap`. Discriminated
@@ -161,13 +175,19 @@ export interface CreateTestAppSurfaceSpecOptions {
161
175
  * Mirrors `create_app_server`'s route assembly: consumer routes +
162
176
  * factory-managed bootstrap routes + surface generation — including its
163
177
  * refusal of an `rpc_endpoints` or `ws_endpoints` path outside the auth
164
- * middleware's scope (`assert_endpoint_in_auth_scope`). If
178
+ * middleware's scope (`assert_endpoint_in_auth_scope`) and of a
179
+ * `transform_middleware` that doesn't keep the proxy and auth stack mounted
180
+ * (`assert_middleware_stack_mounted`). The middleware list mirrors the real
181
+ * one: a stub `trusted_proxy` spec at `'*'`, then the stub auth stack. If
165
182
  * `create_app_server` changes how it wires routes, update this helper
166
183
  * to stay in sync (single source of truth for all consumers).
167
184
  *
168
185
  * @param options - surface spec options
169
186
  * @returns the surface spec for the standard suites
170
- * @throws Error when an endpoint path is outside `AUTH_MIDDLEWARE_PATH`
187
+ * @throws Error when an endpoint path is outside `AUTH_MIDDLEWARE_PATH`, a
188
+ * `ws_endpoints` spec declares no `allowed_origins` and no `allowed_origins`
189
+ * option is given, or `transform_middleware` moves, drops, duplicates,
190
+ * reorders, wraps, or replaces the stub proxy spec or a stub auth spec
171
191
  */
172
192
  export declare const create_test_app_surface_spec: (options: CreateTestAppSurfaceSpecOptions) => AppSurfaceSpec;
173
193
  //# 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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"}
@@ -19,7 +19,7 @@ import { resolve_ws_endpoints } from '../actions/ws_endpoint_spec.js';
19
19
  import { AUDIT_LOG_SSE_MAX_PER_SCOPE } from '../realtime/sse_auth_guard.js';
20
20
  import { SubscriberRegistry } from '../realtime/subscriber_registry.js';
21
21
  import { BaseServerEnv } from '../server/env.js';
22
- import { AUTH_MIDDLEWARE_PATH, assert_endpoint_in_auth_scope } from '../auth/middleware.js';
22
+ import { AUTH_MIDDLEWARE_PATH, assert_endpoint_in_auth_scope, assert_middleware_stack_mounted } from '../auth/middleware.js';
23
23
  /**
24
24
  * Create a Proxy that throws descriptive errors on any property access or method call.
25
25
  *
@@ -99,7 +99,8 @@ const stub_db = create_noop_stub('stub_db');
99
99
  * immediately; `notify` is a no-op; `add_listener` throws, so a test that
100
100
  * wires a listener fails loudly instead of silently never firing
101
101
  * (`create_recording_audit_emitter` accepts listeners); `listener_count`
102
- * returns 0. Tests asserting on real audit-row persistence (or on listener
102
+ * returns 0; `drain_inflight` resolves immediately, since nothing is ever
103
+ * written. Tests asserting on real audit-row persistence (or on listener
103
104
  * fan-out) build a real emitter via `create_audit_emitter` against a stub or
104
105
  * real DB — `create_test_app` already does this on the test backend.
105
106
  */
@@ -111,7 +112,8 @@ export const create_test_audit_emitter = () => ({
111
112
  add_listener: () => {
112
113
  throw new Error('create_test_audit_emitter accepts no listeners — use create_recording_audit_emitter');
113
114
  },
114
- listener_count: () => 0
115
+ listener_count: () => 0,
116
+ drain_inflight: async () => { }
115
117
  });
116
118
  /**
117
119
  * Build a no-op `AuditLogSse` for tests that wire `audit_sse` into the
@@ -159,16 +161,27 @@ export const create_stub_app_deps = () => ({
159
161
  audit: create_test_audit_emitter(),
160
162
  connection_closer: create_realtime_closer()
161
163
  });
162
- /** Create the API middleware stub array matching `create_auth_middleware_specs` output. */
164
+ /** A fresh pass-through middleware — a distinct handler per call, unlike the shared `stub_mw`. */
165
+ const create_stub_mw = () => async (_c, next) => next();
166
+ /**
167
+ * Create the API middleware stub array matching `create_auth_middleware_specs`
168
+ * output. Like the real factory's, each spec gets its own pass-through handler,
169
+ * so `assert_middleware_stack_mounted` can tell the layers apart by identity.
170
+ */
163
171
  export const create_stub_api_middleware = (options) => {
164
172
  const specs = [
165
- { name: 'origin', path: AUTH_MIDDLEWARE_PATH, handler: stub_mw, errors: { 403: ApiError } },
166
- { name: 'session', path: AUTH_MIDDLEWARE_PATH, handler: stub_mw },
167
- { name: 'request_context', path: AUTH_MIDDLEWARE_PATH, handler: stub_mw },
173
+ {
174
+ name: 'origin',
175
+ path: AUTH_MIDDLEWARE_PATH,
176
+ handler: create_stub_mw(),
177
+ errors: { 403: ApiError }
178
+ },
179
+ { name: 'session', path: AUTH_MIDDLEWARE_PATH, handler: create_stub_mw() },
180
+ { name: 'request_context', path: AUTH_MIDDLEWARE_PATH, handler: create_stub_mw() },
168
181
  {
169
182
  name: 'bearer_auth',
170
183
  path: AUTH_MIDDLEWARE_PATH,
171
- handler: stub_mw,
184
+ handler: create_stub_mw(),
172
185
  errors: { 401: ApiError, 403: ApiError, 429: RateLimitError }
173
186
  }
174
187
  ];
@@ -176,7 +189,7 @@ export const create_stub_api_middleware = (options) => {
176
189
  specs.push({
177
190
  name: 'daemon_token',
178
191
  path: AUTH_MIDDLEWARE_PATH,
179
- handler: stub_mw,
192
+ handler: create_stub_mw(),
180
193
  errors: { 401: ApiError, 500: ApiError, 503: ApiError }
181
194
  });
182
195
  }
@@ -213,6 +226,21 @@ export const create_stub_app_server_context = (session_options) => {
213
226
  audit_sse: null
214
227
  };
215
228
  };
229
+ /**
230
+ * Narrow a WS endpoint spec that carries its own `allowed_origins` — the only
231
+ * way it resolves when no server list is given to default from.
232
+ *
233
+ * @throws Error when the spec declares no `allowed_origins`
234
+ */
235
+ const to_self_resolved_ws_endpoint = (endpoint) => {
236
+ const { allowed_origins } = endpoint;
237
+ if (!allowed_origins) {
238
+ throw new Error(`create_test_app_surface_spec: ws endpoint ${endpoint.path} has no allowed_origins — ` +
239
+ "pass the server's list as create_test_app_surface_spec's allowed_origins option, " +
240
+ 'or set allowed_origins on the spec');
241
+ }
242
+ return { ...endpoint, allowed_origins };
243
+ };
216
244
  /**
217
245
  * Create an `AppSurfaceSpec` for the standard testing suites.
218
246
  *
@@ -226,13 +254,19 @@ export const create_stub_app_server_context = (session_options) => {
226
254
  * Mirrors `create_app_server`'s route assembly: consumer routes +
227
255
  * factory-managed bootstrap routes + surface generation — including its
228
256
  * refusal of an `rpc_endpoints` or `ws_endpoints` path outside the auth
229
- * middleware's scope (`assert_endpoint_in_auth_scope`). If
257
+ * middleware's scope (`assert_endpoint_in_auth_scope`) and of a
258
+ * `transform_middleware` that doesn't keep the proxy and auth stack mounted
259
+ * (`assert_middleware_stack_mounted`). The middleware list mirrors the real
260
+ * one: a stub `trusted_proxy` spec at `'*'`, then the stub auth stack. If
230
261
  * `create_app_server` changes how it wires routes, update this helper
231
262
  * to stay in sync (single source of truth for all consumers).
232
263
  *
233
264
  * @param options - surface spec options
234
265
  * @returns the surface spec for the standard suites
235
- * @throws Error when an endpoint path is outside `AUTH_MIDDLEWARE_PATH`
266
+ * @throws Error when an endpoint path is outside `AUTH_MIDDLEWARE_PATH`, a
267
+ * `ws_endpoints` spec declares no `allowed_origins` and no `allowed_origins`
268
+ * option is given, or `transform_middleware` moves, drops, duplicates,
269
+ * reorders, wraps, or replaces the stub proxy spec or a stub auth spec
236
270
  */
237
271
  export const create_test_app_surface_spec = (options) => {
238
272
  const ctx = create_stub_app_server_context(options.session_options);
@@ -255,11 +289,9 @@ export const create_test_app_surface_spec = (options) => {
255
289
  // Resolve ws endpoints (mirrors create_app_server). Surface-only —
256
290
  // no `register_ws_endpoint` call here, so no `create_upgrade_websocket` needed.
257
291
  const declared_ws_endpoints = typeof options.ws_endpoints === 'function' ? options.ws_endpoints(ctx) : options.ws_endpoints;
258
- // without the server's list, an origin-less spec reaches surface
259
- // generation unresolved and throws there
260
292
  const resolved_ws_endpoints = options.allowed_origins
261
293
  ? resolve_ws_endpoints(declared_ws_endpoints, options.allowed_origins)
262
- : declared_ws_endpoints;
294
+ : declared_ws_endpoints?.map(to_self_resolved_ws_endpoint);
263
295
  for (const endpoint of resolved_ws_endpoints ?? []) {
264
296
  assert_endpoint_in_auth_scope('ws_endpoints', endpoint.path);
265
297
  }
@@ -277,9 +309,17 @@ export const create_test_app_surface_spec = (options) => {
277
309
  ])
278
310
  : [];
279
311
  const route_specs = [...consumer_routes, ...rpc_route_specs, ...bootstrap_route_specs];
280
- let middleware_specs = create_stub_api_middleware();
312
+ // mirrors `create_app_server`'s `[proxy_spec, ...auth_middleware]`
313
+ const stack_specs = [
314
+ { name: 'trusted_proxy', path: '*', handler: create_stub_mw() },
315
+ ...create_stub_api_middleware()
316
+ ];
317
+ let middleware_specs = stack_specs;
281
318
  if (options.transform_middleware) {
282
- middleware_specs = options.transform_middleware(middleware_specs);
319
+ // the transform gets copies in a fresh array, so mutating what it was
320
+ // handed can't rewrite the originals the check matches against
321
+ middleware_specs = options.transform_middleware(stack_specs.map((spec) => ({ ...spec })));
322
+ assert_middleware_stack_mounted(stack_specs, middleware_specs);
283
323
  }
284
324
  return create_app_surface_spec({
285
325
  middleware_specs,