@fuzdev/fuz_app 0.134.0 → 0.136.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 (118) hide show
  1. package/dist/actions/CLAUDE.md +93 -11
  2. package/dist/actions/register_action_ws.d.ts +23 -3
  3. package/dist/actions/register_action_ws.d.ts.map +1 -1
  4. package/dist/actions/register_action_ws.js +57 -37
  5. package/dist/actions/transports.d.ts +23 -0
  6. package/dist/actions/transports.d.ts.map +1 -1
  7. package/dist/actions/transports.js +23 -0
  8. package/dist/actions/transports_ws_backend.d.ts +133 -10
  9. package/dist/actions/transports_ws_backend.d.ts.map +1 -1
  10. package/dist/actions/transports_ws_backend.js +237 -34
  11. package/dist/actions/ws_endpoint_spec.d.ts +9 -0
  12. package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
  13. package/dist/actions/ws_outbound_writer.d.ts +206 -0
  14. package/dist/actions/ws_outbound_writer.d.ts.map +1 -0
  15. package/dist/actions/ws_outbound_writer.js +431 -0
  16. package/dist/auth/CLAUDE.md +10 -4
  17. package/dist/auth/account_action_specs.d.ts +7 -0
  18. package/dist/auth/account_action_specs.d.ts.map +1 -1
  19. package/dist/auth/account_action_specs.js +11 -1
  20. package/dist/auth/audit_emitter.d.ts +4 -0
  21. package/dist/auth/audit_emitter.d.ts.map +1 -1
  22. package/dist/auth/audit_emitter.js +18 -2
  23. package/dist/auth/audit_log_routes.d.ts.map +1 -1
  24. package/dist/auth/audit_log_routes.js +3 -1
  25. package/dist/auth/cell_action_specs.d.ts +10 -0
  26. package/dist/auth/cell_action_specs.d.ts.map +1 -1
  27. package/dist/auth/cell_action_specs.js +17 -3
  28. package/dist/auth/cell_actions.js +1 -1
  29. package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
  30. package/dist/auth/role_grant_offer_action_specs.js +7 -2
  31. package/dist/auth/role_schema.d.ts +14 -3
  32. package/dist/auth/role_schema.d.ts.map +1 -1
  33. package/dist/auth/role_schema.js +14 -2
  34. package/dist/auth/scope_kind_schema.d.ts +10 -0
  35. package/dist/auth/scope_kind_schema.d.ts.map +1 -1
  36. package/dist/auth/scope_kind_schema.js +11 -0
  37. package/dist/db/migrate.d.ts +1 -1
  38. package/dist/db/migrate.js +2 -2
  39. package/dist/env/load.d.ts +1 -1
  40. package/dist/env/load.js +1 -1
  41. package/dist/http/CLAUDE.md +2 -1
  42. package/dist/http/body_limit.d.ts +74 -0
  43. package/dist/http/body_limit.d.ts.map +1 -0
  44. package/dist/http/body_limit.js +100 -0
  45. package/dist/http/db_routes.d.ts.map +1 -1
  46. package/dist/http/db_routes.js +5 -3
  47. package/dist/http/error_schemas.d.ts +2 -0
  48. package/dist/http/error_schemas.d.ts.map +1 -1
  49. package/dist/http/error_schemas.js +2 -0
  50. package/dist/realtime/outbound_budget.d.ts +91 -0
  51. package/dist/realtime/outbound_budget.d.ts.map +1 -0
  52. package/dist/realtime/outbound_budget.js +108 -0
  53. package/dist/realtime/sse.d.ts +36 -3
  54. package/dist/realtime/sse.d.ts.map +1 -1
  55. package/dist/realtime/sse.js +58 -6
  56. package/dist/realtime/sse_constants.d.ts +12 -0
  57. package/dist/realtime/sse_constants.d.ts.map +1 -1
  58. package/dist/realtime/sse_constants.js +12 -0
  59. package/dist/server/app_server.d.ts.map +1 -1
  60. package/dist/server/app_server.js +3 -6
  61. package/dist/testing/CLAUDE.md +96 -19
  62. package/dist/testing/admin_integration.js +1 -1
  63. package/dist/testing/audit_completeness.d.ts +1 -1
  64. package/dist/testing/audit_completeness.js +1 -1
  65. package/dist/testing/audit_drift_guard.d.ts +19 -6
  66. package/dist/testing/audit_drift_guard.d.ts.map +1 -1
  67. package/dist/testing/audit_drift_guard.js +25 -9
  68. package/dist/testing/cross_backend/body_size.d.ts +18 -5
  69. package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
  70. package/dist/testing/cross_backend/body_size.js +233 -34
  71. package/dist/testing/cross_backend/body_size_smuggling.d.ts +0 -12
  72. package/dist/testing/cross_backend/body_size_smuggling.d.ts.map +1 -1
  73. package/dist/testing/cross_backend/body_size_smuggling.js +103 -108
  74. package/dist/testing/cross_backend/capabilities.d.ts +3 -21
  75. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  76. package/dist/testing/cross_backend/capabilities.js +1 -3
  77. package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
  78. package/dist/testing/cross_backend/cell_crud.js +31 -2
  79. package/dist/testing/cross_backend/conformance_case.d.ts +3 -3
  80. package/dist/testing/cross_backend/credential_header_robustness.d.ts.map +1 -1
  81. package/dist/testing/cross_backend/credential_header_robustness.js +18 -54
  82. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  83. package/dist/testing/cross_backend/default_backend_configs.js +2 -8
  84. package/dist/testing/cross_backend/role_grant_role_grammar.d.ts +6 -0
  85. package/dist/testing/cross_backend/role_grant_role_grammar.d.ts.map +1 -0
  86. package/dist/testing/cross_backend/role_grant_role_grammar.js +117 -0
  87. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +2 -3
  88. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
  89. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +2 -3
  90. package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
  91. package/dist/testing/cross_backend/testing_backdoor.js +10 -4
  92. package/dist/testing/cross_backend/testing_reset_actions.d.ts +87 -2
  93. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  94. package/dist/testing/cross_backend/testing_reset_actions.js +106 -2
  95. package/dist/testing/cross_backend/token_lifetime.d.ts.map +1 -1
  96. package/dist/testing/cross_backend/token_lifetime.js +34 -1
  97. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
  98. package/dist/testing/cross_backend/ts_spine_backend_config.js +8 -13
  99. package/dist/testing/cross_backend/ws_outbound_overflow.d.ts +29 -0
  100. package/dist/testing/cross_backend/ws_outbound_overflow.d.ts.map +1 -0
  101. package/dist/testing/cross_backend/ws_outbound_overflow.js +276 -0
  102. package/dist/testing/integration.js +1 -1
  103. package/dist/testing/schema_introspect.d.ts +30 -2
  104. package/dist/testing/schema_introspect.d.ts.map +1 -1
  105. package/dist/testing/schema_introspect.js +38 -7
  106. package/dist/testing/transports/raw_http.d.ts +78 -0
  107. package/dist/testing/transports/raw_http.d.ts.map +1 -0
  108. package/dist/testing/transports/raw_http.js +141 -0
  109. package/dist/testing/transports/ws_transport.d.ts +4 -2
  110. package/dist/testing/transports/ws_transport.d.ts.map +1 -1
  111. package/dist/testing/transports/ws_transport.js +2 -1
  112. package/dist/testing/ws_round_trip.d.ts +32 -2
  113. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  114. package/dist/testing/ws_round_trip.js +26 -4
  115. package/package.json +1 -1
  116. package/dist/env/resolve.d.ts +0 -163
  117. package/dist/env/resolve.d.ts.map +0 -1
  118. package/dist/env/resolve.js +0 -267
