@fuzdev/fuz_app 0.129.0 → 0.131.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 (110) hide show
  1. package/dist/actions/CLAUDE.md +23 -6
  2. package/dist/actions/connection_closer.d.ts +3 -1
  3. package/dist/actions/connection_closer.d.ts.map +1 -1
  4. package/dist/actions/register_action_ws.d.ts +46 -0
  5. package/dist/actions/register_action_ws.d.ts.map +1 -1
  6. package/dist/actions/register_action_ws.js +108 -14
  7. package/dist/actions/rpc_client.d.ts.map +1 -1
  8. package/dist/actions/transports.d.ts +2 -1
  9. package/dist/actions/transports.d.ts.map +1 -1
  10. package/dist/actions/transports.js +2 -1
  11. package/dist/actions/transports_ws_backend.d.ts +38 -4
  12. package/dist/actions/transports_ws_backend.d.ts.map +1 -1
  13. package/dist/actions/transports_ws_backend.js +69 -4
  14. package/dist/auth/audit_log_routes.d.ts +7 -3
  15. package/dist/auth/audit_log_routes.d.ts.map +1 -1
  16. package/dist/auth/audit_log_routes.js +50 -26
  17. package/dist/auth/scope_kind_schema.d.ts.map +1 -1
  18. package/dist/auth/scope_kind_schema.js +1 -3
  19. package/dist/db/db_pg.js +1 -1
  20. package/dist/http/CLAUDE.md +1 -1
  21. package/dist/http/jsonrpc_errors.d.ts.map +1 -1
  22. package/dist/http/jsonrpc_errors.js +7 -5
  23. package/dist/realtime/sse.d.ts +14 -1
  24. package/dist/realtime/sse.d.ts.map +1 -1
  25. package/dist/realtime/sse.js +77 -1
  26. package/dist/realtime/sse_auth_guard.js +1 -1
  27. package/dist/realtime/subscriber_registry.d.ts +44 -8
  28. package/dist/realtime/subscriber_registry.d.ts.map +1 -1
  29. package/dist/realtime/subscriber_registry.js +64 -10
  30. package/dist/runtime/mock.js +1 -1
  31. package/dist/server/app_server.d.ts +4 -1
  32. package/dist/server/app_server.d.ts.map +1 -1
  33. package/dist/testing/CLAUDE.md +21 -6
  34. package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
  35. package/dist/testing/cross_backend/account_lifecycle.js +0 -3
  36. package/dist/testing/cross_backend/action_manifest.d.ts +0 -4
  37. package/dist/testing/cross_backend/action_manifest.d.ts.map +1 -1
  38. package/dist/testing/cross_backend/action_manifest.js +0 -4
  39. package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -1
  40. package/dist/testing/cross_backend/actor_lookup.js +0 -3
  41. package/dist/testing/cross_backend/actor_search.d.ts.map +1 -1
  42. package/dist/testing/cross_backend/actor_search.js +0 -2
  43. package/dist/testing/cross_backend/app_settings.d.ts.map +1 -1
  44. package/dist/testing/cross_backend/app_settings.js +0 -2
  45. package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
  46. package/dist/testing/cross_backend/body_size.js +0 -3
  47. package/dist/testing/cross_backend/body_size_smuggling.d.ts.map +1 -1
  48. package/dist/testing/cross_backend/body_size_smuggling.js +0 -2
  49. package/dist/testing/cross_backend/cell_cross_helpers.d.ts.map +1 -1
  50. package/dist/testing/cross_backend/cell_cross_helpers.js +0 -3
  51. package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
  52. package/dist/testing/cross_backend/cell_crud.js +0 -3
  53. package/dist/testing/cross_backend/cell_gated_create.d.ts +0 -2
  54. package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
  55. package/dist/testing/cross_backend/cell_gated_create.js +0 -4
  56. package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -1
  57. package/dist/testing/cross_backend/cell_grant_role.js +0 -2
  58. package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
  59. package/dist/testing/cross_backend/cell_relations.js +0 -3
  60. package/dist/testing/cross_backend/cookie_attributes.d.ts.map +1 -1
  61. package/dist/testing/cross_backend/cookie_attributes.js +0 -3
  62. package/dist/testing/cross_backend/credential_header_robustness.d.ts.map +1 -1
  63. package/dist/testing/cross_backend/credential_header_robustness.js +0 -2
  64. package/dist/testing/cross_backend/default_spine_surface.d.ts +0 -7
  65. package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
  66. package/dist/testing/cross_backend/fact_serving.d.ts.map +1 -1
  67. package/dist/testing/cross_backend/fact_serving.js +0 -3
  68. package/dist/testing/cross_backend/full_spine_mount.d.ts +0 -5
  69. package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
  70. package/dist/testing/cross_backend/identity_parity.d.ts.map +1 -1
  71. package/dist/testing/cross_backend/identity_parity.js +0 -2
  72. package/dist/testing/cross_backend/login_security.d.ts.map +1 -1
  73. package/dist/testing/cross_backend/login_security.js +0 -3
  74. package/dist/testing/cross_backend/origin.d.ts.map +1 -1
  75. package/dist/testing/cross_backend/origin.js +0 -3
  76. package/dist/testing/cross_backend/peer_ping_ws.js +4 -4
  77. package/dist/testing/cross_backend/query_shape.d.ts.map +1 -1
  78. package/dist/testing/cross_backend/query_shape.js +0 -3
  79. package/dist/testing/cross_backend/ready.d.ts.map +1 -1
  80. package/dist/testing/cross_backend/ready.js +0 -3
  81. package/dist/testing/cross_backend/role_grant_offer_enumeration.d.ts.map +1 -1
  82. package/dist/testing/cross_backend/role_grant_offer_enumeration.js +0 -3
  83. package/dist/testing/cross_backend/role_grant_participation.d.ts.map +1 -1
  84. package/dist/testing/cross_backend/role_grant_participation.js +0 -3
  85. package/dist/testing/cross_backend/session_cap.d.ts.map +1 -1
  86. package/dist/testing/cross_backend/session_cap.js +0 -3
  87. package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts.map +1 -1
  88. package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +0 -3
  89. package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
  90. package/dist/testing/cross_backend/testing_backdoor.js +0 -3
  91. package/dist/testing/cross_backend/testing_server_bun.js +1 -1
  92. package/dist/testing/cross_backend/token_lifetime.d.ts.map +1 -1
  93. package/dist/testing/cross_backend/token_lifetime.js +0 -4
  94. package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
  95. package/dist/testing/cross_backend/token_scope_surface.js +0 -3
  96. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +1 -1
  97. package/dist/testing/cross_backend/ts_spine_backend_config.js +1 -1
  98. package/dist/testing/cross_backend/ws_action_rate_limit.d.ts.map +1 -1
  99. package/dist/testing/cross_backend/ws_action_rate_limit.js +0 -3
  100. package/dist/testing/cross_backend/ws_connection_cap.d.ts.map +1 -1
  101. package/dist/testing/cross_backend/ws_connection_cap.js +0 -3
  102. package/dist/testing/cross_backend/ws_inbound_dispatch.d.ts +20 -0
  103. package/dist/testing/cross_backend/ws_inbound_dispatch.d.ts.map +1 -0
  104. package/dist/testing/cross_backend/ws_inbound_dispatch.js +177 -0
  105. package/dist/testing/transports/sse_frame_reader.d.ts.map +1 -1
  106. package/dist/testing/transports/sse_frame_reader.js +4 -1
  107. package/dist/ui/Datatable.svelte +2 -2
  108. package/dist/ui/ui_format.d.ts.map +1 -1
  109. package/dist/ui/ui_format.js +2 -0
  110. package/package.json +19 -19
