@fuzdev/fuz_app 0.118.0 → 0.120.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 (169) hide show
  1. package/dist/actions/CLAUDE.md +301 -73
  2. package/dist/actions/action_rpc.d.ts.map +1 -1
  3. package/dist/actions/action_rpc.js +16 -1
  4. package/dist/actions/connection_closer.d.ts +136 -51
  5. package/dist/actions/connection_closer.d.ts.map +1 -1
  6. package/dist/actions/connection_closer.js +133 -34
  7. package/dist/actions/register_action_ws.d.ts +176 -24
  8. package/dist/actions/register_action_ws.d.ts.map +1 -1
  9. package/dist/actions/register_action_ws.js +581 -241
  10. package/dist/actions/register_ws_endpoint.d.ts +1 -0
  11. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  12. package/dist/actions/register_ws_endpoint.js +1 -0
  13. package/dist/actions/socket.svelte.d.ts +43 -9
  14. package/dist/actions/socket.svelte.d.ts.map +1 -1
  15. package/dist/actions/socket.svelte.js +59 -8
  16. package/dist/actions/transports.d.ts +55 -1
  17. package/dist/actions/transports.d.ts.map +1 -1
  18. package/dist/actions/transports.js +55 -1
  19. package/dist/actions/transports_ws_auth_guard.d.ts +53 -67
  20. package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
  21. package/dist/actions/transports_ws_auth_guard.js +87 -111
  22. package/dist/actions/transports_ws_backend.d.ts +212 -30
  23. package/dist/actions/transports_ws_backend.d.ts.map +1 -1
  24. package/dist/actions/transports_ws_backend.js +327 -70
  25. package/dist/actions/ws_endpoint_spec.d.ts +26 -13
  26. package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
  27. package/dist/auth/CLAUDE.md +113 -32
  28. package/dist/auth/account_actions.d.ts +12 -18
  29. package/dist/auth/account_actions.d.ts.map +1 -1
  30. package/dist/auth/account_actions.js +47 -33
  31. package/dist/auth/account_routes.d.ts +5 -11
  32. package/dist/auth/account_routes.d.ts.map +1 -1
  33. package/dist/auth/account_routes.js +40 -36
  34. package/dist/auth/account_schema.d.ts +27 -0
  35. package/dist/auth/account_schema.d.ts.map +1 -1
  36. package/dist/auth/account_schema.js +22 -0
  37. package/dist/auth/admin_actions.d.ts +8 -17
  38. package/dist/auth/admin_actions.d.ts.map +1 -1
  39. package/dist/auth/admin_actions.js +33 -21
  40. package/dist/auth/api_token_queries.d.ts +23 -2
  41. package/dist/auth/api_token_queries.d.ts.map +1 -1
  42. package/dist/auth/api_token_queries.js +39 -3
  43. package/dist/auth/audit_emitter.d.ts +61 -38
  44. package/dist/auth/audit_emitter.d.ts.map +1 -1
  45. package/dist/auth/audit_emitter.js +57 -14
  46. package/dist/auth/audit_log_route_schema.js +7 -7
  47. package/dist/auth/audit_log_routes.d.ts +31 -6
  48. package/dist/auth/audit_log_routes.d.ts.map +1 -1
  49. package/dist/auth/audit_log_routes.js +85 -11
  50. package/dist/auth/audit_log_schema.d.ts +48 -0
  51. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  52. package/dist/auth/audit_log_schema.js +58 -0
  53. package/dist/auth/cleanup.d.ts +203 -28
  54. package/dist/auth/cleanup.d.ts.map +1 -1
  55. package/dist/auth/cleanup.js +222 -26
  56. package/dist/auth/deps.d.ts +31 -3
  57. package/dist/auth/deps.d.ts.map +1 -1
  58. package/dist/auth/resolved_auth.d.ts +105 -0
  59. package/dist/auth/resolved_auth.d.ts.map +1 -0
  60. package/dist/auth/resolved_auth.js +124 -0
  61. package/dist/auth/session_middleware.d.ts +8 -1
  62. package/dist/auth/session_middleware.d.ts.map +1 -1
  63. package/dist/auth/session_middleware.js +9 -3
  64. package/dist/auth/session_queries.d.ts +31 -6
  65. package/dist/auth/session_queries.d.ts.map +1 -1
  66. package/dist/auth/session_queries.js +31 -6
  67. package/dist/auth/standard_rpc_actions.d.ts +9 -9
  68. package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
  69. package/dist/auth/standard_rpc_actions.js +3 -3
  70. package/dist/db/CLAUDE.md +19 -8
  71. package/dist/db/fact_disk_storage.d.ts +10 -8
  72. package/dist/db/fact_disk_storage.d.ts.map +1 -1
  73. package/dist/db/fact_disk_storage.js +13 -17
  74. package/dist/db/fact_store.d.ts +39 -38
  75. package/dist/db/fact_store.d.ts.map +1 -1
  76. package/dist/db/fact_store.js +48 -86
  77. package/dist/db/file_fact_url.d.ts +6 -6
  78. package/dist/db/file_fact_url.js +6 -6
  79. package/dist/db/status.d.ts +58 -1
  80. package/dist/db/status.d.ts.map +1 -1
  81. package/dist/db/status.js +205 -77
  82. package/dist/http/CLAUDE.md +7 -3
  83. package/dist/http/db_routes.d.ts +6 -2
  84. package/dist/http/db_routes.d.ts.map +1 -1
  85. package/dist/http/db_routes.js +2 -2
  86. package/dist/periodic.d.ts +80 -0
  87. package/dist/periodic.d.ts.map +1 -0
  88. package/dist/periodic.js +122 -0
  89. package/dist/realtime/sse_auth_guard.d.ts +79 -49
  90. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  91. package/dist/realtime/sse_auth_guard.js +103 -73
  92. package/dist/realtime/subscriber_registry.d.ts +119 -8
  93. package/dist/realtime/subscriber_registry.d.ts.map +1 -1
  94. package/dist/realtime/subscriber_registry.js +184 -25
  95. package/dist/server/app_backend.d.ts +12 -1
  96. package/dist/server/app_backend.d.ts.map +1 -1
  97. package/dist/server/app_backend.js +5 -2
  98. package/dist/server/app_server.d.ts +55 -15
  99. package/dist/server/app_server.d.ts.map +1 -1
  100. package/dist/server/app_server.js +41 -13
  101. package/dist/server/serve_fact_route.d.ts +10 -7
  102. package/dist/server/serve_fact_route.d.ts.map +1 -1
  103. package/dist/server/serve_fact_route.js +11 -8
  104. package/dist/testing/CLAUDE.md +140 -48
  105. package/dist/testing/app_server.d.ts +2 -2
  106. package/dist/testing/app_server.d.ts.map +1 -1
  107. package/dist/testing/app_server.js +11 -2
  108. package/dist/testing/audit_drift_guard.d.ts +1 -48
  109. package/dist/testing/audit_drift_guard.d.ts.map +1 -1
  110. package/dist/testing/audit_drift_guard.js +0 -42
  111. package/dist/testing/connection_closer_helpers.d.ts +9 -22
  112. package/dist/testing/connection_closer_helpers.d.ts.map +1 -1
  113. package/dist/testing/connection_closer_helpers.js +11 -17
  114. package/dist/testing/cross_backend/capabilities.d.ts +67 -13
  115. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  116. package/dist/testing/cross_backend/capabilities.js +27 -12
  117. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  118. package/dist/testing/cross_backend/default_backend_configs.js +21 -0
  119. package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
  120. package/dist/testing/cross_backend/role_grant_offer_notification_ws.js +13 -4
  121. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
  122. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +4 -1
  123. package/dist/testing/cross_backend/session_cap.d.ts +13 -0
  124. package/dist/testing/cross_backend/session_cap.d.ts.map +1 -1
  125. package/dist/testing/cross_backend/session_cap.js +55 -19
  126. package/dist/testing/cross_backend/sse_round_trip.d.ts +10 -2
  127. package/dist/testing/cross_backend/sse_round_trip.d.ts.map +1 -1
  128. package/dist/testing/cross_backend/sse_round_trip.js +93 -26
  129. package/dist/testing/cross_backend/testing_server_bun.js +4 -4
  130. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
  131. package/dist/testing/cross_backend/ts_spine_backend_config.js +10 -2
  132. package/dist/testing/cross_backend/ws_connection_cap.d.ts +35 -0
  133. package/dist/testing/cross_backend/ws_connection_cap.d.ts.map +1 -0
  134. package/dist/testing/cross_backend/ws_connection_cap.js +131 -0
  135. package/dist/testing/cross_backend/ws_round_trip.d.ts +2 -1
  136. package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
  137. package/dist/testing/cross_backend/ws_round_trip.js +156 -19
  138. package/dist/testing/sse_round_trip.d.ts +6 -4
  139. package/dist/testing/sse_round_trip.d.ts.map +1 -1
  140. package/dist/testing/stubs.d.ts +1 -1
  141. package/dist/testing/stubs.d.ts.map +1 -1
  142. package/dist/testing/stubs.js +6 -4
  143. package/dist/testing/transports/sse_transport.d.ts +1 -1
  144. package/dist/testing/transports/ws_client.d.ts +7 -0
  145. package/dist/testing/transports/ws_client.d.ts.map +1 -1
  146. package/dist/testing/transports/ws_raw_client.d.ts +51 -0
  147. package/dist/testing/transports/ws_raw_client.d.ts.map +1 -0
  148. package/dist/testing/transports/ws_raw_client.js +156 -0
  149. package/dist/testing/transports/ws_transport.d.ts +28 -0
  150. package/dist/testing/transports/ws_transport.d.ts.map +1 -1
  151. package/dist/testing/transports/ws_transport.js +45 -1
  152. package/dist/testing/ws_round_trip.d.ts +8 -3
  153. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  154. package/dist/testing/ws_round_trip.js +19 -4
  155. package/dist/ui/AdminAccounts.svelte +4 -3
  156. package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
  157. package/dist/ui/AdminOverview.svelte +3 -1
  158. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
  159. package/dist/ui/CLAUDE.md +22 -6
  160. package/dist/ui/admin_accounts_state.svelte.d.ts +15 -1
  161. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
  162. package/dist/ui/admin_accounts_state.svelte.js +16 -0
  163. package/dist/ui/auth_state.svelte.d.ts +25 -1
  164. package/dist/ui/auth_state.svelte.d.ts.map +1 -1
  165. package/dist/ui/auth_state.svelte.js +28 -1
  166. package/package.json +2 -2
  167. package/dist/server/file_fact_fetcher.d.ts +0 -42
  168. package/dist/server/file_fact_fetcher.d.ts.map +0 -1
  169. package/dist/server/file_fact_fetcher.js +0 -60