@@ -9,7 +9,6 @@
9
9
  */
10
10
  import { Hono } from 'hono';
11
11
  import { logger } from 'hono/logger';
12
- import { bodyLimit } from 'hono/body-limit';
13
12
  import { z } from 'zod';
14
13
  import { session_cookie_options } from '../auth/session_cookie.js';
15
14
  import { create_audit_log_sse, create_sse_connection_closer, audit_log_event_specs } from '../realtime/sse_auth_guard.js';
@@ -29,7 +28,7 @@ import { flush_pending_effects, flush_post_commit_effects } from '../http/pendin
29
28
  import { assert_endpoint_in_auth_scope, assert_middleware_stack_mounted, create_auth_middleware_specs } from '../auth/middleware.js';
30
29
  import { fuz_auth_guard_resolver } from '../auth/auth_guard_resolver.js';
31
30
  import { create_fuz_authorization_handler } from '../auth/request_context.js';
32
- import { ERROR_PAYLOAD_TOO_LARGE } from '../http/error_schemas.js';
31
+ import { create_body_limit_middleware } from '../http/body_limit.js';
33
32
  import { create_rpc_endpoint } from '../actions/action_rpc.js';
34
33
  import { register_ws_endpoint } from '../actions/register_ws_endpoint.js';
35
34
  import { resolve_ws_endpoints } from '../actions/ws_endpoint_spec.js';