@@ -362,7 +362,7 @@ and `allow_fallback: boolean` (default `true`). Explicit
362
362
  - `WS_CLOSE_CLIENT_HEARTBEAT_TIMEOUT = 4002` — client observed receive-silence past its receive timeout (`resolve_heartbeat_receive_timeout`).
363
363
  - `WS_CLOSE_SERVER_HEARTBEAT_TIMEOUT = 4003` — server observed receive-silence past `DEFAULT_SERVER_HEARTBEAT_TIMEOUT` (60s).
364
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.
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. Also the close for an upgrade the shutdown refuses — pending at the close-all, or born closed after it. Not a revocation: the client reconnects under its ordinary backoff.
366
366
  - `WS_CLOSE_MESSAGE_TOO_BIG = 1009` — server received a message over its `max_message_bytes` cap (RFC 6455 "Message Too Big").
367
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.
368
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.
@@ -487,7 +487,15 @@ each closed connection's controller):
487
487
 
488
488
  `close_all_sockets()` closes every connection, pending and admitted, with
489
489
  `WS_CLOSE_GOING_AWAY` instead — the shutdown close, removing and aborting each
490
- the same way.
490
+ the same way — and leaves the transport **closing** for good (`is_closing()`):
491
+ every later registration is **born closed**. `register_pending` and
492
+ `add_connection` then insert nothing, abort the caller's controller, and close
493
+ the socket with `WS_CLOSE_GOING_AWAY` before returning an id that names
494
+ nothing, so `admit` refuses it — an upgrade in flight when the server shuts
495
+ down is closed rather than outliving it. The flag is set in the same
496
+ synchronous step as the closes, so an admission that finds its entry gone
497
+ during shutdown reads the shutdown, never a revocation. Twin of the Rust
498
+ registry's closing flag.
491
499
 
492
500
  Fan-out, to admitted connections only: `send(notification)` broadcasts to every
493
501
  connection; `broadcast_filtered(message, predicate)` runs per-connection ACL
@@ -542,7 +550,10 @@ a `SubscriberRegistry` (`close_all_sockets` is `SubscriberRegistry.close_all`).
542
550
  Closing is idempotent and synchronous. `close_all_sockets` is the one method no
543
551
  revocation calls: `AppServer.close` runs it on `deps.connection_closer` to end
544
552
  every live connection of the backend at shutdown, and it has no
545
- `ConnectionCloseTarget` variant — nor a counterpart on the Rust
553
+ `ConnectionCloseTarget` variant. It also leaves each member closing — the WS
554
+ transport and `SubscriberRegistry` refuse every later registration (born
555
+ closed) — so a closed transport or registry hosts nothing more. Its Rust
556
+ counterpart is `ShutdownCloser::close_all_sockets`, a supertrait of
546
557
  `SocketRevoker`.
547
558
 
548
559
  **Every revocation closes after its commit.** A handler never calls a closer
@@ -727,6 +738,8 @@ HTTP RPC also calls. `register_action_ws` owns only WS-specific concerns:
727
738
 
