@fuzdev/fuz_app 0.126.0 → 0.128.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/CLAUDE.md +37 -21
- 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 -2
- 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 +13 -9
- package/dist/auth/audit_emitter.d.ts +59 -12
- package/dist/auth/audit_emitter.d.ts.map +1 -1
- package/dist/auth/audit_emitter.js +53 -15
- 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/http/CLAUDE.md +3 -3
- package/dist/http/surface.d.ts +12 -11
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +1 -15
- package/dist/rate_limiter.d.ts +19 -1
- package/dist/rate_limiter.d.ts.map +1 -1
- package/dist/rate_limiter.js +23 -0
- package/dist/realtime/sse_auth_guard.d.ts +3 -1
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/server/app_server.d.ts +41 -13
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +105 -50
- package/dist/testing/CLAUDE.md +51 -38
- package/dist/testing/app_server.d.ts +1 -1
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +15 -14
- package/dist/testing/attack_surface.d.ts +1 -1
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/audit_drift_guard.d.ts +4 -2
- 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_backend_configs.d.ts +3 -34
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +1 -31
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +1 -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 +3 -3
- 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 +49 -5
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +27 -18
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +30 -19
- package/dist/testing/cross_backend/testing_server_core.d.ts +11 -11
- package/dist/testing/cross_backend/testing_server_core.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_server_core.js +2 -2
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +0 -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 +2 -4
- package/dist/testing/cross_backend/ws_action_rate_limit.d.ts +1 -1
- package/dist/testing/cross_backend/ws_action_rate_limit.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_round_trip.d.ts +2 -1
- package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_round_trip.js +40 -4
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +4 -3
- package/dist/testing/stubs.d.ts +26 -6
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +56 -16
- package/dist/testing/ws_round_trip.d.ts +7 -31
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +0 -38
- package/package.json +1 -1
|
@@ -12,9 +12,9 @@ import { logger } from 'hono/logger';
|
|
|
12
12
|
import { bodyLimit } from 'hono/body-limit';
|
|
13
13
|
import { z } from 'zod';
|
|
14
14
|
import { session_cookie_options } from '../auth/session_cookie.js';
|
|
15
|
-
import { create_audit_log_sse, audit_log_event_specs } from '../realtime/sse_auth_guard.js';
|
|
15
|
+
import { create_audit_log_sse, create_sse_connection_closer, audit_log_event_specs } from '../realtime/sse_auth_guard.js';
|
|
16
16
|
import { BaseServerEnv } from './env.js';
|
|
17
|
-
import { RateLimiter, default_login_ip_rate_limit, default_login_account_rate_limit, default_action_account_rate_limit, default_action_ip_rate_limit } from '../rate_limiter.js';
|
|
17
|
+
import { RateLimiter, announce_rate_limiters_disabled, default_login_ip_rate_limit, default_login_account_rate_limit, default_action_account_rate_limit, default_action_ip_rate_limit } from '../rate_limiter.js';
|
|
18
18
|
// Side-effect import: augments Hono's ContextVariableMap so consumers
|
|
19
19
|
// that import app_server get type-safe c.get('auth_session_id') etc.
|
|
20
20
|
import '../hono_context.js';
|
|
@@ -26,7 +26,7 @@ import { apply_middleware_specs, apply_route_specs, prefix_route_specs } from '.
|
|
|
26
26
|
import { check_bootstrap_status, create_bootstrap_route_specs } from '../auth/bootstrap_routes.js';
|
|
27
27
|
import { create_surface_route_spec } from '../http/common_routes.js';
|
|
28
28
|
import { flush_pending_effects, flush_post_commit_effects } from '../http/pending_effects.js';
|
|
29
|
-
import { assert_endpoint_in_auth_scope, create_auth_middleware_specs } from '../auth/middleware.js';
|
|
29
|
+
import { assert_endpoint_in_auth_scope, assert_middleware_stack_mounted, create_auth_middleware_specs } from '../auth/middleware.js';
|
|
30
30
|
import { fuz_auth_guard_resolver } from '../auth/auth_guard_resolver.js';
|
|
31
31
|
import { create_fuz_authorization_handler } from '../auth/request_context.js';
|
|
32
32
|
import { ERROR_PAYLOAD_TOO_LARGE } from '../http/error_schemas.js';
|
|
@@ -80,16 +80,53 @@ export const DEFAULT_MAX_BODY_SIZE = 1024 * 1024;
|
|
|
80
80
|
* case; consumers can call `require_audit_sse(ctx)` / `require_audit_sse(server)`
|
|
81
81
|
* to assert the invariant.
|
|
82
82
|
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
83
|
+
* Assembly acquires a few things that outlive it — the rate limiters it
|
|
84
|
+
* builds, the listeners it registers on `deps.audit`, the members it adds to
|
|
85
|
+
* `deps.connection_closer` — and records a release for each as it goes. When
|
|
86
|
+
* assembly throws, every release recorded so far runs, last acquired first
|
|
87
|
+
* (one that throws is logged and the rest still run), and the original error
|
|
88
|
+
* is rethrown: a failed assembly leaves the backend's emitter and closer as
|
|
89
|
+
* it found them and no limiter timer running. When `auth_cleanup` is set, the
|
|
90
|
+
* auth cleanup schedule starts as the last step of assembly — nothing after
|
|
91
|
+
* it can throw, so an assembly that throws leaves no schedule running.
|
|
85
92
|
*
|
|
86
93
|
* The returned `close` owns shutdown: it stops that schedule, closes every
|
|
87
|
-
* live connection on the backend's closer,
|
|
88
|
-
*
|
|
94
|
+
* live connection on the backend's closer, runs the same releases, and
|
|
95
|
+
* closes the database (`AppServer.close`).
|
|
89
96
|
*
|
|
90
97
|
* @returns assembled Hono app, backend, surface build, and bootstrap status
|
|
98
|
+
* @throws Error from any assembly step, after releasing what assembly acquired
|
|
91
99
|
*/
|
|
92
100
|
export const create_app_server = async (options) => {
|
|
101
|
+
const releases = [];
|
|
102
|
+
try {
|
|
103
|
+
return await assemble_app_server(options, releases);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
run_releases(releases, options.backend.deps.log, 'a failed assembly');
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Run `releases` last-first and empty it. Each runs in its own `try`, so one
|
|
112
|
+
* that throws is logged and the rest still run.
|
|
113
|
+
*/
|
|
114
|
+
const run_releases = (releases, log, during) => {
|
|
115
|
+
for (const release of releases.splice(0).reverse()) {
|
|
116
|
+
try {
|
|
117
|
+
release();
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
log.error(`create_app_server: a release during ${during} failed:`, error);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* The body of `create_app_server`. Every acquisition that outlives assembly
|
|
126
|
+
* pushes its release onto `releases` as it happens, so a throw at any later
|
|
127
|
+
* step unwinds exactly what was acquired.
|
|
128
|
+
*/
|
|
129
|
+
const assemble_app_server = async (options, releases) => {
|
|
93
130
|
const { backend } = options;
|
|
94
131
|
const { deps } = backend;
|
|
95
132
|
const { log } = deps;
|
|
@@ -97,16 +134,18 @@ export const create_app_server = async (options) => {
|
|
|
97
134
|
// one is a default instance under 'enforced', `null` under
|
|
98
135
|
// 'disabled_for_testing'. One instance per surface — see
|
|
99
136
|
// `AppServerOptions.login_ip_rate_limiter` for why these are not a single
|
|
100
|
-
// shared bucket. The ones built here are the server's to dispose on
|
|
137
|
+
// shared bucket. The ones built here are the server's to dispose — on
|
|
138
|
+
// close, or when assembly fails.
|
|
101
139
|
const rate_limiter_mode = options.rate_limiters ?? 'enforced';
|
|
102
|
-
const built_rate_limiters = [];
|
|
103
140
|
const resolve_rate_limiter = (option, defaults) => {
|
|
104
141
|
if (option !== undefined)
|
|
105
142
|
return option;
|
|
106
|
-
if (rate_limiter_mode === 'disabled_for_testing')
|
|
143
|
+
if (rate_limiter_mode === 'disabled_for_testing') {
|
|
144
|
+
announce_rate_limiters_disabled();
|
|
107
145
|
return null;
|
|
146
|
+
}
|
|
108
147
|
const limiter = new RateLimiter({ ...defaults });
|
|
109
|
-
|
|
148
|
+
releases.push(() => limiter.dispose());
|
|
110
149
|
return limiter;
|
|
111
150
|
};
|
|
112
151
|
const login_ip_rate_limiter = resolve_rate_limiter(options.login_ip_rate_limiter, default_login_ip_rate_limit);
|
|
@@ -119,16 +158,19 @@ export const create_app_server = async (options) => {
|
|
|
119
158
|
// Factory-managed audit SSE — registers a listener on the bound emitter
|
|
120
159
|
// so SSE fan-out runs alongside the consumer's `on_audit_event`
|
|
121
160
|
// without rebuilding `AppDeps`, and adds the registry to the backend's
|
|
122
|
-
// closer so revocations close its streams.
|
|
161
|
+
// closer so revocations close its streams. The closer membership is added
|
|
162
|
+
// here rather than through `create_audit_log_sse`'s option, so its remover
|
|
163
|
+
// is the server's.
|
|
123
164
|
const audit_sse = options.audit_log_sse
|
|
124
165
|
? create_audit_log_sse({
|
|
125
166
|
log,
|
|
126
167
|
role: typeof options.audit_log_sse === 'object' ? options.audit_log_sse.role : undefined,
|
|
127
|
-
connection_closer:
|
|
168
|
+
connection_closer: null
|
|
128
169
|
})
|
|
129
170
|
: null;
|
|
130
171
|
if (audit_sse) {
|
|
131
|
-
deps.
|
|
172
|
+
releases.push(deps.connection_closer.add(create_sse_connection_closer(audit_sse.registry)));
|
|
173
|
+
releases.push(deps.audit.add_listener(audit_sse.on_audit_event));
|
|
132
174
|
}
|
|
133
175
|
// Proxy middleware
|
|
134
176
|
const proxy_spec = create_proxy_middleware_spec({ ...options.proxy, log });
|
|
@@ -138,9 +180,13 @@ export const create_app_server = async (options) => {
|
|
|
138
180
|
session_options: options.session_options,
|
|
139
181
|
daemon_token_state: options.daemon_token_state
|
|
140
182
|
});
|
|
141
|
-
|
|
183
|
+
const stack_specs = [proxy_spec, ...auth_middleware];
|
|
184
|
+
let middleware_specs = stack_specs;
|
|
142
185
|
if (options.transform_middleware) {
|
|
143
|
-
|
|
186
|
+
// the transform gets copies in a fresh array, so mutating what it was
|
|
187
|
+
// handed can't rewrite the originals the check matches against
|
|
188
|
+
middleware_specs = options.transform_middleware(stack_specs.map((spec) => ({ ...spec })));
|
|
189
|
+
assert_middleware_stack_mounted(stack_specs, middleware_specs);
|
|
144
190
|
}
|
|
145
191
|
// Bootstrap status
|
|
146
192
|
// - undefined / 'disabled': no route mounted; placeholder status.
|
|
@@ -221,8 +267,10 @@ export const create_app_server = async (options) => {
|
|
|
221
267
|
const resolved_ws_endpoints = resolve_ws_endpoints(typeof options.ws_endpoints === 'function'
|
|
222
268
|
? options.ws_endpoints(context)
|
|
223
269
|
: options.ws_endpoints, options.allowed_origins);
|
|
224
|
-
|
|
270
|
+
// the endpoints to mount and the adapter factory they need — null when none resolve
|
|
271
|
+
let ws_mount = null;
|
|
225
272
|
if (resolved_ws_endpoints?.length) {
|
|
273
|
+
const create_upgrade_websocket = options.create_upgrade_websocket;
|
|
226
274
|
if (create_upgrade_websocket === undefined) {
|
|
227
275
|
throw new Error('create_app_server: ws_endpoints resolved non-empty but create_upgrade_websocket is missing. ' +
|
|
228
276
|
"Pass a factory returning the Hono adapter's upgradeWebSocket helper.");
|
|
@@ -230,12 +278,34 @@ export const create_app_server = async (options) => {
|
|
|
230
278
|
for (const endpoint of resolved_ws_endpoints) {
|
|
231
279
|
assert_endpoint_in_auth_scope('ws_endpoints', endpoint.path);
|
|
232
280
|
}
|
|
281
|
+
ws_mount = { endpoints: resolved_ws_endpoints, create_upgrade_websocket };
|
|
233
282
|
}
|
|
234
283
|
// Surface route (default: enabled)
|
|
235
284
|
if (options.surface_route !== false) {
|
|
236
285
|
factory_routes.push(create_surface_route_spec(surface_ref));
|
|
237
286
|
}
|
|
238
287
|
const route_specs = [...consumer_routes, ...factory_routes];
|
|
288
|
+
// WS path checks — pure data, so they run before anything mounts.
|
|
289
|
+
// `register_ws_endpoint` mounts a `GET path` upgrade route: a duplicate
|
|
290
|
+
// path, or one a `RouteSpec` already registered as `GET path`, would be
|
|
291
|
+
// silently shadowed by Hono's last-wins semantics — fail fast instead.
|
|
292
|
+
if (ws_mount) {
|
|
293
|
+
const route_spec_get_paths = new Set();
|
|
294
|
+
for (const r of route_specs) {
|
|
295
|
+
if (r.method === 'GET')
|
|
296
|
+
route_spec_get_paths.add(r.path);
|
|
297
|
+
}
|
|
298
|
+
const seen_paths = new Set();
|
|
299
|
+
for (const endpoint of ws_mount.endpoints) {
|
|
300
|
+
if (seen_paths.has(endpoint.path)) {
|
|
301
|
+
throw new Error(`create_app_server: duplicate ws_endpoints path: ${endpoint.path}`);
|
|
302
|
+
}
|
|
303
|
+
if (route_spec_get_paths.has(endpoint.path)) {
|
|
304
|
+
throw new Error(`create_app_server: ws_endpoints path collides with a GET RouteSpec: ${endpoint.path}`);
|
|
305
|
+
}
|
|
306
|
+
seen_paths.add(endpoint.path);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
239
309
|
// Surface + logging
|
|
240
310
|
const surface_middleware = options.post_route_middleware
|
|
241
311
|
? [...middleware_specs, ...options.post_route_middleware]
|
|
@@ -317,12 +387,6 @@ export const create_app_server = async (options) => {
|
|
|
317
387
|
log_startup_summary(surface_spec.surface, log, options.env_values);
|
|
318
388
|
// Hono app assembly
|
|
319
389
|
const app = new Hono();
|
|
320
|
-
// The adapter helper for the WS mount below — built from this app, which
|
|
321
|
-
// Node's `createNodeWebSocket({app})` needs, and only when there is
|
|
322
|
-
// something to mount.
|
|
323
|
-
const upgrade_websocket = resolved_ws_endpoints?.length && create_upgrade_websocket
|
|
324
|
-
? create_upgrade_websocket(app)
|
|
325
|
-
: null;
|
|
326
390
|
// Two-queue side-effect flush. `pending_effects` collects eager
|
|
327
391
|
// fire-and-forget promises (audit emits, api-token usage). `post_commit_effects` collects deferred thunks pushed via
|
|
328
392
|
// `emit_after_commit` (WS notifications, anything that must observe a
|
|
@@ -386,17 +450,10 @@ export const create_app_server = async (options) => {
|
|
|
386
450
|
// adjacent to the consumer routes and ahead of the static fallback —
|
|
387
451
|
// matches the "WS mount is route registration" mental model.
|
|
388
452
|
const mounted_ws_endpoints = {};
|
|
389
|
-
if (
|
|
390
|
-
//
|
|
391
|
-
//
|
|
392
|
-
|
|
393
|
-
// GET route — fail fast instead.
|
|
394
|
-
const route_spec_get_paths = new Set();
|
|
395
|
-
for (const r of route_specs) {
|
|
396
|
-
if (r.method === 'GET')
|
|
397
|
-
route_spec_get_paths.add(r.path);
|
|
398
|
-
}
|
|
399
|
-
const seen_paths = new Set();
|
|
453
|
+
if (ws_mount) {
|
|
454
|
+
// the adapter helper — built from this app, which Node's
|
|
455
|
+
// `createNodeWebSocket({app})` needs, and only when there is something to mount
|
|
456
|
+
const upgrade_websocket = ws_mount.create_upgrade_websocket(app);
|
|
400
457
|
// Dedupe `auth_guard` wiring by transport reference — two specs
|
|
401
458
|
// sharing one transport instance get a single listener,
|
|
402
459
|
// otherwise revocation events would fire `close_sockets_for_*`
|
|
@@ -406,14 +463,7 @@ export const create_app_server = async (options) => {
|
|
|
406
463
|
// (even with explicit `auth_guard: false`) cannot opt out. To
|
|
407
464
|
// disable, every spec sharing the transport must pass `auth_guard: false`.
|
|
408
465
|
const guarded_transports = new WeakSet();
|
|
409
|
-
for (const endpoint of
|
|
410
|
-
if (seen_paths.has(endpoint.path)) {
|
|
411
|
-
throw new Error(`create_app_server: duplicate ws_endpoints path: ${endpoint.path}`);
|
|
412
|
-
}
|
|
413
|
-
if (route_spec_get_paths.has(endpoint.path)) {
|
|
414
|
-
throw new Error(`create_app_server: ws_endpoints path collides with a GET RouteSpec: ${endpoint.path}`);
|
|
415
|
-
}
|
|
416
|
-
seen_paths.add(endpoint.path);
|
|
466
|
+
for (const endpoint of ws_mount.endpoints) {
|
|
417
467
|
const { transport: endpoint_transport } = register_ws_endpoint({
|
|
418
468
|
app,
|
|
419
469
|
path: endpoint.path,
|
|
@@ -422,8 +472,8 @@ export const create_app_server = async (options) => {
|
|
|
422
472
|
db: deps.db,
|
|
423
473
|
actions: endpoint.actions,
|
|
424
474
|
transport: endpoint.transport,
|
|
425
|
-
//
|
|
426
|
-
connection_closer:
|
|
475
|
+
// added below instead, so the remover is the server's
|
|
476
|
+
connection_closer: null,
|
|
427
477
|
max_connections_per_account: endpoint.max_connections_per_account,
|
|
428
478
|
heartbeat: endpoint.heartbeat,
|
|
429
479
|
artificial_delay: endpoint.artificial_delay,
|
|
@@ -436,13 +486,15 @@ export const create_app_server = async (options) => {
|
|
|
436
486
|
action_account_rate_limiter
|
|
437
487
|
});
|
|
438
488
|
mounted_ws_endpoints[endpoint.path] = endpoint_transport;
|
|
489
|
+
// every revocation handler's close reaches this transport
|
|
490
|
+
releases.push(deps.connection_closer.add(endpoint_transport));
|
|
439
491
|
if (endpoint.auth_guard !== false && !guarded_transports.has(endpoint_transport)) {
|
|
440
492
|
guarded_transports.add(endpoint_transport);
|
|
441
|
-
deps.audit.add_listener(create_ws_auth_guard(endpoint_transport, log));
|
|
493
|
+
releases.push(deps.audit.add_listener(create_ws_auth_guard(endpoint_transport, log)));
|
|
442
494
|
}
|
|
443
495
|
if (endpoint.extra_audit_handlers?.length) {
|
|
444
496
|
for (const handler of endpoint.extra_audit_handlers) {
|
|
445
|
-
deps.audit.add_listener(handler);
|
|
497
|
+
releases.push(deps.audit.add_listener(handler));
|
|
446
498
|
}
|
|
447
499
|
}
|
|
448
500
|
}
|
|
@@ -459,8 +511,10 @@ export const create_app_server = async (options) => {
|
|
|
459
511
|
}
|
|
460
512
|
}
|
|
461
513
|
// Auth cleanup — opt-in, and last: nothing after it can throw, so a failed
|
|
462
|
-
// assembly never leaves a schedule running. Its
|
|
463
|
-
// `
|
|
514
|
+
// assembly never leaves a schedule running. Its own throw (an invalid
|
|
515
|
+
// `interval_ms`) comes before any pass starts, and unwinds `releases` like
|
|
516
|
+
// any other. Its closes go through `deps.connection_closer`, which the
|
|
517
|
+
// mounts above have filled.
|
|
464
518
|
const auth_cleanup = options.auth_cleanup
|
|
465
519
|
? start_auth_cleanup(deps, options.auth_cleanup === true ? undefined : options.auth_cleanup)
|
|
466
520
|
: null;
|
|
@@ -478,8 +532,9 @@ export const create_app_server = async (options) => {
|
|
|
478
532
|
catch (error) {
|
|
479
533
|
log.error('create_app_server: closing live connections on shutdown failed:', error);
|
|
480
534
|
}
|
|
481
|
-
|
|
482
|
-
|
|
535
|
+
// then what assembly acquired — the audit listeners and closer members
|
|
536
|
+
// this server added, and the limiters it built
|
|
537
|
+
run_releases(releases, log, 'shutdown');
|
|
483
538
|
await backend.close();
|
|
484
539
|
};
|
|
485
540
|
return {
|
package/dist/testing/CLAUDE.md
CHANGED
|
@@ -33,11 +33,11 @@ time (never runtime), where a throwing guard would break `vite build`.
|
|
|
33
33
|
- `stub_mw` — pass-through middleware (`async (_c, next) => next()`).
|
|
34
34
|
- `stub_app_deps` — frozen `AppDeps`, every capability throwing, `audit` a no-op `AuditEmitter` from `create_test_audit_emitter`, `connection_closer` an empty `RealtimeCloser`.
|
|
35
35
|
- `create_stub_app_deps()` — factory: fresh `AppDeps` with no-op keyring/password/`delete_file`, a `read_secure_file` that throws ENOENT (the no-token-file state), a `create_noop_stub` DB, silent `Logger`, no-op `audit`, an empty `connection_closer`.
|
|
36
|
-
- `create_test_audit_emitter()` — no-op `AuditEmitter`; `emit` / `emit_role_grant_target` no-op, `emit_pool` resolves immediately, `notify` no-op, `add_listener` throws (use `create_recording_audit_emitter` for a listener-accepting emitter), `listener_count` returns 0.
|
|
36
|
+
- `create_test_audit_emitter()` — no-op `AuditEmitter`; `emit` / `emit_role_grant_target` no-op, `emit_pool` resolves immediately, `notify` no-op, `add_listener` throws (use `create_recording_audit_emitter` for a listener-accepting emitter), `listener_count` returns 0, `drain_inflight` resolves immediately.
|
|
37
37
|
- `create_stub_audit_sse()` — no-op `AuditLogSse` for surface-test wiring without booting real SSE. `on_audit_event` no-op (nothing is broadcast); `registry` is a fresh `SubscriberRegistry` (live `.count` / `.close_*` for registry-state tests, isolated per call). For real SSE plumbing build via `create_audit_log_sse` against `create_test_app`.
|
|
38
|
-
- `create_stub_api_middleware({include_daemon_token?})` — stub `MiddlewareSpec[]` matching `create_auth_middleware_specs`'s output (origin/session/request_context/bearer_auth, optional daemon_token) for surface generation without booting real auth. See `auth/CLAUDE.md` §Middleware for the real stack.
|
|
38
|
+
- `create_stub_api_middleware({include_daemon_token?})` — stub `MiddlewareSpec[]` matching `create_auth_middleware_specs`'s output (origin/session/request_context/bearer_auth, optional daemon_token) for surface generation without booting real auth. Each spec gets its own pass-through handler (not the shared `stub_mw`), so `assert_middleware_stack_mounted` tells the layers apart. See `auth/CLAUDE.md` §Middleware for the real stack.
|
|
39
39
|
- `create_stub_app_server_context(session_options)` — stub `AppServerContext`; rate limiters null, `bootstrap_status.available: false`.
|
|
40
|
-
- `create_test_app_surface_spec(options)` — builds an `AppSurfaceSpec` mirroring `create_app_server`'s route assembly (consumer routes + stub middleware + surface generation). `CreateTestAppSurfaceSpecOptions`: `session_options`, `create_route_specs`, `env_schema?`, `event_specs?`, `rpc_endpoints?`, `ws_endpoints?`, `allowed_origins?`, `transform_middleware?`, `bootstrap?`. Throws, like `create_app_server`, on an endpoint path outside `AUTH_MIDDLEWARE_PATH` (`assert_endpoint_in_auth_scope`). Bootstrap is opt-in (symmetric with `create_app_server` — omit to skip; pass the same value as prod to mount routes at `bootstrap.route_prefix ?? '/api/account'`). Single source of truth for attack-surface tests — track `create_app_server` wiring changes here.
|
|
40
|
+
- `create_test_app_surface_spec(options)` — builds an `AppSurfaceSpec` mirroring `create_app_server`'s route assembly (consumer routes + stub middleware + surface generation). Its middleware list mirrors the real one — a stub `trusted_proxy` spec at `'*'`, then the stub auth stack — so every route's `applicable_middleware` starts with `trusted_proxy` (a route outside `/api/*` now lists just `['trusted_proxy']`), as on the live server. `CreateTestAppSurfaceSpecOptions`: `session_options`, `create_route_specs`, `env_schema?`, `event_specs?`, `rpc_endpoints?`, `ws_endpoints?`, `allowed_origins?`, `transform_middleware?`, `bootstrap?`. Throws, like `create_app_server`, on an endpoint path outside `AUTH_MIDDLEWARE_PATH` (`assert_endpoint_in_auth_scope`) and on a `transform_middleware` that moves, drops, duplicates, reorders, wraps, or replaces the stub proxy spec or a stub auth spec (`assert_middleware_stack_mounted`); the transform receives shallow copies in a fresh array, so an in-place mutation can't slip past the check. Throws on a `ws_endpoints` spec with no `allowed_origins` of its own when no `allowed_origins` option is given (there is no server list to default from). Bootstrap is opt-in (symmetric with `create_app_server` — omit to skip; pass the same value as prod to mount routes at `bootstrap.route_prefix ?? '/api/account'`). Single source of truth for attack-surface tests — track `create_app_server` wiring changes here.
|
|
41
41
|
|
|
42
42
|
Throwing stubs surface mock escape: a test that accidentally reaches into
|
|
43
43
|
stub territory breaks immediately with a label-scoped error rather than
|
|
@@ -100,7 +100,7 @@ mismatch.
|
|
|
100
100
|
### `audit_drift_guard.ts` — audit-emission validation
|
|
101
101
|
|
|
102
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.
|
|
103
|
-
- `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. Replaces per-file capturing emitters previously duplicated across `password_change.test.ts`, `audit_log.test.ts`, etc.
|
|
103
|
+
- `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
104
|
- `RecordingAuditEmitter` — `{emitter: AuditEmitter; calls: Array<AuditLogInput>}`.
|
|
105
105
|
|
|
106
106
|
### `connection_closer_helpers.ts` — `ConnectionCloser` test doubles
|
|
@@ -179,15 +179,17 @@ Types:
|
|
|
179
179
|
`create_test_app` hard-codes the test-friendly `AppServerOptions`:
|
|
180
180
|
`allowed_origins: [/^http:\/\/localhost/]`, stub proxy pinned to `127.0.0.1`,
|
|
181
181
|
`env_schema: z.object({})`, `rate_limiters: 'disabled_for_testing'` (every
|
|
182
|
-
rate limiter `null
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
rate limiter `null` — a limiter passed through `app_options` wins over the
|
|
183
|
+
mode, so a rate-limit test enables just the one it pins; the mode's stderr
|
|
184
|
+
banner prints once per module instance — once per test file in an isolated
|
|
185
|
+
vitest project, once per run under `isolate: false`, ../../../docs/usage.md §Rate limiters), static daemon token state (no rotation, keeper already set),
|
|
185
186
|
**`await_pending_effects: true`** (fire-and-forget effects complete before
|
|
186
187
|
the response returns so tests can assert on side effects inline), and silent
|
|
187
188
|
logger. Override via `app_options`. `create_test_app_for_bootstrap` disables
|
|
188
189
|
the limiters the same way. `cleanup()` is the assembled server's idempotent
|
|
189
|
-
`close` — it also closes the server's live connections
|
|
190
|
-
limiters it built
|
|
190
|
+
`close` — it also closes the server's live connections, disposes the
|
|
191
|
+
limiters it built, and removes the audit listeners and closer members it
|
|
192
|
+
added.
|
|
191
193
|
|
|
192
194
|
`auth_cleanup` is left at its default, **off**, here and in
|
|
193
195
|
`create_test_app_for_bootstrap`: a background pass would delete rows and
|
|
@@ -551,7 +553,7 @@ Three layers:
|
|
|
551
553
|
|
|
552
554
|
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.
|
|
553
555
|
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).
|
|
554
|
-
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).
|
|
556
|
+
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).
|
|
555
557
|
|
|
556
558
|
`WsClient` (in `transports/ws_client.ts`):
|
|
557
559
|
`{send, request<R>, close, messages, wait_for, wait_for_close, close_code, close_reason}`.
|
|
@@ -572,8 +574,6 @@ of undefined`). `wait_for(predicate, timeout_ms?)` checks already-received
|
|
|
572
574
|
messages first, then waits for new arrivals (default 1000ms); drops the
|
|
573
575
|
waiter on timeout so the `waiters` array doesn't grow.
|
|
574
576
|
|
|
575
|
-
`keeper_identity()` — convenience for `{credential_type: 'daemon_token', roles: [ROLE_KEEPER]}`.
|
|
576
|
-
|
|
577
577
|
## Data exposure + rate limiting
|
|
578
578
|
|
|
579
579
|
### `data_exposure.ts` — `describe_data_exposure_tests`
|
|
@@ -979,10 +979,11 @@ source of truth for wire-shape conformance.
|
|
|
979
979
|
same TCP write as the upgrade request reach the socket; `false` Bun, whose
|
|
980
980
|
HTTP parser answers such a request `400`, and `false` in-process, where there
|
|
981
981
|
is no socket to write raw bytes to).
|
|
982
|
-
`ws_account_actions`
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
982
|
+
`ws_account_actions` (the WS endpoint mounts the account actions — `true` on
|
|
983
|
+
fuz_app's own spine presets, `false` in the family defaults) gates the WS
|
|
984
|
+
round-trip suite's self-revocation case and, with `rpc_path`, its
|
|
985
|
+
WS-mutation audit case (which calls `_testing_drain_effects`);
|
|
986
|
+
`ws_self_revocation_reply` says what the self-revocation case requires: the reply ahead of the close (`true`, the TS
|
|
986
987
|
family) or the close alone (`false`, the Rust family, whose socket loop
|
|
987
988
|
drops the response once the connection is closed — a recorded divergence).
|
|
988
989
|
|
|
@@ -1127,11 +1128,15 @@ time**, so the negative upgrade cases assert the upgrade itself rejects, not
|
|
|
1127
1128
|
a per-message error; the close cases prove a revocation closes an
|
|
1128
1129
|
already-open socket, since per-message dispatch never re-checks credential
|
|
1129
1130
|
validity. Omit `rpc_path` to skip the close cases (consumers without the
|
|
1130
|
-
standard account and admin actions on their RPC endpoint).
|
|
1131
|
+
standard account and admin actions on their RPC endpoint). Two cases are gated
|
|
1131
1132
|
on `capabilities.ws_account_actions` (the WS endpoint mounts the account
|
|
1132
1133
|
actions): a socket that revokes its own sessions over that socket is closed
|
|
1133
1134
|
with `WS_CLOSE_SESSION_REVOKED`, and — where the backend declares
|
|
1134
|
-
`capabilities.ws_self_revocation_reply` — reads the reply first
|
|
1135
|
+
`capabilities.ws_self_revocation_reply` — reads the reply first; and (with
|
|
1136
|
+
`rpc_path`) a socket mints an API token over WS, and its `token_create` audit
|
|
1137
|
+
row is read back after `_testing_drain_effects` — a WS reply can precede its
|
|
1138
|
+
audit write, so the drain is the barrier (the binary mounts it via
|
|
1139
|
+
`create_testing_actions`).
|
|
1135
1140
|
Otherwise **consumer-agnostic** — it drives only the `heartbeat` protocol action
|
|
1136
1141
|
(guaranteed on every WS endpoint by `assert_ws_endpoints_include_protocol_actions`),
|
|
1137
1142
|
so it validates the transport without touching domain WS methods. Gated on
|
|
@@ -1450,18 +1455,25 @@ are `src/test/auth/cell_crud_parity.db.test.ts`
|
|
|
1450
1455
|
`testing_reset_actions.ts` TSDoc for the audit + WS fan-out rationale
|
|
1451
1456
|
that rejected a `_testing_seed_role_grant` shape.
|
|
1452
1457
|
|
|
1453
|
-
Same module also exports `create_testing_drain_effects_action()` — the
|
|
1458
|
+
Same module also exports `create_testing_drain_effects_action(audit)` — the
|
|
1454
1459
|
`_testing_drain_effects` RPC action (daemon-token-gated, like
|
|
1455
|
-
`_testing_reset`). It awaits
|
|
1456
|
-
|
|
1457
|
-
cross-process audit assertion fires before reading (no poll/sleep).
|
|
1458
|
-
|
|
1459
|
-
`
|
|
1460
|
-
|
|
1461
|
-
`
|
|
1462
|
-
|
|
1463
|
-
in
|
|
1464
|
-
|
|
1460
|
+
`_testing_reset`). It awaits `audit.drain_inflight()` so a following
|
|
1461
|
+
`audit_log_list` is authoritative — the deterministic barrier a
|
|
1462
|
+
cross-process audit assertion fires before reading (no poll/sleep). The
|
|
1463
|
+
emitter must be built with `create_audit_emitter({track_inflight: true})`
|
|
1464
|
+
(the spine binary's `audit_factory` does), or the drain resolves at once.
|
|
1465
|
+
The barrier is needed whatever the transport: HTTP on a binary running
|
|
1466
|
+
`await_pending_effects: true` settles a mutation's writes before
|
|
1467
|
+
responding, but WS replies before its effects flush, so a WS mutation's
|
|
1468
|
+
audit write can still be in flight when its reply arrives. The drain waits
|
|
1469
|
+
until no tracked `emit` write is outstanding — writes started while it
|
|
1470
|
+
waits included — the same wait-for-zero as the Rust spine's
|
|
1471
|
+
`AuditEmitter::drain_inflight` over `new_with_inflight_tracking`.
|
|
1472
|
+
`create_testing_actions` bundles it over `deps.audit`; suites that mount
|
|
1473
|
+
their own endpoint (e.g. the in-process
|
|
1474
|
+
`account_lifecycle_parity.db.test.ts`) add it directly, over a tracked
|
|
1475
|
+
emitter, so the shared suite body can call the barrier on every backend
|
|
1476
|
+
uniformly.
|
|
1465
1477
|
|
|
1466
1478
|
Also bundled: `_testing_mint_session` — mints a backdated-expiry
|
|
1467
1479
|
`auth_session` row for an account (via `mint_test_session` in `app_server.ts`)
|
|
@@ -1594,12 +1606,15 @@ drift-guarded by `src/test/cross_backend/spine_expected_schema.db.test.ts`
|
|
|
1594
1606
|
|
|
1595
1607
|
### Testing-backdoor credential gate — `cross_backend/testing_backdoor.ts`
|
|
1596
1608
|
|
|
1597
|
-
`describe_testing_backdoor_cross_tests({setup_test, rpc_path?})` —
|
|
1609
|
+
`describe_testing_backdoor_cross_tests({setup_test, rpc_path?, ws?})` —
|
|
1598
1610
|
the negative-credential parity suite for the `_testing_*` backdoor actions.
|
|
1599
|
-
For each
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1611
|
+
For each `_testing_*` action except `_testing_drain_effects` (the three
|
|
1612
|
+
privileged writes plus the three introspection reads) it fires three principals over real HTTP: **anonymous** →
|
|
1613
|
+
401 `unauthenticated`, **session** → 403 `forbidden` `credential_type_required`,
|
|
1614
|
+
**bearer** → 403 `forbidden` `credential_type_required` — and, when `ws`
|
|
1615
|
+
(`{base_url, ws_path, origin?}`) is passed, sends each on a socket opened with
|
|
1616
|
+
the keeper's session cookie, which answers the same `forbidden`
|
|
1617
|
+
`credential_type_required` (a socket is no way around the daemon-token channel) —
|
|
1603
1618
|
proving the daemon-token gate that fences each backdoor action holds end-to-end
|
|
1604
1619
|
on the real dispatcher (the spec-derived `describe_rpc_attack_surface_tests`
|
|
1605
1620
|
never enumerates them because they're off the declared surface). Each method is
|
|
@@ -1731,12 +1746,10 @@ TEST-NET-2 addresses nothing else sends. The backends are started by the
|
|
|
1731
1746
|
backend configs (`ts_spine_*_backend_config`, `rust_spine_stub_backend_config`),
|
|
1732
1747
|
which set `ACTION_RATE_LIMIT_ENABLED_ENV` (`FUZ_ACTION_RATE_LIMIT_ENABLED`) and
|
|
1733
1748
|
`ACTION_RATE_LIMIT_MAX_ATTEMPTS_ENV` (`FUZ_ACTION_RATE_LIMIT_MAX_ATTEMPTS`) from
|
|
1734
|
-
`
|
|
1749
|
+
`spine_surface_constants.ts` — both spine binaries build one IP + one account
|
|
1735
1750
|
limiter (the action defaults' windows, the cap replacing both
|
|
1736
1751
|
`max_attempts`) shared by RPC and WS, and refuse to boot on a cap without the
|
|
1737
|
-
flag. Off,
|
|
1738
|
-
default posture: its auth families charge an always-on per-account limiter at
|
|
1739
|
-
the production cap, its dispatchers none. Runs under the
|
|
1752
|
+
flag. Off, neither spine builds an action limiter. Runs under the
|
|
1740
1753
|
`cross_backend_security` dual-spawn beside the login-security suite. The TS
|
|
1741
1754
|
spine charges every classed action in its dispatcher; the Rust stub charges
|
|
1742
1755
|
`account_token_create` and the other auth-family specs inside their handlers
|
|
@@ -302,7 +302,7 @@ export interface TestApp {
|
|
|
302
302
|
create_daemon_token_headers: (extra?: Record<string, string>) => Record<string, string>;
|
|
303
303
|
/** Create an additional account with credentials. */
|
|
304
304
|
create_account: (options?: CreateTestAppAccountArgs) => Promise<TestAccount>;
|
|
305
|
-
/** Cleanup resources — `AppServer.close`: stops an opted-in auth cleanup schedule, closes live connections, disposes the limiters the server built, then the backend's `close`. Idempotent. */
|
|
305
|
+
/** Cleanup resources — `AppServer.close`: stops an opted-in auth cleanup schedule, closes live connections, disposes the limiters the server built and removes the audit listeners and closer members it added, then the backend's `close`. Idempotent. */
|
|
306
306
|
cleanup: () => Promise<void>;
|
|
307
307
|
}
|
|
308
308
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -256,6 +256,19 @@ export const create_test_app_server = async (options) => {
|
|
|
256
256
|
cleanup: () => backend.close()
|
|
257
257
|
};
|
|
258
258
|
};
|
|
259
|
+
/**
|
|
260
|
+
* The `create_app_server` options both test-app builders share — a fresh
|
|
261
|
+
* object per call, spread ahead of the per-builder options and `app_options`.
|
|
262
|
+
*/
|
|
263
|
+
const test_app_server_base_options = () => ({
|
|
264
|
+
allowed_origins: [/^http:\/\/localhost/],
|
|
265
|
+
proxy: { trusted_proxies: ['127.0.0.1'], get_connection_ip: () => '127.0.0.1' },
|
|
266
|
+
env_schema: z.object({}),
|
|
267
|
+
// every limiter off unless `app_options` passes one — a rate-limit test
|
|
268
|
+
// enables just the limiter it pins
|
|
269
|
+
rate_limiters: 'disabled_for_testing',
|
|
270
|
+
await_pending_effects: true
|
|
271
|
+
});
|
|
259
272
|
/**
|
|
260
273
|
* Create a fully assembled test app with a Hono server, middleware, and routes.
|
|
261
274
|
*
|
|
@@ -283,15 +296,9 @@ export const create_test_app = async (options) => {
|
|
|
283
296
|
keeper_account_id: test_server.account.id
|
|
284
297
|
};
|
|
285
298
|
const result = await create_app_server({
|
|
299
|
+
...test_app_server_base_options(),
|
|
286
300
|
backend: test_server,
|
|
287
301
|
session_options: options.session_options,
|
|
288
|
-
allowed_origins: [/^http:\/\/localhost/],
|
|
289
|
-
proxy: { trusted_proxies: ['127.0.0.1'], get_connection_ip: () => '127.0.0.1' },
|
|
290
|
-
env_schema: z.object({}),
|
|
291
|
-
// every limiter off unless `app_options` passes one — a rate-limit test
|
|
292
|
-
// enables just the limiter it pins
|
|
293
|
-
rate_limiters: 'disabled_for_testing',
|
|
294
|
-
await_pending_effects: true,
|
|
295
302
|
daemon_token_state,
|
|
296
303
|
rpc_endpoints: options.rpc_endpoints,
|
|
297
304
|
bootstrap: options.bootstrap,
|
|
@@ -418,15 +425,9 @@ export const create_test_app_for_bootstrap = async (options) => {
|
|
|
418
425
|
keeper_account_id: null
|
|
419
426
|
};
|
|
420
427
|
const result = await create_app_server({
|
|
428
|
+
...test_app_server_base_options(),
|
|
421
429
|
backend,
|
|
422
430
|
session_options,
|
|
423
|
-
allowed_origins: [/^http:\/\/localhost/],
|
|
424
|
-
proxy: { trusted_proxies: ['127.0.0.1'], get_connection_ip: () => '127.0.0.1' },
|
|
425
|
-
env_schema: z.object({}),
|
|
426
|
-
// every limiter off unless `app_options` passes one — a rate-limit test
|
|
427
|
-
// enables just the limiter it pins
|
|
428
|
-
rate_limiters: 'disabled_for_testing',
|
|
429
|
-
await_pending_effects: true,
|
|
430
431
|
daemon_token_state,
|
|
431
432
|
rpc_endpoints: options.rpc_endpoints,
|
|
432
433
|
bootstrap,
|
|
@@ -53,7 +53,7 @@ export interface StandardAttackSurfaceOptions {
|
|
|
53
53
|
snapshot_path: string;
|
|
54
54
|
/** Expected public routes, e.g. `['GET /health', 'POST /api/account/login']`. */
|
|
55
55
|
expected_public_routes: Array<string>;
|
|
56
|
-
/** Expected middleware names for API routes, e.g. `['origin', 'session', 'request_context', 'bearer_auth']`. */
|
|
56
|
+
/** Expected middleware names for API routes, e.g. `['trusted_proxy', 'origin', 'session', 'request_context', 'bearer_auth']`. */
|
|
57
57
|
expected_api_middleware: Array<string>;
|
|
58
58
|
/** All roles in the app (e.g. `['admin', 'keeper']`). */
|
|
59
59
|
roles: Array<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAoB7B,OAAO,EAQN,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,MAAM,yBAAyB,CAAC;AAejC,OAAO,EAA6B,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAsDpF,oFAAoF;AACpF,MAAM,WAAW,sBAAsB;IACtC,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,sBAAsB,KAAG,IA2J3E,CAAC;AAIF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uCAAuC,GACnD,UAAU,2BAA2B,GAAG,IAAI,GAAG,SAAS,KACtD,2BAA2B,GAAG,IAWhC,CAAC;AAEF,0DAA0D;AAC1D,MAAM,WAAW,4BAA4B;IAC5C,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,sBAAsB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,
|
|
1
|
+
{"version":3,"file":"attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAoB7B,OAAO,EAQN,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,MAAM,yBAAyB,CAAC;AAejC,OAAO,EAA6B,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAsDpF,oFAAoF;AACpF,MAAM,WAAW,sBAAsB;IACtC,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,sBAAsB,KAAG,IA2J3E,CAAC;AAIF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uCAAuC,GACnD,UAAU,2BAA2B,GAAG,IAAI,GAAG,SAAS,KACtD,2BAA2B,GAAG,IAWhC,CAAC;AAEF,0DAA0D;AAC1D,MAAM,WAAW,4BAA4B;IAC5C,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,sBAAsB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,iIAAiI;IACjI,uBAAuB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvC,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,eAAe,CAAC,EAAE,4BAA4B,CAAC;IAC/C;;;;;;;;;;;OAWG;IACH,sBAAsB,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAC5D;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,sCAAsC,GAClD,SAAS,4BAA4B,KACnC,IA4EF,CAAC"}
|
|
@@ -46,8 +46,10 @@ export interface RecordingAuditEmitter {
|
|
|
46
46
|
* forward from the input envelope. Tests asserting on role-grant-shape
|
|
47
47
|
* emissions read out of the same homogeneous `calls` array.
|
|
48
48
|
* `notify` is a no-op; `add_listener` records into a local array that
|
|
49
|
-
* `listener_count` reports
|
|
50
|
-
*
|
|
49
|
+
* `listener_count` reports, and returns a remover with the production
|
|
50
|
+
* semantics (per registration, idempotent) — registered listeners never fire,
|
|
51
|
+
* since this emitter captures `emit` shapes, not fan-out. `drain_inflight` resolves at once:
|
|
52
|
+
* a recorded call writes nothing, so nothing is ever in flight.
|
|
51
53
|
*
|
|
52
54
|
* `emit` AND `emit_pool` both append to `calls`, so a test reads either
|
|
53
55
|
* entry point's writes off the same array.
|
|
@@ -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
|
|
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"}
|