@@ -24,6 +24,64 @@
24
24
  * runs inside `perform_action` on every message via the same gates HTTP
25
25
  * RPC uses.
26
26
  *
27
+ * ## Admission: the revocation race
28
+ *
29
+ * The auth middleware resolves the credential before the handshake, and the
30
+ * socket exists only once `onOpen` fires. Every revocation (session revoke,
31
+ * logout, password change, token revoke, account delete) closes the sockets
32
+ * *registered* under what it revoked, so one landing in that gap would close
33
+ * nothing, and the socket would then be admitted on the dead credential and
34
+ * keep it: per-message dispatch re-authorizes the *actor*, never the session
35
+ * or token — and its admission could evict a live socket of the same account
36
+ * past the cap.
37
+ *
38
+ * `onOpen` closes the gap in three steps: register the connection **pending**
39
+ * (`BackendWebsocketTransport.register_pending` — closeable by every
40
+ * revocation, inert otherwise), re-read the credential
41
+ * (`revalidate_resolved_auth`), then `BackendWebsocketTransport.admit`. A
42
+ * revocation whose close ran after the registration found the pending entry,
43
+ * so `admit` refuses; one that committed before the re-read is seen by it. The
44
+ * cap is enforced only by `admit`, so a refused upgrade evicts nothing, and
45
+ * pending registrations never count toward it.
46
+ *
47
+ * That argument needs every revocation to close only after its commit —
48
+ * otherwise a close could miss the registration while the re-read still sees
49
+ * the credential alive. Every one does: the revocation handlers queue their
50
+ * close behind the commit (`queue_connection_close`), and the audit emitter
51
+ * announces a success row to the listeners behind it too. So no interleaving
52
+ * admits a revoked credential; `docs/security.md` §Connection Admission says
53
+ * what that rests on.
54
+ *
55
+ * A refused upgrade is closed with `WS_CLOSE_SESSION_REVOKED` (4001, the
56
+ * reason a revoked socket gets), or `WS_CLOSE_INTERNAL_ERROR` (1011) when the
57
+ * re-read itself failed — fail closed, never admitted unchecked. The handshake
58
+ * has already answered `101`, so a refusal can only be a close frame.
59
+ *
60
+ * Frames that arrive before admission are queued, not dispatched: nothing runs
61
+ * on a credential that has not been re-read. They dispatch in arrival order
62
+ * once the socket is admitted and `on_socket_open` has completed, and are
63
+ * dropped unread on a refusal. The queue holds at most
64
+ * `MAX_PRE_ADMISSION_FRAMES` frames and
65
+ * `PRE_ADMISSION_QUEUE_BYTES_FACTOR` × `max_message_bytes` bytes; past either
66
+ * the socket is closed.
67
+ *
68
+ * The upgrade's role gate is *not* re-read: a role revocation closes no
69
+ * WebSocket in the first place (per-message dispatch re-reads role grants).
70
+ *
71
+ * ## A server-closed socket dispatches nothing
72
+ *
73
+ * A runtime adapter can keep delivering inbound frames after the server calls
74
+ * `ws.close(…)`, until the close handshake completes (`@hono/node-ws` does —
75
+ * up to the `ws` close timeout when the client withholds its close frame).
76
+ * So every server-side close — revocation, cap eviction, heartbeat timeout,
77
+ * an oversized message, a refused admission — removes the connection from the
78
+ * transport and aborts the socket's signal *at close time*, and `onMessage`
79
+ * drops any frame on a socket that has ended. A connection the transport no
80
+ * longer holds though nothing closed its socket (a bare `remove_connection`)
81
+ * is ended on its next frame (`BackendWebsocketTransport.is_registered`), with
82
+ * `WS_CLOSE_INTERNAL_ERROR` — it must not linger as a socket that reads
83
+ * everything and answers nothing.
84
+ *
27
85
  * @module
