@fuzdev/fuz_app 0.125.0 → 0.127.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 +56 -20
- package/dist/actions/action_rpc.d.ts +14 -11
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +5 -4
- package/dist/actions/connection_closer.d.ts +13 -3
- package/dist/actions/connection_closer.d.ts.map +1 -1
- package/dist/actions/connection_closer.js +7 -5
- package/dist/actions/register_action_ws.d.ts +18 -13
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +8 -6
- package/dist/actions/register_ws_endpoint.d.ts +6 -3
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +6 -3
- package/dist/actions/transports.d.ts +9 -0
- package/dist/actions/transports.d.ts.map +1 -1
- package/dist/actions/transports.js +9 -0
- package/dist/actions/transports_ws_backend.d.ts +15 -0
- package/dist/actions/transports_ws_backend.d.ts.map +1 -1
- package/dist/actions/transports_ws_backend.js +25 -5
- package/dist/actions/ws_endpoint_spec.d.ts +24 -4
- package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
- package/dist/actions/ws_endpoint_spec.js +15 -1
- package/dist/auth/middleware.d.ts +29 -1
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +35 -1
- package/dist/http/CLAUDE.md +3 -3
- package/dist/http/surface.d.ts +12 -9
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/rate_limiter.d.ts +43 -3
- package/dist/rate_limiter.d.ts.map +1 -1
- package/dist/rate_limiter.js +27 -3
- package/dist/realtime/sse_auth_guard.d.ts +2 -1
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +4 -2
- package/dist/realtime/subscriber_registry.d.ts +12 -0
- package/dist/realtime/subscriber_registry.d.ts.map +1 -1
- package/dist/realtime/subscriber_registry.js +18 -2
- package/dist/server/app_server.d.ts +128 -55
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +110 -61
- package/dist/server/app_server_context.d.ts +17 -9
- package/dist/server/app_server_context.d.ts.map +1 -1
- package/dist/testing/CLAUDE.md +83 -25
- package/dist/testing/app_server.d.ts +6 -3
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +20 -20
- package/dist/testing/connection_closer_helpers.d.ts +10 -4
- package/dist/testing/connection_closer_helpers.d.ts.map +1 -1
- package/dist/testing/connection_closer_helpers.js +4 -0
- package/dist/testing/cross_backend/capabilities.d.ts +3 -3
- package/dist/testing/cross_backend/default_backend_configs.d.ts +12 -12
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +11 -10
- package/dist/testing/cross_backend/full_spine_mount.d.ts +35 -11
- package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
- package/dist/testing/cross_backend/full_spine_mount.js +26 -9
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +16 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +5 -4
- package/dist/testing/cross_backend/spine_surface_constants.d.ts +43 -3
- package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_surface_constants.js +43 -3
- package/dist/testing/cross_backend/testing_backdoor.d.ts +15 -1
- package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_backdoor.js +51 -6
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +6 -3
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +6 -3
- package/dist/testing/cross_backend/testing_server_bun.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_server_bun.js +5 -1
- package/dist/testing/cross_backend/testing_server_core.d.ts +72 -41
- package/dist/testing/cross_backend/testing_server_core.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_server_core.js +22 -14
- package/dist/testing/cross_backend/testing_server_deno.js +1 -1
- package/dist/testing/cross_backend/testing_server_node.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_server_node.js +20 -7
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +15 -2
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/ts_spine_backend_config.js +10 -8
- package/dist/testing/cross_backend/ws_action_rate_limit.d.ts +26 -0
- package/dist/testing/cross_backend/ws_action_rate_limit.d.ts.map +1 -0
- package/dist/testing/cross_backend/ws_action_rate_limit.js +261 -0
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +4 -3
- package/dist/testing/stubs.d.ts +17 -4
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +46 -13
- package/dist/testing/transports/ws_transport.d.ts +7 -0
- package/dist/testing/transports/ws_transport.d.ts.map +1 -1
- package/dist/testing/transports/ws_transport.js +1 -1
- package/dist/testing/ws_round_trip.d.ts +43 -37
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +28 -46
- package/package.json +2 -2
|
@@ -7,15 +7,20 @@ import '../assert_dev_env.js';
|
|
|
7
7
|
* the `cross_backend/*` suites (and the cross-impl bench) can drive it the
|
|
8
8
|
* same way they drive the Rust spine. This module owns the runtime-neutral
|
|
9
9
|
* orchestration — stale-daemon check, daemon-info write, serve, post-serve
|
|
10
|
-
* WS attach, graceful
|
|
10
|
+
* WS attach, graceful shutdown — and delegates the runtime-boundary
|
|
11
11
|
* primitives (HTTP serve, WS upgrade construction, signals, pid, exit) to a
|
|
12
|
-
*
|
|
13
|
-
* `testing/cross_backend/testing_server_node.ts` (`@hono/node-server` + `@hono/node-ws`)
|
|
14
|
-
* `testing/cross_backend/testing_server_deno.ts` (`Deno.serve` + `@hono/deno`)
|
|
12
|
+
* `TestingServerAdapter`. The shipped adapters are
|
|
13
|
+
* `testing/cross_backend/testing_server_node.ts` (`@hono/node-server` + `@hono/node-ws`),
|
|
14
|
+
* `testing/cross_backend/testing_server_deno.ts` (`Deno.serve` + `@hono/deno`), and
|
|
15
|
+
* `testing/cross_backend/testing_server_bun.ts` (`Bun.serve` + `@hono/bun`).
|
|
15
16
|
*
|
|
16
|
-
* The app itself — routes, RPC, DB, `_testing_reset`,
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* The app itself — routes, RPC, DB, `_testing_reset`, WS endpoints — is the
|
|
18
|
+
* caller's `StartTestingServerOptions.build_app` seam, so this core
|
|
19
|
+
* stays domain-free. WebSocket endpoints mount through `create_app_server`'s
|
|
20
|
+
* `ws_endpoints`: `build_app` asks its context to prepare the runtime's
|
|
21
|
+
* upgrade helper and passes the resulting factory as
|
|
22
|
+
* `create_upgrade_websocket`; the core attaches it to the server once
|
|
23
|
+
* `serve` returns (Node's `injectWebSocket`). fuz_app's own `testing_spine_server` passes a
|
|
19
24
|
* no-domain build; consumers (zzz, fuz_forge) pass their domain build.
|
|
20
25
|
*
|
|
21
26
|
* **NEVER ships in a release.** This module lives under `cross_backend/` and
|
|
@@ -33,32 +38,52 @@ import type { RuntimeDeps } from '../../runtime/deps.js';
|
|
|
33
38
|
/**
|
|
34
39
|
* Adapter-built handle to a bound HTTP server.
|
|
35
40
|
*
|
|
36
|
-
* `shutdown` stops accepting new connections and
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
41
|
+
* `shutdown` stops accepting new connections at once and settles when the
|
|
42
|
+
* in-flight ones have drained — the core starts it, closes the app (which
|
|
43
|
+
* ends live WebSockets), then awaits it. `native` is an adapter-specific
|
|
44
|
+
* server reference — used by Node's `@hono/node-ws` `injectWebSocket(server)`
|
|
45
|
+
* post-serve hook.
|
|
40
46
|
*/
|
|
41
47
|
export interface ServeHandle {
|
|
42
48
|
shutdown: () => Promise<void>;
|
|
43
49
|
/** Adapter-specific server ref for post-serve hooks. Type-erased at the seam. */
|
|
44
50
|
native?: unknown;
|
|
45
51
|
}
|
|
52
|
+
/** Options for `TestingServerAdapter.prepare_websocket`. */
|
|
53
|
+
export interface PrepareWebsocketOptions {
|
|
54
|
+
/**
|
|
55
|
+
* Largest inbound WebSocket message, in bytes — pass the largest
|
|
56
|
+
* `WsEndpointSpec.max_message_bytes` of the mounted endpoints; a smaller
|
|
57
|
+
* value refuses messages the endpoint would accept. Node's `ws` server takes
|
|
58
|
+
* it as its frame cap (`maxPayload`), so an oversized message is refused
|
|
59
|
+
* before it is buffered whole; Deno and Bun ignore it, leaving the
|
|
60
|
+
* per-message check `register_ws_endpoint` makes (both close with
|
|
61
|
+
* `WS_CLOSE_MESSAGE_TOO_BIG`). The default sits far below `ws`'s own
|
|
62
|
+
* 100 MiB, so a build raising an endpoint's cap must pass it here too.
|
|
63
|
+
*
|
|
64
|
+
* @default DEFAULT_WS_MAX_MESSAGE_BYTES
|
|
65
|
+
*/
|
|
66
|
+
max_message_bytes?: number;
|
|
67
|
+
}
|
|
46
68
|
/**
|
|
47
69
|
* Result of an adapter's WS preparation step.
|
|
48
70
|
*
|
|
49
|
-
* `
|
|
50
|
-
*
|
|
51
|
-
* `serve()` returns a
|
|
52
|
-
* `injectWebSocket(server)
|
|
71
|
+
* `create_upgrade_websocket` is the factory a build passes to
|
|
72
|
+
* `create_app_server` as `AppServerOptions.create_upgrade_websocket`.
|
|
73
|
+
* `attach_to_server` runs after `serve()` returns a `ServeHandle` —
|
|
74
|
+
* Node uses it for `injectWebSocket(server)`, and it does nothing when the
|
|
75
|
+
* factory was never called (no `ws_endpoints`); Deno and Bun leave it
|
|
76
|
+
* undefined.
|
|
53
77
|
*/
|
|
54
78
|
export interface PreparedWebsocket {
|
|
55
|
-
|
|
79
|
+
create_upgrade_websocket: (app: Hono) => UpgradeWebSocket;
|
|
56
80
|
attach_to_server?: (handle: ServeHandle) => void;
|
|
57
81
|
}
|
|
58
82
|
/**
|
|
59
83
|
* Runtime adapter contract for the test-binary entry. Each adapter
|
|
60
|
-
* (`testing/cross_backend/testing_server_node.ts`, `testing/cross_backend/testing_server_deno.ts
|
|
61
|
-
* hands the shape to
|
|
84
|
+
* (`testing/cross_backend/testing_server_node.ts`, `testing/cross_backend/testing_server_deno.ts`,
|
|
85
|
+
* `testing/cross_backend/testing_server_bun.ts`) implements this and hands the shape to
|
|
86
|
+
* `start_testing_server`.
|
|
62
87
|
*/
|
|
63
88
|
export interface TestingServerAdapter {
|
|
64
89
|
/** Human-readable runtime label for log output (e.g. `"Node"`, `"Deno"`). */
|
|
@@ -67,9 +92,9 @@ export interface TestingServerAdapter {
|
|
|
67
92
|
runtime: RuntimeDeps;
|
|
68
93
|
/** Extract the raw TCP connection IP from a Hono context. */
|
|
69
94
|
get_connection_ip: (c: Context) => string | undefined;
|
|
70
|
-
/**
|
|
71
|
-
prepare_websocket: (
|
|
72
|
-
/** Bind `app.fetch` to `port` on `hostname`; return a
|
|
95
|
+
/** Prepare the runtime's WS upgrade helper and its post-serve attach step. */
|
|
96
|
+
prepare_websocket: (options?: PrepareWebsocketOptions) => PreparedWebsocket;
|
|
97
|
+
/** Bind `app.fetch` to `port` on `hostname`; return a `ServeHandle`. */
|
|
73
98
|
serve: (options: {
|
|
74
99
|
fetch: Hono['fetch'];
|
|
75
100
|
port: number;
|
|
@@ -82,24 +107,31 @@ export interface TestingServerAdapter {
|
|
|
82
107
|
/** Forceful exit on graceful-shutdown completion or fatal error. */
|
|
83
108
|
exit: (code: number) => never;
|
|
84
109
|
}
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
110
|
+
/** What the core hands a `StartTestingServerOptions.build_app` seam. */
|
|
111
|
+
export interface BuildTestingAppContext {
|
|
112
|
+
/**
|
|
113
|
+
* Prepare the runtime adapter's WS upgrade helper and return the factory
|
|
114
|
+
* to pass to `create_app_server` as `create_upgrade_websocket`. The core
|
|
115
|
+
* runs the adapter's attach step once the server is bound, so a build
|
|
116
|
+
* that mounts `ws_endpoints` needs nothing else. An HTTP-only build
|
|
117
|
+
* doesn't call it. Call it at most once per build: the Node adapter
|
|
118
|
+
* attaches one upgrade listener per preparation, and two would race on
|
|
119
|
+
* the shared socket.
|
|
120
|
+
*/
|
|
121
|
+
prepare_websocket: (options?: PrepareWebsocketOptions) => (app: Hono) => UpgradeWebSocket;
|
|
122
|
+
}
|
|
123
|
+
/** The assembled app a `StartTestingServerOptions.build_app` seam returns. */
|
|
94
124
|
export interface BuiltTestingApp {
|
|
95
|
-
/** The assembled Hono app (HTTP routes
|
|
125
|
+
/** The assembled Hono app (HTTP routes, RPC, and WS endpoints already mounted). */
|
|
96
126
|
app: Hono;
|
|
97
|
-
/**
|
|
127
|
+
/**
|
|
128
|
+
* Tear down backend(s) + DB + any rotation on graceful shutdown. Called
|
|
129
|
+
* after the listener stops accepting connections and before its drain is
|
|
130
|
+
* awaited, so it must end live WebSockets (`AppServer.close` does).
|
|
131
|
+
*/
|
|
98
132
|
close: () => Promise<void>;
|
|
99
|
-
/** Mount WS endpoint(s) given the runtime-prepared upgrade closure. */
|
|
100
|
-
mount_websocket?: (upgrade_websocket: UpgradeWebSocket) => void;
|
|
101
133
|
}
|
|
102
|
-
/** Options for
|
|
134
|
+
/** Options for `start_testing_server`. */
|
|
103
135
|
export interface StartTestingServerOptions {
|
|
104
136
|
/** Runtime-boundary adapter (Node or Deno). */
|
|
105
137
|
adapter: TestingServerAdapter;
|
|
@@ -120,10 +152,9 @@ export interface StartTestingServerOptions {
|
|
|
120
152
|
/**
|
|
121
153
|
* Build the app. Closes over the entry's runtime + connection-IP getter
|
|
122
154
|
* + password deps + resolved config — so this core never touches the
|
|
123
|
-
* domain. Returns the assembled app
|
|
124
|
-
* `mount_websocket` hook.
|
|
155
|
+
* domain. Returns the assembled app and a `close` teardown.
|
|
125
156
|
*/
|
|
126
|
-
build_app: () => Promise<BuiltTestingApp>;
|
|
157
|
+
build_app: (context: BuildTestingAppContext) => Promise<BuiltTestingApp>;
|
|
127
158
|
/** Optional logger; defaults to a `[daemon_name]`-namespaced `Logger`. */
|
|
128
159
|
log?: LoggerType;
|
|
129
160
|
}
|
|
@@ -141,9 +172,9 @@ export declare const is_loopback_host: (host: string) => boolean;
|
|
|
141
172
|
* Boot a test-mode server using the supplied runtime adapter.
|
|
142
173
|
*
|
|
143
174
|
* Mirrors a production `start_server` at the surface level — stale-daemon
|
|
144
|
-
* check, daemon-info write, bind, graceful
|
|
145
|
-
* caller's no-domain (or domain)
|
|
146
|
-
* and the runtime boundary is the
|
|
175
|
+
* check, daemon-info write, bind, graceful shutdown — but the app is the
|
|
176
|
+
* caller's no-domain (or domain) `StartTestingServerOptions.build_app`
|
|
177
|
+
* and the runtime boundary is the `TestingServerAdapter`. Refuses any
|
|
147
178
|
* non-loopback bind host (the test binary must stay on loopback — see
|
|
148
179
|
* `is_loopback_host`).
|
|
149
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":"AAAA,OAAO,sBAAsB,CAAC;AAE9B
|
|
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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"}
|
|
@@ -18,9 +18,9 @@ export const is_loopback_host = (host) => {
|
|
|
18
18
|
* Boot a test-mode server using the supplied runtime adapter.
|
|
19
19
|
*
|
|
20
20
|
* Mirrors a production `start_server` at the surface level — stale-daemon
|
|
21
|
-
* check, daemon-info write, bind, graceful
|
|
22
|
-
* caller's no-domain (or domain)
|
|
23
|
-
* and the runtime boundary is the
|
|
21
|
+
* check, daemon-info write, bind, graceful shutdown — but the app is the
|
|
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
|
*/
|
|
@@ -42,12 +42,14 @@ export const start_testing_server = async (options) => {
|
|
|
42
42
|
log.warn(`stale daemon.json (pid ${stale.pid} not running), replacing`);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
const prepared_websockets = [];
|
|
46
|
+
const built = await build_app({
|
|
47
|
+
prepare_websocket: (ws_options) => {
|
|
48
|
+
const prepared = adapter.prepare_websocket(ws_options);
|
|
49
|
+
prepared_websockets.push(prepared);
|
|
50
|
+
return prepared.create_upgrade_websocket;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
51
53
|
await write_daemon_info(runtime, daemon_name, {
|
|
52
54
|
version: 1,
|
|
53
55
|
pid: adapter.pid,
|
|
@@ -57,7 +59,8 @@ export const start_testing_server = async (options) => {
|
|
|
57
59
|
});
|
|
58
60
|
log.info(`Listening on http://${host}:${port} (${adapter.runtime_label}, test mode)`);
|
|
59
61
|
const server = adapter.serve({ fetch: built.app.fetch, port, hostname: host });
|
|
60
|
-
|
|
62
|
+
for (const prepared of prepared_websockets)
|
|
63
|
+
prepared.attach_to_server?.(server);
|
|
61
64
|
let shutting_down = false;
|
|
62
65
|
const shutdown = async () => {
|
|
63
66
|
if (shutting_down)
|
|
@@ -65,11 +68,16 @@ export const start_testing_server = async (options) => {
|
|
|
65
68
|
shutting_down = true;
|
|
66
69
|
log.info('shutting down...');
|
|
67
70
|
try {
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
|
|
71
|
+
// Stop accepting first so no new request reaches a closed DB, but
|
|
72
|
+
// don't await the drain yet: live WebSockets hold it open until
|
|
73
|
+
// `built.close()` ends them (`AppServer.close`), so awaiting it
|
|
74
|
+
// first would wait on sockets nothing is closing. A request already
|
|
75
|
+
// in flight when the signal lands may still see the backend close
|
|
76
|
+
// under it — acceptable for a test binary, where shutdown follows
|
|
77
|
+
// the suite.
|
|
78
|
+
const drained = server.shutdown();
|
|
72
79
|
await built.close();
|
|
80
|
+
await drained;
|
|
73
81
|
}
|
|
74
82
|
catch (error) {
|
|
75
83
|
log.error('shutdown error:', error);
|
|
@@ -23,7 +23,7 @@ export const create_deno_testing_adapter = () => ({
|
|
|
23
23
|
runtime: create_deno_runtime([]),
|
|
24
24
|
get_connection_ip: (c) => c.env?.remoteAddr?.hostname,
|
|
25
25
|
// Deno's WS upgrade is module-level and stateless — no post-serve attach.
|
|
26
|
-
prepare_websocket: () => ({
|
|
26
|
+
prepare_websocket: () => ({ create_upgrade_websocket: () => upgradeWebSocket }),
|
|
27
27
|
serve: ({ fetch, port, hostname }) => {
|
|
28
28
|
const server = Deno.serve({ port, hostname }, fetch);
|
|
29
29
|
const handle = { shutdown: () => server.shutdown(), native: server };
|
|
@@ -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;
|
|
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,mDAAmD;AACnD,eAAO,MAAM,2BAA2B,QAAO,oBA+B7C,CAAC"}
|
|
@@ -3,9 +3,12 @@ import '../assert_dev_env.js';
|
|
|
3
3
|
* Node runtime adapter for spawnable cross-process test server binaries.
|
|
4
4
|
*
|
|
5
5
|
* Binds `@hono/node-server`'s `serve()` and `@hono/node-ws`'s two-phase
|
|
6
|
-
* `createNodeWebSocket({app})` / `injectWebSocket(server)
|
|
7
|
-
* `
|
|
8
|
-
* and
|
|
6
|
+
* `createNodeWebSocket({app})` / `injectWebSocket(server)`: the factory
|
|
7
|
+
* `prepare_websocket` returns builds the first half from the app
|
|
8
|
+
* `create_app_server` assembles, and the core runs the second once `serve()`
|
|
9
|
+
* returns. The shared `testing/cross_backend/testing_server_core.ts` owns
|
|
10
|
+
* the rest. A test binary builds this adapter and hands it to
|
|
11
|
+
* `start_testing_server` alongside its `build_app` seam.
|
|
9
12
|
*
|
|
10
13
|
* `@hono/node-server` + `@hono/node-ws` are **optional** peer deps (same
|
|
11
14
|
* posture as `ws`) — only test binaries import them; production bundles
|
|
@@ -17,6 +20,7 @@ import process from 'node:process';
|
|
|
17
20
|
import { serve } from '@hono/node-server';
|
|
18
21
|
import { getConnInfo } from '@hono/node-server/conninfo';
|
|
19
22
|
import { createNodeWebSocket } from '@hono/node-ws';
|
|
23
|
+
import { DEFAULT_WS_MAX_MESSAGE_BYTES } from '../../actions/transports.js';
|
|
20
24
|
import { create_node_runtime } from '../../runtime/node.js';
|
|
21
25
|
const node_serve_handle = (server) => ({
|
|
22
26
|
shutdown: () => new Promise((resolve, reject) => {
|
|
@@ -29,14 +33,23 @@ export const create_node_testing_adapter = () => ({
|
|
|
29
33
|
runtime_label: 'Node',
|
|
30
34
|
runtime: create_node_runtime(),
|
|
31
35
|
get_connection_ip: (c) => getConnInfo(c).remote.address,
|
|
32
|
-
prepare_websocket: (
|
|
33
|
-
const
|
|
36
|
+
prepare_websocket: (options) => {
|
|
37
|
+
const max_payload = options?.max_message_bytes ?? DEFAULT_WS_MAX_MESSAGE_BYTES;
|
|
38
|
+
let node_ws;
|
|
34
39
|
return {
|
|
35
|
-
|
|
40
|
+
create_upgrade_websocket: (app) => {
|
|
41
|
+
node_ws = createNodeWebSocket({ app });
|
|
42
|
+
// `@hono/node-ws` builds its `ws` server with no options and takes
|
|
43
|
+
// none, so its frame cap would stay `ws`'s 100 MiB default. `ws` reads
|
|
44
|
+
// `options.maxPayload` per upgrade, so setting it here — before any
|
|
45
|
+
// connection — caps every socket at the endpoint's message limit.
|
|
46
|
+
node_ws.wss.options.maxPayload = max_payload;
|
|
47
|
+
return node_ws.upgradeWebSocket;
|
|
48
|
+
},
|
|
36
49
|
attach_to_server: (handle) => {
|
|
37
50
|
// `handle.native` is the `ServerType` from `serve()` —
|
|
38
51
|
// type-erased at the {@link ServeHandle} seam, so it downcasts here.
|
|
39
|
-
injectWebSocket(handle.native);
|
|
52
|
+
node_ws?.injectWebSocket(handle.native);
|
|
40
53
|
}
|
|
41
54
|
};
|
|
42
55
|
},
|
|
@@ -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. */
|
|
@@ -47,6 +45,21 @@ export interface TsSpineBackendConfigOptions {
|
|
|
47
45
|
* client IP. Mirrors `SpineStubBackendConfigOptions.enable_login_rate_limit`.
|
|
48
46
|
*/
|
|
49
47
|
readonly enable_login_rate_limit?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Enable the action rate limiters (`FUZ_ACTION_RATE_LIMIT_ENABLED=true`) —
|
|
50
|
+
* one per-IP and one per-account limiter shared by the RPC and WS
|
|
51
|
+
* endpoints. Off by default, like `enable_login_rate_limit`; set only for
|
|
52
|
+
* the dedicated security cross project, which drives them through
|
|
53
|
+
* `ws_action_rate_limit.ts`. Mirrors `SpineStubBackendConfigOptions.enable_action_rate_limit`.
|
|
54
|
+
*/
|
|
55
|
+
readonly enable_action_rate_limit?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Replace both action limiters' `max_attempts`
|
|
58
|
+
* (`FUZ_ACTION_RATE_LIMIT_MAX_ATTEMPTS`) so a suite reaches the cap in a few
|
|
59
|
+
* calls. Requires `enable_action_rate_limit` — the binary refuses to boot
|
|
60
|
+
* otherwise. Mirrors `SpineStubBackendConfigOptions.action_rate_limit_max_attempts`.
|
|
61
|
+
*/
|
|
62
|
+
readonly action_rate_limit_max_attempts?: number;
|
|
50
63
|
}
|
|
51
64
|
/**
|
|
52
65
|
* `BackendConfig` for the Node TS spine binary — spawned via `gro run`
|
|
@@ -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;
|
|
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,15 +1,14 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
2
|
import { build_test_backend_paths } from './build_test_backend_paths.js';
|
|
3
|
-
import {
|
|
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
|
|
10
8
|
* `/ready` deploy gate in `build_spine_app`), `peer_request` (the backend
|
|
11
9
|
* WS transport's `request_connection` path drives server→client `peer/ping`),
|
|
12
|
-
* `ws_account_actions` (the binary mounts
|
|
10
|
+
* `ws_account_actions` (the binary mounts its full action set on `/api/ws`,
|
|
11
|
+
* the account actions among them),
|
|
13
12
|
* and `cell_gated_create` (the binary's `full_spine_mount` mounts the
|
|
14
13
|
* `test_cell_gated_create_authorize` policy).
|
|
15
14
|
*/
|
|
@@ -78,7 +77,8 @@ export const ts_spine_node_backend_config = (options = {}) => {
|
|
|
78
77
|
paths,
|
|
79
78
|
extra_env: {
|
|
80
79
|
[TS_SPINE_DIR_ENV]: paths.root,
|
|
81
|
-
...login_rate_limit_env(enable_login_rate_limit)
|
|
80
|
+
...login_rate_limit_env(enable_login_rate_limit),
|
|
81
|
+
...action_rate_limit_env(options)
|
|
82
82
|
},
|
|
83
83
|
capabilities: ts_spine_capabilities
|
|
84
84
|
}),
|
|
@@ -105,7 +105,8 @@ export const ts_spine_bun_backend_config = (options = {}) => {
|
|
|
105
105
|
paths,
|
|
106
106
|
extra_env: {
|
|
107
107
|
[TS_SPINE_DIR_ENV]: paths.root,
|
|
108
|
-
...login_rate_limit_env(enable_login_rate_limit)
|
|
108
|
+
...login_rate_limit_env(enable_login_rate_limit),
|
|
109
|
+
...action_rate_limit_env(options)
|
|
109
110
|
},
|
|
110
111
|
capabilities: ts_spine_bun_capabilities
|
|
111
112
|
}),
|
|
@@ -149,7 +150,8 @@ export const ts_spine_deno_backend_config = (options = {}) => {
|
|
|
149
150
|
paths,
|
|
150
151
|
extra_env: {
|
|
151
152
|
[TS_SPINE_DIR_ENV]: paths.root,
|
|
152
|
-
...login_rate_limit_env(enable_login_rate_limit)
|
|
153
|
+
...login_rate_limit_env(enable_login_rate_limit),
|
|
154
|
+
...action_rate_limit_env(options)
|
|
153
155
|
},
|
|
154
156
|
capabilities: ts_spine_capabilities
|
|
155
157
|
}),
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import type { SetupTest } from './setup.js';
|
|
3
|
+
/** Options for `describe_ws_action_rate_limit_cross_tests`. */
|
|
4
|
+
export interface WsActionRateLimitCrossTestOptions {
|
|
5
|
+
/** Per-test fixture producer (cross-process only — see the module doc). */
|
|
6
|
+
readonly setup_test: SetupTest;
|
|
7
|
+
/** Base URL the backend is reachable at (e.g. `http://localhost:1177`). */
|
|
8
|
+
readonly base_url: string;
|
|
9
|
+
/** WebSocket endpoint path (e.g. `/api/ws`). */
|
|
10
|
+
readonly ws_path: string;
|
|
11
|
+
/** JSON-RPC endpoint path. Default `/api/rpc` (the spine convention). */
|
|
12
|
+
readonly rpc_path?: string;
|
|
13
|
+
/**
|
|
14
|
+
* The `max_attempts` both action limiters were started with
|
|
15
|
+
* (`action_rate_limit_max_attempts` on the backend config). At least 2 —
|
|
16
|
+
* the shared-bucket case splits the budget across the two transports.
|
|
17
|
+
*/
|
|
18
|
+
readonly max_attempts: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Register the action rate-limit suite: the account axis over WS, one account
|
|
22
|
+
* bucket across HTTP RPC and WS, the per-IP axis keyed by `X-Forwarded-For`
|
|
23
|
+
* across both transports, and the account axis at the RPC and WS dispatchers.
|
|
24
|
+
*/
|
|
25
|
+
export declare const describe_ws_action_rate_limit_cross_tests: (options: WsActionRateLimitCrossTestOptions) => void;
|
|
26
|
+
//# sourceMappingURL=ws_action_rate_limit.d.ts.map
|
|
@@ -0,0 +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,+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"}
|