@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
|
@@ -19,14 +19,15 @@ import '../assert_dev_env.js';
|
|
|
19
19
|
* every WS endpoint — so the suite needs no knowledge of a consumer's domain
|
|
20
20
|
* WS methods. It validates the transport, not the domain.
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* disallowed-origin upgrade
|
|
26
|
-
* upgrade time (not per message), so the
|
|
27
|
-
* itself rejects rather than a per-message
|
|
22
|
+
* Three cases mirror the upgrade stack `register_ws_endpoint` wires (origin
|
|
23
|
+
* check → `require_auth` → dispatch): an authenticated upgrade round-trips
|
|
24
|
+
* `heartbeat` (the first case), and an anonymous upgrade and a
|
|
25
|
+
* disallowed-origin upgrade are refused (after the spine-parity cases).
|
|
26
|
+
* Per-connection auth is enforced at upgrade time (not per message), so the
|
|
27
|
+
* negative cases assert the upgrade itself rejects rather than a per-message
|
|
28
|
+
* error frame.
|
|
28
29
|
*
|
|
29
|
-
* Spine-parity cases follow. Both spines admit a connection only after
|
|
30
|
+
* Spine-parity cases follow the first. Both spines admit a connection only after
|
|
30
31
|
* re-reading its credential, and requests that arrive first wait for that
|
|
31
32
|
* rather than being dropped: requests sent the moment the socket opens are all
|
|
32
33
|
* answered, and — gated on `capabilities.ws_handshake_pipelining`, with the
|
|
@@ -38,7 +39,8 @@ import '../assert_dev_env.js';
|
|
|
38
39
|
* `DEFAULT_WS_MAX_MESSAGE_BYTES`) closes the socket with
|
|
39
40
|
* `WS_CLOSE_MESSAGE_TOO_BIG`.
|
|
40
41
|
*
|
|
41
|
-
* The
|
|
42
|
+
* The cases after the refused upgrades (gated on `rpc_path`) cover
|
|
43
|
+
* server-initiated close.
|
|
42
44
|
* Per-message dispatch never re-checks credential validity, so an open socket
|
|
43
45
|
* ends only when a revocation closes it — which every revocation does, once
|
|
44
46
|
* its transaction commits. An authenticated socket is dropped when the
|
|
@@ -48,14 +50,18 @@ import '../assert_dev_env.js';
|
|
|
48
50
|
* `rpc_path` to skip them (consumers without the standard account and admin
|
|
49
51
|
* actions on their RPC endpoint).
|
|
50
52
|
*
|
|
51
|
-
*
|
|
53
|
+
* The last cases are gated on `capabilities.ws_account_actions` — the backend's
|
|
52
54
|
* WS endpoint mounts the self-service account actions: a socket that revokes
|
|
53
55
|
* its own sessions *over that socket* is closed with
|
|
54
56
|
* `WS_CLOSE_SESSION_REVOKED`, and — on a backend declaring
|
|
55
57
|
* `capabilities.ws_self_revocation_reply` — reads the reply to its request
|
|
56
58
|
* first. The TS spine queues the close behind the commit and sends the reply
|
|
57
59
|
* ahead of it, so the caller is never left without an answer; the Rust spine
|
|
58
|
-
* does not answer yet, which that flag records.
|
|
60
|
+
* does not answer yet, which that flag records. Under the same gate (and
|
|
61
|
+
* `rpc_path`), a socket mints an API token over WS and the token's
|
|
62
|
+
* `token_create` audit row is read back after `_testing_drain_effects` — the
|
|
63
|
+
* barrier a WS mutation needs, since a WS reply can precede its audit write.
|
|
64
|
+
* The binary's `create_testing_actions` mount provides the drain.
|
|
59
65
|
*
|
|
60
66
|
* Gated on `capabilities.ws` — backends without an end-to-end WS transport
|
|
61
67
|
* skip (the cases still surface as `.skip` in the report). Cross-process
|
|
@@ -68,14 +74,15 @@ import { assert, describe } from 'vitest';
|
|
|
68
74
|
import { assert_rejects } from '@fuzdev/fuz_util/testing.ts';
|
|
69
75
|
import { heartbeat_action_spec } from '../../actions/heartbeat.js';
|
|
70
76
|
import { DEFAULT_WS_MAX_MESSAGE_BYTES, WS_CLOSE_MESSAGE_TOO_BIG, WS_CLOSE_SESSION_REVOKED } from '../../actions/transports.js';
|
|
71
|
-
import { account_session_revoke_all_action_spec } from '../../auth/account_action_specs.js';
|
|
72
|
-
import { account_delete_action_spec } from '../../auth/admin_action_specs.js';
|
|
77
|
+
import { account_session_revoke_all_action_spec, account_token_create_action_spec } from '../../auth/account_action_specs.js';
|
|
78
|
+
import { account_delete_action_spec, audit_log_list_action_spec, AuditLogListOutput } from '../../auth/admin_action_specs.js';
|
|
73
79
|
import { JSONRPC_ERROR_CODES } from '../../http/jsonrpc_errors.js';
|
|
74
80
|
import { is_response_for } from '../transports/ws_client.js';
|
|
75
81
|
import { create_ws_transport } from '../transports/ws_transport.js';
|
|
76
82
|
import { connect_raw_ws, is_raw_ws_close, RAW_WS_OPCODE_TEXT } from '../transports/ws_raw_client.js';
|
|
77
83
|
import { create_rpc_post_init } from '../rpc_helpers.js';
|
|
78
84
|
import { test_if } from './capabilities.js';
|
|
85
|
+
import { cross_rpc_call, expect_output } from './cell_cross_helpers.js';
|
|
79
86
|
/** Origin guaranteed to fail the `http://localhost:*` allowlist the test backends run with. */
|
|
80
87
|
const DISALLOWED_ORIGIN = 'http://disallowed.example';
|
|
81
88
|
/**
|
|
@@ -84,7 +91,8 @@ const DISALLOWED_ORIGIN = 'http://disallowed.example';
|
|
|
84
91
|
* oversized message closing with `WS_CLOSE_MESSAGE_TOO_BIG`, anonymous-upgrade
|
|
85
92
|
* refusal, disallowed-origin refusal, and — when `rpc_path` is supplied —
|
|
86
93
|
* session revocation and account deletion closing the live socket, plus a
|
|
87
|
-
* self-revoking socket reading its reply before its close
|
|
94
|
+
* self-revoking socket reading its reply before its close, and a WS mutation's
|
|
95
|
+
* audit row read back after `_testing_drain_effects`.
|
|
88
96
|
*/
|
|
89
97
|
export const describe_cross_process_ws_tests = (options) => {
|
|
90
98
|
const { setup_test, capabilities, base_url, ws_path, origin, rpc_path, max_message_bytes = DEFAULT_WS_MAX_MESSAGE_BYTES } = options;
|
|
@@ -308,5 +316,35 @@ export const describe_cross_process_ws_tests = (options) => {
|
|
|
308
316
|
await client.close();
|
|
309
317
|
}
|
|
310
318
|
});
|
|
319
|
+
// A WS mutation is answered before its effects flush, so its audit row
|
|
320
|
+
// can land after the reply. `_testing_drain_effects` is the barrier: once
|
|
321
|
+
// it returns, the row is visible on either spine.
|
|
322
|
+
test_if(capabilities.ws && capabilities.ws_account_actions && rpc_path !== undefined, "a WS mutation's audit row is visible after _testing_drain_effects", async () => {
|
|
323
|
+
const fixture = await setup_test();
|
|
324
|
+
const client = await create_ws_transport({
|
|
325
|
+
base_url,
|
|
326
|
+
ws_path,
|
|
327
|
+
cookies: fixture.transport.cookies(),
|
|
328
|
+
origin
|
|
329
|
+
});
|
|
330
|
+
try {
|
|
331
|
+
await client.request(1, account_token_create_action_spec.method, {
|
|
332
|
+
name: 'ws drain probe',
|
|
333
|
+
scope: { kind: 'full' },
|
|
334
|
+
lifetime: { kind: 'eternal' }
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
finally {
|
|
338
|
+
await client.close();
|
|
339
|
+
}
|
|
340
|
+
const drained = await cross_rpc_call(fixture.fresh_transport({ origin: null }), rpc_path, '_testing_drain_effects', undefined, fixture.create_daemon_token_headers());
|
|
341
|
+
assert.ok(drained.ok, `_testing_drain_effects failed: ${JSON.stringify(drained.error)}`);
|
|
342
|
+
// `_testing_reset` wiped audit_log at setup, so the mint is the only
|
|
343
|
+
// token_create event
|
|
344
|
+
const listed = expect_output(await cross_rpc_call(fixture.transport, rpc_path, audit_log_list_action_spec.method, { event_type: 'token_create' }, fixture.create_session_headers()), AuditLogListOutput);
|
|
345
|
+
assert.strictEqual(listed.events.length, 1, JSON.stringify(listed.events));
|
|
346
|
+
assert.strictEqual(listed.events[0].outcome, 'success');
|
|
347
|
+
assert.strictEqual(listed.events[0].account_id, fixture.account.id);
|
|
348
|
+
});
|
|
311
349
|
});
|
|
312
350
|
};
|
package/dist/testing/stubs.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { AppDeps } from '../auth/deps.js';
|
|
|
6
6
|
import type { AuditEmitter } from '../auth/audit_emitter.js';
|
|
7
7
|
import type { BootstrapServerOptions } from '../server/app_server.js';
|
|
8
8
|
import type { AppServerContext } from '../server/app_server_context.js';
|
|
9
|
+
import type { AppBackend } from '../server/app_backend.js';
|
|
9
10
|
import { Db } from '../db/db.js';
|
|
10
11
|
import { type RouteSpec } from '../http/route_spec.js';
|
|
11
12
|
import { type AppSurfaceSpec, type RpcEndpointSpec } from '../http/surface.js';
|
|
@@ -62,7 +63,8 @@ export declare const stub_mw: (_c: any, next: any) => Promise<void>;
|
|
|
62
63
|
* immediately; `notify` is a no-op; `add_listener` throws, so a test that
|
|
63
64
|
* wires a listener fails loudly instead of silently never firing
|
|
64
65
|
* (`create_recording_audit_emitter` accepts listeners); `listener_count`
|
|
65
|
-
* returns 0
|
|
66
|
+
* returns 0; `drain_inflight` resolves immediately, since nothing is ever
|
|
67
|
+
* written. Tests asserting on real audit-row persistence (or on listener
|
|
66
68
|
* fan-out) build a real emitter via `create_audit_emitter` against a stub or
|
|
67
69
|
* real DB — `create_test_app` already does this on the test backend.
|
|
68
70
|
*/
|
|
@@ -78,13 +80,34 @@ export declare const create_test_audit_emitter: () => AuditEmitter;
|
|
|
78
80
|
* `create_audit_log_sse` against `create_test_app`.
|
|
79
81
|
*/
|
|
80
82
|
export declare const create_stub_audit_sse: () => AuditLogSse;
|
|
81
|
-
/** Stub `AppDeps` for auth surface tests — throws on any method access. */
|
|
82
|
-
export declare const stub_app_deps: AppDeps;
|
|
83
83
|
/**
|
|
84
84
|
* Create no-op `AppDeps` for auth surface testing.
|
|
85
85
|
*/
|
|
86
86
|
export declare const create_stub_app_deps: () => AppDeps;
|
|
87
|
-
/**
|
|
87
|
+
/** Options for `create_stub_app_backend`. */
|
|
88
|
+
export interface CreateStubAppBackendOptions {
|
|
89
|
+
/** The backend's deps. Default a fresh `create_stub_app_deps()`. */
|
|
90
|
+
deps?: AppDeps;
|
|
91
|
+
/** The backend's `close`. Default one that does nothing. */
|
|
92
|
+
close?: () => Promise<void>;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Create a stub `AppBackend` with no database behind it — no migration
|
|
96
|
+
* results, and a `close` that does nothing unless one is passed. For
|
|
97
|
+
* assembling a server (`create_app_server`) in tests that never reach the
|
|
98
|
+
* database; one whose `close` does nothing can host server after server.
|
|
99
|
+
*
|
|
100
|
+
* @param options - the deps and `close` to use in place of the defaults
|
|
101
|
+
*/
|
|
102
|
+
export declare const create_stub_app_backend: (options?: CreateStubAppBackendOptions) => AppBackend;
|
|
103
|
+
/**
|
|
104
|
+
* Create the API middleware stub array matching `create_auth_middleware_specs`
|
|
105
|
+
* output — the same names, paths, order, and declared `errors` (the surface
|
|
106
|
+
* merges middleware errors into every route under the path, so a stub that
|
|
107
|
+
* declared more than the real stack would widen the test surface). Like the
|
|
108
|
+
* real factory's, each spec gets its own pass-through handler, so
|
|
109
|
+
* `assert_middleware_stack_mounted` can tell the layers apart by identity.
|
|
110
|
+
*/
|
|
88
111
|
export declare const create_stub_api_middleware: (options?: {
|
|
89
112
|
/** Include the daemon_token middleware layer. */
|
|
90
113
|
include_daemon_token?: boolean;
|
|
@@ -136,7 +159,15 @@ export interface CreateTestAppSurfaceSpecOptions {
|
|
|
136
159
|
* the exact patterns the upgrade gate matches.
|
|
137
160
|
*/
|
|
138
161
|
allowed_origins?: ReadonlyArray<RegExp>;
|
|
139
|
-
/**
|
|
162
|
+
/**
|
|
163
|
+
* Transform middleware array — `AppServerOptions.transform_middleware`.
|
|
164
|
+
* Receives shallow copies of a stub `trusted_proxy` spec (at `'*'`)
|
|
165
|
+
* followed by the stub auth stack, in a fresh array, as
|
|
166
|
+
* `create_app_server` passes the real ones. Throws, like
|
|
167
|
+
* `create_app_server`, if the result doesn't keep each of those specs
|
|
168
|
+
* mounted once at its original path in its original order
|
|
169
|
+
* (`assert_middleware_stack_mounted`).
|
|
170
|
+
*/
|
|
140
171
|
transform_middleware?: (specs: Array<MiddlewareSpec>) => Array<MiddlewareSpec>;
|
|
141
172
|
/**
|
|
142
173
|
* Bootstrap config — symmetric with `AppServerOptions.bootstrap`. Discriminated
|
|
@@ -148,6 +179,23 @@ export interface CreateTestAppSurfaceSpecOptions {
|
|
|
148
179
|
* Surface assembly only reads `route_prefix` (default `'/api/account'`).
|
|
149
180
|
*/
|
|
150
181
|
bootstrap?: BootstrapServerOptions;
|
|
182
|
+
/**
|
|
183
|
+
* Symmetric with `AppServerOptions.surface_route` — `true` lists the
|
|
184
|
+
* admin-only `GET /api/surface`, which `create_app_server` mounts only when
|
|
185
|
+
* opted in. Pass the same value to both entry points so the surface tests
|
|
186
|
+
* probe the route exactly when the live server serves it.
|
|
187
|
+
*
|
|
188
|
+
* @default false
|
|
189
|
+
*/
|
|
190
|
+
surface_route?: boolean;
|
|
191
|
+
/**
|
|
192
|
+
* List the `daemon_token` middleware layer, as `create_app_server` mounts
|
|
193
|
+
* it when given a `daemon_token_state`. Pass `true` exactly when the live
|
|
194
|
+
* server gets one — `create_test_app` always passes one.
|
|
195
|
+
*
|
|
196
|
+
* @default false
|
|
197
|
+
*/
|
|
198
|
+
daemon_token?: boolean;
|
|
151
199
|
}
|
|
152
200
|
/**
|
|
153
201
|
* Create an `AppSurfaceSpec` for the standard testing suites.
|
|
@@ -159,18 +207,25 @@ export interface CreateTestAppSurfaceSpecOptions {
|
|
|
159
207
|
* suites consume the spec object this function returns, not the JSON
|
|
160
208
|
* file.
|
|
161
209
|
*
|
|
162
|
-
* Mirrors `create_app_server`'s route assembly: consumer
|
|
163
|
-
* factory-managed bootstrap routes
|
|
210
|
+
* Mirrors `create_app_server`'s route assembly, in its order: consumer
|
|
211
|
+
* routes, then the factory-managed bootstrap routes, RPC endpoint routes, and
|
|
212
|
+
* `GET /api/surface` (with `surface_route: true`), then surface
|
|
213
|
+
* generation — including its
|
|
164
214
|
* refusal of an `rpc_endpoints` or `ws_endpoints` path outside the auth
|
|
165
|
-
* middleware's scope (`assert_endpoint_in_auth_scope`)
|
|
215
|
+
* middleware's scope (`assert_endpoint_in_auth_scope`) and of a
|
|
216
|
+
* `transform_middleware` that doesn't keep the proxy and auth stack mounted
|
|
217
|
+
* (`assert_middleware_stack_mounted`). The middleware list mirrors the real
|
|
218
|
+
* one: a stub `trusted_proxy` spec at `'*'`, then the stub auth stack (with
|
|
219
|
+
* `daemon_token` when `options.daemon_token` is set). If
|
|
166
220
|
* `create_app_server` changes how it wires routes, update this helper
|
|
167
221
|
* to stay in sync (single source of truth for all consumers).
|
|
168
222
|
*
|
|
169
223
|
* @param options - surface spec options
|
|
170
224
|
* @returns the surface spec for the standard suites
|
|
171
|
-
* @throws Error when an endpoint path is outside `AUTH_MIDDLEWARE_PATH`,
|
|
225
|
+
* @throws Error when an endpoint path is outside `AUTH_MIDDLEWARE_PATH`, a
|
|
172
226
|
* `ws_endpoints` spec declares no `allowed_origins` and no `allowed_origins`
|
|
173
|
-
* option is given
|
|
227
|
+
* option is given, or `transform_middleware` moves, drops, duplicates,
|
|
228
|
+
* reorders, wraps, or replaces the stub proxy spec or a stub auth spec
|
|
174
229
|
*/
|
|
175
230
|
export declare const create_test_app_surface_spec: (options: CreateTestAppSurfaceSpecOptions) => AppSurfaceSpec;
|
|
176
231
|
//# sourceMappingURL=stubs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stubs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/stubs.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAa7B,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"stubs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/stubs.ts"],"names":[],"mappings":"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"}
|
package/dist/testing/stubs.js
CHANGED
|
@@ -8,18 +8,19 @@ import './assert_dev_env.js';
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
import { Logger } from '@fuzdev/fuz_util/log.ts';
|
|
11
|
-
import { ApiError
|
|
11
|
+
import { ApiError } from '../http/error_schemas.js';
|
|
12
12
|
import { create_realtime_closer } from '../actions/connection_closer.js';
|
|
13
13
|
import { Db } from '../db/db.js';
|
|
14
14
|
import { prefix_route_specs } from '../http/route_spec.js';
|
|
15
15
|
import { bootstrap_route_shape } from '../auth/bootstrap_route_schema.js';
|
|
16
16
|
import { create_rpc_endpoint } from '../actions/action_rpc.js';
|
|
17
|
+
import { create_surface_route_spec } from '../server/surface_route.js';
|
|
17
18
|
import { create_app_surface_spec } from '../http/surface.js';
|
|
18
19
|
import { resolve_ws_endpoints } from '../actions/ws_endpoint_spec.js';
|
|
19
20
|
import { AUDIT_LOG_SSE_MAX_PER_SCOPE } from '../realtime/sse_auth_guard.js';
|
|
20
21
|
import { SubscriberRegistry } from '../realtime/subscriber_registry.js';
|
|
21
22
|
import { BaseServerEnv } from '../server/env.js';
|
|
22
|
-
import { AUTH_MIDDLEWARE_PATH, assert_endpoint_in_auth_scope } from '../auth/middleware.js';
|
|
23
|
+
import { AUTH_MIDDLEWARE_PATH, assert_endpoint_in_auth_scope, assert_middleware_stack_mounted } from '../auth/middleware.js';
|
|
23
24
|
/**
|
|
24
25
|
* Create a Proxy that throws descriptive errors on any property access or method call.
|
|
25
26
|
*
|
|
@@ -99,7 +100,8 @@ const stub_db = create_noop_stub('stub_db');
|
|
|
99
100
|
* immediately; `notify` is a no-op; `add_listener` throws, so a test that
|
|
100
101
|
* wires a listener fails loudly instead of silently never firing
|
|
101
102
|
* (`create_recording_audit_emitter` accepts listeners); `listener_count`
|
|
102
|
-
* returns 0
|
|
103
|
+
* returns 0; `drain_inflight` resolves immediately, since nothing is ever
|
|
104
|
+
* written. Tests asserting on real audit-row persistence (or on listener
|
|
103
105
|
* fan-out) build a real emitter via `create_audit_emitter` against a stub or
|
|
104
106
|
* real DB — `create_test_app` already does this on the test backend.
|
|
105
107
|
*/
|
|
@@ -111,7 +113,8 @@ export const create_test_audit_emitter = () => ({
|
|
|
111
113
|
add_listener: () => {
|
|
112
114
|
throw new Error('create_test_audit_emitter accepts no listeners — use create_recording_audit_emitter');
|
|
113
115
|
},
|
|
114
|
-
listener_count: () => 0
|
|
116
|
+
listener_count: () => 0,
|
|
117
|
+
drain_inflight: async () => { }
|
|
115
118
|
});
|
|
116
119
|
/**
|
|
117
120
|
* Build a no-op `AuditLogSse` for tests that wire `audit_sse` into the
|
|
@@ -133,17 +136,6 @@ export const create_stub_audit_sse = () => {
|
|
|
133
136
|
registry
|
|
134
137
|
};
|
|
135
138
|
};
|
|
136
|
-
/** Stub `AppDeps` for auth surface tests — throws on any method access. */
|
|
137
|
-
export const stub_app_deps = {
|
|
138
|
-
read_secure_file: create_throwing_stub('read_secure_file'),
|
|
139
|
-
delete_file: create_throwing_stub('delete_file'),
|
|
140
|
-
keyring: create_throwing_stub('keyring'),
|
|
141
|
-
password: create_throwing_stub('password'),
|
|
142
|
-
db: create_throwing_stub('db'),
|
|
143
|
-
log: create_throwing_stub('log'),
|
|
144
|
-
audit: create_test_audit_emitter(),
|
|
145
|
-
connection_closer: create_realtime_closer()
|
|
146
|
-
};
|
|
147
139
|
/**
|
|
148
140
|
* Create no-op `AppDeps` for auth surface testing.
|
|
149
141
|
*/
|
|
@@ -159,25 +151,54 @@ export const create_stub_app_deps = () => ({
|
|
|
159
151
|
audit: create_test_audit_emitter(),
|
|
160
152
|
connection_closer: create_realtime_closer()
|
|
161
153
|
});
|
|
162
|
-
/**
|
|
154
|
+
/**
|
|
155
|
+
* Create a stub `AppBackend` with no database behind it — no migration
|
|
156
|
+
* results, and a `close` that does nothing unless one is passed. For
|
|
157
|
+
* assembling a server (`create_app_server`) in tests that never reach the
|
|
158
|
+
* database; one whose `close` does nothing can host server after server.
|
|
159
|
+
*
|
|
160
|
+
* @param options - the deps and `close` to use in place of the defaults
|
|
161
|
+
*/
|
|
162
|
+
export const create_stub_app_backend = (options) => ({
|
|
163
|
+
db_type: 'pglite-memory',
|
|
164
|
+
db_name: 'test',
|
|
165
|
+
migration_results: [],
|
|
166
|
+
close: options?.close ?? (async () => { }),
|
|
167
|
+
deps: options?.deps ?? create_stub_app_deps()
|
|
168
|
+
});
|
|
169
|
+
/** A fresh pass-through middleware — a distinct handler per call, unlike the shared `stub_mw`. */
|
|
170
|
+
const create_stub_mw = () => async (_c, next) => next();
|
|
171
|
+
/**
|
|
172
|
+
* Create the API middleware stub array matching `create_auth_middleware_specs`
|
|
173
|
+
* output — the same names, paths, order, and declared `errors` (the surface
|
|
174
|
+
* merges middleware errors into every route under the path, so a stub that
|
|
175
|
+
* declared more than the real stack would widen the test surface). Like the
|
|
176
|
+
* real factory's, each spec gets its own pass-through handler, so
|
|
177
|
+
* `assert_middleware_stack_mounted` can tell the layers apart by identity.
|
|
178
|
+
*/
|
|
163
179
|
export const create_stub_api_middleware = (options) => {
|
|
164
180
|
const specs = [
|
|
165
|
-
{
|
|
166
|
-
|
|
167
|
-
|
|
181
|
+
{
|
|
182
|
+
name: 'origin',
|
|
183
|
+
path: AUTH_MIDDLEWARE_PATH,
|
|
184
|
+
handler: create_stub_mw(),
|
|
185
|
+
errors: { 403: ApiError }
|
|
186
|
+
},
|
|
187
|
+
{ name: 'session', path: AUTH_MIDDLEWARE_PATH, handler: create_stub_mw() },
|
|
188
|
+
{ name: 'request_context', path: AUTH_MIDDLEWARE_PATH, handler: create_stub_mw() },
|
|
168
189
|
{
|
|
169
190
|
name: 'bearer_auth',
|
|
170
191
|
path: AUTH_MIDDLEWARE_PATH,
|
|
171
|
-
handler:
|
|
172
|
-
errors: {
|
|
192
|
+
handler: create_stub_mw(),
|
|
193
|
+
errors: {}
|
|
173
194
|
}
|
|
174
195
|
];
|
|
175
196
|
if (options?.include_daemon_token) {
|
|
176
197
|
specs.push({
|
|
177
198
|
name: 'daemon_token',
|
|
178
199
|
path: AUTH_MIDDLEWARE_PATH,
|
|
179
|
-
handler:
|
|
180
|
-
errors: {
|
|
200
|
+
handler: create_stub_mw(),
|
|
201
|
+
errors: {}
|
|
181
202
|
});
|
|
182
203
|
}
|
|
183
204
|
return specs;
|
|
@@ -191,16 +212,10 @@ export const create_stub_api_middleware = (options) => {
|
|
|
191
212
|
* @param session_options - consumer's session config (required — varies per app)
|
|
192
213
|
*/
|
|
193
214
|
export const create_stub_app_server_context = (session_options) => {
|
|
194
|
-
const
|
|
215
|
+
const backend = create_stub_app_backend();
|
|
195
216
|
return {
|
|
196
|
-
deps,
|
|
197
|
-
backend
|
|
198
|
-
deps,
|
|
199
|
-
db_type: 'pglite-memory',
|
|
200
|
-
db_name: 'test',
|
|
201
|
-
migration_results: [],
|
|
202
|
-
close: async () => { }
|
|
203
|
-
},
|
|
217
|
+
deps: backend.deps,
|
|
218
|
+
backend,
|
|
204
219
|
bootstrap_status: { available: false, token_path: null },
|
|
205
220
|
session_options,
|
|
206
221
|
login_ip_rate_limiter: null,
|
|
@@ -238,18 +253,25 @@ const to_self_resolved_ws_endpoint = (endpoint) => {
|
|
|
238
253
|
* suites consume the spec object this function returns, not the JSON
|
|
239
254
|
* file.
|
|
240
255
|
*
|
|
241
|
-
* Mirrors `create_app_server`'s route assembly: consumer
|
|
242
|
-
* factory-managed bootstrap routes
|
|
256
|
+
* Mirrors `create_app_server`'s route assembly, in its order: consumer
|
|
257
|
+
* routes, then the factory-managed bootstrap routes, RPC endpoint routes, and
|
|
258
|
+
* `GET /api/surface` (with `surface_route: true`), then surface
|
|
259
|
+
* generation — including its
|
|
243
260
|
* refusal of an `rpc_endpoints` or `ws_endpoints` path outside the auth
|
|
244
|
-
* middleware's scope (`assert_endpoint_in_auth_scope`)
|
|
261
|
+
* middleware's scope (`assert_endpoint_in_auth_scope`) and of a
|
|
262
|
+
* `transform_middleware` that doesn't keep the proxy and auth stack mounted
|
|
263
|
+
* (`assert_middleware_stack_mounted`). The middleware list mirrors the real
|
|
264
|
+
* one: a stub `trusted_proxy` spec at `'*'`, then the stub auth stack (with
|
|
265
|
+
* `daemon_token` when `options.daemon_token` is set). If
|
|
245
266
|
* `create_app_server` changes how it wires routes, update this helper
|
|
246
267
|
* to stay in sync (single source of truth for all consumers).
|
|
247
268
|
*
|
|
248
269
|
* @param options - surface spec options
|
|
249
270
|
* @returns the surface spec for the standard suites
|
|
250
|
-
* @throws Error when an endpoint path is outside `AUTH_MIDDLEWARE_PATH`,
|
|
271
|
+
* @throws Error when an endpoint path is outside `AUTH_MIDDLEWARE_PATH`, a
|
|
251
272
|
* `ws_endpoints` spec declares no `allowed_origins` and no `allowed_origins`
|
|
252
|
-
* option is given
|
|
273
|
+
* option is given, or `transform_middleware` moves, drops, duplicates,
|
|
274
|
+
* reorders, wraps, or replaces the stub proxy spec or a stub auth spec
|
|
253
275
|
*/
|
|
254
276
|
export const create_test_app_surface_spec = (options) => {
|
|
255
277
|
const ctx = create_stub_app_server_context(options.session_options);
|
|
@@ -291,12 +313,42 @@ export const create_test_app_surface_spec = (options) => {
|
|
|
291
313
|
{ ...bootstrap_route_shape, handler: stub_handler }
|
|
292
314
|
])
|
|
293
315
|
: [];
|
|
294
|
-
|
|
295
|
-
|
|
316
|
+
// the surface route mirrors `create_app_server`'s opt-in mount — its handler
|
|
317
|
+
// serves the surface generated below, backfilled into the same ref
|
|
318
|
+
const surface_ref = {
|
|
319
|
+
surface: {
|
|
320
|
+
middleware: [],
|
|
321
|
+
routes: [],
|
|
322
|
+
rpc_endpoints: [],
|
|
323
|
+
ws_endpoints: [],
|
|
324
|
+
env: [],
|
|
325
|
+
events: [],
|
|
326
|
+
diagnostics: []
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
const surface_route_specs = options.surface_route
|
|
330
|
+
? [create_surface_route_spec(surface_ref)]
|
|
331
|
+
: [];
|
|
332
|
+
// `create_app_server`'s order: consumer routes, then the factory routes
|
|
333
|
+
const route_specs = [
|
|
334
|
+
...consumer_routes,
|
|
335
|
+
...bootstrap_route_specs,
|
|
336
|
+
...rpc_route_specs,
|
|
337
|
+
...surface_route_specs
|
|
338
|
+
];
|
|
339
|
+
// mirrors `create_app_server`'s `[proxy_spec, ...auth_middleware]`
|
|
340
|
+
const stack_specs = [
|
|
341
|
+
{ name: 'trusted_proxy', path: '*', handler: create_stub_mw() },
|
|
342
|
+
...create_stub_api_middleware({ include_daemon_token: options.daemon_token })
|
|
343
|
+
];
|
|
344
|
+
let middleware_specs = stack_specs;
|
|
296
345
|
if (options.transform_middleware) {
|
|
297
|
-
|
|
346
|
+
// the transform gets copies in a fresh array, so mutating what it was
|
|
347
|
+
// handed can't rewrite the originals the check matches against
|
|
348
|
+
middleware_specs = options.transform_middleware(stack_specs.map((spec) => ({ ...spec })));
|
|
349
|
+
assert_middleware_stack_mounted(stack_specs, middleware_specs);
|
|
298
350
|
}
|
|
299
|
-
|
|
351
|
+
const surface_spec = create_app_surface_spec({
|
|
300
352
|
middleware_specs,
|
|
301
353
|
route_specs,
|
|
302
354
|
env_schema: options.env_schema ?? BaseServerEnv,
|
|
@@ -304,4 +356,6 @@ export const create_test_app_surface_spec = (options) => {
|
|
|
304
356
|
rpc_endpoints: resolved_rpc_endpoints,
|
|
305
357
|
ws_endpoints: resolved_ws_endpoints
|
|
306
358
|
});
|
|
359
|
+
surface_ref.surface = surface_spec.surface;
|
|
360
|
+
return surface_spec;
|
|
307
361
|
};
|
|
@@ -202,9 +202,7 @@ export interface WsTestHarness {
|
|
|
202
202
|
/**
|
|
203
203
|
* Open a mock connection. Resolves after `on_socket_open` (and the
|
|
204
204
|
* transport's `register_ws`) completes, so broadcasts issued
|
|
205
|
-
* immediately after the `await` reach the connection.
|
|
206
|
-
* revisions returned synchronously and required a `settle_open()`
|
|
207
|
-
* microtask drain — no longer necessary.
|
|
205
|
+
* immediately after the `await` reach the connection.
|
|
208
206
|
*
|
|
209
207
|
* Returns the shared `WsClient` interface — same surface the
|
|
210
208
|
* cross-process driver in `transports/ws_transport.ts` implements,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/ws_round_trip.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ws_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/ws_round_trip.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* Attack-surface viewer. Fetches `GET /api/surface` (REST — not RPC, since
|
|
4
4
|
* the surface dump exists outside the action surface) and delegates
|
|
5
5
|
* rendering to `SurfaceExplorer`. Surfaces a retry button on fetch
|
|
6
|
-
* failures.
|
|
6
|
+
* failures. The server must opt in to the route
|
|
7
|
+
* (`create_app_server({surface_route: true})`), which is admin-only and
|
|
8
|
+
* refused to a narrowed API token.
|
|
7
9
|
*
|
|
8
10
|
* @module
|
|
9
11
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminSurface.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminSurface.svelte"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AdminSurface.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminSurface.svelte"],"names":[],"mappings":"AAoEA,QAAA,MAAM,YAAY,2DAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
package/dist/ui/CLAUDE.md
CHANGED
|
@@ -326,7 +326,9 @@ destructive actions.
|
|
|
326
326
|
- `AdminSettings.svelte` — shell for `OpenSignupToggle` + the logged-in
|
|
327
327
|
account line + logout `ConfirmButton`. No direct RPC calls.
|
|
328
328
|
- `AdminSurface.svelte` — attack-surface viewer. Fetches
|
|
329
|
-
`/api/surface` (REST) and delegates to `SurfaceExplorer`.
|
|
329
|
+
`/api/surface` (REST) and delegates to `SurfaceExplorer`. The server must
|
|
330
|
+
opt in with `create_app_server({surface_route: true})`; the route is
|
|
331
|
+
admin-only and refused to a narrowed API token.
|
|
330
332
|
- `OpenSignupToggle.svelte` — single checkbox bound to
|
|
331
333
|
`AppSettingsState.settings.open_signup`. Consumes
|
|
332
334
|
`app_settings_rpc_context`.
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* would otherwise need to compensate for.
|
|
29
29
|
* - **`AbortSignal` threaded to the callback** — RPC clients that accept
|
|
30
30
|
* a signal (or `fetch`) get cancellation for free; callers can also
|
|
31
|
-
* pass an external `signal` via
|
|
31
|
+
* pass an external `signal` via `RunOptions` to bind the slot's
|
|
32
32
|
* lifetime to a component / page.
|
|
33
33
|
* - **`preserve_error_on_retry`** — opt-in to keeping the previous error
|
|
34
34
|
* visible while a retry is pending (default clears at the start of
|
|
@@ -68,7 +68,7 @@ export interface AsyncSlotOptions<T, E = string> {
|
|
|
68
68
|
initial?: T;
|
|
69
69
|
/**
|
|
70
70
|
* Convert a caught throw into the error value stored in
|
|
71
|
-
*
|
|
71
|
+
* `AsyncSlot.error`. Default extracts `Error.message` (falling
|
|
72
72
|
* back to `'Request failed'` for non-Error throws). Pass
|
|
73
73
|
* `to_rpc_error_message` to unwrap JSON-RPC `data.reason` codes.
|
|
74
74
|
*/
|
|
@@ -87,7 +87,7 @@ export interface RunOptions {
|
|
|
87
87
|
/**
|
|
88
88
|
* External signal chained into the slot's internal controller. Aborts
|
|
89
89
|
* the in-flight run when fired (alongside automatic supersession by
|
|
90
|
-
* the next `run()` and manual
|
|
90
|
+
* the next `run()` and manual `AsyncSlot.abort` calls).
|
|
91
91
|
*/
|
|
92
92
|
signal?: AbortSignal;
|
|
93
93
|
}
|
|
@@ -96,7 +96,7 @@ export interface RunOptions {
|
|
|
96
96
|
*
|
|
97
97
|
* @typeParam T - The success payload type. Use `void` for write-only
|
|
98
98
|
* actions whose response isn't worth retaining.
|
|
99
|
-
* @typeParam E - The shape of
|
|
99
|
+
* @typeParam E - The shape of `AsyncSlot.error`. Defaults to
|
|
100
100
|
* `string` (set by the default `map_error`). Narrow to a structured
|
|
101
101
|
* type by providing a `map_error` that returns it.
|
|
102
102
|
*/
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* would otherwise need to compensate for.
|
|
29
29
|
* - **`AbortSignal` threaded to the callback** — RPC clients that accept
|
|
30
30
|
* a signal (or `fetch`) get cancellation for free; callers can also
|
|
31
|
-
* pass an external `signal` via
|
|
31
|
+
* pass an external `signal` via `RunOptions` to bind the slot's
|
|
32
32
|
* lifetime to a component / page.
|
|
33
33
|
* - **`preserve_error_on_retry`** — opt-in to keeping the previous error
|
|
34
34
|
* visible while a retry is pending (default clears at the start of
|
|
@@ -64,7 +64,7 @@ import { to_error_message } from '@fuzdev/fuz_util/error.ts';
|
|
|
64
64
|
*
|
|
65
65
|
* @typeParam T - The success payload type. Use `void` for write-only
|
|
66
66
|
* actions whose response isn't worth retaining.
|
|
67
|
-
* @typeParam E - The shape of
|
|
67
|
+
* @typeParam E - The shape of `AsyncSlot.error`. Defaults to
|
|
68
68
|
* `string` (set by the default `map_error`). Narrow to a structured
|
|
69
69
|
* type by providing a `map_error` that returns it.
|
|
70
70
|
*/
|
|
@@ -85,7 +85,7 @@ export class AsyncSlot {
|
|
|
85
85
|
#controller = null;
|
|
86
86
|
/**
|
|
87
87
|
* Tracks whether any `run()` or `set()` has ever produced a success
|
|
88
|
-
* result. Used by
|
|
88
|
+
* result. Used by `abort` to revert to `'success'` (vs `'initial'`)
|
|
89
89
|
* — explicit flag instead of inspecting `data` so the discriminator
|
|
90
90
|
* stays correct for `T = void` (where success-`data` is `undefined`)
|
|
91
91
|
* and for nullable `T`s where `null` is a legitimate success value.
|
|
@@ -183,7 +183,7 @@ export class AsyncSlot {
|
|
|
183
183
|
}
|
|
184
184
|
/**
|
|
185
185
|
* Abort the in-flight run (if any) and null out the controller field.
|
|
186
|
-
* Shared by
|
|
186
|
+
* Shared by `abort`, `set`, and `reset`.
|
|
187
187
|
*/
|
|
188
188
|
#clear_controller(reason) {
|
|
189
189
|
this.#controller?.abort(reason);
|