@@ -483,10 +482,7 @@ const assemble_app_server = async (options, releases) => {
483
482
  // Default 1 MiB; pass null to disable.
484
483
  if (options.max_body_size !== null) {
485
484
  const max_size = options.max_body_size ?? DEFAULT_MAX_BODY_SIZE;
486
- app.use(bodyLimit({
487
- maxSize: max_size,
488
- onError: (c) => c.json({ error: ERROR_PAYLOAD_TOO_LARGE }, 413)
489
- }));
485
+ app.use(create_body_limit_middleware(max_size));
490
486
  }
491
487
  apply_middleware_specs(app, middleware_specs);
492
488
  const authorize = create_fuz_authorization_handler({ db: deps.db });
@@ -524,6 +520,7 @@ const assemble_app_server = async (options, releases) => {
524
520
  // added below instead, so the remover is the server's
525
521
  connection_closer: null,
526
522
  max_connections_per_account: endpoint.max_connections_per_account,
523
+ outbound_queue_bytes: endpoint.outbound_queue_bytes,
527
524
  heartbeat: endpoint.heartbeat,
528
525
  artificial_delay: endpoint.artificial_delay,
529
526
  max_message_bytes: endpoint.max_message_bytes,
@@ -99,7 +99,8 @@ mismatch.
99
99
 
100
100
  ### `audit_drift_guard.ts` — audit-emission validation
101
101
 
102
- - `install_audit_drift_guard()` — `beforeEach` resets + `afterEach` zero-checks `audit_metadata_validation_failures` + `audit_unknown_event_type_failures` counters from `auth/audit_log_queries.ts`. Call once at the top of any `describe_db` block firing audit emits — production validation is fail-open, so without this any regression shipping a typo'd `event_type` or undeclared metadata field is silent. Pair with `await_pending_effects: true` (the `create_test_app` default) so fire-and-forget audit writes complete by response time.
102
+ - `install_audit_drift_guard()` — `beforeEach` resets + `afterEach` zero-checks the `audit_metadata_validation_failures` + `audit_unknown_event_type_failures` counters from `auth/audit_log_queries.ts` and the emitter's write-failure counter (`get_audit_write_failures` in `auth/audit_emitter.ts`). Call once at the top of any `describe_db` block firing audit emits — production validation and the emitter's write are fail-open, so without this any regression shipping a typo'd `event_type` or undeclared metadata field, or losing a row the database refused, is silent. A test that fails a write on purpose resets the counter (`reset_audit_write_failures`) once it has asserted it.
103
+ - `assert_no_audit_drift()` — the zero-check itself, for a test that manages the counters. Pair with `await_pending_effects: true` (the `create_test_app` default) so fire-and-forget audit writes complete by response time.
103
104
  - `create_recording_audit_emitter(calls_ref?)` — no-op `AuditEmitter` pushing every `emit` and `emit_pool` call into `calls`. Pass `calls_ref` to write into a caller-owned array; omit to let the helper allocate. Returns `{emitter, calls}` — destructure `emitter` as the `audit` dep and read `calls` to assert. `add_listener` records (never fires) and returns a remover with the production semantics, so `listener_count` tracks what a server registered and released. Replaces per-file capturing emitters previously duplicated across `password_change.test.ts`, `audit_log.test.ts`, etc.
104
105
  - `RecordingAuditEmitter` — `{emitter: AuditEmitter; calls: Array<AuditLogInput>}`.
105
106
 
@@ -223,7 +224,7 @@ test-only by construction.
223
224
 
224
225
  ### `schema_introspect.ts` — `query_schema_snapshot`
225
226
 
226
- - `query_schema_snapshot(db, options?)` — introspects a live DB into a deterministic `SchemaSnapshot` via `pg_catalog` + `information_schema`. Captures tables, columns (with `udt_name` to distinguish int4/int8), indexes (`indexdef`), constraints (`pg_get_constraintdef`), sequences, and enum types (`pg_enum` labels in declared `enumsortorder`, so a `cell_visibility` label-set/order drift is gated). The `schema_version` migration tracker is always excluded — it's framework bookkeeping, not domain schema, and impls organize migration namespaces differently. Twinned by `fuz_db::query_schema_snapshot` (Rust); the `_testing_schema_snapshot` RPC's wire validator is the shared `SchemaSnapshot`, so the enum field must serialize on both sides.
227
+ - `query_schema_snapshot(db, options?)` — introspects a live DB into a deterministic `SchemaSnapshot` via `pg_catalog` + `information_schema`. Throws on pglet when the snapshot holds an index, unless `accept_pglet_index_definitions` is passed: pglet's `indexdef` omits index modifiers, so its index definitions read as false differences against PostgreSQL and false agreement between two pglet schemas. Captures tables, columns (with `udt_name` to distinguish int4/int8), indexes (`indexdef`), constraints (`pg_get_constraintdef`), sequences, and enum types (`pg_enum` labels in declared `enumsortorder`, so a `cell_visibility` label-set/order drift is gated). The `schema_version` migration tracker is always excluded — it's framework bookkeeping, not domain schema; twin impls keep it identical (same namespaces, names and partitioning), which `query_migration_tracker` + `assert_migration_trackers_equal` gate separately. Twinned by `fuz_db::query_schema_snapshot` (Rust); the `_testing_schema_snapshot` RPC's wire validator is the shared `SchemaSnapshot`, so the enum field must serialize on both sides.
227
228
  - `SchemaSnapshot` — the Zod schema is canonical (co-located in `schema_introspect.ts`; the cross-impl `_testing_schema_snapshot` RPC action reuses it as its wire validator, and the type is `z.infer`'d from it). Fully JSON-serializable; every collection deterministically sorted on capture so structural equality is stable across runs.
228
229
  - `query_migration_tracker(db)` / `MigrationTracker` — the migration-_identity_ twin of the snapshot: reads the `schema_version` tracker rows (`[{namespace, name, sequence}]`) the snapshot deliberately _excludes_. `MigrationTracker` is the `_testing_migration_tracker` RPC's wire validator; pairs with `assert_migration_trackers_equal` to gate that the two spines record byte-identical migration identity (the swap-freely invariant), catching a migration-name or namespace-partitioning drift the provenance-agnostic schema snapshot can't see.
229
230
 
@@ -235,7 +236,7 @@ test-only by construction.
235
236
  - `SchemaDiff` — tagged-union per drift kind: `table_only_in`, `column_only_in`, `column_field_differs`, `index_only_in`, `index_definition_differs`, `constraint_only_in`, `constraint_differs`, `sequence_only_in`, `sequence_data_type_differs`, `enum_only_in`, `enum_labels_differ` (enum labels compared positionally — declared order is significant).
236
237
  - `diff_migration_trackers(a, b)` / `format_migration_tracker_diffs(diffs, labels?)` / `assert_migration_trackers_equal(a, b, labels?)` — the migration-identity twins of the snapshot diff/format/assert, over two `MigrationTracker`s. `MigrationTrackerDiff` is keyed on `(namespace, name)` (the `schema_version` PK): `tracker_row_only_in` (a migration rename or namespace re-partition) and `tracker_sequence_differs` (a re-order).
237
238
 
238
- fuz_app's own spine gates this cross-process via the `cross_backend_parity` project (`schema_parity.cross.test.ts` + the dual-spawn `global_setup_schema_parity.ts`), diffing the TS spine ↔ `testing_spine_stub` full schema — `npm run test:cross:parity`. The forge has its own deno↔rust parity gate.
239
+ fuz_app's own spine gates this cross-process via the `cross_backend_parity` project (`schema_parity.cross.test.ts` + the dual-spawn `global_setup_schema_parity.ts`), diffing the TS spine ↔ `testing_spine_stub` full schema — `npm run test:cross:parity`. Both legs run on PostgreSQL there: index definitions are compared as the engine renders them, and pglet (what `memory://` gives a TS backend) leaves sort direction, `NULLS` placement, operator classes and `NULLS NOT DISTINCT` out of `indexdef`, so a snapshot taken on it differs from a PostgreSQL one with identical DDL. The forge has its own deno↔rust parity gate.
239
240
 
240
241
  ## Cross-impl RPC-manifest parity
241
242
 
@@ -552,7 +553,7 @@ cover real sockets.
552
553
 
553
554
  Three layers:
554
555
 
555
- 1. **Primitives** — `create_fake_ws()`, `create_fake_hono_context(opts)`, `create_stub_upgrade()`, `MinimalActionEnvironment`, `dispatch_ws_message(on_message, event, ws)`. The fake context sets the keys the production auth middleware sets — `AUTH_SESSION_TOKEN_HASH_KEY` included, as `hash_session_token(auth_session_id)` — because the dispatcher reads the credential through `get_resolved_auth(c)`; a hand-built upgrade context must do the same. `create_stub_upgrade()` stands in for a runtime adapter's `upgradeWebSocket`: it passes a request without `Upgrade: websocket` to `next()`, and records an upgrade request — its real `Context` and the endpoint's events factory — in `upgrades`, answering `200`. So a test can send `app.request('/api/ws', {headers: {upgrade: 'websocket', cookie | authorization, origin, 'x-forwarded-for'}})` through an assembled app's whole middleware chain and build the socket's events from the `Context` that chain produced (`src/test/server/create_app_server_ws_endpoints.dispatch.db.test.ts`); `get_create_events()` still returns the last mounted factory, for a hand-built context.
556
+ 1. **Primitives** — `create_fake_ws(options?)` (a real `WSContext` over capture arrays `sends` / `closes`, plus `socket` — the fake runtime socket behind `ws.raw`, shaped like Node's `ws`: a `bufferedAmount` the transport's outbound writer reads for backpressure, `stalled` (start it with `{stalled: true}`) to model a client that stopped reading, and `terminate` / `terminated` for the frameless cut; `readyState` reads open until closed or terminated), `create_fake_hono_context(opts)`, `create_stub_upgrade()`, `MinimalActionEnvironment`, `dispatch_ws_message(on_message, event, ws)`. The fake context sets the keys the production auth middleware sets — `AUTH_SESSION_TOKEN_HASH_KEY` included, as `hash_session_token(auth_session_id)` — because the dispatcher reads the credential through `get_resolved_auth(c)`; a hand-built upgrade context must do the same. `create_stub_upgrade()` stands in for a runtime adapter's `upgradeWebSocket`: it passes a request without `Upgrade: websocket` to `next()`, and records an upgrade request — its real `Context` and the endpoint's events factory — in `upgrades`, answering `200`. So a test can send `app.request('/api/ws', {headers: {upgrade: 'websocket', cookie | authorization, origin, 'x-forwarded-for'}})` through an assembled app's whole middleware chain and build the socket's events from the `Context` that chain produced (`src/test/server/create_app_server_ws_endpoints.dispatch.db.test.ts`); `get_create_events()` still returns the last mounted factory, for a hand-built context.
556
557
  2. **Harness** — `create_ws_test_harness({actions, transport?, heartbeat?, log?, on_socket_open?, on_socket_close?})` → `WsTestHarness`. `connect(identity?)` is async and resolves after the connection is admitted and `on_socket_open` completes, so broadcasts sent immediately after `await harness.connect()` reach the client. The harness pre-bakes its `RequestContext` (`TEST_CONTEXT_PRESET_KEY`), which also skips the admission credential re-read — there are no session or token rows behind a harness identity — so tests of the re-read itself drive `register_action_ws` with a real database and no preset (`src/test/actions/register_action_ws.admission.db.test.ts`). The harness threads its own `create_stub_db()` into the dispatcher's `db` slot so handlers declaring `side_effects: true` execute under the same transaction wrap they would in production (the stub's `transaction(fn)` synchronously calls `fn(stub_db)`); domain deps reach handlers via factory closures, the same way HTTP RPC factories already wire them. Audit fan-out runs through whatever `audit` emitter the consumer supplied to its action factory closure (typically `create_test_audit_emitter()` for unit harnesses).
557
558
  3. **Round-trip helpers** — predicates + wire-frame types live in `transports/ws_client.ts` (shared with the cross-process `ws_transport.ts` impl): `is_notification(method)`, `is_notification_with<P>(method, match)` (type-guard combinator — narrows `wait_for` return type), `is_response_for(id)`, `JsonrpcNotificationFrame<P>` / `JsonrpcSuccessResponseFrame<R>` / `JsonrpcErrorResponseFrame<D>` (typed wire-frame shapes distinct from the runtime Zod schemas in `http/jsonrpc.ts` — generic over `params` / `result` / `data` so tests narrow without casts).
558
559
 
@@ -927,7 +928,7 @@ source of truth for wire-shape conformance.
927
928
  - `testing/cross_backend/capabilities.ts` — `BackendCapabilities` vocabulary
928
929
  (the **gating** flags `ws` / `sse` / `cell_crud` / `cell_relations` /
929
930
  `cell_gated_create` / `account_lifecycle` / `fact_serving` / `ready` /
930
- `account_status` / `oversized_reject_closes_connection` / `peer_request` /
931
+ `account_status` / `peer_request` /
931
932
  `ws_handshake_pipelining` / `ws_account_actions` / `ws_self_revocation_reply` —
932
933
  each has a `test_if`
933
934
  reader (`ws_self_revocation_reply` forks one case's assertion instead); `peer_request` (server-initiated requests — the ActionPeer
@@ -971,15 +972,17 @@ source of truth for wire-shape conformance.
971
972
  case — `GET /api/account/status` is bundled into `create_account_route_specs`,
972
973
  so every spine serves it (`true`); the gate fails loud if a declaring backend
973
974
  doesn't mount it (replacing the old runtime 404-sniff-skip).
974
- `oversized_reject_closes_connection` gates the strong half of the body-size
975
- smuggling probe (`true` Node/Deno/Rust — they close on an oversized reject;
976
- `false` Bun — `Bun.serve` drains + keepalives but frames correctly, so the
977
- suite's no-desync half still runs).
975
+ No flag covers the body-size smuggling probe: every backend closes the
976
+ connection after an oversized-body 413 (at once on Node, Bun and the Rust
977
+ spine, once the declared body has arrived on Deno), so the pipelined request
978
+ goes unanswered. Bun 1.4.3 is the tested floor — an older Bun kept the
979
+ connection and served the next request, correctly framed, and fails the probe.
978
980
  `ws_handshake_pipelining` gates the WS round-trip suite's
979
981
  frames-with-the-handshake case (`true` Node/Deno/Rust — frames written in the
980
- same TCP write as the upgrade request reach the socket; `false` Bun, whose
981
- HTTP parser answers such a request `400`, and `false` in-process, where there
982
- is no socket to write raw bytes to).
982
+ same TCP write as the upgrade request reach the socket; `false` Bun, which
983
+ answers such a request `400` when the upgrade follows an asynchronous check,
984
+ as it does behind auth, and `false` in-process, where there is no socket to
985
+ write raw bytes to).
983
986
  `ws_account_actions` (the WS endpoint mounts the account actions — `true` on
984
987
  fuz_app's own spine presets, `false` in the family defaults) gates the WS
985
988
  round-trip suite's self-revocation case and, with `rpc_path`, its
@@ -1218,6 +1221,36 @@ ws_path})`. The wire literals are independent copies of the producers'
1218
1221
  constants, so a value changed on one spine fails here. Cross-process only;
1219
1222
  fuz_app's own wiring is `src/test/cross_backend/ws_inbound_dispatch.cross.test.ts`.
1220
1223
 
1224
+ ### `cross_backend/ws_outbound_overflow.ts` — `describe_ws_outbound_overflow_cross_tests`
1225
+
1226
+ Real-upgrade parity for the **per-connection outbound bound** — the byte
1227
+ budget both spines hold a WebSocket's waiting frames to
1228
+ (`BackendWebsocketTransport`'s `outbound_queue_bytes`, the Rust `fuz_realtime`
1229
+ outbound queue; same default, same per-frame charge). The close code and its
1230
+ reason are wire contract — a client reads `WS_CLOSE_OUTBOUND_OVERFLOW` as "you
1231
+ fell behind, reconnect" — so this is the pin that runs on both.
1232
+ `describe_ws_outbound_overflow_cross_tests({setup_test, capabilities, base_url,
1233
+ ws_path, origin?, rpc_path?})` drives `_testing_flood_notifications`, which
1234
+ queues a burst onto the caller's own connection in one synchronous run, so the
1235
+ burst waits at once whether or not the client reads and the overflow is
1236
+ deterministic. Cases: a burst within the budget arrives whole and in order
1237
+ ahead of its response, the socket left open — a small one, and one of a few
1238
+ mebibytes in megabyte frames, most of the default budget, which waits on the backend's write pump where megabyte frames outrun the socket's buffer (Deno; Node and Bun write the burst through); a burst far past it closes the
1239
+ socket with the code and the exact reason, no response for the flooding request
1240
+ arrives, and any flood frames that did arrive are a contiguous prefix of the
1241
+ burst — while a second connection on the same account keeps dispatching; a
1242
+ burst past the action's own size limit is refused `invalid_params` with the
1243
+ socket left open; and, with `rpc_path`, the action over HTTP refuses
1244
+ `invalid_params` with reason `no_connection`. Not asserted, because each
1245
+ implementation decides them: how many frames accepted before the overflow
1246
+ reach the client, and the boundary a burst of tiny frames overflows at. The
1247
+ flood action is daemon-token-gated, so the flooding socket upgrades with the
1248
+ daemon token and no `Origin` (`create_ws_transport`'s `origin: null`). The wire
1249
+ literals are independent copies of the producers' constants. Gated on
1250
+ `capabilities.ws`; cross-process only, and only for a backend that mounts the
1251
+ flood action on its WS endpoint. fuz_app's own wiring is
1252
+ `src/test/cross_backend/ws_outbound_overflow.cross.test.ts`.
1253
+
1221
1254
  ### `cross_backend/ws_connection_cap.ts` — `describe_ws_connection_cap_cross_tests`
1222
1255
 
1223
1256
  Real-upgrade parity for the **per-account WebSocket connection cap** — the
@@ -1312,7 +1345,7 @@ an audit row, an admin account listing, and an offer's `created_at` /
1312
1345
  `expires_at`. `cross_backend/setup.ts` carries the four bases every
1313
1346
  imperative cross suite aliases: `CrossSuiteOptions` (`setup_test` alone),
1314
1347
  `CapabilityGatedCrossSuiteOptions` (+ `capabilities` — `ready`),
1315
- `RpcPathCrossSuiteOptions` (+ `rpc_path?` — origin, body-size, actor
1348
+ `RpcPathCrossSuiteOptions` (+ `rpc_path?` — origin, body-size (which adds `base_url`), actor
1316
1349
  lookup/search, app settings, testing backdoor), and the intersection
1317
1350
  `RpcPathCapabilityGatedCrossSuiteOptions` (every `cell_*` suite, query-shape,
1318
1351
  token-scope surface, fact-serving, account lifecycle). A suite takes the
@@ -1325,7 +1358,8 @@ the option type states whether the bundle is read.
1325
1358
  edit/read/delete → 404 IDOR mask; admin reaches any; dup active `path` → 409;
1326
1359
  `path` write by non-admin → 403 on create + update; `cell_get` with no
1327
1360
  id/path → `invalid_params`; null-auth `cell_list` `created_by` →
1328
- `invalid_params`).
1361
+ `invalid_params`; a `kind` at `CELL_KIND_LENGTH_MAX` stored whole, one
1362
+ UTF-16 code unit past it → `invalid_params` on create and as a list filter).
1329
1363
  - **`describe_cell_relations_cross_tests`** (gates on
1330
1364
  `capabilities.cell_relations`) — the verbs beyond CRUD: grant lifecycle
1331
1365
  (actor-shaped editor grant enables edit, manage-tier `cell_grant_list`,
@@ -1408,7 +1442,7 @@ are `src/test/auth/cell_crud_parity.db.test.ts`
1408
1442
  request (`{result}` / `{error}` / `undefined` to swallow), the path
1409
1443
  `describe_peer_ping_ws_tests` drives. With no `on_request` the seam is inert
1410
1444
  (a server-initiated request flows to `messages` as before).
1411
- - `testing/transports/ws_transport.ts` — `create_ws_transport({base_url, ws_path, cookies, origin?, on_request?, headers?})`
1445
+ - `testing/transports/ws_transport.ts` — `create_ws_transport({base_url, ws_path, cookies, origin?, on_request?, headers?})` (`origin: null` sends no `Origin`, which a daemon-token upgrade needs)
1412
1446
  builds a real-upgrade WS client using the `ws` npm package (optional
1413
1447
  peerDep; consumers wiring cross-process tests `npm install --save-dev ws`).
1414
1448
  Threads the keeper cookie onto the upgrade so per-action auth succeeds on
@@ -1430,6 +1464,21 @@ are `src/test/auth/cell_crud_parity.db.test.ts`
1430
1464
  write as the upgrade request, so before the connection can have been
1431
1465
  admitted). Small unfragmented text frames only. Helpers `is_raw_ws_close`,
1432
1466
  `raw_ws_close_code`, `RAW_WS_OPCODE_TEXT` / `_CLOSE`.
1467
+ - `testing/transports/raw_http.ts` — `raw_http_exchange(base_url, request_bytes, {read_timeout_ms, until?})`
1468
+ → `RawHttpExchange` (`{received, closed_by_server}`): writes caller-built
1469
+ HTTP/1.1 bytes to a raw `node:net` socket once and reads what the server
1470
+ sends until it closes (`until: 'close'`, the default), until one whole
1471
+ response has arrived (`until: 'response'` — for a server that answers and
1472
+ keeps the connection), or until the read timeout. For what `fetch` can't
1473
+ send: two requests in one write, duplicate or malformed headers, a request
1474
+ head whose body never follows, an unterminated chunked body. Write errors are
1475
+ swallowed, and a write error ends the socket — so a caller that must read a
1476
+ refusal reliably sends nothing the server will leave unread. Helpers
1477
+ `raw_http_parse_response` (the first response, `null` until its head and
1478
+ framed body are whole), `raw_http_count_responses` (unanchored status-line
1479
+ count, so a response concatenated after a body is seen),
1480
+ `raw_http_first_status`. Shared by the body-size, body-size smuggling, and
1481
+ credential-header suites.
1433
1482
  - `testing/transports/sse_frame_reader.ts` — `create_sse_frame_reader(reader, default_timeout_ms?)`,
1434
1483
  the transport-agnostic SSE framing core over a
1435
1484
  `ReadableStreamDefaultReader<Uint8Array>`: `\n\n` framing, per-read timeout,
@@ -1496,6 +1545,17 @@ are `src/test/auth/cell_crud_parity.db.test.ts`
1496
1545
  emitter, so the shared suite body can call the barrier on every backend
1497
1546
  uniformly.
1498
1547
 
1548
+ Also bundled: `_testing_flood_notifications`
1549
+ (`create_testing_flood_notifications_action()`) — queues `count`
1550
+ notifications of `size` payload bytes each (method `_testing_flood`, params
1551
+ `{index, payload}`) onto the caller's own WebSocket connection in one
1552
+ synchronous run and answers `{queued: count}`, so a suite can push a
1553
+ connection's outbound queue past its byte budget from outside. Refused with
1554
+ `invalid_params` past `TESTING_FLOOD_MAX_COUNT`, `TESTING_FLOOD_MAX_SIZE`, or
1555
+ `TESTING_FLOOD_MAX_TOTAL_BYTES` (`count` × `size`), and over HTTP (reason
1556
+ `no_connection` — there is no connection to queue onto). The Rust mirror is
1557
+ `fuz_testing::create_testing_flood_notifications_action_spec`.
1558
+
1499
1559
  Also bundled: `_testing_mint_session` — mints a backdated-expiry
1500
1560
  `auth_session` row for an account (via `mint_test_session` in `app_server.ts`)
1501
1561
  and returns its signed cookie value (future-dated payload). Backs the
@@ -1601,14 +1661,31 @@ module's own tests (`account_status.test.ts`, `action_rpc.test.ts`,
1601
1661
  `serve_fact_route.db.test.ts`); fuz_app's own wiring is
1602
1662
  `cross_backend/query_shape.cross.test.ts`.
1603
1663
 
1664
+ ### Grant-verb role grammar parity — `cross_backend/role_grant_role_grammar.ts`
1665
+
1666
+ `describe_role_grant_role_grammar_cross_tests({setup_test, rpc_path?})` — an
1667
+ off-grammar `role` (uppercase, hyphen, digit, leading or trailing underscore,
1668
+ space, empty, or all lowercase and one character past the length cap) on `role_grant_assign` and `role_grant_offer_create` answers
1669
+ `invalid_params` with no `data.reason`, and after the refusals no
1670
+ `role_grant_create` / `role_grant_offer_create` audit row carries one of those
1671
+ strings. The control — an on-grammar role no registry holds, refused past
1672
+ validation with `role_not_web_grantable` — keeps the audit read from being
1673
+ vacuous, and the same refusal for a role exactly at the length cap pins that
1674
+ boundary. Exists because the action-manifest parity gate captures no param
1675
+ schemas while the Rust spine parses params by hand, and the grant verbs write
1676
+ the role they refuse into their failure rows. Ungated; cross-process only (it
1677
+ reads the audit log behind `_testing_drain_effects`). fuz_app's own wiring is
1678
+ `src/test/cross_backend/role_grant_role_grammar.cross.test.ts`.
1679
+
1604
1680
  ### API-token lifetime parity — `cross_backend/token_lifetime.ts`
1605
1681
 
1606
1682
  `describe_token_lifetime_cross_tests({setup_test, rpc_path?})` — the
1607
1683
  imperative round-trip on `account_token_create`'s required `lifetime` union:
1608
1684
  a `{kind:'ttl', days}` mint returns and stores a bounded `expires_at`
1609
1685
  (create output + `account_token_list` read-back), `{kind:'eternal'}` stores
1610
- NULL, and an omitted or out-of-range lifetime 400s identically on both
1611
- spines. Exists because the action-manifest parity gate captures no param
1686
+ NULL, an omitted or out-of-range lifetime 400s identically on both
1687
+ spines, and a `name` at `API_TOKEN_NAME_LENGTH_MAX` mints and reads back whole
1688
+ while one UTF-16 code unit past it is `invalid_params`. Exists because the action-manifest parity gate captures no param
1612
1689
  schemas while the Rust spine hand-parses params — a spine that silently
1613
1690
  dropped the field would answer the same mint with a 200 and an eternal
1614
1691
  token; only the stored-state read-back catches it. Ungated (mint + list are
@@ -1645,8 +1722,8 @@ drift-guarded by `src/test/cross_backend/spine_expected_schema.db.test.ts`
1645
1722
 
1646
1723
  `describe_testing_backdoor_cross_tests({setup_test, rpc_path?, ws?})` —
1647
1724
  the negative-credential parity suite for the `_testing_*` backdoor actions.
1648
- For each `_testing_*` action except `_testing_drain_effects` (the three
1649
- privileged writes plus the three introspection reads) it fires three principals over real HTTP: **anonymous** →
1725
+ For each `_testing_*` action (the privileged writes, the audit barrier, the
1726
+ connection flood, and the introspection reads) it fires three principals over real HTTP: **anonymous** →
1650
1727
  401 `unauthenticated`, **session** → 403 `forbidden` `credential_type_required`,
1651
1728
  **bearer** → 403 `forbidden` `credential_type_required` — and, when `ws`
1652
1729
  (`{base_url, ws_path, origin?}`) is passed, sends each on a socket opened with
@@ -5,7 +5,7 @@ import './assert_dev_env.js';
5
5
  * `describe_standard_admin_integration_tests` creates a composable test suite
6
6
  * that exercises admin account listing, role_grant grant/revoke (via the RPC
7
7
  * surface — see `role_grant_offer_create` / `role_grant_revoke`), session/token
8
- * management, and audit log routes against a real PGlite database.
8
+ * management, and audit log routes against a real database.
9
9
  *
10
10
  * Consumers call it with their route factory, session config, role schema,
11
11
  * and RPC endpoint specs — all admin route tests come for free.
@@ -34,7 +34,7 @@ export interface AuditCompletenessTestOptions {
34
34
  * Composable audit log completeness test suite.
35
35
  *
36
36
  * Verifies that every auth mutation route produces the correct audit log
37
- * event type. Exercises routes via HTTP requests against a real PGlite
37
+ * event type. Exercises routes via HTTP requests against a real
38
38
  * database, then reads events back through the `audit_log_list` RPC
39
39
  * (the production observation path the admin UI consumes).
40
40
  *
@@ -96,7 +96,7 @@ const json_session_headers = (fixture, extra) => fixture.create_session_headers(
96
96
  * Composable audit log completeness test suite.
97
97
  *
98
98
  * Verifies that every auth mutation route produces the correct audit log
99
- * event type. Exercises routes via HTTP requests against a real PGlite
99
+ * event type. Exercises routes via HTTP requests against a real
100
100
  * database, then reads events back through the `audit_log_list` RPC
101
101
  * (the production observation path the admin UI consumes).
102
102
  *
@@ -1,26 +1,39 @@
1
1
  import './assert_dev_env.js';
2
- import type { AuditEmitter } from '../auth/audit_emitter.js';
2
+ import { type AuditEmitter } from '../auth/audit_emitter.js';
3
3
  import type { AuditLogInput } from '../auth/audit_log_schema.js';
4
4
  /**
5
5
  * Register per-test `beforeEach` + `afterEach` hooks that catch any audit
6
6
  * emission with a metadata shape that fails its `audit_metadata_schemas`
7
- * entry, or an `event_type` not present in the active `AuditLogConfig`.
7
+ * entry, an `event_type` not present in the active `AuditLogConfig`, or a
8
+ * row the emitter failed to write.
8
9
  *
9
10
  * The production validation in `query_audit_log` is fail-open — it bumps
10
11
  * process-wide counters and proceeds, so a regression that emits an
11
12
  * undeclared metadata field or a typo'd event-type lands a row that
12
- * passes downstream queries but breaks forensics. Tests that exercise
13
- * audit emits should fail loudly when this happens.
13
+ * passes downstream queries but breaks forensics. The emitter's pool write
14
+ * is fail-open too: a refused `INSERT` is logged and counted, the request
15
+ * succeeds, and the row is gone. Tests that exercise audit emits should fail
16
+ * loudly when either happens.
14
17
  *
15
18
  * Call at the top of every `describe` / `describe_db` block that fires
16
19
  * audit writes through `deps.audit.emit`. Resets counters before each
17
- * test and asserts zero on completion.
20
+ * test and asserts zero on completion. A test that makes a write fail on
21
+ * purpose calls `reset_audit_write_failures` (`auth/audit_emitter.ts`) once
22
+ * it has asserted the count.
18
23
  *
19
24
  * Pair with `await_pending_effects: true` (the default for
20
25
  * `create_test_app`) so fire-and-forget audit writes have completed by
21
26
  * the time the after-each check observes counter state.
22
27
  */
23
28
  export declare const install_audit_drift_guard: () => void;
29
+ /**
30
+ * Assert the process-wide audit counters are zero — the check
31
+ * `install_audit_drift_guard` runs after each test. Exported for a test that
32
+ * manages the counters itself.
33
+ *
34
+ * @throws AssertionError naming the first counter that is not zero
35
+ */
36
+ export declare const assert_no_audit_drift: () => void;
24
37
  /**
25
38
  * Pair returned by `create_recording_audit_emitter` — the
26
39
  * `AuditEmitter` to inject as `deps.audit`, plus the shared `calls`
@@ -36,7 +49,7 @@ export interface RecordingAuditEmitter {
36
49
  * Build a no-op `AuditEmitter` that records every `emit`, `emit_pool`, and
37
50
  * `emit_role_grant_target` call into `calls` as an `AuditLogInput`. Use to
38
51
  * capture audit metadata shapes in unit tests (e.g. password change failure
39
- * outcome, role-grant create denial) without standing up the full PGlite +
52
+ * outcome, role-grant create denial) without standing up a database and the
40
53
  * `query_audit_log` pipeline.
41
54
  *
42
55
  * **Capture scope — all four production fan-out shapes.**
@@ -1 +1 @@
1
- {"version":3,"file":"audit_drift_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_drift_guard.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAU7B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAEhF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,yBAAyB,QAAO,IAiB5C,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,8BAA8B,GAC1C,YAAY,KAAK,CAAC,aAAa,CAAC,KAC9B,qBAqCF,CAAC"}
1
+ {"version":3,"file":"audit_drift_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_drift_guard.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAU7B,OAAO,EAGN,KAAK,YAAY,EACjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAEhF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,yBAAyB,QAAO,IAO5C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,QAAO,IAgBxC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,8BAA8B,GAC1C,YAAY,KAAK,CAAC,aAAa,CAAC,KAC9B,qBAqCF,CAAC"}
@@ -1,20 +1,26 @@
1
1
  import './assert_dev_env.js';
2
2
  import { assert, beforeEach, afterEach } from 'vitest';
3
3
  import { get_audit_metadata_validation_failures, get_audit_unknown_event_type_failures, reset_audit_metadata_validation_failures, reset_audit_unknown_event_type_failures } from '../auth/audit_log_queries.js';
4
+ import { get_audit_write_failures, reset_audit_write_failures } from '../auth/audit_emitter.js';
4
5
  /**
5
6
  * Register per-test `beforeEach` + `afterEach` hooks that catch any audit
6
7
  * emission with a metadata shape that fails its `audit_metadata_schemas`
7
- * entry, or an `event_type` not present in the active `AuditLogConfig`.
8
+ * entry, an `event_type` not present in the active `AuditLogConfig`, or a
9
+ * row the emitter failed to write.
8
10
  *
9
11
  * The production validation in `query_audit_log` is fail-open — it bumps
10
12
  * process-wide counters and proceeds, so a regression that emits an
11
13
  * undeclared metadata field or a typo'd event-type lands a row that
12
- * passes downstream queries but breaks forensics. Tests that exercise
13
- * audit emits should fail loudly when this happens.
14
+ * passes downstream queries but breaks forensics. The emitter's pool write
15
+ * is fail-open too: a refused `INSERT` is logged and counted, the request
16
+ * succeeds, and the row is gone. Tests that exercise audit emits should fail
17
+ * loudly when either happens.
14
18
  *
15
19
  * Call at the top of every `describe` / `describe_db` block that fires
16
20
  * audit writes through `deps.audit.emit`. Resets counters before each
17
- * test and asserts zero on completion.
21
+ * test and asserts zero on completion. A test that makes a write fail on
22
+ * purpose calls `reset_audit_write_failures` (`auth/audit_emitter.ts`) once
23
+ * it has asserted the count.
18
24
  *
19
25
  * Pair with `await_pending_effects: true` (the default for
20
26
  * `create_test_app`) so fire-and-forget audit writes have completed by
@@ -24,17 +30,27 @@ export const install_audit_drift_guard = () => {
24
30
  beforeEach(() => {
25
31
  reset_audit_metadata_validation_failures();
26
32
  reset_audit_unknown_event_type_failures();
33
+ reset_audit_write_failures();
27
34
  });
28
- afterEach(() => {
29
- assert.strictEqual(get_audit_metadata_validation_failures(), 0, 'audit metadata failed schema validation — see audit_log_schema.audit_metadata_schemas');
30
- assert.strictEqual(get_audit_unknown_event_type_failures(), 0, 'audit emitted an unknown event_type — see AUDIT_EVENT_TYPES');
31
- });
35
+ afterEach(assert_no_audit_drift);
36
+ };
37
+ /**
38
+ * Assert the process-wide audit counters are zero — the check
39
+ * `install_audit_drift_guard` runs after each test. Exported for a test that
40
+ * manages the counters itself.
41
+ *
42
+ * @throws AssertionError naming the first counter that is not zero
43
+ */
44
+ export const assert_no_audit_drift = () => {
45
+ assert.strictEqual(get_audit_metadata_validation_failures(), 0, 'audit metadata failed schema validation — see audit_log_schema.audit_metadata_schemas');
46
+ assert.strictEqual(get_audit_unknown_event_type_failures(), 0, 'audit emitted an unknown event_type — see AUDIT_EVENT_TYPES');
47
+ assert.strictEqual(get_audit_write_failures(), 0, 'an audit row failed to write and was lost — the emitter logged the database error');
32
48
  };
33
49
  /**
34
50
  * Build a no-op `AuditEmitter` that records every `emit`, `emit_pool`, and
35
51
  * `emit_role_grant_target` call into `calls` as an `AuditLogInput`. Use to
36
52
  * capture audit metadata shapes in unit tests (e.g. password change failure
37
- * outcome, role-grant create denial) without standing up the full PGlite +
53
+ * outcome, role-grant create denial) without standing up a database and the
38
54
  * `query_audit_log` pipeline.
39
55
  *
40
56
  * **Capture scope — all four production fan-out shapes.**
@@ -2,11 +2,24 @@ import '../assert_dev_env.js';
2
2
  import type { RpcPathCrossSuiteOptions } from './setup.js';
3
3
  /**
4
4
  * Options for the body-size parity suite — the standard RPC-dispatched
5
- * cross-suite shape (`setup_test` / `rpc_path`); aliases the shared
6
- * `RpcPathCrossSuiteOptions` rather than minting a duplicate. The limit is
7
- * on every spine, so no case is capability-gated and the flag bundle is not
8
- * on the shape.
5
+ * cross-suite shape (`setup_test` / `rpc_path`) plus where the backend's
6
+ * socket is. The limit is on every spine, so no case is capability-gated and
7
+ * the flag bundle is not on the shape.
9
8
  */
10
- export type BodySizeCrossTestOptions = RpcPathCrossSuiteOptions;
9
+ export interface BodySizeCrossTestOptions extends RpcPathCrossSuiteOptions {
10
+ /**
11
+ * Base URL the spawned backend is reachable at (e.g. `http://localhost:1178`),
12
+ * or `null` for an in-process run, which has no socket. Given a URL, the
13
+ * over-cap cases write to the socket directly and send only the bytes the
14
+ * refusal needs; with `null` they send whole bodies through the fixture's
15
+ * transport.
16
+ *
17
+ * Pass the URL whenever the fixture's transport reaches a real socket.
18
+ * `null` there uploads each whole over-cap body through `fetch`, where the
19
+ * write races the close that follows the 413 and the case fails some of the
20
+ * time.
21
+ */
22
+ readonly base_url: string | null;
23
+ }
11
24
  export declare const describe_body_size_cross_tests: (options: BodySizeCrossTestOptions) => void;
12
25
  //# sourceMappingURL=body_size.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"body_size.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/body_size.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA0D9B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAG3D;;;;;;GAMG;AACH,MAAM,MAAM,wBAAwB,GAAG,wBAAwB,CAAC;AAmDhE,eAAO,MAAM,8BAA8B,GAAI,SAAS,wBAAwB,KAAG,IA0ClF,CAAC"}
1
+ {"version":3,"file":"body_size.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/body_size.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA8F9B,OAAO,KAAK,EAAE,wBAAwB,EAAe,MAAM,YAAY,CAAC;AAGxE;;;;;GAKG;AACH,MAAM,WAAW,wBAAyB,SAAQ,wBAAwB;IACzE;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AA2HD,eAAO,MAAM,8BAA8B,GAAI,SAAS,wBAAwB,KAAG,IAyLlF,CAAC"}