@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
package/dist/actions/CLAUDE.md
CHANGED
|
@@ -201,9 +201,13 @@ nothing). fuz_app mounts no bridged routes itself; this is consumer-facing.
|
|
|
201
201
|
|
|
202
202
|
## Single JSON-RPC 2.0 endpoint (`actions/action_rpc.ts`)
|
|
203
203
|
|
|
204
|
-
`create_rpc_endpoint({path, actions, log}): RouteSpec[]`
|
|
205
|
-
route specs on the same path (GET + POST) that share one
|
|
206
|
-
dispatcher.
|
|
204
|
+
`create_rpc_endpoint({path, actions, log, action_ip_rate_limiter, action_account_rate_limiter}): RouteSpec[]`
|
|
205
|
+
produces **two** route specs on the same path (GET + POST) that share one
|
|
206
|
+
internal dispatcher. The two limiters are required `RateLimiter | null` with
|
|
207
|
+
no default — `null` is the explicit off switch — and the same holds on
|
|
208
|
+
`register_action_ws` / `register_ws_endpoint`, so a hand mount can't run
|
|
209
|
+
unthrottled by omission. `create_app_server` passes its own to every endpoint
|
|
210
|
+
it mounts (../../../docs/usage.md §Rate limiters). Per-action auth lives inside the dispatcher; the outer routes
|
|
207
211
|
use `auth: {account: 'none', actor: 'none'}` and `transaction: false`.
|
|
208
212
|
|
|
209
213
|
The HTTP RPC dispatcher is a thin shim around `perform_action`
|
|
@@ -358,6 +362,7 @@ and `allow_fallback: boolean` (default `true`). Explicit
|
|
|
358
362
|
- `WS_CLOSE_CLIENT_HEARTBEAT_TIMEOUT = 4002` — client observed receive-silence past its receive timeout (`resolve_heartbeat_receive_timeout`).
|
|
359
363
|
- `WS_CLOSE_SERVER_HEARTBEAT_TIMEOUT = 4003` — server observed receive-silence past `DEFAULT_SERVER_HEARTBEAT_TIMEOUT` (60s).
|
|
360
364
|
- `WS_CLOSE_CONNECTION_LIMIT = 4004` — server closed this socket to admit a newer one on the same account, past the per-account connection cap (reason `connection limit`). Not a revocation: the client stays `closed` with `superseded` set and no reconnect, until `connect()` is called. Twin of the Rust spine's constant.
|
|
365
|
+
- `WS_CLOSE_GOING_AWAY = 1001` — the server is shutting down (reason `WS_CLOSE_GOING_AWAY_REASON`, `Server shutting down`): `close_all_sockets`, which `AppServer.close` runs. Not a revocation: the client reconnects under its ordinary backoff.
|
|
361
366
|
- `WS_CLOSE_MESSAGE_TOO_BIG = 1009` — server received a message over its `max_message_bytes` cap (RFC 6455 "Message Too Big").
|
|
362
367
|
- `WS_CLOSE_POLICY_VIOLATION = 1008` — a socket sent too much while it was still opening (before admission and `on_socket_open` completed): past `MAX_PRE_ADMISSION_FRAMES` frames or the `PRE_ADMISSION_QUEUE_BYTES_FACTOR` byte budget.
|
|
363
368
|
- `WS_CLOSE_INTERNAL_ERROR = 1011` — the admission credential re-check itself failed (reason `internal error`), `on_socket_open` threw (reason `socket bootstrap failed`), or the socket's connection was removed from the transport without a close (reason `connection unregistered`). Not a revocation: the client reconnects under its ordinary backoff. Twin of the Rust spine's constant.
|
|
@@ -455,7 +460,7 @@ the newcomer instead would let half-open sockets lock a user out with their
|
|
|
455
460
|
own dead connections, and let a stolen credential hold every slot ahead of the
|
|
456
461
|
real user. The check runs at **admission** (`admit`, or `add_connection`),
|
|
457
462
|
which `register_action_ws` reaches in `onOpen` — after every upgrade gate
|
|
458
|
-
(origin, auth, token scope, role) and the credential re-read — so a refused or
|
|
463
|
+
(origin, auth, token scope, acting actor, role) and the credential re-read — so a refused or
|
|
459
464
|
just-revoked request never closes someone's socket. An evicted connection
|
|
460
465
|
leaves the transport's map at once — no broadcast or peer request reaches it,
|
|
461
466
|
its pending peer requests drain as `connection_gone`, it stops counting toward
|
|
@@ -480,6 +485,10 @@ each closed connection's controller):
|
|
|
480
485
|
- `close_sockets_for_token(api_token_id)`
|
|
481
486
|
- `close_sockets_for_account(account_id)` — coarse, covers session + bearer + daemon-token
|
|
482
487
|
|
|
488
|
+
`close_all_sockets()` closes every connection, pending and admitted, with
|
|
489
|
+
`WS_CLOSE_GOING_AWAY` instead — the shutdown close, removing and aborting each
|
|
490
|
+
the same way.
|
|
491
|
+
|
|
483
492
|
Fan-out, to admitted connections only: `send(notification)` broadcasts to every
|
|
484
493
|
connection; `broadcast_filtered(message, predicate)` runs per-connection ACL
|
|
485
494
|
predicate over `ConnectionIdentity`; `send_to_account` wraps `broadcast_filtered` and
|
|
@@ -523,12 +532,18 @@ interface ConnectionCloser {
|
|
|
523
532
|
close_sockets_for_session: (session_token_hash: string) => number;
|
|
524
533
|
close_sockets_for_token: (api_token_id: string) => number;
|
|
525
534
|
close_sockets_for_account: (account_id: Uuid) => number;
|
|
535
|
+
close_all_sockets: () => number; // shutdown, never a revocation
|
|
526
536
|
}
|
|
527
537
|
```
|
|
528
538
|
|
|
529
539
|
`BackendWebsocketTransport` satisfies it structurally;
|
|
530
540
|
`create_sse_connection_closer(registry)` (`realtime/sse_auth_guard.ts`) adapts
|
|
531
|
-
a `SubscriberRegistry
|
|
541
|
+
a `SubscriberRegistry` (`close_all_sockets` is `SubscriberRegistry.close_all`).
|
|
542
|
+
Closing is idempotent and synchronous. `close_all_sockets` is the one method no
|
|
543
|
+
revocation calls: `AppServer.close` runs it on `deps.connection_closer` to end
|
|
544
|
+
every live connection of the backend at shutdown, and it has no
|
|
545
|
+
`ConnectionCloseTarget` variant — nor a counterpart on the Rust
|
|
546
|
+
`SocketRevoker`.
|
|
532
547
|
|
|
533
548
|
**Every revocation closes after its commit.** A handler never calls a closer
|
|
534
549
|
inline: it calls `queue_connection_close(ctx, deps.connection_closer, target)`,
|
|
@@ -553,7 +568,8 @@ carry caller-supplied ids.
|
|
|
553
568
|
`RealtimeCloser` — a `ConnectionCloser` plus `add(closer)` (idempotent by
|
|
554
569
|
reference) — whose every close fans out to each member and returns the sum.
|
|
555
570
|
`create_app_backend` puts one on `AppDeps.connection_closer`; `create_app_server`
|
|
556
|
-
adds each mounted WS transport and the audit stream registry
|
|
571
|
+
adds each mounted WS transport and the audit stream registry, and its
|
|
572
|
+
`AppServer.close` runs `close_all_sockets` across them all. Twin of
|
|
557
573
|
`RealtimeRevoker`. A member that throws does not stop the fan-out — every
|
|
558
574
|
member is called, then the first error is thrown for the caller to log;
|
|
559
575
|
`BackendWebsocketTransport` and `SubscriberRegistry.close_by_identity` treat a
|
|
@@ -620,12 +636,30 @@ runs after server context is assembled so action lists can depend on
|
|
|
620
636
|
`ctx.deps` / `ctx.action_*_rate_limiter`. Each entry is auto-mounted via
|
|
621
637
|
`register_ws_endpoint` against the assembled Hono app.
|
|
622
638
|
|
|
623
|
-
`
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
the
|
|
627
|
-
|
|
628
|
-
`
|
|
639
|
+
`create_upgrade_websocket: (app: Hono) => UpgradeWebSocket` returns the
|
|
640
|
+
Hono adapter helper. It is a factory because Node's `createNodeWebSocket({app})`
|
|
641
|
+
needs the app `create_app_server` builds: it is called once, with that app
|
|
642
|
+
(the same instance as `AppServer.app`), after the app exists and before the
|
|
643
|
+
mount — and only when `ws_endpoints` resolves non-empty. Deno and Bun pass
|
|
644
|
+
`() => upgradeWebSocket`. `create_app_server` throws when `ws_endpoints`
|
|
645
|
+
resolves non-empty but the factory is missing; a factory returning `[]` does
|
|
646
|
+
NOT trip the check, so feature-flag gated WS surfaces stay safe.
|
|
647
|
+
|
|
648
|
+
What every auto-mount gets, so no hand mount has to remember it: the
|
|
649
|
+
`action_ip_rate_limiter` / `action_account_rate_limiter` instances the RPC
|
|
650
|
+
endpoints charge (one budget per action across transports), the server's
|
|
651
|
+
`allowed_origins` when the spec sets none (`resolve_ws_endpoints`, read by
|
|
652
|
+
both the mount and the surface), registration ahead of
|
|
653
|
+
`post_route_middleware` and `static_serving`, the surface entry, and the
|
|
654
|
+
guards below. A `ws_endpoints` or `rpc_endpoints` path outside the auth
|
|
655
|
+
middleware's scope (`AUTH_MIDDLEWARE_PATH`, `'/api/*'`, from
|
|
656
|
+
`auth/middleware.ts` — matched with `middleware_applies`, so `/api` itself is
|
|
657
|
+
inside) throws at assembly (`assert_endpoint_in_auth_scope`, which
|
|
658
|
+
`create_test_app_surface_spec` also runs): there no session or bearer is
|
|
659
|
+
parsed and every caller would be unauthenticated.
|
|
660
|
+
|
|
661
|
+
`WsEndpointSpec` fields: `path`, `allowed_origins?` (default: the server's),
|
|
662
|
+
`actions`,
|
|
629
663
|
`required_roles?`, `transport?`, `max_connections_per_account?`, `heartbeat?`,
|
|
630
664
|
`artificial_delay?`, `max_message_bytes?`,
|
|
631
665
|
`on_socket_open?`, `on_socket_close?`, `auth_guard?` (default `true`,
|
|
@@ -664,15 +698,17 @@ Composes the standard upgrade stack:
|
|
|
664
698
|
|
|
665
699
|
1. `verify_request_source(allowed_origins)`
|
|
666
700
|
2. `require_auth`
|
|
667
|
-
3.
|
|
668
|
-
4.
|
|
669
|
-
5.
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
`
|
|
701
|
+
3. `require_token_scope('surface:ws_upgrade')` — a narrowed token cannot hold a socket
|
|
702
|
+
4. Upgrade-time authorization phase — resolves the acting actor, seeds `REQUEST_CONTEXT_KEY` for the inner `register_action_ws`
|
|
703
|
+
5. Optional `require_role(required_roles)` — any-of disjunction (coarse upgrade-time gate; per-action `auth` in each spec still applies at dispatch time)
|
|
704
|
+
6. Delegates to `register_action_ws`
|
|
705
|
+
|
|
706
|
+
Extends `RegisterActionWsOptions` with `allowed_origins` (required here —
|
|
707
|
+
there is no server list to default from) and optional `required_roles`, and
|
|
708
|
+
inherits its required `connection_closer` and action limiters. Returns
|
|
673
709
|
`{transport}`. Most consumers reach for
|
|
674
|
-
`ws_endpoints` above
|
|
675
|
-
|
|
710
|
+
`ws_endpoints` above. Call it directly only to mount a WS endpoint outside
|
|
711
|
+
`create_app_server`'s assembly.
|
|
676
712
|
|
|
677
713
|
### `register_action_ws` — lower-level dispatcher
|
|
678
714
|
|
|
@@ -238,21 +238,23 @@ export interface CreateRpcEndpointOptions {
|
|
|
238
238
|
log: Logger;
|
|
239
239
|
/**
|
|
240
240
|
* Per-IP rate limiter consulted for actions whose spec declares
|
|
241
|
-
* `rate_limit: 'ip'` or `'both'`.
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
241
|
+
* `rate_limit: 'ip'` or `'both'`. Required, with no default: pass
|
|
242
|
+
* `null` to turn the IP check off, so an unthrottled endpoint is a
|
|
243
|
+
* choice the call site states rather than an option it forgot.
|
|
244
|
+
* `create_app_server` passes its own (a default limiter unless
|
|
245
|
+
* configured). Share one instance with the WebSocket action dispatcher —
|
|
246
|
+
* one budget per action, not per transport.
|
|
245
247
|
*/
|
|
246
|
-
action_ip_rate_limiter
|
|
248
|
+
action_ip_rate_limiter: RateLimiter | null;
|
|
247
249
|
/**
|
|
248
250
|
* Per-account rate limiter consulted for actions whose spec declares
|
|
249
251
|
* `rate_limit: 'account'` or `'both'`. Keyed on
|
|
250
252
|
* `request_context.account.id` (account-grain — billed to the
|
|
251
253
|
* authenticated account regardless of which actor was resolved).
|
|
252
|
-
* `null`
|
|
253
|
-
* WebSocket action dispatcher.
|
|
254
|
+
* Required, with no default: pass `null` to turn the account check off.
|
|
255
|
+
* Share one instance with the WebSocket action dispatcher.
|
|
254
256
|
*/
|
|
255
|
-
action_account_rate_limiter
|
|
257
|
+
action_account_rate_limiter: RateLimiter | null;
|
|
256
258
|
}
|
|
257
259
|
/**
|
|
258
260
|
* Single JSON-RPC 2.0 endpoint — the canonical RPC transport binding.
|
|
@@ -273,8 +275,9 @@ export interface CreateRpcEndpointOptions {
|
|
|
273
275
|
* a UUID and treats anything else as omitted.
|
|
274
276
|
* 5. **Post-authorization auth** — enforce role / keeper requirements
|
|
275
277
|
* against the request context.
|
|
276
|
-
* 6. **
|
|
277
|
-
*
|
|
278
|
+
* 6. **Rate limit** — per-action IP / account throttling, ahead of params
|
|
279
|
+
* validation so a malformed call still charges.
|
|
280
|
+
* 7. **Validate params** — parse input against the action's `input` schema.
|
|
278
281
|
* 8. **Dispatch** — acquire DB handle (transaction for mutations, pool for reads),
|
|
279
282
|
* construct `ActionContext`, call handler, return JSON-RPC response.
|
|
280
283
|
*
|
|
@@ -285,7 +288,7 @@ export interface CreateRpcEndpointOptions {
|
|
|
285
288
|
* inside the dispatcher, and `transaction: false` because transaction scope
|
|
286
289
|
* is per-action (mutations get a transaction, reads get pool).
|
|
287
290
|
*
|
|
288
|
-
* @param options - endpoint path, actions, and
|
|
291
|
+
* @param options - endpoint path, actions, logger, and the two action rate limiters
|
|
289
292
|
* @returns route specs (GET + POST) ready for `apply_route_specs`
|
|
290
293
|
* @throws Error if two actions share the same `spec.method` (registration-time
|
|
291
294
|
* duplicate detection); also throws if any action's `spec.input` is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action_rpc.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_rpc.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"action_rpc.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_rpc.ts"],"names":[],"mappings":"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"}
|
|
@@ -103,8 +103,9 @@ const jsonrpc_error_envelope = (id, error) => ({
|
|
|
103
103
|
* a UUID and treats anything else as omitted.
|
|
104
104
|
* 5. **Post-authorization auth** — enforce role / keeper requirements
|
|
105
105
|
* against the request context.
|
|
106
|
-
* 6. **
|
|
107
|
-
*
|
|
106
|
+
* 6. **Rate limit** — per-action IP / account throttling, ahead of params
|
|
107
|
+
* validation so a malformed call still charges.
|
|
108
|
+
* 7. **Validate params** — parse input against the action's `input` schema.
|
|
108
109
|
* 8. **Dispatch** — acquire DB handle (transaction for mutations, pool for reads),
|
|
109
110
|
* construct `ActionContext`, call handler, return JSON-RPC response.
|
|
110
111
|
*
|
|
@@ -115,7 +116,7 @@ const jsonrpc_error_envelope = (id, error) => ({
|
|
|
115
116
|
* inside the dispatcher, and `transaction: false` because transaction scope
|
|
116
117
|
* is per-action (mutations get a transaction, reads get pool).
|
|
117
118
|
*
|
|
118
|
-
* @param options - endpoint path, actions, and
|
|
119
|
+
* @param options - endpoint path, actions, logger, and the two action rate limiters
|
|
119
120
|
* @returns route specs (GET + POST) ready for `apply_route_specs`
|
|
120
121
|
* @throws Error if two actions share the same `spec.method` (registration-time
|
|
121
122
|
* duplicate detection); also throws if any action's `spec.input` is
|
|
@@ -123,7 +124,7 @@ const jsonrpc_error_envelope = (id, error) => ({
|
|
|
123
124
|
* `z.void()` for parameterless methods).
|
|
124
125
|
*/
|
|
125
126
|
export const create_rpc_endpoint = (options) => {
|
|
126
|
-
const { path: endpoint_path, actions, log, action_ip_rate_limiter
|
|
127
|
+
const { path: endpoint_path, actions, log, action_ip_rate_limiter, action_account_rate_limiter } = options;
|
|
127
128
|
const { action_map } = compile_action_registry(actions, 'RPC action');
|
|
128
129
|
/**
|
|
129
130
|
* HTTP-shape dispatch shim — the GET/POST entry points share this:
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
import type { Uuid } from '@fuzdev/fuz_util/id.ts';
|
|
46
46
|
import { type EmitAfterCommitContext } from '../http/pending_effects.js';
|
|
47
47
|
/**
|
|
48
|
-
* Close live connections by identity.
|
|
48
|
+
* Close live connections by identity, or all of them.
|
|
49
49
|
*
|
|
50
50
|
* Each method returns how many connections it closed. Closing is idempotent:
|
|
51
51
|
* a connection already gone isn't counted, so a handler's close and an audit
|
|
@@ -70,10 +70,18 @@ export interface ConnectionCloser {
|
|
|
70
70
|
* credentials.
|
|
71
71
|
*/
|
|
72
72
|
close_sockets_for_account: (account_id: Uuid) => number;
|
|
73
|
+
/**
|
|
74
|
+
* Close every connection, whoever opened it — the shutdown close
|
|
75
|
+
* `AppServer.close` runs. Not a revocation: a WebSocket gets
|
|
76
|
+
* `WS_CLOSE_GOING_AWAY`, so its client reconnects rather than treating
|
|
77
|
+
* its credential as revoked.
|
|
78
|
+
*/
|
|
79
|
+
close_all_sockets: () => number;
|
|
73
80
|
}
|
|
74
81
|
/**
|
|
75
82
|
* Which connections a revocation closes — the argument of
|
|
76
|
-
* `queue_connection_close`, one variant per
|
|
83
|
+
* `queue_connection_close`, one variant per credential close of
|
|
84
|
+
* `ConnectionCloser` (`close_all_sockets` is shutdown, never a revocation).
|
|
77
85
|
*/
|
|
78
86
|
export type ConnectionCloseTarget = {
|
|
79
87
|
kind: 'session';
|
|
@@ -121,7 +129,9 @@ export declare const noop_connection_closer: ConnectionCloser;
|
|
|
121
129
|
*
|
|
122
130
|
* Each close fans out to every member and returns the sum of what each
|
|
123
131
|
* closed. A revocation handler holds this rather than one transport, so its
|
|
124
|
-
* close reaches the WebSocket transports and the SSE registries alike
|
|
132
|
+
* close reaches the WebSocket transports and the SSE registries alike, and
|
|
133
|
+
* `AppServer.close` runs its `close_all_sockets` to end every live connection
|
|
134
|
+
* of the backend at shutdown.
|
|
125
135
|
*
|
|
126
136
|
* A member that throws does not stop the fan-out: every member is still
|
|
127
137
|
* called, and the first error is thrown once they all have been — so the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection_closer.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/connection_closer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAGnD,OAAO,EAAqB,KAAK,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAE5F;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,gBAAgB;IAChC,qGAAqG;IACrG,yBAAyB,EAAE,CAAC,kBAAkB,EAAE,MAAM,KAAK,MAAM,CAAC;IAClE,4EAA4E;IAC5E,uBAAuB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1D;;;;OAIG;IACH,yBAAyB,EAAE,CAAC,UAAU,EAAE,IAAI,KAAK,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"connection_closer.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/connection_closer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAGnD,OAAO,EAAqB,KAAK,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAE5F;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,gBAAgB;IAChC,qGAAqG;IACrG,yBAAyB,EAAE,CAAC,kBAAkB,EAAE,MAAM,KAAK,MAAM,CAAC;IAClE,4EAA4E;IAC5E,uBAAuB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1D;;;;OAIG;IACH,yBAAyB,EAAE,CAAC,UAAU,EAAE,IAAI,KAAK,MAAM,CAAC;IACxD;;;;;OAKG;IACH,iBAAiB,EAAE,MAAM,MAAM,CAAC;CAChC;AAED;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAC9B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,IAAI,CAAA;CAAE,CAAC;AAEzC;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAC7B,QAAQ,gBAAgB,EACxB,QAAQ,qBAAqB,KAC3B,MAWF,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sBAAsB,GAClC,KAAK,sBAAsB,EAC3B,QAAQ,gBAAgB,EACxB,QAAQ,qBAAqB,KAC3B,IAIF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,gBAKnC,CAAC;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACvD;;;;;;OAMG;IACH,GAAG,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACxC;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,QAAO,cAiCzC,CAAC"}
|
|
@@ -92,7 +92,8 @@ export const queue_connection_close = (ctx, closer, target) => {
|
|
|
92
92
|
export const noop_connection_closer = Object.freeze({
|
|
93
93
|
close_sockets_for_session: () => 0,
|
|
94
94
|
close_sockets_for_token: () => 0,
|
|
95
|
-
close_sockets_for_account: () => 0
|
|
95
|
+
close_sockets_for_account: () => 0,
|
|
96
|
+
close_all_sockets: () => 0
|
|
96
97
|
});
|
|
97
98
|
/**
|
|
98
99
|
* Create an empty `RealtimeCloser`.
|
|
@@ -105,7 +106,7 @@ export const noop_connection_closer = Object.freeze({
|
|
|
105
106
|
*/
|
|
106
107
|
export const create_realtime_closer = () => {
|
|
107
108
|
const members = new Set();
|
|
108
|
-
const
|
|
109
|
+
const fan_out = (close) => {
|
|
109
110
|
let count = 0;
|
|
110
111
|
let failed = false;
|
|
111
112
|
let first_error;
|
|
@@ -130,8 +131,9 @@ export const create_realtime_closer = () => {
|
|
|
130
131
|
add: (closer) => {
|
|
131
132
|
members.add(closer);
|
|
132
133
|
},
|
|
133
|
-
close_sockets_for_session: (session_token_hash) =>
|
|
134
|
-
close_sockets_for_token: (api_token_id) =>
|
|
135
|
-
close_sockets_for_account: (account_id) =>
|
|
134
|
+
close_sockets_for_session: (session_token_hash) => fan_out((member) => member.close_sockets_for_session(session_token_hash)),
|
|
135
|
+
close_sockets_for_token: (api_token_id) => fan_out((member) => member.close_sockets_for_token(api_token_id)),
|
|
136
|
+
close_sockets_for_account: (account_id) => fan_out((member) => member.close_sockets_for_account(account_id)),
|
|
137
|
+
close_all_sockets: () => fan_out((member) => member.close_all_sockets())
|
|
136
138
|
});
|
|
137
139
|
};
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WebSocket JSON-RPC dispatch — the low-level WS transport binding.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Consumers declare WS endpoints on `create_app_server`'s `ws_endpoints`,
|
|
5
|
+
* which mounts each through `register_ws_endpoint`
|
|
6
|
+
* (`actions/register_ws_endpoint.ts`) — this function wrapped in the
|
|
7
|
+
* standard upgrade stack (origin check + auth + token scope + actor
|
|
8
|
+
* resolution + optional role). This module stays exported as the
|
|
9
|
+
* lower-level entry point for tests that drive the dispatcher directly via
|
|
10
|
+
* `create_ws_test_harness`.
|
|
9
11
|
*
|
|
10
12
|
* Symmetric to `create_rpc_endpoint` (from `actions/action_rpc.ts`):
|
|
11
13
|
* both transports parse their wire envelope, then call the shared
|
|
@@ -301,19 +303,22 @@ export interface RegisterActionWsOptions {
|
|
|
301
303
|
on_socket_close?: (ctx: SocketCloseContext) => void | Promise<void>;
|
|
302
304
|
/**
|
|
303
305
|
* Per-IP rate limiter consulted for actions whose spec declares
|
|
304
|
-
* `rate_limit: 'ip'` or `'both'
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
306
|
+
* `rate_limit: 'ip'` or `'both'`, keyed on the client IP read at upgrade
|
|
307
|
+
* time. Required, with no default: pass `null` to turn the IP check off,
|
|
308
|
+
* so an unthrottled socket is a choice the call site states rather than
|
|
309
|
+
* an option it forgot. Share one instance with the HTTP RPC dispatcher so
|
|
310
|
+
* one budget covers both transports per action — `create_app_server`
|
|
311
|
+
* does, for the endpoints it mounts from `ws_endpoints`.
|
|
308
312
|
*/
|
|
309
|
-
action_ip_rate_limiter
|
|
313
|
+
action_ip_rate_limiter: RateLimiter | null;
|
|
310
314
|
/**
|
|
311
315
|
* Per-account rate limiter consulted for actions whose spec declares
|
|
312
316
|
* `rate_limit: 'account'` or `'both'`. Keyed on
|
|
313
|
-
* `request_context.account.id`.
|
|
314
|
-
* account check.
|
|
317
|
+
* `request_context.account.id`. Required, with no default: pass `null`
|
|
318
|
+
* to turn the account check off. Share one instance with the HTTP RPC
|
|
319
|
+
* dispatcher.
|
|
315
320
|
*/
|
|
316
|
-
action_account_rate_limiter
|
|
321
|
+
action_account_rate_limiter: RateLimiter | null;
|
|
317
322
|
}
|
|
318
323
|
/** Result of `register_action_ws`. */
|
|
319
324
|
export interface RegisterActionWsResult {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register_action_ws.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/register_action_ws.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"register_action_ws.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/register_action_ws.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WebSocket JSON-RPC dispatch — the low-level WS transport binding.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Consumers declare WS endpoints on `create_app_server`'s `ws_endpoints`,
|
|
5
|
+
* which mounts each through `register_ws_endpoint`
|
|
6
|
+
* (`actions/register_ws_endpoint.ts`) — this function wrapped in the
|
|
7
|
+
* standard upgrade stack (origin check + auth + token scope + actor
|
|
8
|
+
* resolution + optional role). This module stays exported as the
|
|
9
|
+
* lower-level entry point for tests that drive the dispatcher directly via
|
|
10
|
+
* `create_ws_test_harness`.
|
|
9
11
|
*
|
|
10
12
|
* Symmetric to `create_rpc_endpoint` (from `actions/action_rpc.ts`):
|
|
11
13
|
* both transports parse their wire envelope, then call the shared
|
|
@@ -216,7 +218,7 @@ export const register_action_ws = (options) => {
|
|
|
216
218
|
throw new Error(`register_action_ws: max_connections_per_account configures the transport created for ${options.path}, ` +
|
|
217
219
|
'but a transport was supplied — set the cap on that BackendWebsocketTransport instead');
|
|
218
220
|
}
|
|
219
|
-
const { path, app, upgradeWebSocket, actions, db, heartbeat = true, artificial_delay = 0, max_message_bytes = DEFAULT_WS_MAX_MESSAGE_BYTES, log = new Logger('[ws]'), on_socket_open, on_socket_close, action_ip_rate_limiter
|
|
221
|
+
const { path, app, upgradeWebSocket, actions, db, heartbeat = true, artificial_delay = 0, max_message_bytes = DEFAULT_WS_MAX_MESSAGE_BYTES, log = new Logger('[ws]'), on_socket_open, on_socket_close, action_ip_rate_limiter, action_account_rate_limiter } = options;
|
|
220
222
|
const transport = options.transport ??
|
|
221
223
|
new BackendWebsocketTransport({
|
|
222
224
|
max_connections_per_account: options.max_connections_per_account,
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Composed WebSocket endpoint registration — the
|
|
3
|
-
*
|
|
2
|
+
* Composed WebSocket endpoint registration — the primitive
|
|
3
|
+
* `create_app_server` mounts each `ws_endpoints` entry with. An app assembled
|
|
4
|
+
* by `create_app_server` declares its endpoints there rather than calling this,
|
|
5
|
+
* and gets the shared action rate limiters, the origin default, the surface
|
|
6
|
+
* entry, and the audit guard with them.
|
|
4
7
|
*
|
|
5
|
-
* Wraps the standard upgrade stack
|
|
8
|
+
* Wraps the standard upgrade stack:
|
|
6
9
|
*
|
|
7
10
|
* 1. `verify_request_source(allowed_origins)` — reject disallowed origins
|
|
8
11
|
* before the upgrade handshake runs.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register_ws_endpoint.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/register_ws_endpoint.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"register_ws_endpoint.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/register_ws_endpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAaH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGvD,OAAO,EAEN,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,MAAM,yBAAyB,CAAC;AAEjC,0CAA0C;AAC1C,MAAM,WAAW,yBAA0B,SAAQ,uBAAuB;IACzE;;;;OAIG;IACH,eAAe,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;CACzC;AAgDD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,oBAAoB,GAChC,SAAS,yBAAyB,KAChC,sBAoBF,CAAC"}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Composed WebSocket endpoint registration — the
|
|
3
|
-
*
|
|
2
|
+
* Composed WebSocket endpoint registration — the primitive
|
|
3
|
+
* `create_app_server` mounts each `ws_endpoints` entry with. An app assembled
|
|
4
|
+
* by `create_app_server` declares its endpoints there rather than calling this,
|
|
5
|
+
* and gets the shared action rate limiters, the origin default, the surface
|
|
6
|
+
* entry, and the audit guard with them.
|
|
4
7
|
*
|
|
5
|
-
* Wraps the standard upgrade stack
|
|
8
|
+
* Wraps the standard upgrade stack:
|
|
6
9
|
*
|
|
7
10
|
* 1. `verify_request_source(allowed_origins)` — reject disallowed origins
|
|
8
11
|
* before the upgrade handshake runs.
|
|
@@ -39,6 +39,15 @@ export declare const WS_CLOSE_SERVER_HEARTBEAT_TIMEOUT = 4003;
|
|
|
39
39
|
* spine's `WS_CLOSE_CONNECTION_LIMIT`.
|
|
40
40
|
*/
|
|
41
41
|
export declare const WS_CLOSE_CONNECTION_LIMIT = 4004;
|
|
42
|
+
/**
|
|
43
|
+
* WebSocket close code — the server is shutting down (RFC 6455 §7.4.1 "Going
|
|
44
|
+
* Away"). Sent to every live socket by `close_all_sockets`, which
|
|
45
|
+
* `AppServer.close` runs. Not a revocation: the credential is still good, so a
|
|
46
|
+
* client reconnects under its ordinary backoff once a server is back.
|
|
47
|
+
*/
|
|
48
|
+
export declare const WS_CLOSE_GOING_AWAY = 1001;
|
|
49
|
+
/** The close reason sent with `WS_CLOSE_GOING_AWAY`. */
|
|
50
|
+
export declare const WS_CLOSE_GOING_AWAY_REASON = "Server shutting down";
|
|
42
51
|
/**
|
|
43
52
|
* WebSocket close code — an inbound message exceeded the receiver's size cap
|
|
44
53
|
* (RFC 6455 §7.4.1 "Message Too Big").
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transports.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/transports.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EACX,gCAAgC,EAChC,gCAAgC,EAChC,mBAAmB,EACnB,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,oBAAoB,CAAC;AACjE,sEAAsE;AACtE,eAAO,MAAM,iCAAiC,OAAO,CAAC;AACtD,yEAAyE;AACzE,eAAO,MAAM,iCAAiC,OAAO,CAAC;AACtD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAC9C;;;GAGG;AACH,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAC9C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,QAAc,CAAC;AAIxD;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,EAAE,WAAW,MAAM,KAAG,MAAM,GAAG,IAI3E,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,MAA8C,CAAC;AAK1F,eAAO,MAAM,aAAa,aAAa,CAAC;AACxC,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACpC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACzB,cAAc,EAAE,aAAa,CAAC;IAE9B,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/F,IAAI,CACH,OAAO,EAAE,mBAAmB,EAC5B,OAAO,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IACxC,IAAI,CACH,OAAO,EAAE,gCAAgC,EACzC,OAAO,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC,gCAAgC,GAAG,IAAI,CAAC,CAAC;IACpD,QAAQ,EAAE,MAAM,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,qBAAa,UAAU;;IAItB;;;OAGG;IACH,cAAc,EAAE,OAAO,CAAQ;IAE/B;;;;;OAKG;IACH,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAQ9C;;;;;OAKG;IACH,qBAAqB,CAAC,cAAc,EAAE,aAAa,GAAG,IAAI;IAM1D;;;;;;OAMG;IACH,aAAa,CAAC,cAAc,CAAC,EAAE,aAAa,GAAG,SAAS,GAAG,IAAI;IAO/D,QAAQ,IAAI,OAAO,GAAG,IAAI;IAM1B,qBAAqB,IAAI,SAAS,GAAG,IAAI;IAIzC,0BAA0B,IAAI,aAAa,GAAG,IAAI;IAIlD,qBAAqB,CAAC,cAAc,EAAE,aAAa,GAAG,SAAS,GAAG,IAAI;CAwCtE"}
|
|
1
|
+
{"version":3,"file":"transports.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/transports.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EACX,gCAAgC,EAChC,gCAAgC,EAChC,mBAAmB,EACnB,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,oBAAoB,CAAC;AACjE,sEAAsE;AACtE,eAAO,MAAM,iCAAiC,OAAO,CAAC;AACtD,yEAAyE;AACzE,eAAO,MAAM,iCAAiC,OAAO,CAAC;AACtD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAC9C;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,OAAO,CAAC;AACxC,wDAAwD;AACxD,eAAO,MAAM,0BAA0B,yBAAyB,CAAC;AACjE;;;GAGG;AACH,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAC9C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,QAAc,CAAC;AAIxD;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,EAAE,WAAW,MAAM,KAAG,MAAM,GAAG,IAI3E,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,MAA8C,CAAC;AAK1F,eAAO,MAAM,aAAa,aAAa,CAAC;AACxC,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACpC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACzB,cAAc,EAAE,aAAa,CAAC;IAE9B,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/F,IAAI,CACH,OAAO,EAAE,mBAAmB,EAC5B,OAAO,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IACxC,IAAI,CACH,OAAO,EAAE,gCAAgC,EACzC,OAAO,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC,gCAAgC,GAAG,IAAI,CAAC,CAAC;IACpD,QAAQ,EAAE,MAAM,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,qBAAa,UAAU;;IAItB;;;OAGG;IACH,cAAc,EAAE,OAAO,CAAQ;IAE/B;;;;;OAKG;IACH,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAQ9C;;;;;OAKG;IACH,qBAAqB,CAAC,cAAc,EAAE,aAAa,GAAG,IAAI;IAM1D;;;;;;OAMG;IACH,aAAa,CAAC,cAAc,CAAC,EAAE,aAAa,GAAG,SAAS,GAAG,IAAI;IAO/D,QAAQ,IAAI,OAAO,GAAG,IAAI;IAM1B,qBAAqB,IAAI,SAAS,GAAG,IAAI;IAIzC,0BAA0B,IAAI,aAAa,GAAG,IAAI;IAIlD,qBAAqB,CAAC,cAAc,EAAE,aAAa,GAAG,SAAS,GAAG,IAAI;CAwCtE"}
|
|
@@ -38,6 +38,15 @@ export const WS_CLOSE_SERVER_HEARTBEAT_TIMEOUT = 4003;
|
|
|
38
38
|
* spine's `WS_CLOSE_CONNECTION_LIMIT`.
|
|
39
39
|
*/
|
|
40
40
|
export const WS_CLOSE_CONNECTION_LIMIT = 4004;
|
|
41
|
+
/**
|
|
42
|
+
* WebSocket close code — the server is shutting down (RFC 6455 §7.4.1 "Going
|
|
43
|
+
* Away"). Sent to every live socket by `close_all_sockets`, which
|
|
44
|
+
* `AppServer.close` runs. Not a revocation: the credential is still good, so a
|
|
45
|
+
* client reconnects under its ordinary backoff once a server is back.
|
|
46
|
+
*/
|
|
47
|
+
export const WS_CLOSE_GOING_AWAY = 1001;
|
|
48
|
+
/** The close reason sent with `WS_CLOSE_GOING_AWAY`. */
|
|
49
|
+
export const WS_CLOSE_GOING_AWAY_REASON = 'Server shutting down';
|
|
41
50
|
/**
|
|
42
51
|
* WebSocket close code — an inbound message exceeded the receiver's size cap
|
|
43
52
|
* (RFC 6455 §7.4.1 "Message Too Big").
|
|
@@ -252,6 +252,21 @@ export declare class BackendWebsocketTransport implements FilterableBroadcastTra
|
|
|
252
252
|
* `WS_CLOSE_SESSION_REVOKED`
|
|
253
253
|
*/
|
|
254
254
|
close_sockets_for_token(api_token_id: string): number;
|
|
255
|
+
/**
|
|
256
|
+
* Close every connection, pending or admitted, with `WS_CLOSE_GOING_AWAY` —
|
|
257
|
+
* the shutdown close `AppServer.close` runs. Not a revocation: the client
|
|
258
|
+
* reconnects under its ordinary backoff.
|
|
259
|
+
*
|
|
260
|
+
* Each closed connection is removed and its controller aborted, as a
|
|
261
|
+
* revocation does, so its heartbeat timer stops and nothing more dispatches
|
|
262
|
+
* on it. A socket whose `close` throws does not stop the loop.
|
|
263
|
+
*
|
|
264
|
+
* @returns the number of sockets closed
|
|
265
|
+
* @mutates this - removes every connection, aborts its registered
|
|
266
|
+
* controller, and closes its underlying `WSContext` with `WS_CLOSE_GOING_AWAY`
|
|
267
|
+
* @throws the first error a socket's `close` threw, after every connection was attempted
|
|
268
|
+
*/
|
|
269
|
+
close_all_sockets(): number;
|
|
255
270
|
send(message: JsonrpcRequest, options?: TransportSendOptions): Promise<JsonrpcResponseOrError>;
|
|
256
271
|
send(message: JsonrpcNotification, options?: TransportSendOptions): Promise<JsonrpcErrorResponse | null>;
|
|
257
272
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transports_ws_backend.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/transports_ws_backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAe,KAAK,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAEhE,OAAO,KAAK,EAEX,gCAAgC,EAChC,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,
|
|
1
|
+
{"version":3,"file":"transports_ws_backend.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/transports_ws_backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAe,KAAK,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAEhE,OAAO,KAAK,EAEX,gCAAgC,EAChC,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,EAMN,KAAK,SAAS,EACd,KAAK,oBAAoB,EACzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAEN,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,MAAM,mBAAmB,CAAC;AAI3B;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IAClC,sEAAsE;IACtE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,4CAA4C;IAC5C,UAAU,EAAE,IAAI,CAAC;IACjB,sEAAsE;IACtE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,4BAA6B,SAAQ,SAAS;IAC9D,kBAAkB,EAAE,CACnB,OAAO,EAAE,gCAAgC,EACzC,SAAS,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,OAAO,KAChD,MAAM,CAAC;CACZ;AAED,qDAAqD;AACrD,eAAO,MAAM,iCAAiC,GAC7C,WAAW,SAAS,KAClB,SAAS,IAAI,4BAEqE,CAAC;AAEtF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mCAAmC,KAAK,CAAC;AAEtD,+CAA+C;AAC/C,MAAM,WAAW,gCAAgC;IAChD;;;;;;;;;;;;;;OAcG;IACH,2BAA2B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAqBD,qBAAa,yBAA0B,YAAW,4BAA4B;;IAC7E,QAAQ,CAAC,cAAc,EAAG,uBAAuB,CAAU;IAkB3D;;;OAGG;gBACS,OAAO,CAAC,EAAE,gCAAgC;IActD;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,gBAAgB,CACf,EAAE,EAAE,SAAS,EACb,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,UAAU,EAAE,IAAI,EAChB,YAAY,GAAE,MAAM,GAAG,IAAW,EAClC,gBAAgB,GAAE,eAAe,GAAG,IAAW,GAC7C,IAAI;IAIP;;;;;;;;;;;;;;;OAeG;IACH,KAAK,CAAC,aAAa,EAAE,IAAI,GAAG,OAAO;IAanC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,cAAc,CACb,EAAE,EAAE,SAAS,EACb,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,UAAU,EAAE,IAAI,EAChB,YAAY,GAAE,MAAM,GAAG,IAAW,EAClC,gBAAgB,GAAE,eAAe,GAAG,IAAW,GAC7C,IAAI;IAyBP;;;;;;;;;;;;;;;;OAgBG;IACH,iBAAiB,CAAC,aAAa,EAAE,IAAI,GAAG,IAAI;IA8C5C;;;;;;;;OAQG;IACH,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAIrD;;;;;;;;OAQG;IACH,yBAAyB,CAAC,UAAU,EAAE,IAAI,GAAG,MAAM;IAInD;;;;;;;;;;;;OAYG;IACH,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAIrD;;;;;;;;;;;;;OAaG;IACH,iBAAiB,IAAI,MAAM;IA4DrB,IAAI,CACT,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC,sBAAsB,CAAC;IAC5B,IAAI,CACT,OAAO,EAAE,mBAAmB,EAC5B,OAAO,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IA2CvC;;;;;;;;;;OAUG;IACH,kBAAkB,CACjB,OAAO,EAAE,gCAAgC,EACzC,SAAS,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,OAAO,GAClD,MAAM;IAeT;;;;;;;;;;;;;OAaG;IACH,eAAe,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,gCAAgC,GAAG,MAAM;IAIpF;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,kBAAkB,CACjB,aAAa,EAAE,IAAI,EACnB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,oBAAoB,GAAG,SAAS,EACxC,OAAO,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,kBAAkB,CAAC;IAqB9B;;;;;;;;;;OAUG;IACH,qBAAqB,CACpB,aAAa,EAAE,IAAI,EACnB,QAAQ,EAAE,eAAe,GAAG,oBAAoB,GAC9C,OAAO;IAIV;;;;;;;;;OASG;IACH,aAAa,CAAC,aAAa,EAAE,IAAI,GAAG,OAAO;IAI3C,QAAQ,IAAI,OAAO;IAInB;;;;;;;;OAQG;IACH,oBAAoB,IAAI,MAAM;IAQ9B;;;;;OAKG;IACH,4BAA4B,IAAI,MAAM;CAOtC"}
|
|
@@ -51,7 +51,7 @@ import { Logger } from '@fuzdev/fuz_util/log.ts';
|
|
|
51
51
|
import { create_uuid } from '@fuzdev/fuz_util/id.ts';
|
|
52
52
|
import { jsonrpc_error_messages } from '../http/jsonrpc_errors.js';
|
|
53
53
|
import { create_jsonrpc_error_response, create_jsonrpc_request, to_jsonrpc_message_id, is_jsonrpc_request } from '../http/jsonrpc_helpers.js';
|
|
54
|
-
import { WS_CLOSE_CONNECTION_LIMIT, WS_CLOSE_SESSION_REVOKED, WS_CLOSE_SESSION_REVOKED_REASON } from './transports.js';
|
|
54
|
+
import { WS_CLOSE_CONNECTION_LIMIT, WS_CLOSE_GOING_AWAY, WS_CLOSE_GOING_AWAY_REASON, WS_CLOSE_SESSION_REVOKED, WS_CLOSE_SESSION_REVOKED_REASON } from './transports.js';
|
|
55
55
|
import { PendingPeerRequests } from './peer_request.js';
|
|
56
56
|
/** Type guard for `FilterableBroadcastTransport`. */
|
|
57
57
|
export const is_filterable_broadcast_transport = (transport) => 'broadcast_filtered' in transport &&
|
|
@@ -227,10 +227,13 @@ export class BackendWebsocketTransport {
|
|
|
227
227
|
* socket's connection is already removed and its handlers aborted, so
|
|
228
228
|
* nothing more is dispatched or delivered on it.
|
|
229
229
|
*
|
|
230
|
+
* @param predicate - which connections to close
|
|
231
|
+
* @param code - the close code each socket gets
|
|
232
|
+
* @param reason - the close reason each socket gets
|
|
230
233
|
* @returns the number of sockets closed
|
|
231
234
|
* @throws the first error a socket's `close` threw, after every match was attempted
|
|
232
235
|
*/
|
|
233
|
-
#close_where(predicate) {
|
|
236
|
+
#close_where(predicate, code = WS_CLOSE_SESSION_REVOKED, reason = WS_CLOSE_SESSION_REVOKED_REASON) {
|
|
234
237
|
let count = 0;
|
|
235
238
|
let failed = false;
|
|
236
239
|
let first_error;
|
|
@@ -238,7 +241,7 @@ export class BackendWebsocketTransport {
|
|
|
238
241
|
for (const [connection_id, entry] of this.#connections) {
|
|
239
242
|
if (predicate(entry.identity)) {
|
|
240
243
|
try {
|
|
241
|
-
this.#
|
|
244
|
+
this.#end_connection(connection_id, entry, code, reason);
|
|
242
245
|
count++;
|
|
243
246
|
}
|
|
244
247
|
catch (error) {
|
|
@@ -293,18 +296,35 @@ export class BackendWebsocketTransport {
|
|
|
293
296
|
close_sockets_for_token(api_token_id) {
|
|
294
297
|
return this.#close_where((id) => id.api_token_id === api_token_id);
|
|
295
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* Close every connection, pending or admitted, with `WS_CLOSE_GOING_AWAY` —
|
|
301
|
+
* the shutdown close `AppServer.close` runs. Not a revocation: the client
|
|
302
|
+
* reconnects under its ordinary backoff.
|
|
303
|
+
*
|
|
304
|
+
* Each closed connection is removed and its controller aborted, as a
|
|
305
|
+
* revocation does, so its heartbeat timer stops and nothing more dispatches
|
|
306
|
+
* on it. A socket whose `close` throws does not stop the loop.
|
|
307
|
+
*
|
|
308
|
+
* @returns the number of sockets closed
|
|
309
|
+
* @mutates this - removes every connection, aborts its registered
|
|
310
|
+
* controller, and closes its underlying `WSContext` with `WS_CLOSE_GOING_AWAY`
|
|
311
|
+
* @throws the first error a socket's `close` threw, after every connection was attempted
|
|
312
|
+
*/
|
|
313
|
+
close_all_sockets() {
|
|
314
|
+
return this.#close_where(() => true, WS_CLOSE_GOING_AWAY, WS_CLOSE_GOING_AWAY_REASON);
|
|
315
|
+
}
|
|
296
316
|
#cleanup_connection(connection_id) {
|
|
297
317
|
this.#connections.delete(connection_id);
|
|
298
318
|
// Wake any handler still awaiting a reply on this socket — the peer is
|
|
299
319
|
// gone, so the request can never complete.
|
|
300
320
|
this.#pending.drain(connection_id);
|
|
301
321
|
}
|
|
302
|
-
#
|
|
322
|
+
#end_connection(connection_id, entry, code, reason) {
|
|
303
323
|
this.#cleanup_connection(connection_id);
|
|
304
324
|
// abort before the close so a handler observing `ctx.signal` bails out
|
|
305
325
|
// without waiting for the close handshake
|
|
306
326
|
entry.abort_controller?.abort();
|
|
307
|
-
entry.ws.close(
|
|
327
|
+
entry.ws.close(code, reason);
|
|
308
328
|
}
|
|
309
329
|
/**
|
|
310
330
|
* Make room for one more admitted connection on `account_id`: when the
|