728
739
  - **Wire envelope parsing** — JSON.parse → batch rejection → peer-response routing → envelope-version guard → notification interception (cancel, silent drop) → `JsonrpcRequest.safeParse` → per-message dispatch. The version guard is what makes `{method: 'x'}` and `{jsonrpc: '1.0', method: 'x'}` an `invalid_request` answer rather than a silently dropped notification, matching the first check the Rust `classify` runs. Peer-response routing is the twin's response shape exactly — no `method` key, a string/number `id`, exactly one of `result` / `error` — so a hybrid frame like `{method, id, result}` stays a dispatchable request instead of being demuxed and swallowed. The envelope schema is the same one the HTTP POST path runs, so `params: null`, an array `params`, or a non-string/number `id` answers `invalid_request` (-32600) on both transports rather than reaching the dispatcher as `invalid_params`. It runs **after** the notification/cancel block: notifications carry no id to echo and stay unvalidated, and `cancel` routing precedes it. The version guard echoes the id via `to_jsonrpc_envelope_id` (a scalar frame answers `id: null`), the later steps via `to_jsonrpc_message_id`.
729
740
  - **Cancel-notification interception** — `{request_id → AbortController}` map; aborts the matching pending controller before the cancel bubbles past the dispatcher
741
+ - **Duplicate live ids refused** — a request whose id names a request still running on the socket is answered `invalid_request` with `data.reason` `ERROR_DUPLICATE_REQUEST_ID` and not dispatched; the running request keeps its controller, its cancel entry, and its reply id. An id is free again once its request is answered. Same refusal on the Rust spine
742
+ - **In-flight ceiling** — at most `MAX_INBOUND_DISPATCHES_PER_CONNECTION` (128) requests run at once per socket (`@hono/node-ws` does not await `onMessage`, so nothing else bounds them); one more is answered `queue_overflow` (`WS_INBOUND_DISPATCH_OVERFLOW_MESSAGE`) and not dispatched, and the socket stays open and keeps reading — a reply to a server-initiated request still gets through. The duplicate check runs first; neither refusal reaches `perform_action`, so neither charges the action rate limiters. Same cap, message, and order as the Rust spine's `MAX_INBOUND_DISPATCHES_PER_CONN`
730
743
  - **Socket-scoped notify** — `(method, params) => ws.send(notification)`, threaded into `perform_action` as `notify`
731
744
  - **Composed abort signal** — `AbortSignal.any([socket_close, per_request_cancel])`, threaded as `signal`
732
745
  - **Connection lifecycle** — admission (`transport.register_pending` → credential re-read → `transport.admit`, which enforces the per-account connection cap) / `remove_connection`, `on_socket_open` / `_close` hooks, server heartbeat
@@ -741,8 +754,12 @@ audit stream shares, read off the keys the auth middleware set), then
741
754
  meanwhile, closes the socket with `WS_CLOSE_SESSION_REVOKED` and the reason a
742
755
  revoked socket gets; a re-check that throws closes it with
743
756
  `WS_CLOSE_INTERNAL_ERROR` (`internal error`) — fail closed, never admitted
744
- unchecked. A refused socket gets nothing else written to it, evicts nothing,
745
- and runs neither lifecycle hook. An upgrade with no resolved credential on its
757
+ unchecked. During shutdown a refusal is the shutdown's instead: an upgrade
758
+ pending at the close-all was closed by it with `WS_CLOSE_GOING_AWAY` and its
759
+ controller aborted, so the refusal after the re-read — even one the closing
760
+ database failed — writes nothing more; an upgrade that registers after it is
761
+ born closed and skips the re-read. A refused socket gets nothing else written
762
+ to it, evicts nothing, and runs neither lifecycle hook. An upgrade with no resolved credential on its
746
763
  context, or one on another account than its request context, throws before the
747
764
  socket exists. The test-preset escape hatch
748
765
  (`TEST_CONTEXT_PRESET_KEY`) skips the re-read — a pre-baked credential has no
@@ -859,7 +876,7 @@ A timeout unregisters the connection and aborts its signal along with the
859
876
  Two abort signals composed via `AbortSignal.any`:
860
877
 
861
878
  - `socket_abort_controller` — per-socket, fires when the socket ends: at the server's close (revocation, cap eviction, heartbeat timeout, …) or the client's. Registered with the transport, which aborts it on the closes it makes. Drives every handler's `ctx.signal`.
862
- - `pending_controllers: Map<JsonrpcRequestId, AbortController>` — per-request. Registered before dispatch, cleared in `finally` so late cancels for a completed id (or a reused id) can't null-abort the wrong handler. Unknown cancels no-op.
879
+ - `pending_controllers: Map<JsonrpcRequestId, AbortController>` — per-request. Registered before dispatch (one entry per running request — a duplicate live id is refused — so its size is the in-flight count the ceiling bounds), cleared in `finally` so late cancels for a completed id (or a reused id) can't null-abort the wrong handler. Unknown cancels no-op.
863
880
 
864
881
  ## Protocol actions (`actions/protocol.ts`)
865
882
 
@@ -74,7 +74,9 @@ export interface ConnectionCloser {
74
74
  * Close every connection, whoever opened it — the shutdown close
75
75
  * `AppServer.close` runs. Not a revocation: a WebSocket gets
76
76
  * `WS_CLOSE_GOING_AWAY`, so its client reconnects rather than treating
77
- * its credential as revoked.
77
+ * its credential as revoked. The transport is left closing: a
78
+ * registration that arrives afterward is born closed, refused at once
79
+ * (`BackendWebsocketTransport` and `SubscriberRegistry` both do).
78
80
  */
79
81
  close_all_sockets: () => number;
80
82
  }
