@fuzdev/fuz_app 0.127.0 → 0.129.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/CLAUDE.md +35 -18
- package/dist/actions/connection_closer.d.ts +16 -4
- package/dist/actions/connection_closer.d.ts.map +1 -1
- package/dist/actions/connection_closer.js +20 -4
- package/dist/actions/register_action_ws.d.ts +10 -3
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +6 -3
- package/dist/actions/register_ws_endpoint.d.ts +3 -1
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +3 -1
- package/dist/auth/CLAUDE.md +14 -10
- package/dist/auth/actor_lookup_action_specs.d.ts +2 -2
- package/dist/auth/actor_lookup_action_specs.js +2 -2
- package/dist/auth/audit_emitter.d.ts +73 -19
- package/dist/auth/audit_emitter.d.ts.map +1 -1
- package/dist/auth/audit_emitter.js +57 -21
- package/dist/auth/bearer_auth.d.ts +3 -2
- package/dist/auth/bearer_auth.d.ts.map +1 -1
- package/dist/auth/bearer_auth.js +3 -2
- package/dist/auth/middleware.d.ts +30 -0
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +59 -0
- package/dist/auth/request_context.d.ts +1 -1
- package/dist/auth/request_context.js +1 -1
- package/dist/auth/token_scope.d.ts +4 -2
- package/dist/auth/token_scope.d.ts.map +1 -1
- package/dist/auth/token_scope.js +10 -2
- package/dist/http/CLAUDE.md +3 -4
- package/dist/http/auth_shape.d.ts +3 -2
- package/dist/http/auth_shape.d.ts.map +1 -1
- package/dist/http/auth_shape.js +3 -2
- package/dist/http/common_routes.d.ts +1 -14
- package/dist/http/common_routes.d.ts.map +1 -1
- package/dist/http/common_routes.js +1 -19
- package/dist/http/ip_canonical.d.ts +1 -1
- package/dist/http/ip_canonical.js +1 -1
- package/dist/realtime/sse_auth_guard.d.ts +6 -2
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +3 -1
- package/dist/server/app_server.d.ts +157 -19
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +226 -128
- package/dist/server/surface_route.d.ts +32 -0
- package/dist/server/surface_route.d.ts.map +1 -0
- package/dist/server/surface_route.js +46 -0
- package/dist/testing/CLAUDE.md +50 -32
- package/dist/testing/app_server.d.ts +40 -5
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +84 -68
- package/dist/testing/attack_surface.d.ts +1 -1
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +1 -1
- package/dist/testing/audit_drift_guard.d.ts +5 -3
- package/dist/testing/audit_drift_guard.d.ts.map +1 -1
- package/dist/testing/audit_drift_guard.js +14 -4
- package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
- package/dist/testing/cross_backend/account_lifecycle.js +1 -2
- package/dist/testing/cross_backend/capabilities.d.ts +3 -1
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_spine_surface.d.ts +2 -2
- package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_spine_surface.js +5 -2
- package/dist/testing/cross_backend/identity_parity.js +1 -1
- package/dist/testing/cross_backend/in_process_setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/in_process_setup.js +4 -1
- package/dist/testing/cross_backend/method_coverage.d.ts +1 -1
- package/dist/testing/cross_backend/method_coverage.d.ts.map +1 -1
- package/dist/testing/cross_backend/method_coverage.js +2 -2
- package/dist/testing/cross_backend/peer_ping_ws.d.ts +1 -1
- package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts +1 -1
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/setup.d.ts +4 -4
- package/dist/testing/cross_backend/setup.js +1 -1
- package/dist/testing/cross_backend/sse_round_trip.d.ts +1 -1
- package/dist/testing/cross_backend/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +51 -32
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +70 -51
- package/dist/testing/cross_backend/testing_server_bun.d.ts +1 -1
- package/dist/testing/cross_backend/testing_server_bun.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_server_bun.js +80 -64
- package/dist/testing/cross_backend/testing_server_core.d.ts +10 -4
- package/dist/testing/cross_backend/testing_server_core.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_server_deno.d.ts +1 -1
- package/dist/testing/cross_backend/testing_server_deno.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_server_deno.js +1 -1
- package/dist/testing/cross_backend/testing_server_node.d.ts +1 -1
- package/dist/testing/cross_backend/testing_server_node.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_server_node.js +2 -2
- package/dist/testing/cross_backend/ws_round_trip.d.ts +3 -2
- package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_round_trip.js +51 -13
- package/dist/testing/stubs.d.ts +65 -10
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +95 -41
- package/dist/testing/ws_round_trip.d.ts +1 -3
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/ui/AdminSurface.svelte +3 -1
- package/dist/ui/AdminSurface.svelte.d.ts.map +1 -1
- package/dist/ui/CLAUDE.md +3 -1
- package/dist/ui/async_slot.svelte.d.ts +4 -4
- package/dist/ui/async_slot.svelte.js +4 -4
- package/dist/ui/keyed_async_slot.svelte.d.ts +4 -4
- package/dist/ui/keyed_async_slot.svelte.js +3 -3
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import '../assert_dev_env.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Test-binary RPC actions for cross-process integration tests.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* The daemon-token-authed actions bundled by `create_testing_actions`:
|
|
6
6
|
* **`_testing_reset`** (DB wipe + keeper re-seed), **`_testing_drain_effects`**
|
|
7
7
|
* (audit barrier), **`_testing_mint_session`** (forge an
|
|
8
8
|
* expired-by-construction server-side session for the expiry conformance
|
|
@@ -31,11 +31,11 @@ import '../assert_dev_env.js';
|
|
|
31
31
|
* new keeper + secondary credentials return as the action output so
|
|
32
32
|
* the per-test fixture closes over them.
|
|
33
33
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
34
|
+
* In-process and cross-process keeper lifetimes match: both modes run
|
|
35
|
+
* against a freshly bootstrapped keeper per test, so mutation-cascade
|
|
36
|
+
* tests (password change, revoke-all, hardcoded-username signup
|
|
37
|
+
* uniqueness) and direct keeper-vs-admin probes work uniformly
|
|
38
|
+
* cross-process.
|
|
39
39
|
*
|
|
40
40
|
* **Keeper ≠ admin.** The `keeper` and `admin` roles are independent.
|
|
41
41
|
* Keeper authorizes daemon-token / bootstrap paths; admin authorizes
|
|
@@ -47,9 +47,8 @@ import '../assert_dev_env.js';
|
|
|
47
47
|
* via `extra_accounts: [{username, roles: [ROLE_KEEPER]}]` so the
|
|
48
48
|
* account is seeded at this same bootstrap-equivalent step.
|
|
49
49
|
*
|
|
50
|
-
* **No free-form runtime bypass.**
|
|
51
|
-
*
|
|
52
|
-
* was rejected because a runtime bypass would let tests skip the
|
|
50
|
+
* **No free-form runtime bypass.** There is no action for arbitrary
|
|
51
|
+
* direct role grants: a runtime bypass would let tests skip the
|
|
53
52
|
* production consent flow's side-effects (audit emit, WS fan-out) and
|
|
54
53
|
* silently mask bugs in those paths. The bypass that does exist —
|
|
55
54
|
* `extra_accounts` — is framed as bootstrap-time seeding, the same
|
|
@@ -73,10 +72,10 @@ import { Uuid } from '@fuzdev/fuz_util/id.ts';
|
|
|
73
72
|
import { fact_hash_bytes } from '@fuzdev/fuz_util/fact_hash.ts';
|
|
74
73
|
import { FactHashSchema } from '@fuzdev/fuz_util/hash_schemas.ts';
|
|
75
74
|
import { rpc_action } from '../../actions/action_rpc.js';
|
|
75
|
+
import { emit_after_commit } from '../../http/pending_effects.js';
|
|
76
76
|
import { protocol_action_specs } from '../../actions/protocol.js';
|
|
77
77
|
import { query_put_fact } from '../../db/fact_queries.js';
|
|
78
78
|
import { ROLE_ADMIN, ROLE_KEEPER } from '../../auth/role_schema.js';
|
|
79
|
-
import { auth_integration_truncate_tables } from '../db.js';
|
|
80
79
|
import { query_schema_snapshot, SchemaSnapshot, query_migration_tracker, MigrationTracker } from '../schema_introspect.js';
|
|
81
80
|
import { ActionManifest, build_action_manifest } from './action_manifest.js';
|
|
82
81
|
import { query_create_actor } from '../../auth/account_queries.js';
|
|
@@ -86,8 +85,8 @@ import { DEFAULT_TEST_PASSWORD } from '../test_credentials.js';
|
|
|
86
85
|
* Shared `auth` axis for every `_testing_*` action: keeper-only via the
|
|
87
86
|
* daemon-token credential, no acting actor. This is the entire structural
|
|
88
87
|
* fence on the backdoor surface (these actions run direct DB writes the
|
|
89
|
-
* production wire never exposes), so
|
|
90
|
-
* rather than re-declaring it — a single source of truth the gate test
|
|
88
|
+
* production wire never exposes), so every `_testing_*` spec references this
|
|
89
|
+
* one const rather than re-declaring it — a single source of truth the gate test
|
|
91
90
|
* (`testing_actions_auth.test.ts`) pins. Mirrors the Rust `DAEMON_TOKEN_ONLY`
|
|
92
91
|
* / shared `AuthSpec` in `fuz_testing`.
|
|
93
92
|
*/
|
|
@@ -168,16 +167,21 @@ export const testing_reset_action_spec = {
|
|
|
168
167
|
* the cross-backend conformance suite uses in place of a poll/sleep before
|
|
169
168
|
* asserting on audit rows.
|
|
170
169
|
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
170
|
+
* The handler awaits `AuditEmitter.drain_inflight`, which waits until no
|
|
171
|
+
* tracked `emit` write is outstanding — so the binary's emitter must be built
|
|
172
|
+
* with `track_inflight: true`, or the drain resolves at once and is no barrier.
|
|
173
|
+
* Tracking is what makes it a barrier whatever the transport: an **HTTP**
|
|
174
|
+
* mutation on a binary running `await_pending_effects: true` has its writes
|
|
175
|
+
* settled by the time it responds, but a **WS** mutation is answered before
|
|
176
|
+
* its effects flush (reply-then-close for a self-revocation), so its audit
|
|
177
|
+
* write can still be in flight when the caller reads the reply. The Rust
|
|
178
|
+
* spine does the same await in `AuditEmitter::drain_inflight`, over an
|
|
179
|
+
* emitter built with `new_with_inflight_tracking`.
|
|
180
|
+
*
|
|
181
|
+
* Covered: every `emit` that ran before the drain was dispatched, and any
|
|
182
|
+
* started while it waits. A write that starts after the drain resolves is
|
|
183
|
+
* not — nor are eager writes outside `emit` (session touch, token usage),
|
|
184
|
+
* which only the dispatching transport's own flush awaits.
|
|
181
185
|
*
|
|
182
186
|
* `auth` gates on the daemon-token credential, matching `_testing_reset`.
|
|
183
187
|
*/
|
|
@@ -246,13 +250,18 @@ export const testing_mint_session_action_spec = {
|
|
|
246
250
|
'signed cookie value (exercises the authoritative server-side DB-row expiry gate).'
|
|
247
251
|
};
|
|
248
252
|
/**
|
|
249
|
-
* Build the standalone `_testing_drain_effects` action
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
* suite asserts on audit rows (the spine binary bundles it via
|
|
253
|
+
* Build the standalone `_testing_drain_effects` action over `audit` — the
|
|
254
|
+
* emitter the backend's handlers emit through (`deps.audit`), built with
|
|
255
|
+
* `track_inflight: true` (see the spec doc). Mount it on any test endpoint
|
|
256
|
+
* whose suite asserts on audit rows (the spine binary bundles it via
|
|
253
257
|
* `create_testing_actions`; in-process suites mount it directly).
|
|
258
|
+
*
|
|
259
|
+
* @param audit - the backend's emitter, whose in-flight writes the action awaits
|
|
254
260
|
*/
|
|
255
|
-
export const create_testing_drain_effects_action = () => rpc_action(testing_drain_effects_action_spec, async () =>
|
|
261
|
+
export const create_testing_drain_effects_action = (audit) => rpc_action(testing_drain_effects_action_spec, async () => {
|
|
262
|
+
await audit.drain_inflight();
|
|
263
|
+
return { ok: true };
|
|
264
|
+
});
|
|
256
265
|
/**
|
|
257
266
|
* `_testing_put_fact` — seed an **embedded** fact (`fact.bytes`) for the
|
|
258
267
|
* cross-process fact-serving suite, which drives over real HTTP and has no
|
|
@@ -375,26 +384,31 @@ export const testing_action_manifest_action_spec = {
|
|
|
375
384
|
'{method, auth, side_effects, rate_limit} manifest for cross-impl method-set + ' +
|
|
376
385
|
'auth-shape parity diffing.'
|
|
377
386
|
};
|
|
387
|
+
/**
|
|
388
|
+
* The protocol methods (`heartbeat` / `cancel` / `peer/ping`) the
|
|
389
|
+
* `_testing_action_manifest` dump leaves out, so the cross-impl diff stays
|
|
390
|
+
* apples-to-apples: the two impls organize them differently (`peer/ping` is
|
|
391
|
+
* on the TS spine's WS **and** HTTP-RPC endpoints; heartbeat/cancel WS-only;
|
|
392
|
+
* the Rust stub compiles one shared registry serving both transports), so
|
|
393
|
+
* including them would be a spurious divergence (see `action_manifest.ts`
|
|
394
|
+
* §Scope). The exclusion set is the live `protocol_action_specs` bundle —
|
|
395
|
+
* kept in lockstep with the static `PROTOCOL_ACTION_METHODS` by a drift-guard
|
|
396
|
+
* test — so this stays off the heavyweight `action_codegen` module the spawned
|
|
397
|
+
* binary would otherwise pull in.
|
|
398
|
+
*/
|
|
399
|
+
const PROTOCOL_METHODS = new Set(protocol_action_specs.map((spec) => spec.method));
|
|
378
400
|
/**
|
|
379
401
|
* Build the `_testing_action_manifest` action over a backend's full live RPC
|
|
380
402
|
* mount. The handler closes over a manifest computed once at assembly time
|
|
381
|
-
* from every `mounted` spec
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
403
|
+
* from every `mounted` spec except the protocol methods (`PROTOCOL_METHODS`)
|
|
404
|
+
* **plus this action's own spec** — the manifest action is itself a live
|
|
405
|
+
* method, so it must appear in the dump it serves. `mounted` excludes it
|
|
406
|
+
* (it's appended to the mount *after* the rest is assembled, in
|
|
407
|
+
* `build_full_spine_rpc_actions`), so its static spec is folded in here; the
|
|
408
|
+
* Rust mirror folds in its own descriptor the same way.
|
|
386
409
|
*
|
|
387
|
-
*
|
|
388
|
-
* the manifest here so the cross-impl diff stays apples-to-apples: the two
|
|
389
|
-
* impls organize them differently (`peer/ping` is on the TS spine's WS **and**
|
|
390
|
-
* HTTP-RPC endpoints; heartbeat/cancel WS-only; the Rust stub compiles one
|
|
391
|
-
* shared registry serving both transports), so including them would be a
|
|
392
|
-
* spurious divergence (see `action_manifest.ts` §Scope). The exclusion set is
|
|
393
|
-
* the live `protocol_action_specs` bundle — kept in lockstep with the static
|
|
394
|
-
* `PROTOCOL_ACTION_METHODS` by a drift-guard test — so this stays off the
|
|
395
|
-
* heavyweight `action_codegen` module the spawned binary would otherwise pull in.
|
|
410
|
+
* @param mounted - every other action on the backend's live RPC mount
|
|
396
411
|
*/
|
|
397
|
-
const PROTOCOL_METHODS = new Set(protocol_action_specs.map((spec) => spec.method));
|
|
398
412
|
export const create_testing_action_manifest_action = (mounted) => {
|
|
399
413
|
const manifest = build_action_manifest([
|
|
400
414
|
...mounted.map((action) => action.spec).filter((spec) => !PROTOCOL_METHODS.has(spec.method)),
|
|
@@ -405,9 +419,10 @@ export const create_testing_action_manifest_action = (mounted) => {
|
|
|
405
419
|
/**
|
|
406
420
|
* Build the testing RPC actions for a test binary's registry.
|
|
407
421
|
*
|
|
408
|
-
* Returns `
|
|
409
|
-
*
|
|
410
|
-
*
|
|
422
|
+
* Returns the bundled `_testing_*` actions (module doc). The test binary
|
|
423
|
+
* calls this at server-assembly time and registers the result on its
|
|
424
|
+
* dispatcher. `_testing_drain_effects` drains `deps.audit`, so the binary's
|
|
425
|
+
* `audit_factory` builds the emitter with `track_inflight: true`.
|
|
411
426
|
*
|
|
412
427
|
* The reset action's table-wipe list mirrors
|
|
413
428
|
* `auth_integration_truncate_tables` from `testing/db.ts` — the
|
|
@@ -499,9 +514,15 @@ export const create_testing_actions = (deps, options) => {
|
|
|
499
514
|
// 6. Refresh the daemon-token cache so subsequent daemon-token
|
|
500
515
|
// requests resolve to the freshly seeded keeper. The
|
|
501
516
|
// middleware's lazy-refresh path only fires when the cached
|
|
502
|
-
// id is null
|
|
503
|
-
//
|
|
504
|
-
|
|
517
|
+
// id is null, so the reset sets it — after the commit, so a
|
|
518
|
+
// reset that rolls back (a throwing `reset_state`, a failed
|
|
519
|
+
// COMMIT) leaves the cache on the keeper still in the database
|
|
520
|
+
// rather than one that never landed, and the next reset can
|
|
521
|
+
// still authenticate.
|
|
522
|
+
const keeper_account_id = keeper.account.id;
|
|
523
|
+
emit_after_commit(ctx, () => {
|
|
524
|
+
daemon_token_state.keeper_account_id = keeper_account_id;
|
|
525
|
+
});
|
|
505
526
|
// 7. Fire domain-state reset (zzz workspaces/terminals/scratch,
|
|
506
527
|
// fuz_forge cell/fact/file truncation, or no-op for spine_stub).
|
|
507
528
|
// Pass the transactional `ctx.db` so DB-domain truncation runs
|
|
@@ -533,10 +554,8 @@ export const create_testing_actions = (deps, options) => {
|
|
|
533
554
|
});
|
|
534
555
|
return { hash };
|
|
535
556
|
}),
|
|
536
|
-
create_testing_drain_effects_action(),
|
|
557
|
+
create_testing_drain_effects_action(deps.audit),
|
|
537
558
|
create_testing_schema_snapshot_action(),
|
|
538
559
|
create_testing_migration_tracker_action()
|
|
539
560
|
];
|
|
540
561
|
};
|
|
541
|
-
/** Set of auth-namespace tables `_testing_reset` wipes. Mirrored by the coverage test. */
|
|
542
|
-
export const testing_reset_wiped_tables = auth_integration_truncate_tables;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
2
|
import type { TestingServerAdapter } from './testing_server_core.js';
|
|
3
|
-
/** Build the Bun
|
|
3
|
+
/** Build the Bun `TestingServerAdapter`. */
|
|
4
4
|
export declare const create_bun_testing_adapter: () => TestingServerAdapter;
|
|
5
5
|
//# sourceMappingURL=testing_server_bun.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing_server_bun.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_server_bun.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"testing_server_bun.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_server_bun.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA8B9B,OAAO,KAAK,EAAe,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAyBlF,4CAA4C;AAC5C,eAAO,MAAM,0BAA0B,QAAO,oBAoE7C,CAAC"}
|
|
@@ -23,68 +23,84 @@ import '../assert_dev_env.js';
|
|
|
23
23
|
*/
|
|
24
24
|
import process from 'node:process';
|
|
25
25
|
import { getConnInfo, upgradeWebSocket, websocket } from '@hono/bun';
|
|
26
|
+
import { DEFAULT_WS_MAX_MESSAGE_BYTES } from '../../actions/transports.js';
|
|
26
27
|
import { create_node_runtime } from '../../runtime/node.js';
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
28
|
+
/**
|
|
29
|
+
* How far above the endpoint's message limit Bun's own frame cap sits. Bun
|
|
30
|
+
* drops a socket past `maxPayloadLength` without a close frame (the client sees
|
|
31
|
+
* 1006), unlike Node's `ws`, which closes with `WS_CLOSE_MESSAGE_TOO_BIG` — so
|
|
32
|
+
* the cap leaves headroom for the per-message check in `register_ws_endpoint`
|
|
33
|
+
* to answer a message just over the limit with that code, and still refuses a
|
|
34
|
+
* grossly oversized one before buffering it toward Bun's 16 MiB default.
|
|
35
|
+
*/
|
|
36
|
+
const BUN_WS_MAX_PAYLOAD_MULTIPLIER = 2;
|
|
37
|
+
/** Build the Bun `TestingServerAdapter`. */
|
|
38
|
+
export const create_bun_testing_adapter = () => {
|
|
39
|
+
// set by `prepare_websocket`, read by `serve` — the core prepares before it serves
|
|
40
|
+
let max_message_bytes = DEFAULT_WS_MAX_MESSAGE_BYTES;
|
|
41
|
+
return {
|
|
42
|
+
runtime_label: 'Bun',
|
|
43
|
+
runtime: create_node_runtime(),
|
|
44
|
+
get_connection_ip: (c) => getConnInfo(c).remote.address,
|
|
45
|
+
// Bun's WS upgrade is module-level and stateless (like Deno) — no
|
|
46
|
+
// post-serve attach. The `websocket` handler is threaded into `serve`
|
|
47
|
+
// below, where `Bun.serve` wants it, carrying the frame cap.
|
|
48
|
+
prepare_websocket: (options) => {
|
|
49
|
+
max_message_bytes = options?.max_message_bytes ?? DEFAULT_WS_MAX_MESSAGE_BYTES;
|
|
50
|
+
return { create_upgrade_websocket: () => upgradeWebSocket };
|
|
51
|
+
},
|
|
52
|
+
serve: ({ fetch, port, hostname }) => {
|
|
53
|
+
const server = Bun.serve({
|
|
54
|
+
fetch: fetch,
|
|
55
|
+
port,
|
|
56
|
+
hostname,
|
|
57
|
+
// Harmless for HTTP-only binaries — Bun only invokes it for sockets
|
|
58
|
+
// upgraded via `upgradeWebSocket`.
|
|
59
|
+
websocket: {
|
|
60
|
+
...websocket,
|
|
61
|
+
maxPayloadLength: BUN_WS_MAX_PAYLOAD_MULTIPLIER * max_message_bytes
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const handle = {
|
|
65
|
+
// Bun bug (1.3.14): after a *server-initiated* WebSocket close
|
|
66
|
+
// (`ServerWebSocket.close()` / `@hono/bun`'s `WSContext.close()`),
|
|
67
|
+
// `server.stop()` never resolves — Bun doesn't decrement its
|
|
68
|
+
// active-connection count for a server-closed socket, so the stop
|
|
69
|
+
// waits forever for a connection it already closed. The trigger is
|
|
70
|
+
// orthogonal to HTTP load, hono-vs-raw `Bun.serve`, in-vs-cross
|
|
71
|
+
// process, the force flag (`stop()`/`stop(false)`/`stop(true)` all
|
|
72
|
+
// hang), and the client runtime — a single server-closed WS is
|
|
73
|
+
// necessary and sufficient. Client-initiated close or leaving the
|
|
74
|
+
// socket open both stop cleanly in ~0ms. In this suite the trigger is
|
|
75
|
+
// any server-initiated WS close — a revocation's (the `ws.cross.test.ts`
|
|
76
|
+
// close-on-revoke cases), a cap eviction's, an oversized message's —
|
|
77
|
+
// which is why teardown hangs after those and not under HTTP-only or
|
|
78
|
+
// client-closed WS traffic.
|
|
79
|
+
//
|
|
80
|
+
// So initiate a force-close (`true` drops active connections, no
|
|
81
|
+
// drain) but DON'T await it: awaiting hangs `start_testing_server`'s
|
|
82
|
+
// shutdown forever — `built.close()` and `exit(0)` never run, and the
|
|
83
|
+
// spawning harness blocks on a child that never exits (observed as a
|
|
84
|
+
// multi-minute hang needing SIGKILL). The force-close still tears the
|
|
85
|
+
// live sockets down; the `exit(0)` the core fires immediately after
|
|
86
|
+
// does the real teardown a few ms later. Node/Deno don't need this —
|
|
87
|
+
// their `shutdown()`/`close()` resolve normally. The `.catch` guards a
|
|
88
|
+
// future Bun that rejects rather than hangs; if a future Bun resolves
|
|
89
|
+
// the promise after a server-initiated close, revert to
|
|
90
|
+
// `await server.stop(true)` to mirror the Node/Deno adapters.
|
|
91
|
+
shutdown: () => {
|
|
92
|
+
void Promise.resolve(server.stop(true)).catch(() => { });
|
|
93
|
+
return Promise.resolve();
|
|
94
|
+
},
|
|
95
|
+
native: server
|
|
96
|
+
};
|
|
97
|
+
return handle;
|
|
98
|
+
},
|
|
99
|
+
pid: process.pid,
|
|
100
|
+
register_shutdown_signals: (handler) => {
|
|
101
|
+
process.on('SIGINT', () => void handler());
|
|
102
|
+
process.on('SIGTERM', () => void handler());
|
|
103
|
+
},
|
|
104
|
+
exit: (code) => process.exit(code)
|
|
105
|
+
};
|
|
106
|
+
};
|
|
@@ -56,10 +56,16 @@ export interface PrepareWebsocketOptions {
|
|
|
56
56
|
* `WsEndpointSpec.max_message_bytes` of the mounted endpoints; a smaller
|
|
57
57
|
* value refuses messages the endpoint would accept. Node's `ws` server takes
|
|
58
58
|
* it as its frame cap (`maxPayload`), so an oversized message is refused
|
|
59
|
-
* before it is buffered whole
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
59
|
+
* before it is buffered whole. Bun takes twice it as its frame cap
|
|
60
|
+
* (`maxPayloadLength`): past its cap Bun drops the socket without a close
|
|
61
|
+
* frame, so the headroom leaves a message just over the limit to the
|
|
62
|
+
* per-message check `register_ws_endpoint` makes, while a grossly oversized
|
|
63
|
+
* one is still refused before it is buffered whole. Deno ignores it — its
|
|
64
|
+
* upgrade takes no frame cap — leaving that per-message check alone. Every
|
|
65
|
+
* runtime closes a message just over the limit with
|
|
66
|
+
* `WS_CLOSE_MESSAGE_TOO_BIG`. The default sits far below `ws`'s own 100 MiB
|
|
67
|
+
* and Bun's 16 MiB, so a build raising an endpoint's cap must pass it here
|
|
68
|
+
* too.
|
|
63
69
|
*
|
|
64
70
|
* @default DEFAULT_WS_MAX_MESSAGE_BYTES
|
|
65
71
|
*/
|
|
@@ -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":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAU,KAAK,MAAM,IAAI,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAG5E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,iFAAiF;IACjF,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,4DAA4D;AAC5D,MAAM,WAAW,uBAAuB;IACvC
|
|
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,5 +1,5 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
2
|
import type { TestingServerAdapter } from './testing_server_core.js';
|
|
3
|
-
/** Build the Deno
|
|
3
|
+
/** Build the Deno `TestingServerAdapter`. */
|
|
4
4
|
export declare const create_deno_testing_adapter: () => TestingServerAdapter;
|
|
5
5
|
//# sourceMappingURL=testing_server_deno.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing_server_deno.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_server_deno.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAsB9B,OAAO,KAAK,EAAe,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAelF,
|
|
1
|
+
{"version":3,"file":"testing_server_deno.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_server_deno.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAsB9B,OAAO,KAAK,EAAe,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAelF,6CAA6C;AAC7C,eAAO,MAAM,2BAA2B,QAAO,oBAkB7C,CAAC"}
|
|
@@ -17,7 +17,7 @@ import '../assert_dev_env.js';
|
|
|
17
17
|
*/
|
|
18
18
|
import { upgradeWebSocket } from '@hono/deno';
|
|
19
19
|
import { create_deno_runtime } from '../../runtime/deno.js';
|
|
20
|
-
/** Build the Deno
|
|
20
|
+
/** Build the Deno `TestingServerAdapter`. */
|
|
21
21
|
export const create_deno_testing_adapter = () => ({
|
|
22
22
|
runtime_label: 'Deno',
|
|
23
23
|
runtime: create_deno_runtime([]),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
2
|
import type { TestingServerAdapter } from './testing_server_core.js';
|
|
3
|
-
/** Build the Node
|
|
3
|
+
/** Build the Node `TestingServerAdapter`. */
|
|
4
4
|
export declare const create_node_testing_adapter: () => TestingServerAdapter;
|
|
5
5
|
//# sourceMappingURL=testing_server_node.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing_server_node.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_server_node.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA2B9B,OAAO,KAAK,EAAe,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAUlF,
|
|
1
|
+
{"version":3,"file":"testing_server_node.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_server_node.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA2B9B,OAAO,KAAK,EAAe,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAUlF,6CAA6C;AAC7C,eAAO,MAAM,2BAA2B,QAAO,oBA+B7C,CAAC"}
|
|
@@ -28,7 +28,7 @@ const node_serve_handle = (server) => ({
|
|
|
28
28
|
}),
|
|
29
29
|
native: server
|
|
30
30
|
});
|
|
31
|
-
/** Build the Node
|
|
31
|
+
/** Build the Node `TestingServerAdapter`. */
|
|
32
32
|
export const create_node_testing_adapter = () => ({
|
|
33
33
|
runtime_label: 'Node',
|
|
34
34
|
runtime: create_node_runtime(),
|
|
@@ -48,7 +48,7 @@ export const create_node_testing_adapter = () => ({
|
|
|
48
48
|
},
|
|
49
49
|
attach_to_server: (handle) => {
|
|
50
50
|
// `handle.native` is the `ServerType` from `serve()` —
|
|
51
|
-
// type-erased at the
|
|
51
|
+
// type-erased at the `ServeHandle` seam, so it downcasts here.
|
|
52
52
|
node_ws?.injectWebSocket(handle.native);
|
|
53
53
|
}
|
|
54
54
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
2
|
import { type BackendCapabilities } from './capabilities.js';
|
|
3
3
|
import type { SetupTest } from './setup.js';
|
|
4
|
-
/** Configuration for
|
|
4
|
+
/** Configuration for `describe_cross_process_ws_tests`. */
|
|
5
5
|
export interface CrossProcessWsTestOptions {
|
|
6
6
|
/**
|
|
7
7
|
* Per-test fixture producer (`default_cross_process_setup(handle)`).
|
|
@@ -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;
|
|
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;AA0G9B,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,mBAAmB,CAAC;AAEtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAK5C,2DAA2D;AAC3D,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"}
|