@fuzdev/fuz_app 0.113.0 → 0.115.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 (129) hide show
  1. package/dist/actions/CLAUDE.md +46 -20
  2. package/dist/actions/action_dispatcher.d.ts +1 -1
  3. package/dist/actions/action_dispatcher.d.ts.map +1 -1
  4. package/dist/actions/action_dispatcher.js +22 -16
  5. package/dist/actions/action_event.d.ts +18 -2
  6. package/dist/actions/action_event.d.ts.map +1 -1
  7. package/dist/actions/action_event.js +63 -19
  8. package/dist/actions/action_event_types.d.ts +22 -0
  9. package/dist/actions/action_event_types.d.ts.map +1 -1
  10. package/dist/actions/action_event_types.js +19 -0
  11. package/dist/actions/compile_action_registry.d.ts +4 -2
  12. package/dist/actions/compile_action_registry.d.ts.map +1 -1
  13. package/dist/actions/compile_action_registry.js +8 -3
  14. package/dist/actions/frontend_rpc_client.d.ts +6 -0
  15. package/dist/actions/frontend_rpc_client.d.ts.map +1 -1
  16. package/dist/actions/frontend_rpc_client.js +2 -1
  17. package/dist/actions/heartbeat.d.ts +8 -4
  18. package/dist/actions/heartbeat.d.ts.map +1 -1
  19. package/dist/actions/heartbeat.js +8 -4
  20. package/dist/actions/peer_ping.d.ts.map +1 -1
  21. package/dist/actions/peer_ping.js +3 -1
  22. package/dist/actions/perform_action.d.ts +1 -1
  23. package/dist/actions/perform_action.d.ts.map +1 -1
  24. package/dist/actions/perform_action.js +6 -4
  25. package/dist/actions/register_action_ws.d.ts +13 -0
  26. package/dist/actions/register_action_ws.d.ts.map +1 -1
  27. package/dist/actions/register_action_ws.js +23 -2
  28. package/dist/actions/rpc_client.d.ts +1 -1
  29. package/dist/actions/rpc_client.d.ts.map +1 -1
  30. package/dist/actions/rpc_client.js +2 -1
  31. package/dist/actions/socket.svelte.d.ts +53 -10
  32. package/dist/actions/socket.svelte.d.ts.map +1 -1
  33. package/dist/actions/socket.svelte.js +84 -23
  34. package/dist/actions/transports.d.ts +24 -0
  35. package/dist/actions/transports.d.ts.map +1 -1
  36. package/dist/actions/transports.js +30 -0
  37. package/dist/actions/transports_http.d.ts +11 -4
  38. package/dist/actions/transports_http.d.ts.map +1 -1
  39. package/dist/actions/transports_http.js +63 -18
  40. package/dist/actions/transports_ws.d.ts +28 -3
  41. package/dist/actions/transports_ws.d.ts.map +1 -1
  42. package/dist/actions/transports_ws.js +33 -4
  43. package/dist/actions/ws_endpoint_spec.d.ts +7 -0
  44. package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
  45. package/dist/auth/CLAUDE.md +52 -8
  46. package/dist/auth/admin_actions.d.ts.map +1 -1
  47. package/dist/auth/admin_actions.js +4 -3
  48. package/dist/auth/audit_log_schema.d.ts +3 -1
  49. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  50. package/dist/auth/audit_log_schema.js +13 -3
  51. package/dist/auth/cell_action_specs.js +1 -1
  52. package/dist/auth/cell_actions.d.ts.map +1 -1
  53. package/dist/auth/cell_actions.js +33 -4
  54. package/dist/auth/cell_authorize.d.ts +3 -0
  55. package/dist/auth/cell_authorize.d.ts.map +1 -1
  56. package/dist/auth/cell_authorize.js +7 -4
  57. package/dist/auth/request_context.d.ts +12 -3
  58. package/dist/auth/request_context.d.ts.map +1 -1
  59. package/dist/auth/request_context.js +12 -3
  60. package/dist/auth/role_grant_offer_action_specs.d.ts +30 -5
  61. package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
  62. package/dist/auth/role_grant_offer_action_specs.js +53 -17
  63. package/dist/auth/role_grant_offer_actions.d.ts +23 -12
  64. package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
  65. package/dist/auth/role_grant_offer_actions.js +132 -38
  66. package/dist/auth/role_grant_offer_queries.d.ts +36 -0
  67. package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
  68. package/dist/auth/role_grant_offer_queries.js +76 -0
  69. package/dist/auth/role_schema.d.ts +12 -0
  70. package/dist/auth/role_schema.d.ts.map +1 -1
  71. package/dist/auth/role_schema.js +12 -0
  72. package/dist/auth/self_service_role_actions.d.ts +14 -0
  73. package/dist/auth/self_service_role_actions.d.ts.map +1 -1
  74. package/dist/auth/self_service_role_actions.js +25 -1
  75. package/dist/http/CLAUDE.md +7 -3
  76. package/dist/http/jsonrpc_helpers.d.ts +0 -5
  77. package/dist/http/jsonrpc_helpers.d.ts.map +1 -1
  78. package/dist/http/jsonrpc_helpers.js +0 -13
  79. package/dist/http/schema_helpers.d.ts +56 -0
  80. package/dist/http/schema_helpers.d.ts.map +1 -1
  81. package/dist/http/schema_helpers.js +91 -0
  82. package/dist/server/app_server.d.ts.map +1 -1
  83. package/dist/server/app_server.js +1 -0
  84. package/dist/server/serve_fact_route.d.ts +3 -1
  85. package/dist/server/serve_fact_route.d.ts.map +1 -1
  86. package/dist/server/serve_fact_route.js +7 -4
  87. package/dist/testing/CLAUDE.md +29 -16
  88. package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
  89. package/dist/testing/cross_backend/cell_gated_create.js +23 -1
  90. package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
  91. package/dist/testing/cross_backend/peer_ping_ws.js +18 -0
  92. package/dist/testing/cross_backend/role_grant_participation.d.ts.map +1 -1
  93. package/dist/testing/cross_backend/role_grant_participation.js +34 -0
  94. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +12 -1
  95. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
  96. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +13 -1
  97. package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts.map +1 -1
  98. package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +9 -7
  99. package/dist/testing/cross_backend/ws_round_trip.d.ts +11 -3
  100. package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
  101. package/dist/testing/cross_backend/ws_round_trip.js +74 -12
  102. package/dist/testing/entities.d.ts +6 -3
  103. package/dist/testing/entities.d.ts.map +1 -1
  104. package/dist/testing/entities.js +4 -1
  105. package/dist/testing/transports/ws_client.d.ts +6 -0
  106. package/dist/testing/transports/ws_client.d.ts.map +1 -1
  107. package/dist/testing/transports/ws_transport.d.ts.map +1 -1
  108. package/dist/testing/transports/ws_transport.js +6 -1
  109. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  110. package/dist/testing/ws_round_trip.js +5 -0
  111. package/dist/ui/BootstrapForm.svelte +14 -10
  112. package/dist/ui/BootstrapForm.svelte.d.ts.map +1 -1
  113. package/dist/ui/CLAUDE.md +19 -7
  114. package/dist/ui/LoginForm.svelte +8 -4
  115. package/dist/ui/LoginForm.svelte.d.ts.map +1 -1
  116. package/dist/ui/RoleGrantOfferForm.svelte +29 -14
  117. package/dist/ui/RoleGrantOfferForm.svelte.d.ts +2 -3
  118. package/dist/ui/RoleGrantOfferForm.svelte.d.ts.map +1 -1
  119. package/dist/ui/SignupForm.svelte +14 -13
  120. package/dist/ui/form_state.svelte.d.ts +24 -11
  121. package/dist/ui/form_state.svelte.d.ts.map +1 -1
  122. package/dist/ui/form_state.svelte.js +38 -10
  123. package/dist/ui/role_grant_offers_state.svelte.d.ts +25 -15
  124. package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -1
  125. package/dist/ui/role_grant_offers_state.svelte.js +7 -1
  126. package/package.json +2 -22
  127. package/dist/actions/request_tracker.svelte.d.ts +0 -77
  128. package/dist/actions/request_tracker.svelte.d.ts.map +0 -1
  129. package/dist/actions/request_tracker.svelte.js +0 -169