@@ -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;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;;;;;;;;;;;;;;;OAeG;IACH,GAAG,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,MAAM,IAAI,CAAC;IAC9C,2EAA2E;IAC3E,YAAY,EAAE,MAAM,MAAM,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,QAAO,cA4CzC,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;;;;;;;OAOG;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;;;;;;;;;;;;;;;OAeG;IACH,GAAG,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,MAAM,IAAI,CAAC;IAC9C,2EAA2E;IAC3E,YAAY,EAAE,MAAM,MAAM,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,QAAO,cA4CzC,CAAC"}
@@ -59,6 +59,14 @@
59
59
  * re-read itself failed — fail closed, never admitted unchecked. The handshake
60
60
  * has already answered `101`, so a refusal can only be a close frame.
61
61
  *
62
+ * Once the server is shutting down (`AppServer.close` ran the transport's
63
+ * `close_all_sockets`) a refusal is `WS_CLOSE_GOING_AWAY` instead, so the
64
+ * client reconnects rather than treating its credential as revoked: an upgrade
65
+ * pending at the close-all is closed by it — and its abort makes the refusal
66
+ * that follows the re-read send nothing more, even when the database closed
67
+ * under that re-read — and one that registers after it is born closed and
68
+ * skips the re-read (`BackendWebsocketTransport` module doc, "Shutdown").
69
+ *
62
70
  * Frames that arrive before admission are queued, not dispatched: nothing runs
63
71
  * on a credential that has not been re-read. They dispatch in arrival order
64
72
  * once the socket is admitted and `on_socket_open` has completed, and are
@@ -137,6 +145,38 @@ export declare const PRE_ADMISSION_QUEUE_BYTES_FACTOR = 4;
137
145
  * overflows its pre-admission queue, by count or by bytes.
138
146
  */
139
147
  export declare const WS_CLOSE_PRE_ADMISSION_OVERFLOW_REASON = "too much sent before the socket opened";
148
+ /**
149
+ * Max requests one socket may have dispatching at once. A request that
150
+ * arrives past it is answered `queue_overflow` with its id
151
+ * (`WS_INBOUND_DISPATCH_OVERFLOW_MESSAGE`) and not dispatched; the socket
152
+ * stays open, and the requests already running keep answering.
153
+ *
154
+ * A runtime adapter can hand frames over without awaiting the previous one's
155
+ * dispatch (`@hono/node-ws` does not await `onMessage`), so without a ceiling
156
+ * one socket could hold any number of handlers — and their pooled database
157
+ * work — at once. Shedding rather than queueing keeps the socket reading, so a
158
+ * reply to a server-initiated request still reaches the handler awaiting it.
159
+ * Generous, so ordinary pipelining never meets it. Same value as the Rust
160
+ * spine's `MAX_INBOUND_DISPATCHES_PER_CONN`.
161
+ *
162
+ * Counted are the requests that passed envelope validation and are not yet
163
+ * answered — the entries of the socket's cancellation map. Notifications,
164
+ * replies to server-initiated requests, and refused frames never count.
165
+ */
166
+ export declare const MAX_INBOUND_DISPATCHES_PER_CONNECTION = 128;
167
+ /**
168
+ * The `queue_overflow` message a request gets past
169
+ * `MAX_INBOUND_DISPATCHES_PER_CONNECTION`. Same text as the Rust spine's.
170
+ */
171
+ export declare const WS_INBOUND_DISPATCH_OVERFLOW_MESSAGE = "too many concurrent requests on this connection";
172
+ /**
173
+ * The `data.reason` of the `invalid_request` a WebSocket request gets when its
174
+ * id names a request still running on the same socket. The duplicate is not
175
+ * dispatched; the running request keeps its id, so its reply, and a `cancel`
176
+ * naming that id, stay its own. An id is free again once its request has been
177
+ * answered.
178
+ */
179
+ export declare const ERROR_DUPLICATE_REQUEST_ID = "duplicate_request_id";
140
180
  /**
141
181
  * Context passed to the `on_socket_open` hook.
142
182
  *
@@ -339,6 +379,12 @@ export interface RegisterActionWsResult {
339
379
  * - Notifications (method + no id) are silently dropped per JSON-RPC spec.
340
380
  * Exception: `cancel` notifications abort the matching pending request's
341
381
  * `ctx.signal` before bubbling out.
382
+ * - A request whose id names a request still running on the socket is
383
+ * answered `invalid_request` with `data.reason` `ERROR_DUPLICATE_REQUEST_ID`
384
+ * and not dispatched.
385
+ * - At most `MAX_INBOUND_DISPATCHES_PER_CONNECTION` requests run at once per
386
+ * socket; one more is answered `queue_overflow` and not dispatched, and the
387
+ * socket stays open.
342
388
  * - Per-message dispatch goes through `perform_action`: pre-authorization
343
389
  * auth (401) → authorization phase → post-authorization auth (403) →
344
390
  * rate limit (429) → input validation (400) → handler (with transaction
@@ -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":"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"}
@@ -59,6 +59,14 @@
59
59
  * re-read itself failed — fail closed, never admitted unchecked. The handshake
60
60
  * has already answered `101`, so a refusal can only be a close frame.
61
61
  *
62
+ * Once the server is shutting down (`AppServer.close` ran the transport's
63
+ * `close_all_sockets`) a refusal is `WS_CLOSE_GOING_AWAY` instead, so the
64
+ * client reconnects rather than treating its credential as revoked: an upgrade
65
+ * pending at the close-all is closed by it — and its abort makes the refusal
66
+ * that follows the re-read send nothing more, even when the database closed
67
+ * under that re-read — and one that registers after it is born closed and
68
+ * skips the re-read (`BackendWebsocketTransport` module doc, "Shutdown").
69
+ *
62
70
  * Frames that arrive before admission are queued, not dispatched: nothing runs
63
71
  * on a credential that has not been re-read. They dispatch in arrival order
64
72
  * once the socket is admitted and `on_socket_open` has completed, and are
@@ -145,6 +153,38 @@ export const PRE_ADMISSION_QUEUE_BYTES_FACTOR = 4;
145
153
  * overflows its pre-admission queue, by count or by bytes.
146
154
  */