28
86
  */
29
87
  import type { Hono } from 'hono';
@@ -33,22 +91,65 @@ import type { Uuid } from '@fuzdev/fuz_util/id.ts';
33
91
  import type { RateLimiter } from '../rate_limiter.js';
34
92
  import type { Db } from '../db/db.js';
35
93
  import { type Action } from './action_types.js';
94
+ import type { RealtimeCloser } from './connection_closer.js';
36
95
  import { BackendWebsocketTransport, type ConnectionIdentity } from './transports_ws_backend.js';
37
96
  export type { Action };
38
97
  /** Default inactivity window before the server closes a silent socket. */
39
98
  export declare const DEFAULT_SERVER_HEARTBEAT_TIMEOUT = 60000;
99
+ /**
100
+ * Max inbound frames one socket may have queued while it is still opening —
101
+ * between the handshake and the end of the credential re-check plus
102
+ * `on_socket_open`. One more closes the socket with
103
+ * `WS_CLOSE_POLICY_VIOLATION`.
104
+ *
105
+ * Frames sent before the socket is open wait rather than dispatch (nothing
106
+ * runs on a credential that has not been re-read), and a runtime adapter hands
107
+ * them over with no backpressure, so the wait needs a bound. This is the count
108
+ * half; `PRE_ADMISSION_QUEUE_BYTES_FACTOR` is the byte half.
109
+ *
110
+ * Sized so a client flushing its durable queue on reopen never meets it at the
111
+ * default `DEFAULT_QUEUE_MAX_SIZE` (`actions/socket.svelte.ts`). A consumer
112
+ * that raises the client's queue above this cap makes a full flush overflow
113
+ * it: the socket closes with `WS_CLOSE_POLICY_VIOLATION`, the flushed requests
114
+ * fail, and the client reconnects.
115
+ */
116
+ export declare const MAX_PRE_ADMISSION_FRAMES = 256;
117
+ /**
118
+ * The byte budget of one socket's pre-admission queue, as a multiple of the
119
+ * endpoint's `max_message_bytes`: the frames queued while the socket is still
120
+ * opening may total at most this many maximum-size messages. The frame that
121
+ * would pass it closes the socket with `WS_CLOSE_POLICY_VIOLATION`.
122
+ *
123
+ * The count cap alone (`MAX_PRE_ADMISSION_FRAMES`) would let each pending
124
+ * socket hold that many full-size messages, and pending sockets are not
125
+ * capped per account — a registration that is never admitted must not be able
126
+ * to pin that much memory. Small requests, which is what a client flushes on
127
+ * reopen, meet the count cap long before this one.
128
+ *
129
+ * A client whose queued requests total more than this budget and flushes
130
+ * them all on reopen overflows it: the socket closes, the flushed requests
131
+ * fail, and the client reconnects.
132
+ */
133
+ export declare const PRE_ADMISSION_QUEUE_BYTES_FACTOR = 4;
134
+ /**
135
+ * The close reason sent with `WS_CLOSE_POLICY_VIOLATION` when a socket
136
+ * overflows its pre-admission queue, by count or by bytes.
137
+ */
138
+ export declare const WS_CLOSE_PRE_ADMISSION_OVERFLOW_REASON = "too much sent before the socket opened";
40
139
  /**
41
140
  * Context passed to the `on_socket_open` hook.
42
141
  *
43
- * Fires after the transport has registered the new connection (so
44
- * `connection_id` is valid) but before any client message can dispatch.
45
- * Consumers use this to bootstrap per-socket domain state — e.g.
46
- * spawning a per-account unit and pushing an initial state snapshot.
142
+ * Fires once the connection is **admitted** — registered in the transport and
143
+ * its credential re-checked (so `connection_id` is valid, and broadcasts reach
144
+ * it) — and before any client message dispatches. A socket refused at
145
+ * admission never reaches it. Consumers use this to bootstrap per-socket
146
+ * domain state — e.g. spawning a per-account unit and pushing an initial state
147
+ * snapshot.
47
148
  */