@@ -544,16 +544,17 @@ Three layers:
544
544
  3. **Round-trip helpers** — predicates + wire-frame types live in `transports/ws_client.ts` (shared with the cross-process `ws_transport.ts` impl): `is_notification(method)`, `is_notification_with<P>(method, match)` (type-guard combinator — narrows `wait_for` return type), `is_response_for(id)`, `JsonrpcNotificationFrame<P>` / `JsonrpcSuccessResponseFrame<R>` / `JsonrpcErrorResponseFrame<D>` (typed wire-frame shapes distinct from the runtime Zod schemas in `http/jsonrpc.ts` — generic over `params` / `result` / `data` so tests narrow without casts). `build_broadcast_api<TApi>({harness, specs})` (in `ws_round_trip.ts`) wires a typed broadcast API against the harness transport.
545
545
 
546
546
  `WsClient` (in `transports/ws_client.ts`):
547
- `{send, request<R>, close, messages, wait_for, wait_for_close}`. The
548
- harness's `connect()` returns this shape; the cross-process
547
+ `{send, request<R>, close, messages, wait_for, wait_for_close, close_code}`.
548
+ The harness's `connect()` returns this shape; the cross-process
549
549
  `create_ws_transport` in `transports/ws_transport.ts` implements the same
550
550
  interface so assertion helpers and suite bodies work against either impl.
551
551
  `wait_for_close(timeout_ms?)` resolves `true` if the server closes the
552
552
  socket within the timeout, `false` on timeout (and `true` immediately when
553
553
  already closed) — the signal for server-initiated close (e.g. an auth-guard
554
554
  revocation), distinct from client-initiated `close()`. Mirrors the SSE frame
