@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.
Files changed (94) hide show
  1. package/dist/actions/CLAUDE.md +56 -20
  2. package/dist/actions/action_rpc.d.ts +14 -11
  3. package/dist/actions/action_rpc.d.ts.map +1 -1
  4. package/dist/actions/action_rpc.js +5 -4
  5. package/dist/actions/connection_closer.d.ts +13 -3
  6. package/dist/actions/connection_closer.d.ts.map +1 -1
  7. package/dist/actions/connection_closer.js +7 -5
  8. package/dist/actions/register_action_ws.d.ts +18 -13
  9. package/dist/actions/register_action_ws.d.ts.map +1 -1
  10. package/dist/actions/register_action_ws.js +8 -6
  11. package/dist/actions/register_ws_endpoint.d.ts +6 -3
  12. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  13. package/dist/actions/register_ws_endpoint.js +6 -3
  14. package/dist/actions/transports.d.ts +9 -0
  15. package/dist/actions/transports.d.ts.map +1 -1
  16. package/dist/actions/transports.js +9 -0
  17. package/dist/actions/transports_ws_backend.d.ts +15 -0
  18. package/dist/actions/transports_ws_backend.d.ts.map +1 -1
  19. package/dist/actions/transports_ws_backend.js +25 -5
  20. package/dist/actions/ws_endpoint_spec.d.ts +24 -4
  21. package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
  22. package/dist/actions/ws_endpoint_spec.js +15 -1
  23. package/dist/auth/middleware.d.ts +29 -1
  24. package/dist/auth/middleware.d.ts.map +1 -1
  25. package/dist/auth/middleware.js +35 -1
  26. package/dist/http/CLAUDE.md +3 -3
  27. package/dist/http/surface.d.ts +12 -9
  28. package/dist/http/surface.d.ts.map +1 -1
  29. package/dist/rate_limiter.d.ts +43 -3
  30. package/dist/rate_limiter.d.ts.map +1 -1
  31. package/dist/rate_limiter.js +27 -3
  32. package/dist/realtime/sse_auth_guard.d.ts +2 -1
  33. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  34. package/dist/realtime/sse_auth_guard.js +4 -2
  35. package/dist/realtime/subscriber_registry.d.ts +12 -0
  36. package/dist/realtime/subscriber_registry.d.ts.map +1 -1
  37. package/dist/realtime/subscriber_registry.js +18 -2
  38. package/dist/server/app_server.d.ts +128 -55
  39. package/dist/server/app_server.d.ts.map +1 -1
  40. package/dist/server/app_server.js +110 -61
  41. package/dist/server/app_server_context.d.ts +17 -9
  42. package/dist/server/app_server_context.d.ts.map +1 -1
  43. package/dist/testing/CLAUDE.md +83 -25
  44. package/dist/testing/app_server.d.ts +6 -3
  45. package/dist/testing/app_server.d.ts.map +1 -1
  46. package/dist/testing/app_server.js +20 -20
  47. package/dist/testing/connection_closer_helpers.d.ts +10 -4
  48. package/dist/testing/connection_closer_helpers.d.ts.map +1 -1
  49. package/dist/testing/connection_closer_helpers.js +4 -0
  50. package/dist/testing/cross_backend/capabilities.d.ts +3 -3
  51. package/dist/testing/cross_backend/default_backend_configs.d.ts +12 -12
  52. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  53. package/dist/testing/cross_backend/default_backend_configs.js +11 -10
  54. package/dist/testing/cross_backend/full_spine_mount.d.ts +35 -11
  55. package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
  56. package/dist/testing/cross_backend/full_spine_mount.js +26 -9
  57. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +16 -1
  58. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
  59. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +5 -4
  60. package/dist/testing/cross_backend/spine_surface_constants.d.ts +43 -3
  61. package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
  62. package/dist/testing/cross_backend/spine_surface_constants.js +43 -3
  63. package/dist/testing/cross_backend/testing_backdoor.d.ts +15 -1
  64. package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
  65. package/dist/testing/cross_backend/testing_backdoor.js +51 -6
  66. package/dist/testing/cross_backend/testing_reset_actions.d.ts +6 -3
  67. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  68. package/dist/testing/cross_backend/testing_reset_actions.js +6 -3
  69. package/dist/testing/cross_backend/testing_server_bun.d.ts.map +1 -1
  70. package/dist/testing/cross_backend/testing_server_bun.js +5 -1
  71. package/dist/testing/cross_backend/testing_server_core.d.ts +72 -41
  72. package/dist/testing/cross_backend/testing_server_core.d.ts.map +1 -1
  73. package/dist/testing/cross_backend/testing_server_core.js +22 -14
  74. package/dist/testing/cross_backend/testing_server_deno.js +1 -1
  75. package/dist/testing/cross_backend/testing_server_node.d.ts.map +1 -1
  76. package/dist/testing/cross_backend/testing_server_node.js +20 -7
  77. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +15 -2
  78. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
  79. package/dist/testing/cross_backend/ts_spine_backend_config.js +10 -8
  80. package/dist/testing/cross_backend/ws_action_rate_limit.d.ts +26 -0
  81. package/dist/testing/cross_backend/ws_action_rate_limit.d.ts.map +1 -0
  82. package/dist/testing/cross_backend/ws_action_rate_limit.js +261 -0
  83. package/dist/testing/middleware.d.ts.map +1 -1
  84. package/dist/testing/middleware.js +4 -3
  85. package/dist/testing/stubs.d.ts +17 -4
  86. package/dist/testing/stubs.d.ts.map +1 -1
  87. package/dist/testing/stubs.js +46 -13
  88. package/dist/testing/transports/ws_transport.d.ts +7 -0
  89. package/dist/testing/transports/ws_transport.d.ts.map +1 -1
  90. package/dist/testing/transports/ws_transport.js +1 -1
  91. package/dist/testing/ws_round_trip.d.ts +43 -37
  92. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  93. package/dist/testing/ws_round_trip.js +28 -46
  94. package/package.json +2 -2