147
155
  export const WS_CLOSE_PRE_ADMISSION_OVERFLOW_REASON = 'too much sent before the socket opened';
156
+ /**
157
+ * Max requests one socket may have dispatching at once. A request that
158
+ * arrives past it is answered `queue_overflow` with its id
159
+ * (`WS_INBOUND_DISPATCH_OVERFLOW_MESSAGE`) and not dispatched; the socket
160
+ * stays open, and the requests already running keep answering.
161
+ *
162
+ * A runtime adapter can hand frames over without awaiting the previous one's
163
+ * dispatch (`@hono/node-ws` does not await `onMessage`), so without a ceiling
164
+ * one socket could hold any number of handlers — and their pooled database
165
+ * work — at once. Shedding rather than queueing keeps the socket reading, so a
166
+ * reply to a server-initiated request still reaches the handler awaiting it.
167
+ * Generous, so ordinary pipelining never meets it. Same value as the Rust
168
+ * spine's `MAX_INBOUND_DISPATCHES_PER_CONN`.
169
+ *
170
+ * Counted are the requests that passed envelope validation and are not yet
171
+ * answered — the entries of the socket's cancellation map. Notifications,
172
+ * replies to server-initiated requests, and refused frames never count.
173
+ */
174
+ export const MAX_INBOUND_DISPATCHES_PER_CONNECTION = 128;
175
+ /**
176
+ * The `queue_overflow` message a request gets past
177
+ * `MAX_INBOUND_DISPATCHES_PER_CONNECTION`. Same text as the Rust spine's.
178
+ */
179
+ export const WS_INBOUND_DISPATCH_OVERFLOW_MESSAGE = 'too many concurrent requests on this connection';
180
+ /**
181
+ * The `data.reason` of the `invalid_request` a WebSocket request gets when its
182
+ * id names a request still running on the same socket. The duplicate is not
183
+ * dispatched; the running request keeps its id, so its reply, and a `cancel`
184
+ * naming that id, stay its own. An id is free again once its request has been
185
+ * answered.
186
+ */
187
+ export const ERROR_DUPLICATE_REQUEST_ID = 'duplicate_request_id';
148
188
  /** Send one JSON-RPC error response frame on `ws`. */