555
- reader's `wait_for_close`. `request` throws with
556
- code + message + data on error frames (so asserting `result.foo` on a
555
+ reader's `wait_for_close`. `close_code` is the connection's close code once
556
+ closed (`1006` for a drop without a close frame), `null` while open.
557
+ `request` throws with code + message + data on error frames (so asserting `result.foo` on a
557
558
  failed request surfaces the real cause, not a `Cannot read property 'foo'
558
559
  of undefined`). `wait_for(predicate, timeout_ms?)` checks already-received
559
560
  messages first, then waits for new arrivals (default 1000ms); drops the
@@ -1079,10 +1080,13 @@ call (not folded into `describe_standard_cross_process_tests`) because it
1079
1080
  needs raw `base_url` / `ws_path` the standard bundle doesn't carry, mirroring
1080
1081
  how `describe_ws_round_trip_tests` sits beside `describe_standard_tests`
1081
1082
  in-process. `describe_cross_process_ws_tests({setup_test, capabilities,
1082
- base_url, ws_path, origin?, rpc_path?})` opens a live `WebSocket` via
1083
- `create_ws_transport` (the `ws` npm package) and asserts up to four cases
1084
- against the upgrade stack `register_ws_endpoint` wires (origin →
1083
+ base_url, ws_path, origin?, rpc_path?, max_message_bytes?})` opens a live
1084
+ `WebSocket` via `create_ws_transport` (the `ws` npm package) and asserts
1085
+ cases against the upgrade stack `register_ws_endpoint` wires (origin →
1085
1086
  `require_auth` → dispatch): authed upgrade round-trips `heartbeat`,
1087
+ `heartbeat` admits only the parameterless shapes (a declared param →
1088
+ `invalid_params`), a message past `max_message_bytes` (default
1089
+ `DEFAULT_WS_MAX_MESSAGE_BYTES`) closes with `WS_CLOSE_MESSAGE_TOO_BIG`,
1086
1090
  anonymous upgrade refused, disallowed-origin upgrade refused, and — gated on
1087
1091
  `rpc_path` — a live socket drops when the account's sessions are revoked
1088
1092
  mid-connection (`account_session_revoke_all` over the keeper session channel
@@ -1144,11 +1148,12 @@ drives the whole round-trip and every outcome (success / `Timeout` / wrong-shape
1144
1148
  `on_request` responder **at construction** (the `create_ws_transport` seam) so
1145
1149
  the inbound server-initiated request is answered as soon as it arrives; the
1146
1150
  security negatives use the raw `WsClient.send` to inject unsolicited /
1147
- cross-connection frames. Seven cases: the positive round-trip plus
1151
+ cross-connection frames. Cases: the positive round-trip plus
1148
1152
  unsolicited-response rejection, per-connection id isolation, never-reply
1149
- `Timeout`, wrong-shape rejection, client-error forwarding, and the HTTP
1150
- no-transport path. Gated on `capabilities.peer_request` — `true` on both the
1151
- Rust spine and the TS spine (the `BackendWebsocketTransport.request_connection`
1153
+ `Timeout`, wrong-shape rejection, an undeclared reply key accepted and dropped
1154
+ (both spines read the reply forward-compatibly), client-error forwarding, and
1155
+ the HTTP no-transport path. Gated on `capabilities.peer_request` — `true` on
1156
+ both the Rust spine and the TS spine (the `BackendWebsocketTransport.request_connection`
1152
1157
  path + `register_action_ws` response correlation); `peer/ping` is a protocol
1153
1158
  action (manifest-excluded), so parity is behavioral here, not via the manifest
1154
1159
  gate. Cross-process only; fuz_app's own wiring is
@@ -1232,8 +1237,11 @@ the option type states whether the bundle is read.
1232
1237
  (gate on `capabilities.cell_gated_create`, **cross-only**) — the parent-aware
1233
1238
  cell-creation authorizer (`CellCreateAuthorize`, **directory model**) +
1234
1239
  the `cell_moderate` verb parity proofs. Both spines mount the same
1235
- `test_cell_gated_create_authorize` policy: a `kind: 'space'` **root** is
1236
- admin-only (a non-admin → **403** `cell_create_forbidden`); a **contribution**
1240
+ `test_cell_gated_create_authorize` policy (its admin bypass and `min_role`
1241
+ read the **global** grant): a `kind: 'space'` **root** is
1242
+ admin-only (a non-admin → **403** `cell_create_forbidden`, and the same for a
1243
+ non-admin `cell_clone` of a viewable space — clone runs the authorizer for
1244
+ every cell it writes); a **contribution**
1237
1245
  under a space is gated by the root's `data.policy[kind] = {min_role,
1238
1246
  moderation_required}`, with `moderation` set per the verdict (`pending`+private
1239
1247
  vs `approved`+author-visibility); a **hidden** parent **404**-masks vs a
@@ -1284,7 +1292,7 @@ are `src/test/auth/cell_crud_parity.db.test.ts`
1284
1292
  transport carries the keeper session cookie in its jar after this call
1285
1293
  resolves.
1286
1294
  - `testing/transports/ws_client.ts` — shared `WsClient` interface (`send` /
1287
- `request` / `close` / `messages` / `wait_for` / `wait_for_close`),
1295
+ `request` / `close` / `messages` / `wait_for` / `wait_for_close` / `close_code`),
1288
1296
  wire-frame types, and
1289
1297
  predicates (`is_notification`, `is_response_for`, ...). Both in-process
1290
1298
  (`ws_round_trip.ts`) and cross-process (`ws_transport.ts`) impls satisfy
@@ -1614,12 +1622,17 @@ non-admin _holder_ of `participant` (and a fresh non-admin) can't offer it (403
1614
1622
  `role_grant_assign` is admin-only** — non-admin holder / fresh → 403
1615
1623
  `insufficient_permissions` (dispatcher gate), anon → 401; plus the
1616
1624
  `to_actor_id`-must-belong-to-`to_account_id` resolution arm (400
1617
- `role_grant_offer_actor_account_mismatch`).
1625
+ `role_grant_offer_actor_account_mismatch`); **(d) builtin roles are
1626
+ global-only** — an unpaired `scope_kind` / `scope_id` → 400 on assign + offer,
1627
+ a scoped `admin` → 400 `role_grant_builtin_scoped` on assign + offer, a builtin
1628
+ offer from a non-global-admin → 403 `role_grant_offer_not_authorized`.
1618
1629
 
1619
1630
  `describe_role_grant_participation_cross_tests` (this module) is the **imperative
1620
1631
  escape hatch** for the multi-step success paths a static row can't express (they
1621
1632
  need a real recipient): admin **assigns** `participant` to a fresh account
1622
- (`{ok, role_grant_id}` + idempotent re-assign), and admin **offers**
1633
+ (`{ok, role_grant_id}` + idempotent re-assign), admin **assigns** a scoped
1634
+ `participant` whose `scope_kind` + `scope_id` round-trip through
1635
+ `admin_account_list` (distinct from the global grant), and admin **offers**
1623
1636
  `participant` → recipient **accepts** → a role_grant lands. The `role_holder`
1624
1637
  principal is seeded via `extra_accounts: [{username, roles: ['participant']}]`
1625
1638
  (bootstrap-cradle direct grant) and named through `principals.role_holder`.
@@ -1 +1 @@
1
- {"version":3,"file":"cell_gated_create.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_gated_create.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAiD9B,OAAO,KAAK,EAAE,uCAAuC,EAAE,MAAM,YAAY,CAAC;AAkB1E,eAAO,MAAM,sCAAsC,GAClD,SAAS,uCAAuC,KAC9C,IAwPF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,kCAAkC,GAC9C,SAAS,uCAAuC,KAC9C,IAwHF,CAAC"}
1
+ {"version":3,"file":"cell_gated_create.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_gated_create.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAyD9B,OAAO,KAAK,EAAE,uCAAuC,EAAE,MAAM,YAAY,CAAC;AAkB1E,eAAO,MAAM,sCAAsC,GAClD,SAAS,uCAAuC,KAC9C,IAwPF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,kCAAkC,GAC9C,SAAS,uCAAuC,KAC9C,IAyKF,CAAC"}
@@ -30,6 +30,9 @@ import '../assert_dev_env.js';
30
30
  * **private** space the caller can't view → **404** `cell_not_found` (the
31
31
  * parent is masked); under a **public** space → **403** (you see it, you
32
32
  * can't contribute).
33
+ * - **clone is no side door** — `cell_clone` runs the authorizer for every cell
34
+ * it writes as a parentless create, so a non-admin clone of a viewable
35
+ * `space` root → **403** `cell_create_forbidden`; admin → succeeds.
33
36
  *
34
37
  * Gated on `capabilities.cell_gated_create` — `true` only on the reference
35
38
  * spine binaries that mount the policy, so it skips for generic consumers and
@@ -41,7 +44,8 @@ import '../assert_dev_env.js';
41
44
  * @module
42
45
  */
43
46
  import { describe, assert } from 'vitest';
44
- import { CellCreateOutput, CellModerateOutput } from '../../auth/cell_action_specs.js';
47
+ import { CellCloneOutput, CellCreateOutput, CellModerateOutput } from '../../auth/cell_action_specs.js';
48
+ import { JSONRPC_ERROR_CODES } from '../../http/jsonrpc_errors.js';
45
49
  import { test_if } from './capabilities.js';
46
50
  import { cross_rpc_call, error_reason, expect_output } from './cell_cross_helpers.js';
47
51
  import { SPINE_RPC_PATH } from './spine_surface_constants.js';
@@ -211,4 +215,22 @@ export const describe_cell_moderate_cross_tests = (options) => {
211
215
  assert.strictEqual(moderated.cell.visibility, 'private', 'rejection leaves the post private');
212
216
  });
213
217
  });
218
+ describe('cell_clone runs the creation authorizer (parity)', () => {
219
+ test_if(capabilities.cell_gated_create, 'a non-admin clone of a viewable `space` root → 403, admin clone succeeds', async () => {
220
+ const fixture = await setup_test();
221
+ const space = expect_output(await cross_rpc_call(fixture.transport, rpc_path, 'cell_create', { kind: SPACE_CELL_KIND, data: SPACE_POLICY, visibility: 'public' }, fixture.create_session_headers()), CellCreateOutput);
222
+ // Every cell a clone writes is a parentless create of the source's
223
+ // kind, gated like `cell_create` — so a caller denied a `space` root
224
+ // can't clone one they can see into one they own.
225
+ const stranger = await fixture.create_account({ username: 'clone_stranger' });
226
+ const denied = await cross_rpc_call(fixture.transport, rpc_path, 'cell_clone', { source_id: space.cell.id }, stranger.create_session_headers());
227
+ assert.ok(!denied.ok, 'a non-admin must not clone a space root');
228
+ assert.strictEqual(denied.error?.code, JSONRPC_ERROR_CODES.forbidden, 'a 403');
229
+ assert.strictEqual(error_reason(denied), 'cell_create_forbidden');
230
+ const cloned = expect_output(await cross_rpc_call(fixture.transport, rpc_path, 'cell_clone', { source_id: space.cell.id }, fixture.create_session_headers()), CellCloneOutput);
231
+ assert.strictEqual(cloned.cell.kind, SPACE_CELL_KIND, 'the admin clones the space');
232
+ assert.notStrictEqual(cloned.cell.id, space.cell.id);
233
+ assert.strictEqual(cloned.cell.moderation, null, 'a clone stamps no moderation');
234
+ });
235
+ });
214
236
  };
@@ -1 +1 @@
1
- {"version":3,"file":"peer_ping_ws.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/peer_ping_ws.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAkD9B,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAuB5C,6DAA6D;AAC7D,MAAM,WAAW,qBAAqB;IACrC,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GAAI,SAAS,qBAAqB,KAAG,IAwN5E,CAAC"}
1
+ {"version":3,"file":"peer_ping_ws.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/peer_ping_ws.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAkD9B,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAuB5C,6DAA6D;AAC7D,MAAM,WAAW,qBAAqB;IACrC,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GAAI,SAAS,qBAAqB,KAAG,IA8O5E,CAAC"}
@@ -136,6 +136,24 @@ export const describe_peer_ping_ws_tests = (options) => {
136
136
  await ws.close();
137
137
  }
138
138
  });
139
+ // A newer client may add reply fields: both spines read the reply
140
+ // forward-compatibly (serde's default / `safe_parse_dropping_unknown_keys`)
141
+ // and return only the declared shape.
142
+ test_if(capabilities.peer_request, 'a reply with an undeclared key is accepted and the key is dropped', async () => {
143
+ const fixture = await setup_test();
144
+ const ws = await open_ws(fixture.create_session_headers().cookie, (req) => {
145
+ const nonce = req.params?.nonce ?? 0;
146
+ return { result: { ...valid_reply(nonce), added_later: true } };
147
+ });
148
+ try {
149
+ const frame = await ping_raw(ws, 6, { nonce: 5, timeout_ms: 2000 });
150
+ assert.ok('result' in frame, `expected success, got ${JSON.stringify(frame)}`);
151
+ assert.deepStrictEqual(frame.result, valid_reply(5));
152
+ }
153
+ finally {
154
+ await ws.close();
155
+ }
156
+ });
139
157
  test_if(capabilities.peer_request, 'a client JSON-RPC error reply surfaces as the action error (ClientError forwarded)', async () => {
140
158
  const fixture = await setup_test();
141
159
  const ws = await open_ws(fixture.create_session_headers().cookie, () => ({
@@ -1 +1 @@
1
- {"version":3,"file":"role_grant_participation.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/role_grant_participation.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAsC9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,0EAA0E;AAC1E,MAAM,WAAW,sCAAsC;IACtD,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,6CAA6C,GACzD,SAAS,sCAAsC,KAC7C,IAgFF,CAAC"}
1
+ {"version":3,"file":"role_grant_participation.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/role_grant_participation.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAyC9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,0EAA0E;AAC1E,MAAM,WAAW,sCAAsC;IACtD,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,6CAA6C,GACzD,SAAS,sCAAsC,KAC7C,IA8IF,CAAC"}
@@ -15,6 +15,9 @@ import '../assert_dev_env.js';
15
15
  * re-assigning the active grant is idempotent (same id).
16
16
  * - **consent flow** — `role_grant_offer_create` of `participant` (admin-only)
17
17
  * → the recipient `role_grant_offer_accept`s → a role_grant lands.
18
+ * - **scoped assign** — `role_grant_assign` of `participant` with a paired
19
+ * `scope_kind` + `scope_id` lands a scoped grant carrying both, read back
20
+ * through `admin_account_list`, distinct from the global grant.
18
21
  *
19
22
  * The single-request gate/denial matrix (grantability refusal, admin-only
20
23
  * conferral, dispatcher admin gate, auth) lives in the declarative table
@@ -55,6 +58,37 @@ export const describe_role_grant_participation_cross_tests = (options) => {
55
58
  assert.ok(second.ok, `re-assign must succeed: ${JSON.stringify(second)}`);
56
59
  assert.strictEqual(second.result.role_grant_id, assigned.role_grant_id, 're-assigning an active grant returns the existing role_grant id');
57
60
  });
61
+ test('admin assigns a scoped participant grant that round-trips scope_kind', async () => {
62
+ const fixture = await setup_test();
63
+ const recipient = await fixture.create_account({ username: 'participation_scoped' });
64
+ const scope_id = '33333333-3333-4333-8333-333333333333';
65
+ const scoped = await rpc(fixture.transport, 'role_grant_assign', {
66
+ to_account_id: recipient.account.id,
67
+ role: SPINE_PARTICIPANT_ROLE,
68
+ scope_kind: 'space',
69
+ scope_id
70
+ }, fixture.create_session_headers());
71
+ assert.ok(scoped.ok, `scoped assign must succeed: ${JSON.stringify(scoped)}`);
72
+ const scoped_id = scoped.result.role_grant_id;
73
+ const global = await rpc(fixture.transport, 'role_grant_assign', { to_account_id: recipient.account.id, role: SPINE_PARTICIPANT_ROLE }, fixture.create_session_headers());
74
+ assert.ok(global.ok, `global assign must succeed: ${JSON.stringify(global)}`);
75
+ const global_id = global.result.role_grant_id;
76
+ assert.notStrictEqual(global_id, scoped_id, 'the global grant is a distinct row');
77
+ const listed = await rpc(fixture.transport, 'admin_account_list', {}, fixture.create_session_headers());
78
+ assert.ok(listed.ok, `admin_account_list must succeed: ${JSON.stringify(listed)}`);
79
+ const entry = listed.result.accounts.find((a) => a.account.id === recipient.account.id);
80
+ assert.ok(entry, 'the recipient is listed');
81
+ const by_id = new Map(entry.role_grants.map((g) => [g.id, g]));
82
+ const scoped_row = by_id.get(scoped_id);
83
+ assert.ok(scoped_row, 'the scoped grant is listed');
84
+ assert.strictEqual(scoped_row.role, SPINE_PARTICIPANT_ROLE);
85
+ assert.strictEqual(scoped_row.scope_kind, 'space', 'scope_kind round-trips');
86
+ assert.strictEqual(scoped_row.scope_id, scope_id, 'scope_id round-trips');
87
+ const global_row = by_id.get(global_id);
88
+ assert.ok(global_row, 'the global grant is listed');
89
+ assert.strictEqual(global_row.scope_kind, null);
90
+ assert.strictEqual(global_row.scope_id, null);
91
+ });
58
92
  test('admin offers the participant app-role and the recipient accepts', async () => {
59
93
  const fixture = await setup_test();
60
94
  const recipient = await fixture.create_account({ username: 'participation_offeree' });
@@ -2,6 +2,14 @@ import '../assert_dev_env.js';
2
2
  import type { BackendConfig } from './backend_config.js';
3
3
  /** Env var naming the prebuilt `testing_spine_stub` binary. Required when `binary_path` is omitted. */
4
4
  export declare const RUST_SPINE_STUB_BIN_ENV = "FUZ_TESTING_RUST_SPINE_STUB_BIN";
5
+ /**
6
+ * Env var overriding the stub's Postgres URL when `database_url` is omitted —
7
+ * e.g. a scratch cluster on a spare port, keeping the run off the machine's
8
+ * default server. Pair it with the libpq `PGHOST` / `PGPORT` the runner's
9
+ * idempotent `createdb` reads, so the database it ensures is the one the URL
10
+ * names.
11
+ */
12
+ export declare const RUST_SPINE_STUB_DATABASE_URL_ENV = "FUZ_TESTING_RUST_SPINE_STUB_DATABASE_URL";
5
13
  /**
6
14
  * Env var the stub reads for the absolute path of the committed
7
15
  * `expected_schema.json` its `/ready` gate introspects against. Pointed at the
@@ -16,7 +24,10 @@ export declare const RUST_SPINE_STUB_DEFAULT_DATABASE_URL = "postgres://localhos
16
24
  export interface SpineStubBackendConfigOptions {
17
25
  /** Listening port. Default `RUST_SPINE_STUB_DEFAULT_PORT`. */
18
26
  readonly port?: number;
19
- /** Postgres connection URL. Default `RUST_SPINE_STUB_DEFAULT_DATABASE_URL`. */
27
+ /**
28
+ * Postgres connection URL. Default `$FUZ_TESTING_RUST_SPINE_STUB_DATABASE_URL`,
29
+ * then `RUST_SPINE_STUB_DEFAULT_DATABASE_URL`.
30
+ */
20
31
  readonly database_url?: string;
21
32
  /**
22
33
  * Prebuilt binary path. Overrides the `FUZ_TESTING_RUST_SPINE_STUB_BIN` env
@@ -1 +1 @@
1
- {"version":3,"file":"rust_spine_stub_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/rust_spine_stub_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAuC9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AASzD,uGAAuG;AACvG,eAAO,MAAM,uBAAuB,oCAAoC,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,wCAAwC,6CAA6C,CAAC;AAkBnG,kGAAkG;AAClG,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,0FAA0F;AAC1F,eAAO,MAAM,oCAAoC,sDACG,CAAC;AAErD,MAAM,WAAW,6BAA6B;IAC7C,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;OAOG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,GAC1C,UAAS,6BAAkC,KACzC,aAiDF,CAAC"}
1
+ {"version":3,"file":"rust_spine_stub_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/rust_spine_stub_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA0C9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AASzD,uGAAuG;AACvG,eAAO,MAAM,uBAAuB,oCAAoC,CAAC;AAEzE;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,6CAA6C,CAAC;AAE3F;;;;;GAKG;AACH,eAAO,MAAM,wCAAwC,6CAA6C,CAAC;AAkBnG,kGAAkG;AAClG,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,0FAA0F;AAC1F,eAAO,MAAM,oCAAoC,sDACG,CAAC;AAErD,MAAM,WAAW,6BAA6B;IAC7C,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;OAOG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,GAC1C,UAAS,6BAAkC,KACzC,aAkDF,CAAC"}
@@ -25,6 +25,9 @@ import '../assert_dev_env.js';
25
25
  * createdb fuz_app_test_rust_spine_stub 2>/dev/null || true
26
26
  * ```