@@ -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[]` produces **two**
205
- route specs on the same path (GET + POST) that share one internal
206
- dispatcher. Per-action auth lives inside the dispatcher; the outer routes
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`. Closing is idempotent and synchronous.
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. Twin of
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
- `upgradeWebSocket` (the Hono adapter helper) is supplied once at the top
624
- level — `create_app_server` throws when `ws_endpoints` resolves non-empty
625
- but `upgradeWebSocket` is missing. A factory returning `[]` does NOT trip
626
- the check, so feature-flag gated WS surfaces stay safe.
627
-
628
- `WsEndpointSpec` fields: `path`, `allowed_origins`, `actions`,
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. Upgrade-time authorization phase — resolves the acting actor, seeds `REQUEST_CONTEXT_KEY` for the inner `register_action_ws`
668
- 4. Optional `require_role(required_roles)` — any-of disjunction (coarse upgrade-time gate; per-action `auth` in each spec still applies at dispatch time)
669
- 5. Delegates to `register_action_ws`
670
-
671
- Extends `RegisterActionWsOptions` with `allowed_origins` and optional
672
- `required_roles`, and inherits its required `connection_closer`. Returns
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; this is the entry test harnesses use when they need
675
- the upgrade stack without `create_app_server`'s full assembly.
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'`. `null` disables the IP check.
242
- * Per-action gate via `action.spec.rate_limit`. Same limiter is
243
- * shared with the WebSocket action dispatcher — one budget per
244
- * action, not per transport.
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?: RateLimiter | null;
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` disables the account check. Same limiter is shared with the
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?: RateLimiter | null;
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. **Validate params** — parse input against the action's `input` schema.
277
- * 7. **Rate limit** — per-action IP / account throttling.
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 logger
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. **Validate params** — parse input against the action's `input` schema.
107
- * 7. **Rate limit** — per-action IP / account throttling.
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 logger
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 = null, action_account_rate_limiter = null } = options;
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 `ConnectionCloser` method.
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;CACxD;AAED;;;GAGG;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,gBAInC,CAAC;AAEH;;;;;;;;;;GAUG;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,cAgCzC,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 close_all = (close) => {
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) => close_all((member) => member.close_sockets_for_session(session_token_hash)),
134
- close_sockets_for_token: (api_token_id) => close_all((member) => member.close_sockets_for_token(api_token_id)),
135
- close_sockets_for_account: (account_id) => close_all((member) => member.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
- * Most consumers should mount WS endpoints via `register_ws_endpoint`
5
- * (`actions/register_ws_endpoint.ts`), which wraps this function with the
6
- * standard upgrade stack (origin check + auth + optional role). This
7
- * module stays exported as the lower-level entry point for tests that
8
- * drive the dispatcher directly via `create_ws_test_harness`.
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'`. `null` (or omitted) disables the
305
- * IP check. Same limiter is shared with the HTTP RPC dispatcher so
306
- * one budget covers both transports per action. Resolved at upgrade
307
- * time and reused for every message on the socket.
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?: RateLimiter | null;
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`. `null` (or omitted) disables the
314
- * account check. Same limiter is shared with the HTTP RPC dispatcher.
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?: RateLimiter | null;
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":"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"}
1
+ {"version":3,"file":"register_action_ws.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/register_action_ws.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuFG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAE3D,OAAO,EAAU,KAAK,MAAM,IAAI,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAWnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAmBtD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAa7D,OAAO,EAAE,yBAAyB,EAAE,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAIhG,YAAY,EAAE,MAAM,EAAE,CAAC;AAEvB,0EAA0E;AAC1E,eAAO,MAAM,gCAAgC,QAAS,CAAC;AAEvD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,gCAAgC,IAAI,CAAC;AAElD;;;GAGG;AACH,eAAO,MAAM,sCAAsC,2CAA2C,CAAC;AAsC/F;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IACjC,qFAAqF;IACrF,EAAE,EAAE,SAAS,CAAC;IACd,8EAA8E;IAC9E,aAAa,EAAE,IAAI,CAAC;IACpB,oDAAoD;IACpD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B;;;OAGG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD;;;;;OAKG;IACH,MAAM,EAAE,WAAW,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,kBAAkB;IAClC;;;;OAIG;IACH,EAAE,EAAE,SAAS,CAAC;IACd,2CAA2C;IAC3C,aAAa,EAAE,IAAI,CAAC;IACpB,8EAA8E;IAC9E,QAAQ,EAAE,kBAAkB,CAAC;CAC7B;AAED,MAAM,WAAW,sBAAsB;IACtC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wCAAwC;AACxC,MAAM,WAAW,uBAAuB;IACvC,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,GAAG,EAAE,IAAI,CAAC;IACV,iEAAiE;IACjE,gBAAgB,EAAE,gBAAgB,CAAC;IACnC;;;;;;;OAOG;IACH,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC/B;;;;;;;;;OASG;IACH,EAAE,EAAE,EAAE,CAAC;IACP;;;;OAIG;IACH,SAAS,CAAC,EAAE,yBAAyB,CAAC;IACtC;;;;;;;;;OASG;IACH,iBAAiB,EAAE,cAAc,GAAG,IAAI,CAAC;IACzC;;;;;;;;OAQG;IACH,2BAA2B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,sBAAsB,CAAC;IAC7C,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;;;;;OAWG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qDAAqD;IACrD,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE;;;;;;;;OAQG;IACH,sBAAsB,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3C;;;;;;OAMG;IACH,2BAA2B,EAAE,WAAW,GAAG,IAAI,CAAC;CAChD;AAED,sCAAsC;AACtC,MAAM,WAAW,sBAAsB;IACtC,yEAAyE;IACzE,SAAS,EAAE,yBAAyB,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,uBAAuB,KAAG,sBAksBrE,CAAC"}
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * WebSocket JSON-RPC dispatch — the low-level WS transport binding.
3
3
  *
4
- * Most consumers should mount WS endpoints via `register_ws_endpoint`
5
- * (`actions/register_ws_endpoint.ts`), which wraps this function with the
6
- * standard upgrade stack (origin check + auth + optional role). This
7
- * module stays exported as the lower-level entry point for tests that
8
- * drive the dispatcher directly via `create_ws_test_harness`.
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 = null, action_account_rate_limiter = null } = options;
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 idiomatic consumer entry
3
- * point for mounting a fuz_app WS endpoint.
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 every consumer writes by hand:
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;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;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
+ {"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 idiomatic consumer entry
3
- * point for mounting a fuz_app WS endpoint.
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 every consumer writes by hand:
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,EAIN,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;IAuC5C;;;;;;;;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;IA4D/C,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"}
1
+ {"version":3,"file":"transports_ws_backend.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/transports_ws_backend.ts"],"names":[],"mappings":"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"}
@@ -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.#revoke_connection(connection_id, entry);
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
- #revoke_connection(connection_id, entry) {
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(WS_CLOSE_SESSION_REVOKED, WS_CLOSE_SESSION_REVOKED_REASON);
327
+ entry.ws.close(code, reason);
308
328
  }
309
329
  /**
310
330
  * Make room for one more admitted connection on `account_id`: when the