149
189
  const send_error_response = (ws, id, error) => {
150
190
  ws.send(JSON.stringify(create_jsonrpc_error_response(id, error)));
@@ -187,6 +227,12 @@ const is_peer_response = (json) => {
187
227
  * - Notifications (method + no id) are silently dropped per JSON-RPC spec.
188
228
  * Exception: `cancel` notifications abort the matching pending request's
189
229
  * `ctx.signal` before bubbling out.
230
+ * - A request whose id names a request still running on the socket is
231
+ * answered `invalid_request` with `data.reason` `ERROR_DUPLICATE_REQUEST_ID`
232
+ * and not dispatched.
233
+ * - At most `MAX_INBOUND_DISPATCHES_PER_CONNECTION` requests run at once per
234
+ * socket; one more is answered `queue_overflow` and not dispatched, and the
235
+ * socket stays open.
190
236
  * - Per-message dispatch goes through `perform_action`: pre-authorization
191
237
  * auth (401) → authorization phase → post-authorization auth (403) →
192
238
  * rate limit (429) → input validation (400) → handler (with transaction
@@ -333,7 +379,10 @@ export const register_action_ws = (options) => {
333
379
  // Populated on request dispatch, cleared in the handler's `finally`
334
380
  // so a late-arriving cancel for a completed id (or a reused id)
335
381
  // can't null-abort a freshly-arrived request. Idempotent: cancel
336
- // for unknown ids no-ops.
382
+ // for unknown ids no-ops. One entry per running request — a
383
+ // request reusing a live id is refused before it gets one — so its
384
+ // size is the in-flight count `MAX_INBOUND_DISPATCHES_PER_CONNECTION`
385
+ // bounds.
337
386
  const pending_controllers = new Map();
338
387
  // Identity is assembled at upgrade time so `on_socket_close` can
339
388
  // read it — by the time the hook runs the transport record is gone,
@@ -407,8 +456,10 @@ export const register_action_ws = (options) => {
407
456
  }
408
457
  };
409
458
  // Refuse an upgrade at admission. A socket that already ended needs no
410
- // close frame: either a revocation closed its pending registration (the
411
- // transport sent the 4001) or the client went away.
459
+ // close frame: a revocation closed its pending registration (the
460
+ // transport sent the 4001), the shutdown did (the 1001 — so a refusal
461
+ // during shutdown never sends a revocation's or a failure's code), or
462
+ // the client went away.
412
463
  const refuse_upgrade = (ws, code, reason) => {
413
464
  if (captured_connection_id !== undefined) {
414
465
  transport.remove_connection(captured_connection_id);
@@ -435,6 +486,17 @@ export const register_action_ws = (options) => {
435
486
  // Pending first, synchronously: from here every revocation finds it.
436
487
  const connection_id = transport.register_pending(ws, token_hash, account_id, api_token_id, socket_abort_controller);
437
488
  captured_connection_id = connection_id;
489
+ // Born closed — the server is shutting down, and the transport has
490
+ // already closed the socket with `WS_CLOSE_GOING_AWAY` — or ended
491
+ // otherwise before the re-read: nothing to re-read for. The removal
492
+ // is a no-op for a born-closed registration, which was never inserted.
493
+ if (socket_abort_controller.signal.aborted) {
494
+ transport.remove_connection(connection_id);
495
+ log.info(transport.is_closing()
496
+ ? 'ws upgrade admission: refused, the server is shutting down'
497
+ : 'ws upgrade admission: socket ended before the re-check', connection_id, account_id);
498
+ return null;
499
+ }
438
500
  if (!upgrade_preset) {
439
501
  let credential_is_live;
440
502
  try {
@@ -443,8 +505,15 @@ export const register_action_ws = (options) => {
443
505
  credential_is_live = await revalidate_resolved_auth({ db }, resolved);
444
506
  }
445
507
  catch (error) {
446
- // fail closed — never admitted unchecked
447
- log.error('ws upgrade admission: credential re-check failed', error);
508
+ // fail closed — never admitted unchecked. During shutdown the
509
+ // database may close under the re-read; that is the shutdown's
510
+ // refusal, not a failure.
511
+ if (transport.is_closing()) {
512
+ log.info('ws upgrade admission: re-check ended by the shutdown', error);
513
+ }
514
+ else {
515
+ log.error('ws upgrade admission: credential re-check failed', error);
516
+ }
448
517
  refuse_upgrade(ws, WS_CLOSE_INTERNAL_ERROR, 'internal error');
449
518
  return null;
450
519
  }
@@ -457,7 +526,12 @@ export const register_action_ws = (options) => {
457
526
  // The cap's eviction happens here, after every gate and the re-read,
458
527
  // so a refused upgrade never closes someone else's socket.
459
528
  if (!transport.admit(connection_id)) {
460
- log.info('ws upgrade admission: closed by a revocation while pending', connection_id, account_id);
529
+ // chosen from the transport's state now, not why the entry closed — the
530
+ // abort carries no per-entry reason, so an entry a revocation closed logs as
531
+ // closed by the shutdown when the shutdown began before this check
532
+ log.info(transport.is_closing()
533
+ ? 'ws upgrade admission: closed by the shutdown while pending'
534
+ : 'ws upgrade admission: closed by a revocation while pending', connection_id, account_id);
461
535
  refuse_upgrade(ws, WS_CLOSE_SESSION_REVOKED, WS_CLOSE_SESSION_REVOKED_REASON);
462
536
  return null;
463
537
  }
@@ -614,6 +688,24 @@ export const register_action_ws = (options) => {
614
688
  return;
615
689
  }
616
690
  const { method, id, params } = envelope.data;
691
+ // A request is live from here until its reply is sent. One reusing
692
+ // a live id is refused rather than dispatched: two requests under
693
+ // one id would share a cancel entry and a reply id, and the first to
694
+ // finish would clear the other's. Checked before the ceiling — a
695
+ // duplicate is the client's error whatever the load, and retrying it
696
+ // cannot succeed while the first runs. Neither refusal reaches
697
+ // `perform_action`, so neither charges the action rate limiters —
698
+ // the same on the Rust spine.
699
+ if (pending_controllers.has(id)) {
700
+ send_error_response(ws, id, jsonrpc_error_messages.invalid_request({ reason: ERROR_DUPLICATE_REQUEST_ID }));
701
+ return;
702
+ }
703
+ // The in-flight ceiling: shed, don't queue — see
704
+ // `MAX_INBOUND_DISPATCHES_PER_CONNECTION`.
705
+ if (pending_controllers.size >= MAX_INBOUND_DISPATCHES_PER_CONNECTION) {
706
+ send_error_response(ws, id, jsonrpc_error_messages.queue_overflow(WS_INBOUND_DISPATCH_OVERFLOW_MESSAGE));
707
+ return;
708
+ }
617
709
  // Per-action method lookup — return method_not_found before
618
710
  // we engage the dispatch machinery. Specs without a handler
619
711
  // (client-only / dispatcher-handled) miss action_map and
@@ -623,16 +715,14 @@ export const register_action_ws = (options) => {
623
715
  send_error_response(ws, id, jsonrpc_error_messages.method_not_found(method));
624
716
  return;
625
717
  }
626
- if (artificial_delay > 0) {
627
- log.debug(`throttling ${artificial_delay}ms`);
628
- await wait(artificial_delay);
629
- }
630
718
  // Per-request controller — fires on explicit `cancel` or when the
631
719
  // socket ends (via the socket_abort_controller chain below).
632
- // Registered before dispatch so a cancel arriving mid-handler
633
- // finds it; cleared in `finally` so late cancels for a
634
- // completed id (or a future request that reuses the id) can't
635
- // null-abort the wrong handler.
720
+ // Registered before dispatch — ahead of `artificial_delay` too, so
721
+ // the request is live (counted, and its id taken) from the moment
722
+ // it is accepted — so a cancel arriving mid-handler finds it;
723
+ // cleared in `finally` so late cancels for a completed id (or a
724
+ // future request that reuses the id) can't null-abort the wrong
725
+ // handler.
636
726
  const request_controller = new AbortController();
637
727
  pending_controllers.set(id, request_controller);
638
728
  // Per-message side-effect queues. `pending_effects` collects
@@ -669,6 +759,10 @@ export const register_action_ws = (options) => {
669
759
  const request_client = (request_method, request_params, request_options) => transport.request_connection(connection_id, request_method, request_params, request_options);
670
760
  const signal = AbortSignal.any([socket_abort_controller.signal, request_controller.signal]);
671
761
  try {
762
+ if (artificial_delay > 0) {
763
+ log.debug(`throttling ${artificial_delay}ms`);
764
+ await wait(artificial_delay);
765
+ }
672
766
  const result = await perform_action({
673
767
  action,
674
768
  raw_params: params,
@@ -1 +1 @@
1
- {"version":3,"file":"rpc_client.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/rpc_client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAQzD,OAAO,EAAsB,KAAK,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAC1F,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAM1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AAC5F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE7D;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,aAAa,GAAG,SAAS,CAAC;AAM/E,uCAAuC;AACvC,MAAM,WAAW,sBAAsB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IACnE,IAAI,EAAE,gBAAgB,CAAC;IACvB,WAAW,EAAE,sBAAsB,CAAC;IACpC;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;IACpE;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,iBAAiB,GAAI,IAAI,SAAS,MAAM,EACpD,SAAS,sBAAsB,CAAC,IAAI,CAAC,KACnC,IA4BF,CAAC;AAuDF;;;;;GAKG;AACH,MAAM,WAAW,oBAAqB,SAAQ,2BAA2B;CAAG;AA6G5E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,WAAW,CAAC,IAAI,IAAI;KAC9B,CAAC,IAAI,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAClC,GAAG,IAAI,EAAE,MAAM,KAAK,KAChB,OAAO,CAAC,MAAM,CAAC;QAAE,KAAK,EAAE,MAAM,MAAM,CAAA;KAAE,EAAE;QAAE,KAAK,EAAE,kBAAkB,CAAA;KAAE,CAAC,CAAC,GACzE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,GACnC,IAAI,CAAC,CAAC,CAAC;CACV,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,mBAAmB,GAAI,IAAI,SAAS,MAAM,EAAE,YAAY,IAAI,KAAG,WAAW,CAAC,IAAI,CA8B3F,CAAC"}
1
+ {"version":3,"file":"rpc_client.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/rpc_client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAQzD,OAAO,EAAsB,KAAK,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAC1F,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAM1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AAC5F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE7D;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,aAAa,GAAG,SAAS,CAAC;AAM/E,uCAAuC;AACvC,MAAM,WAAW,sBAAsB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IACnE,IAAI,EAAE,gBAAgB,CAAC;IACvB,WAAW,EAAE,sBAAsB,CAAC;IACpC;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;IACpE;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,iBAAiB,GAAI,IAAI,SAAS,MAAM,EACpD,SAAS,sBAAsB,CAAC,IAAI,CAAC,KACnC,IA+BF,CAAC;AAuDF;;;;;GAKG;AACH,MAAM,WAAW,oBAAqB,SAAQ,2BAA2B;CAAG;AA6G5E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,WAAW,CAAC,IAAI,IAAI;KAC9B,CAAC,IAAI,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAClC,GAAG,IAAI,EAAE,MAAM,KAAK,KAChB,OAAO,CAAC,MAAM,CAAC;QAAE,KAAK,EAAE,MAAM,MAAM,CAAA;KAAE,EAAE;QAAE,KAAK,EAAE,kBAAkB,CAAA;KAAE,CAAC,CAAC,GACzE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,GACnC,IAAI,CAAC,CAAC,CAAC;CACV,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,mBAAmB,GAAI,IAAI,SAAS,MAAM,EAAE,YAAY,IAAI,KAAG,WAAW,CAAC,IAAI,CA8B3F,CAAC"}
@@ -42,7 +42,8 @@ export declare const WS_CLOSE_CONNECTION_LIMIT = 4004;
42
42
  /**
43
43
  * WebSocket close code — the server is shutting down (RFC 6455 §7.4.1 "Going
44
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
45
+ * `AppServer.close` runs, and to an upgrade the shutdown refuses — one
46
+ * pending at the close-all, or born closed after it. Not a revocation: the credential is still good, so a
46
47
  * client reconnects under its ordinary backoff once a server is back.
47
48
  */
48
49
  export declare const WS_CLOSE_GOING_AWAY = 1001;
@@ -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;;;;;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"}
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;;;;;;GAMG;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"}
@@ -41,7 +41,8 @@ export const WS_CLOSE_CONNECTION_LIMIT = 4004;
41
41
  /**
42
42
  * WebSocket close code — the server is shutting down (RFC 6455 §7.4.1 "Going
43
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
44
+ * `AppServer.close` runs, and to an upgrade the shutdown refuses — one
45
+ * pending at the close-all, or born closed after it. Not a revocation: the credential is still good, so a
45
46
  * client reconnects under its ordinary backoff once a server is back.
46
47
  */
47
48
  export const WS_CLOSE_GOING_AWAY = 1001;
@@ -44,6 +44,22 @@
44
44
  * uses the two-phase form. The twin of the Rust `fuz_realtime`
45
45
  * `ConnectionRegistry`.
46
46
  *
47
+ * ## Shutdown
48
+ *
49
+ * `close_all_sockets` — the close `AppServer.close` runs — closes every
50
+ * connection, pending or admitted, with `WS_CLOSE_GOING_AWAY`, and leaves the
51
+ * transport **closing**: from then on every registration is born closed. A
52
+ * `register_pending` or `add_connection` inserts nothing — it aborts the
53
+ * caller's controller and closes the socket with `WS_CLOSE_GOING_AWAY` at once
54
+ * — so an upgrade still in flight when the server shuts down is closed rather
55
+ * than outliving it. An upgrade registered before the close-all is closed by
56
+ * it; one registered after is born closed; none is admitted. The flag is set
57
+ * in the same synchronous step that closes the live connections, so an `admit`
58
+ * that finds its entry gone and reads `is_closing()` sees the shutdown — a
59
+ * shutdown is never read as a revocation (a revocation the shutdown overtook
60
+ * reads as the shutdown). Closing is permanent: a closed transport hosts no
61
+ * further connections.
62
+ *
47
63
  * @module
48
64
  */
49
65
  import type { WSContext } from 'hono/ws';
@@ -142,6 +158,11 @@ export declare class BackendWebsocketTransport implements FilterableBroadcastTra
142
158
  * The caller owns the entry until it is admitted: every refusal path calls
143
159
  * `remove_connection` with the returned id.
144
160
  *
161
+ * Once the transport is closing (`close_all_sockets`) the registration is
162
+ * born closed: nothing is inserted, `abort_controller` is aborted, and `ws`
163
+ * is closed with `WS_CLOSE_GOING_AWAY` before this returns. The id names
164
+ * nothing, so `admit` refuses it (module doc, "Shutdown").
165
+ *
145
166
  * @param ws - the socket to close on revocation
146
167
  * @param token_hash - blake3 session token hash, or `null` for non-session credentials
147
168
  * @param account_id - the authenticated account
@@ -149,7 +170,8 @@ export declare class BackendWebsocketTransport implements FilterableBroadcastTra
149
170
  * @param abort_controller - the socket-scoped controller to abort when the
150
171
  * transport closes this connection (revocation or cap eviction)
151
172
  * @returns the freshly assigned `connection_id` (branded `Uuid`)
152
- * @mutates this - inserts an un-admitted entry into `#connections`
173
+ * @mutates this - inserts an un-admitted entry into `#connections`, unless
174
+ * the transport is closing
153
175
  */
154
176
  register_pending(ws: WSContext, token_hash: string | null, account_id: Uuid, api_token_id?: string | null, abort_controller?: AbortController | null): Uuid;
155
177
  /**
@@ -192,7 +214,8 @@ export declare class BackendWebsocketTransport implements FilterableBroadcastTra
192
214
  * inserted. A removed connection gets no further broadcast or peer request,
193
215
  * stops counting toward the cap at once, and has its `abort_controller`
194
216
  * aborted; its socket is closed when the close handshake completes. The new
195
- * connection is always admitted.
217
+ * connection is always admitted — unless the transport is closing: then it
218
+ * is born closed, as in `register_pending`, and evicts nothing.
196
219
  *
197
220
  * @returns the freshly assigned `connection_id` (branded `Uuid`)
198
221
  * @mutates this - inserts an admitted entry into `#connections`; past the
@@ -261,12 +284,23 @@ export declare class BackendWebsocketTransport implements FilterableBroadcastTra
261
284
  * revocation does, so its heartbeat timer stops and nothing more dispatches
262
285
  * on it. A socket whose `close` throws does not stop the loop.
263
286
  *
287
+ * Leaves the transport closing, for good: every later registration is born
288
+ * closed (module doc, "Shutdown"). A second call closes nothing.
289
+ *
264
290
  * @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`
291
+ * @mutates this - sets the transport closing, removes every connection,
292
+ * aborts its registered controller, and closes its underlying `WSContext`
293
+ * with `WS_CLOSE_GOING_AWAY`
267
294
  * @throws the first error a socket's `close` threw, after every connection was attempted
268
295
  */
269
296
  close_all_sockets(): number;
297
+ /**
298
+ * Whether `close_all_sockets` has run — the transport is shutting down and
299
+ * every registration from now on is born closed (module doc, "Shutdown").
300
+ * For telemetry and logs: `register_action_ws` reads it to say why an
301
+ * upgrade was refused.
302
+ */
303
+ is_closing(): boolean;
270
304
  send(message: JsonrpcRequest, options?: TransportSendOptions): Promise<JsonrpcResponseOrError>;
271
305
  send(message: JsonrpcNotification, options?: TransportSendOptions): Promise<JsonrpcErrorResponse | null>;
272
306
  /**
@@ -1 +1 @@
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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"}
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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"}