27
27
  *
28
+ * `FUZ_TESTING_RUST_SPINE_STUB_DATABASE_URL` points the stub at another
29
+ * server (e.g. a scratch cluster on a spare port).
30
+ *
28
31
  * The binary self-wipes the auth-namespace schema on every boot
29
32
  * (`FUZ_TESTING_RESET_DB_ON_STARTUP=true`, set by the Rust-family builder),
30
33
  * so no manual `DROP TABLE` between sessions is needed; per-test reset is
@@ -39,6 +42,14 @@ import { SPINE_EXPECTED_SCHEMA_URL } from './spine_surface_constants.js';
39
42
  import { LOGIN_RATE_LIMIT_ENABLED_ENV, make_default_rust_backend_config, rust_default_capabilities } from './default_backend_configs.js';
40
43
  /** Env var naming the prebuilt `testing_spine_stub` binary. Required when `binary_path` is omitted. */
41
44
  export const RUST_SPINE_STUB_BIN_ENV = 'FUZ_TESTING_RUST_SPINE_STUB_BIN';
45
+ /**
46
+ * Env var overriding the stub's Postgres URL when `database_url` is omitted —
47
+ * e.g. a scratch cluster on a spare port, keeping the run off the machine's
48
+ * default server. Pair it with the libpq `PGHOST` / `PGPORT` the runner's
49
+ * idempotent `createdb` reads, so the database it ensures is the one the URL
50
+ * names.
51
+ */
52
+ export const RUST_SPINE_STUB_DATABASE_URL_ENV = 'FUZ_TESTING_RUST_SPINE_STUB_DATABASE_URL';
42
53
  /**
43
54
  * Env var the stub reads for the absolute path of the committed
44
55
  * `expected_schema.json` its `/ready` gate introspects against. Pointed at the
@@ -77,7 +88,8 @@ export const RUST_SPINE_STUB_DEFAULT_DATABASE_URL = 'postgres://localhost/fuz_ap
77
88
  * @throws Error when no binary path is available.
78
89
  */