48
149
  export interface SocketOpenContext {
49
150
  /** The raw WebSocket context — exposed for edge cases; prefer `notify` for sends. */
50
151
  ws: WSContext;
51
- /** Connection id assigned by `BackendWebsocketTransport.add_connection`. */
152
+ /** Connection id assigned by `BackendWebsocketTransport.register_pending`. */
52
153
  connection_id: Uuid;
53
154
  /** Auth identity registered for this connection. */
54
155
  identity: ConnectionIdentity;
@@ -57,23 +158,36 @@ export interface SocketOpenContext {
57
158
  * on per-message handler contexts — same socket-scoped semantics.
58
159
  */
59
160
  notify: (method: string, params: unknown) => void;
60
- /** Fires when this socket closes — threaded through to every handler's `ctx.signal`. */
161
+ /**
162
+ * Fires when this socket ends — the client closed it, or the server did
163
+ * (revocation, cap eviction, heartbeat timeout), at the moment the server
164
+ * closes rather than when the close handshake lands. Threaded through to
165
+ * every handler's `ctx.signal`.
166
+ */
61
167
  signal: AbortSignal;
62
168
  }
63
169
  /**
64
170
  * Context passed to the `on_socket_close` hook.
65
171
  *
66
- * Fires before `transport.remove_connection` runs, so consumer cleanup can
67
- * still read identity before it's torn down. Fires for both client-initiated
68
- * closes (Hono onClose) and server-initiated closes via audit revocation
69
- * (the audit guard calls `ws.close()`, which triggers Hono's onClose).
172
+ * Fires after the transport has removed the connection — `connection_id` and
173
+ * `identity` are the values captured at open, so consumer cleanup reads them
174
+ * from here, not from the transport. Fires for both client-initiated closes
175
+ * (Hono onClose) and server-initiated closes — a revocation, the per-account
176
+ * connection cap's eviction, and a heartbeat timeout all call `ws.close()`,
177
+ * which triggers Hono's onClose. Fires only for a socket that was admitted: one
178
+ * refused at admission never ran `on_socket_open`, so it has no close hook
179
+ * either.
70
180
  */
71
181
  export interface SocketCloseContext {
72
- /** The raw WebSocket context at close time. */
182
+ /**
183
+ * The WebSocket context the close event carried. Not necessarily the object
184
+ * `on_socket_open` received — an adapter may build one per event — so key
185
+ * per-socket state on `connection_id`.
186
+ */
73
187
  ws: WSContext;
74
188
  /** Connection id captured at open time. */
75
189
  connection_id: Uuid;
76
- /** Auth identity captured at open time — still valid even if the transport already cleaned up. */
190
+ /** Auth identity captured at open time — the transport no longer holds it. */
77
191
  identity: ConnectionIdentity;
78
192
  }
79
193
  export interface ServerHeartbeatOptions {
@@ -119,6 +233,27 @@ export interface RegisterActionWsOptions {
119
233
  * handle for `create_ws_auth_guard` and `send_to`/`broadcast`.
120
234
  */
121
235
  transport?: BackendWebsocketTransport;
236
+ /**
237
+ * The backend's closer — `deps.connection_closer`. This endpoint's
238
+ * transport is added to it, so every revocation handler's close reaches
239
+ * the sockets opened here. Required so it can't be forgotten: left out of
240
+ * the closer, a transport's sockets close only through the audit listener,
241
+ * which a failed audit write or a session / token cap eviction never
242
+ * reaches. Pass `null` only when no revocation has to reach this transport
243
+ * (a harness driving the dispatcher directly), or when the transport was
244
+ * added by hand.
245
+ */
246
+ connection_closer: RealtimeCloser | null;
247
+ /**
248
+ * Per-account connection cap for the transport this call creates — see
249
+ * `BackendWebsocketTransportOptions.max_connections_per_account`
250
+ * (evict-oldest, closing with `WS_CLOSE_CONNECTION_LIMIT`; `null`
251
+ * disables). Rejected alongside `transport`: a supplied transport carries
252
+ * its own cap, set where it was constructed.
253
+ *
254
+ * @default DEFAULT_MAX_CONNECTIONS_PER_ACCOUNT
255
+ */
256
+ max_connections_per_account?: number | null;
122
257
  /**
123
258
  * Server-side heartbeat policy. Default-on (receive-silence detection,
124
259
  * 60s timeout). `false` disables the timer entirely — only do this if
@@ -144,17 +279,24 @@ export interface RegisterActionWsOptions {
144
279
  /** Optional logger; defaults to `[ws]` namespace. */
145
280
  log?: LoggerType;
146
281
  /**
147
- * Called once per socket, after the transport registers the connection.
148
- * Awaited before any message is dispatched. Throwing logs an error and
149
- * closes the socket with an `internal_error` frame — a failing bootstrap
150
- * should not leave a partially-initialized socket alive.
282
+ * Called once per socket, after the connection is admitted (module doc,
283
+ * "Admission"). Awaited before any inbound frame is dispatched — every
284
+ * frame the client sends meanwhile waits in arrival order, replies to
285
+ * server-initiated requests included: a hook that awaits
286
+ * `transport.request_connection` on its own connection gets no reply until
287
+ * it returns, so start such a request without awaiting it. Throwing logs an
288
+ * error and closes the socket with an `internal_error` frame and
289
+ * `WS_CLOSE_INTERNAL_ERROR` — a failing bootstrap should not leave a
290
+ * partially-initialized socket alive.
151
291
  */
152
292
  on_socket_open?: (ctx: SocketOpenContext) => void | Promise<void>;
153
293
  /**
154
- * Called once per socket on close, *before* the transport removes the
155
- * connection. Receives `connection_id` and `identity` captured at open
156
- * time, so it is safe to read even when the audit guard has already torn
157
- * down the transport's internal state. Errors are logged and swallowed.
294
+ * Called once per admitted socket on close, *after* the transport has
295
+ * removed the connection — a slow hook never holds a dead entry that
296
+ * broadcasts still target and the per-account cap still counts. Receives
297
+ * `connection_id` and `identity` captured at open time, the same whether the
298
+ * close came from the client, a revocation, or a connection-cap eviction.
299
+ * Errors are logged and swallowed.
158
300
  */
159
301
  on_socket_close?: (ctx: SocketCloseContext) => void | Promise<void>;
160
302
  /**
@@ -193,14 +335,24 @@ export interface RegisterActionWsResult {
193
335
  * wrap iff `spec.side_effects: true`) → DEV output validation.
194
336
  * - Authorization phase runs **per message** — role_grant changes during a
195
337
  * connection lifetime are picked up on the next message without any
196
- * in-place refresh. Authentication invalidation closes the socket via
197
- * `create_ws_auth_guard`.
338
+ * in-place refresh. Authentication invalidation closes the socket: the
339
+ * revocation handlers through `connection_closer`, and `create_ws_auth_guard`
340
+ * on the audit row.
341
+ * - Admission re-reads the credential once, after the handshake: a socket
342
+ * whose session or token was revoked while it was upgrading closes with
343
+ * `WS_CLOSE_SESSION_REVOKED`, and one whose re-check failed with
344
+ * `WS_CLOSE_INTERNAL_ERROR`. Frames sent before admission wait for it.
345
+ * - A socket the server has closed dispatches nothing more, whether or not
346
+ * its client answers the close.
198
347
  *
199
348
  * @returns the transport (supplied or freshly created) — retain it to wire
200
349
  * `create_ws_auth_guard` or broadcast on audit events.
201
350
  * @mutates options.app - registers a `GET path` route via `upgradeWebSocket`
202
- * @mutates options.transport - on every message, adds/removes connections
203
- * in the transport's internal maps via `add_connection` / `remove_connection`
351
+ * @mutates options.transport - per socket, registers, admits, and removes a
352
+ * connection via `register_pending` / `admit` / `remove_connection`
353
+ * @mutates options.connection_closer - adds the endpoint's transport
354
+ * @throws Error when `max_connections_per_account` is passed alongside
355
+ * `transport`, or is neither `null` nor a positive integer
204
356
  */
205
357
  export declare const register_action_ws: (options: RegisterActionWsOptions) => RegisterActionWsResult;
206
358
  //# sourceMappingURL=register_action_ws.d.ts.map
@@ -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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"}