79
90
  export const rust_spine_stub_backend_config = (options = {}) => {
80
- const { port = RUST_SPINE_STUB_DEFAULT_PORT, database_url = RUST_SPINE_STUB_DEFAULT_DATABASE_URL, binary_path = process.env[RUST_SPINE_STUB_BIN_ENV], enable_login_rate_limit, trusted_proxies } = options;
91
+ const { port = RUST_SPINE_STUB_DEFAULT_PORT, database_url = process.env[RUST_SPINE_STUB_DATABASE_URL_ENV] ||
92
+ RUST_SPINE_STUB_DEFAULT_DATABASE_URL, binary_path = process.env[RUST_SPINE_STUB_BIN_ENV], enable_login_rate_limit, trusted_proxies } = options;
81
93
  if (!binary_path) {
82
94
  throw new Error(`rust_spine_stub_backend_config: no binary path — set ${RUST_SPINE_STUB_BIN_ENV} to a prebuilt ` +
83
95
  '`testing_spine_stub` binary (build it with `cargo build -p testing_spine_stub --release`) ' +
@@ -1 +1 @@
1
- {"version":3,"file":"test_cell_gated_create_authorize.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/test_cell_gated_create_authorize.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA+B9B,OAAO,KAAK,EACX,mBAAmB,EAGnB,MAAM,4BAA4B,CAAC;AAGpC,8EAA8E;AAC9E,eAAO,MAAM,eAAe,UAAU,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,gBAAyB,CAAC;AAEzD,yFAAyF;AACzF,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAYD;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,EAAE,mBAoB9C,CAAC"}
1
+ {"version":3,"file":"test_cell_gated_create_authorize.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/test_cell_gated_create_authorize.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA+B9B,OAAO,KAAK,EACX,mBAAmB,EAGnB,MAAM,4BAA4B,CAAC;AAGpC,8EAA8E;AAC9E,eAAO,MAAM,eAAe,UAAU,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,gBAAyB,CAAC;AAEzD,yFAAyF;AACzF,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAYD;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,EAAE,mBAsB9C,CAAC"}
@@ -6,15 +6,15 @@ import '../assert_dev_env.js';
6
6
  * the parent-aware cell-creation authorizer agrees TS↔Rust. The twin of the
7
7
  * Rust `TestCellGatedCreateAuthorize`, **directory model**.
8
8
  *
9
- * Admin bypasses everything (`{allow: true, moderation_required: false}`). For
10
- * a non-admin:
9
+ * A **global** admin bypasses everything (`{allow: true, moderation_required:
10
+ * false}`) — a scoped `admin` row is no admin here. For anyone else:
11
11
  *
12
12
  * - **Root creation** (`root_id` null): `kind: 'space'` is admin-only (denied);
13
13
  * every other parentless kind stays open, so the plain-create `cell_crud` /
14
14
  * `cell_relations` suites are unaffected.
15
15
  * - **Contribution** (`root_id` set): the governing root's
16
16
  * `data.policy[kind] = {min_role?, moderation_required?}` decides — a missing
17
- * entry denies, a present `min_role` the actor lacks denies, and otherwise it
17
+ * entry denies, a present `min_role` the actor lacks globally denies, and otherwise it
18
18
  * admits with the entry's `moderation_required` folded into the verdict. The
19
19
  * root's `data` arrives in `input.root_data` (the handler read it in-tx), so
20
20
  * the predicate is **pure** — no DB read of its own (which also dodges the
@@ -25,7 +25,7 @@ import '../assert_dev_env.js';
25
25
  *
26
26
  * @module
27
27
  */
28
- import { has_role } from '../../auth/request_context.js';
28
+ import { has_scoped_role } from '../../auth/request_context.js';
29
29
  import { ROLE_ADMIN } from '../../auth/role_schema.js';
30
30
  import { SPINE_PARTICIPANT_ROLE } from './spine_surface_constants.js';
31
31
  /** The admin-only root kind the test policy gates (talk's `space` analog). */
@@ -54,8 +54,9 @@ const get_contribution_rule = (data, kind) => {
54
54
  * handler). Byte-equivalent with the Rust `TestCellGatedCreateAuthorize`.
55
55
  */
56
56
  export const test_cell_gated_create_authorize = (auth, input) => {
57
- // Admin bypass — admins create roots + any kind, live immediately.
58
- if (has_role(auth, ROLE_ADMIN))
57
+ // Admin bypass — admins create roots + any kind, live immediately. The
58
+ // global grant only: a scoped `admin` row bypasses nothing.
59
+ if (has_scoped_role(auth, ROLE_ADMIN, null))
59
60
  return { allow: true, moderation_required: false };
60
61
  // Root creation (no governing root): `space` is admin-only; other parentless
61
62
  // kinds stay open (the plain-create suites).
@@ -68,7 +69,8 @@ export const test_cell_gated_create_authorize = (auth, input) => {
68
69
  const rule = get_contribution_rule(input.root_data, input.kind ?? '');
69
70
  if (!rule)
70
71
  return { allow: false };
71
- const admitted = rule.min_role === undefined || has_role(auth, rule.min_role);
72
+ // `min_role` names no scope, so only the global grant satisfies it.
73
+ const admitted = rule.min_role === undefined || has_scoped_role(auth, rule.min_role, null);
72
74
  return admitted
73
75
  ? { allow: true, moderation_required: rule.moderation_required ?? false }
74
76
  : { allow: false };
@@ -24,11 +24,19 @@ export interface CrossProcessWsTestOptions {
24
24
  * actions being mounted on the RPC endpoint.
25
25
  */
26
26
  readonly rpc_path?: string;
27
+ /**
28
+ * The backend's inbound WebSocket message cap, in bytes — the
29
+ * oversized-message case sends a message just past it. Defaults to
30
+ * `DEFAULT_WS_MAX_MESSAGE_BYTES`; pass the consumer's value when it raises
31
+ * the server cap.
32
+ */
33
+ readonly max_message_bytes?: number;
27
34
  }
28
35
  /**
29
- * Register the cross-process WS round-trip suite. Up to four cases over a
30
- * real upgrade: authed `heartbeat` round-trip, anonymous-upgrade refusal,
31
- * disallowed-origin refusal, and — when `rpc_path` is supplied —
36
+ * Register the cross-process WS round-trip suite over a real upgrade: authed
37
+ * `heartbeat` round-trip, `heartbeat`'s parameterless-shape contract, an
38
+ * oversized message closing with `WS_CLOSE_MESSAGE_TOO_BIG`, anonymous-upgrade
39
+ * refusal, disallowed-origin refusal, and — when `rpc_path` is supplied —
32
40
  * session-revocation closing the live socket.
33
41
  */
34
42
  export declare const describe_cross_process_ws_tests: (options: CrossProcessWsTestOptions) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"ws_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ws_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmD9B,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAK5C,iEAAiE;AACjE,MAAM,WAAW,yBAAyB;IACzC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,GAAI,SAAS,yBAAyB,KAAG,IAsEpF,CAAC"}
1
+ {"version":3,"file":"ws_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ws_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAoE9B,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAK5C,iEAAiE;AACjE,MAAM,WAAW,yBAAyB;IACzC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,GAAI,SAAS,yBAAyB,KAAG,IAwIpF,CAAC"}
@@ -24,7 +24,13 @@ import '../assert_dev_env.js';
24
24
  * upgrade time (not per message), so the negative cases assert the upgrade
25
25
  * itself rejects rather than a per-message error frame.
26
26
  *
27
- * A fourth case (gated on `rpc_path`) covers server-initiated close: an
27
+ * Two spine-parity cases follow: `heartbeat` answers the parameterless shapes
28
+ * (absent, `{}`) and refuses a declared param with `invalid_params`, and a
29
+ * message over the backend's cap (`max_message_bytes`, default
30
+ * `DEFAULT_WS_MAX_MESSAGE_BYTES`) closes the socket with
31
+ * `WS_CLOSE_MESSAGE_TOO_BIG`.
32
+ *
33
+ * A final case (gated on `rpc_path`) covers server-initiated close: an
28
34
  * authenticated socket is dropped when the account's sessions are revoked
29
35
  * mid-connection. Per-message dispatch never re-checks credential validity,
30
36
  * so the live socket survives on the audit-fed `create_ws_auth_guard` seam —
@@ -43,29 +49,31 @@ import '../assert_dev_env.js';
43
49
  import { assert, describe } from 'vitest';
44
50
  import { assert_rejects } from '@fuzdev/fuz_util/testing.ts';
45
51
  import { heartbeat_action_spec } from '../../actions/heartbeat.js';
52
+ import { DEFAULT_WS_MAX_MESSAGE_BYTES, WS_CLOSE_MESSAGE_TOO_BIG } from '../../actions/transports.js';
46
53
  import { account_session_revoke_all_action_spec } from '../../auth/account_action_specs.js';
54
+ import { JSONRPC_ERROR_CODES } from '../../http/jsonrpc_errors.js';
55
+ import { is_response_for } from '../transports/ws_client.js';
47
56
  import { create_ws_transport } from '../transports/ws_transport.js';
48
57
  import { create_rpc_post_init } from '../rpc_helpers.js';
49
58
  import { test_if } from './capabilities.js';
50
59
  /** Origin guaranteed to fail the `http://localhost:*` allowlist the test backends run with. */
51
60
  const DISALLOWED_ORIGIN = 'http://disallowed.example';
52
61
  /**
53
- * Register the cross-process WS round-trip suite. Up to four cases over a
54
- * real upgrade: authed `heartbeat` round-trip, anonymous-upgrade refusal,
55
- * disallowed-origin refusal, and — when `rpc_path` is supplied —
62
+ * Register the cross-process WS round-trip suite over a real upgrade: authed
63
+ * `heartbeat` round-trip, `heartbeat`'s parameterless-shape contract, an
64
+ * oversized message closing with `WS_CLOSE_MESSAGE_TOO_BIG`, anonymous-upgrade
65
+ * refusal, disallowed-origin refusal, and — when `rpc_path` is supplied —
56
66
  * session-revocation closing the live socket.
57
67
  */
58
68
  export const describe_cross_process_ws_tests = (options) => {
59
- const { setup_test, capabilities, base_url, ws_path, origin, rpc_path } = options;
69
+ const { setup_test, capabilities, base_url, ws_path, origin, rpc_path, max_message_bytes = DEFAULT_WS_MAX_MESSAGE_BYTES } = options;
70
+ const open_authed = async () => {
71
+ const fixture = await setup_test();
72
+ return create_ws_transport({ base_url, ws_path, cookies: fixture.transport.cookies(), origin });
73
+ };
60
74
  describe('cross-process websocket', () => {
61
75
  test_if(capabilities.ws, 'authenticated upgrade round-trips heartbeat', async () => {
62
- const fixture = await setup_test();
63
- const client = await create_ws_transport({
64
- base_url,
65
- ws_path,
66
- cookies: fixture.transport.cookies(),
67
- origin
68
- });
76
+ const client = await open_authed();
69
77
  try {
70
78
  const result = await client.request(1, heartbeat_action_spec.method, {});
71
79
  assert.deepStrictEqual(result, {}, 'heartbeat returns an empty result over the wire');
@@ -74,6 +82,60 @@ export const describe_cross_process_ws_tests = (options) => {
74
82
  await client.close();
75
83
  }
76
84
  });
85
+ // `heartbeat` is parameterless on both spines (`z.void()` /
86
+ // `require_void_params`): the empty shapes are the no-arg call, and a
87
+ // declared param is `invalid_params`, not ignored.
88
+ test_if(capabilities.ws, 'heartbeat admits only the parameterless shapes', async () => {
89
+ const client = await open_authed();
90
+ try {
91
+ const cases = [
92
+ [1, {}, true],
93
+ [2, { params: {} }, true],
94
+ [3, { params: { stray: 1 } }, false]
95
+ ];
96
+ for (const [id, params, ok] of cases) {
97
+ await client.send({
98
+ jsonrpc: '2.0',
99
+ id,
100
+ method: heartbeat_action_spec.method,
101
+ ...params
102
+ });
103
+ const frame = await client.wait_for(is_response_for(id));
104
+ const label = JSON.stringify(params);
105
+ if (ok) {
106
+ assert.ok('result' in frame, `${label}: ${JSON.stringify(frame)}`);
107
+ assert.deepStrictEqual(frame.result, {}, label);
108
+ }
109
+ else {
110
+ assert.ok('error' in frame, `${label}: ${JSON.stringify(frame)}`);
111
+ assert.strictEqual(frame.error.code, JSONRPC_ERROR_CODES.invalid_params, label);
112
+ }
113
+ }
114
+ }
115
+ finally {
116
+ await client.close();
117
+ }
118
+ });
119
+ // Both spines cap an inbound message (`DEFAULT_WS_MAX_MESSAGE_BYTES` by
120
+ // default) and close the socket on a larger one rather than replying.
121
+ test_if(capabilities.ws, 'an oversized message closes the socket with WS_CLOSE_MESSAGE_TOO_BIG', async () => {
122
+ const client = await open_authed();
123
+ try {
124
+ await client.send({
125
+ jsonrpc: '2.0',
126
+ id: 1,
127
+ method: heartbeat_action_spec.method,
128
+ params: { pad: 'x'.repeat(max_message_bytes) }
129
+ });
130
+ const closed = await client.wait_for_close(5000);
131
+ assert.ok(closed, 'socket did not close within 5s of an oversized message');
132
+ assert.strictEqual(client.close_code, WS_CLOSE_MESSAGE_TOO_BIG);
133
+ }
134
+ finally {
135
+ // the server may reset the connection under the unread tail
136
+ await client.close().catch(() => { });
137
+ }
138
+ });
77
139
  // Per-connection auth fires at upgrade time (`require_auth`), so an
78
140
  // anonymous socket never opens — the upgrade is refused outright.
79
141
  test_if(capabilities.ws, 'anonymous upgrade is refused', async () => {
@@ -1,7 +1,7 @@
1
1
  import './assert_dev_env.js';
2
2
  import type { Account, Actor, RoleGrant } from '../auth/account_schema.js';
3
3
  import type { AuditLogEvent } from '../auth/audit_log_schema.js';
4
- import type { RequestContext } from '../auth/request_context.js';
4
+ import type { RequestActorContext } from '../auth/request_context.js';
5
5
  /** Override type for `create_test_account` — id-like fields accept plain `string`. */
6
6
  export type TestAccountOverrides = Partial<Omit<Account, 'id' | 'created_by' | 'updated_by'>> & {
7
7
  id?: string;
@@ -30,8 +30,11 @@ export type TestRoleGrantOverrides = Partial<Omit<RoleGrant, 'id' | 'actor_id' |
30
30
  };
31
31
  /** Create a test `RoleGrant` with sensible defaults. */
32
32
  export declare const create_test_role_grant: (overrides?: TestRoleGrantOverrides) => RoleGrant;
33
- /** Create a test `RequestContext` with role_grants from partial overrides. */
34
- export declare const create_test_context: (role_grants?: Array<TestRoleGrantOverrides>) => RequestContext;
33
+ /**
34
+ * Create a test `RequestActorContext` (a `RequestContext` with a resolved
35
+ * actor) with role_grants from partial overrides.
36
+ */
37
+ export declare const create_test_context: (role_grants?: Array<TestRoleGrantOverrides>) => RequestActorContext;
35
38
  /** Override type for `create_test_audit_event` — id-like fields accept plain `string`. */
36
39
  export type TestAuditEventOverrides = Partial<Omit<AuditLogEvent, 'id' | 'actor_id' | 'account_id' | 'target_account_id' | 'target_actor_id'>> & {
37
40
  id?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"entities.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/entities.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAiB7B,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,sFAAsF;AACtF,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,YAAY,GAAG,YAAY,CAAC,CAAC,GAAG;IAC/F,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,mBAAmB,GAAI,YAAY,oBAAoB,KAAG,OAarE,CAAC;AAEH,oFAAoF;AACpF,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,YAAY,GAAG,YAAY,CAAC,CAAC,GAAG;IAC3F,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,oDAAoD;AACpD,eAAO,MAAM,iBAAiB,GAAI,YAAY,kBAAkB,KAAG,KAUjE,CAAC;AAEH,yFAAyF;AACzF,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAC3C,IAAI,CACH,SAAS,EACT,IAAI,GAAG,UAAU,GAAG,YAAY,GAAG,UAAU,GAAG,YAAY,GAAG,YAAY,GAAG,iBAAiB,CAC/F,CACD,GAAG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,sBAAsB,GAAI,YAAY,sBAAsB,KAAG,SAgB3E,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,mBAAmB,GAC/B,cAAa,KAAK,CAAC,sBAAsB,CAAQ,KAC/C,cAID,CAAC;AAEH,0FAA0F;AAC1F,MAAM,MAAM,uBAAuB,GAAG,OAAO,CAC5C,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,UAAU,GAAG,YAAY,GAAG,mBAAmB,GAAG,iBAAiB,CAAC,CAC/F,GAAG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,uBAAuB,GAAI,YAAY,uBAAuB,KAAG,aAa5E,CAAC"}
1
+ {"version":3,"file":"entities.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/entities.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAiB7B,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,sFAAsF;AACtF,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,YAAY,GAAG,YAAY,CAAC,CAAC,GAAG;IAC/F,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,mBAAmB,GAAI,YAAY,oBAAoB,KAAG,OAarE,CAAC;AAEH,oFAAoF;AACpF,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,YAAY,GAAG,YAAY,CAAC,CAAC,GAAG;IAC3F,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,oDAAoD;AACpD,eAAO,MAAM,iBAAiB,GAAI,YAAY,kBAAkB,KAAG,KAUjE,CAAC;AAEH,yFAAyF;AACzF,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAC3C,IAAI,CACH,SAAS,EACT,IAAI,GAAG,UAAU,GAAG,YAAY,GAAG,UAAU,GAAG,YAAY,GAAG,YAAY,GAAG,iBAAiB,CAC/F,CACD,GAAG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,sBAAsB,GAAI,YAAY,sBAAsB,KAAG,SAgB3E,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAC/B,cAAa,KAAK,CAAC,sBAAsB,CAAQ,KAC/C,mBAID,CAAC;AAEH,0FAA0F;AAC1F,MAAM,MAAM,uBAAuB,GAAG,OAAO,CAC5C,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,UAAU,GAAG,YAAY,GAAG,mBAAmB,GAAG,iBAAiB,CAAC,CAC/F,GAAG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,uBAAuB,GAAI,YAAY,uBAAuB,KAAG,aAa5E,CAAC"}
@@ -44,7 +44,10 @@ export const create_test_role_grant = (overrides) => {
44
44
  };
45
45
  return overrides ? { ...base, ...overrides } : base;
46
46
  };
47
- /** Create a test `RequestContext` with role_grants from partial overrides. */
47
+ /**
48
+ * Create a test `RequestActorContext` (a `RequestContext` with a resolved
49
+ * actor) with role_grants from partial overrides.
50
+ */
48
51
  export const create_test_context = (role_grants = [{}]) => ({
49
52
  account: create_test_account(),
50
53
  actor: create_test_actor(),
@@ -124,6 +124,12 @@ export interface WsClient {
124
124
  * in `testing/sse_round_trip.ts`.
125
125
  */
126
126
  wait_for_close: (timeout_ms?: number) => Promise<boolean>;
127
+ /**
128
+ * The close code of the connection once it has closed — the server's
129
+ * (e.g. `WS_CLOSE_MESSAGE_TOO_BIG`), the test's own, or `1006` when the
130
+ * connection dropped without a close frame. `null` while open.
131
+ */
132
+ readonly close_code: number | null;
127
133
  /** Every message the server has sent, in arrival order. */
128
134
  readonly messages: ReadonlyArray<unknown>;
129
135
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ws_client.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/transports/ws_client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ws_client.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/transports/ws_client.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ws_transport.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/transports/ws_transport.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAuB9B,OAAO,EAMN,KAAK,QAAQ,EACb,KAAK,kBAAkB,EAEvB,MAAM,gBAAgB,CAAC;AAGxB,sDAAsD;AACtD,MAAM,WAAW,kBAAkB;IAClC,gHAAgH;IAChH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC;;;;;;;OAOG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACzC;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB,GAAU,SAAS,kBAAkB,KAAG,OAAO,CAAC,QAAQ,CA8IvF,CAAC"}
1
+ {"version":3,"file":"ws_transport.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/transports/ws_transport.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAuB9B,OAAO,EAMN,KAAK,QAAQ,EACb,KAAK,kBAAkB,EAEvB,MAAM,gBAAgB,CAAC;AAGxB,sDAAsD;AACtD,MAAM,WAAW,kBAAkB;IAClC,gHAAgH;IAChH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC;;;;;;;OAOG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACzC;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB,GAAU,SAAS,kBAAkB,KAAG,OAAO,CAAC,QAAQ,CAmJvF,CAAC"}