@fuzdev/fuz_app 0.148.0 → 0.150.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 (210) hide show
  1. package/dist/actions/CLAUDE.md +11 -9
  2. package/dist/actions/action_bridge.d.ts +7 -0
  3. package/dist/actions/action_bridge.d.ts.map +1 -1
  4. package/dist/actions/action_bridge.js +10 -0
  5. package/dist/actions/action_event.d.ts.map +1 -1
  6. package/dist/actions/action_event.js +14 -1
  7. package/dist/actions/action_event_types.d.ts +1 -1
  8. package/dist/actions/action_event_types.d.ts.map +1 -1
  9. package/dist/actions/perform_action.d.ts +17 -1
  10. package/dist/actions/perform_action.d.ts.map +1 -1
  11. package/dist/actions/perform_action.js +30 -7
  12. package/dist/actions/register_action_ws.d.ts +8 -2
  13. package/dist/actions/register_action_ws.d.ts.map +1 -1
  14. package/dist/actions/register_action_ws.js +24 -4
  15. package/dist/actions/transports_ws_auth_guard.d.ts +6 -4
  16. package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
  17. package/dist/actions/transports_ws_auth_guard.js +9 -6
  18. package/dist/actions/ws_endpoint_spec.d.ts +1 -1
  19. package/dist/auth/CLAUDE.md +72 -33
  20. package/dist/auth/account_action_specs.d.ts +12 -0
  21. package/dist/auth/account_action_specs.d.ts.map +1 -1
  22. package/dist/auth/account_action_specs.js +16 -2
  23. package/dist/auth/account_actions.d.ts.map +1 -1
  24. package/dist/auth/account_actions.js +7 -4
  25. package/dist/auth/actor_lookup_queries.d.ts +3 -2
  26. package/dist/auth/actor_lookup_queries.d.ts.map +1 -1
  27. package/dist/auth/actor_lookup_queries.js +6 -3
  28. package/dist/auth/actor_search_queries.d.ts +5 -2
  29. package/dist/auth/actor_search_queries.d.ts.map +1 -1
  30. package/dist/auth/actor_search_queries.js +11 -4
  31. package/dist/auth/admin_action_specs.d.ts +4 -4
  32. package/dist/auth/admin_action_specs.d.ts.map +1 -1
  33. package/dist/auth/admin_action_specs.js +33 -12
  34. package/dist/auth/admin_actions.d.ts.map +1 -1
  35. package/dist/auth/admin_actions.js +35 -6
  36. package/dist/auth/all_cell_actions.d.ts +11 -2
  37. package/dist/auth/all_cell_actions.d.ts.map +1 -1
  38. package/dist/auth/all_cell_actions.js +2 -1
  39. package/dist/auth/audit_emitter.d.ts +23 -3
  40. package/dist/auth/audit_emitter.d.ts.map +1 -1
  41. package/dist/auth/audit_emitter.js +14 -4
  42. package/dist/auth/audit_log_schema.d.ts +1 -0
  43. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  44. package/dist/auth/audit_log_schema.js +9 -5
  45. package/dist/auth/bootstrap_account.d.ts +25 -1
  46. package/dist/auth/bootstrap_account.d.ts.map +1 -1
  47. package/dist/auth/bootstrap_account.js +37 -4
  48. package/dist/auth/bootstrap_routes.d.ts +3 -2
  49. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  50. package/dist/auth/bootstrap_routes.js +5 -4
  51. package/dist/auth/cell_action_specs.d.ts +215 -15
  52. package/dist/auth/cell_action_specs.d.ts.map +1 -1
  53. package/dist/auth/cell_action_specs.js +103 -16
  54. package/dist/auth/cell_actions.d.ts +40 -18
  55. package/dist/auth/cell_actions.d.ts.map +1 -1
  56. package/dist/auth/cell_actions.js +126 -19
  57. package/dist/auth/cell_audit_events.d.ts +1 -1
  58. package/dist/auth/cell_audit_events.d.ts.map +1 -1
  59. package/dist/auth/cell_audit_events.js +3 -2
  60. package/dist/auth/cell_audit_metadata.d.ts +11 -0
  61. package/dist/auth/cell_audit_metadata.d.ts.map +1 -1
  62. package/dist/auth/cell_audit_metadata.js +10 -0
  63. package/dist/auth/cell_grant_action_specs.d.ts +7 -0
  64. package/dist/auth/cell_grant_action_specs.d.ts.map +1 -1
  65. package/dist/auth/cell_grant_action_specs.js +7 -0
  66. package/dist/auth/cell_grant_actions.d.ts +36 -4
  67. package/dist/auth/cell_grant_actions.d.ts.map +1 -1
  68. package/dist/auth/cell_grant_actions.js +20 -3
  69. package/dist/auth/credential_type_schema.d.ts +3 -2
  70. package/dist/auth/credential_type_schema.d.ts.map +1 -1
  71. package/dist/auth/credential_type_schema.js +3 -2
  72. package/dist/auth/role_grant_offer_action_specs.d.ts +18 -2
  73. package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
  74. package/dist/auth/role_grant_offer_action_specs.js +30 -6
  75. package/dist/auth/role_grant_offer_actions.d.ts +9 -4
  76. package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
  77. package/dist/auth/role_grant_offer_actions.js +58 -7
  78. package/dist/auth/role_grant_offer_notifications.d.ts +3 -2
  79. package/dist/auth/role_grant_offer_notifications.d.ts.map +1 -1
  80. package/dist/auth/role_grant_offer_notifications.js +3 -2
  81. package/dist/auth/role_grant_offer_queries.d.ts +20 -0
  82. package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
  83. package/dist/auth/role_grant_offer_queries.js +35 -0
  84. package/dist/auth/role_grant_queries.d.ts +27 -9
  85. package/dist/auth/role_grant_queries.d.ts.map +1 -1
  86. package/dist/auth/role_grant_queries.js +40 -13
  87. package/dist/auth/role_schema.d.ts +14 -8
  88. package/dist/auth/role_schema.d.ts.map +1 -1
  89. package/dist/auth/role_schema.js +6 -3
  90. package/dist/auth/self_service_role_actions.d.ts.map +1 -1
  91. package/dist/auth/self_service_role_actions.js +20 -0
  92. package/dist/db/CLAUDE.md +9 -2
  93. package/dist/db/cell_ddl.d.ts +9 -1
  94. package/dist/db/cell_ddl.d.ts.map +1 -1
  95. package/dist/db/cell_ddl.js +11 -1
  96. package/dist/db/cell_queries.d.ts +53 -7
  97. package/dist/db/cell_queries.d.ts.map +1 -1
  98. package/dist/db/cell_queries.js +61 -7
  99. package/dist/http/CLAUDE.md +14 -2
  100. package/dist/http/body_limit.d.ts +3 -1
  101. package/dist/http/body_limit.d.ts.map +1 -1
  102. package/dist/http/body_limit.js +6 -1
  103. package/dist/http/error_schemas.d.ts +8 -0
  104. package/dist/http/error_schemas.d.ts.map +1 -1
  105. package/dist/http/error_schemas.js +12 -0
  106. package/dist/http/jsonrpc.d.ts +9 -0
  107. package/dist/http/jsonrpc.d.ts.map +1 -1
  108. package/dist/http/jsonrpc.js +17 -0
  109. package/dist/http/jsonrpc_errors.d.ts +40 -15
  110. package/dist/http/jsonrpc_errors.d.ts.map +1 -1
  111. package/dist/http/jsonrpc_errors.js +99 -35
  112. package/dist/http/proxy.d.ts +4 -2
  113. package/dist/http/proxy.d.ts.map +1 -1
  114. package/dist/http/proxy.js +7 -2
  115. package/dist/http/surface.d.ts +3 -1
  116. package/dist/http/surface.d.ts.map +1 -1
  117. package/dist/http/surface.js +7 -4
  118. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  119. package/dist/realtime/sse_auth_guard.js +3 -2
  120. package/dist/schema_meta.d.ts +92 -1
  121. package/dist/schema_meta.d.ts.map +1 -1
  122. package/dist/schema_meta.js +117 -0
  123. package/dist/server/app_server.d.ts +13 -5
  124. package/dist/server/app_server.d.ts.map +1 -1
  125. package/dist/server/app_server.js +32 -8
  126. package/dist/testing/CLAUDE.md +196 -82
  127. package/dist/testing/adversarial_input_cases.d.ts +67 -7
  128. package/dist/testing/adversarial_input_cases.d.ts.map +1 -1
  129. package/dist/testing/adversarial_input_cases.js +210 -165
  130. package/dist/testing/app_server.d.ts +3 -1
  131. package/dist/testing/app_server.d.ts.map +1 -1
  132. package/dist/testing/audit_drift_guard.d.ts +3 -2
  133. package/dist/testing/audit_drift_guard.d.ts.map +1 -1
  134. package/dist/testing/audit_drift_guard.js +5 -3
  135. package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -1
  136. package/dist/testing/cross_backend/actor_lookup.js +27 -0
  137. package/dist/testing/cross_backend/audit_cross_helpers.d.ts +6 -0
  138. package/dist/testing/cross_backend/audit_cross_helpers.d.ts.map +1 -1
  139. package/dist/testing/cross_backend/audit_cross_helpers.js +11 -0
  140. package/dist/testing/cross_backend/capabilities.d.ts +29 -5
  141. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  142. package/dist/testing/cross_backend/capabilities.js +10 -6
  143. package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
  144. package/dist/testing/cross_backend/cell_crud.js +117 -2
  145. package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
  146. package/dist/testing/cross_backend/cell_gated_create.js +109 -13
  147. package/dist/testing/cross_backend/cell_moderate.d.ts +7 -1
  148. package/dist/testing/cross_backend/cell_moderate.d.ts.map +1 -1
  149. package/dist/testing/cross_backend/cell_moderate.js +68 -4
  150. package/dist/testing/cross_backend/cell_ref_admission.d.ts.map +1 -1
  151. package/dist/testing/cross_backend/cell_ref_admission.js +18 -9
  152. package/dist/testing/cross_backend/conformance_table.d.ts +5 -0
  153. package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
  154. package/dist/testing/cross_backend/conformance_table.js +12 -10
  155. package/dist/testing/cross_backend/cross_rpc.d.ts +3 -1
  156. package/dist/testing/cross_backend/cross_rpc.d.ts.map +1 -1
  157. package/dist/testing/cross_backend/cross_rpc.js +2 -2
  158. package/dist/testing/cross_backend/default_backend_configs.d.ts +9 -7
  159. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  160. package/dist/testing/cross_backend/default_backend_configs.js +3 -3
  161. package/dist/testing/cross_backend/differential.d.ts +7 -0
  162. package/dist/testing/cross_backend/differential.d.ts.map +1 -1
  163. package/dist/testing/cross_backend/differential.js +17 -0
  164. package/dist/testing/cross_backend/expected_schema.json +1 -0
  165. package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
  166. package/dist/testing/cross_backend/full_spine_mount.js +5 -4
  167. package/dist/testing/cross_backend/role_grant_offer_lifecycle.d.ts.map +1 -1
  168. package/dist/testing/cross_backend/role_grant_offer_lifecycle.js +74 -2
  169. package/dist/testing/cross_backend/rpc_adversarial.d.ts +9 -0
  170. package/dist/testing/cross_backend/rpc_adversarial.d.ts.map +1 -1
  171. package/dist/testing/cross_backend/rpc_adversarial.js +16 -6
  172. package/dist/testing/cross_backend/rust_spine_consumer_surface.d.ts +1 -1
  173. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
  174. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +6 -3
  175. package/dist/testing/cross_backend/setup.d.ts +2 -2
  176. package/dist/testing/cross_backend/spine_suites.d.ts +85 -5
  177. package/dist/testing/cross_backend/spine_suites.d.ts.map +1 -1
  178. package/dist/testing/cross_backend/spine_suites.js +291 -20
  179. package/dist/testing/cross_backend/spine_surface_constants.d.ts +15 -0
  180. package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
  181. package/dist/testing/cross_backend/spine_surface_constants.js +24 -0
  182. package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts +18 -19
  183. package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts.map +1 -1
  184. package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +66 -42
  185. package/dist/testing/cross_backend/testing_backdoor.d.ts +27 -4
  186. package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
  187. package/dist/testing/cross_backend/testing_backdoor.js +136 -41
  188. package/dist/testing/cross_backend/testing_reset_actions.d.ts +30 -15
  189. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  190. package/dist/testing/cross_backend/testing_reset_actions.js +52 -35
  191. package/dist/testing/cross_backend/transport_composition.js +3 -3
  192. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
  193. package/dist/testing/cross_backend/ts_spine_backend_config.js +6 -3
  194. package/dist/testing/rpc_attack_surface.d.ts +10 -0
  195. package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
  196. package/dist/testing/rpc_attack_surface.js +15 -3
  197. package/dist/testing/rpc_envelope_cases.d.ts +5 -2
  198. package/dist/testing/rpc_envelope_cases.d.ts.map +1 -1
  199. package/dist/testing/rpc_envelope_cases.js +4 -1
  200. package/dist/testing/rpc_round_trip.js +3 -3
  201. package/dist/testing/schema_generators.d.ts +99 -2
  202. package/dist/testing/schema_generators.d.ts.map +1 -1
  203. package/dist/testing/schema_generators.js +244 -76
  204. package/dist/testing/stubs.d.ts +4 -3
  205. package/dist/testing/stubs.d.ts.map +1 -1
  206. package/dist/testing/stubs.js +6 -4
  207. package/dist/zod.d.ts +9 -0
  208. package/dist/zod.d.ts.map +1 -1
  209. package/dist/zod.js +20 -0
  210. package/package.json +1 -1
@@ -29,13 +29,16 @@ import '../assert_dev_env.js';
29
29
  *
30
30
  * `_testing_reset` — full DB wipe + keeper re-seed + optional
31
31
  * secondary-account seeding. The
32
- * handler wipes every auth-namespace row (no keeper-preserve filter),
32
+ * handler first fires the consumer-supplied domain-state callback (domain
33
+ * rows depend on auth rows, so a domain table with a restricting FK to
34
+ * `actor` empties before the rows it references go), then wipes every
35
+ * auth-namespace row (no keeper-preserve filter),
33
36
  * flips `bootstrap_lock` back to its post-bootstrap shape, seeds a
34
37
  * fresh keeper account inline (reusing `create_test_account_with_credentials`
35
- * so cross-process matches in-process write semantics), seeds any
36
- * caller-requested `extra_accounts` (also direct-inserted at this
37
- * setup step), refreshes the daemon-token cache to point at the new
38
- * keeper, and fires the consumer-supplied domain-state callback. The
38
+ * so cross-process matches in-process write semantics), queues the
39
+ * daemon-token cache refresh to point at the new keeper once the reset
40
+ * commits, and seeds any caller-requested `extra_accounts` (also
41
+ * direct-inserted at this setup step). The
39
42
  * new keeper + secondary credentials return as the action output so
40
43
  * the per-test fixture closes over them.
41
44
  *
@@ -179,6 +182,11 @@ export const testing_reset_action_spec = {
179
182
  * not — nor are eager writes outside `emit` (session touch, token usage),
180
183
  * which only the dispatching transport's own flush awaits.
181
184
  *
185
+ * The output's `tracked` is `AuditEmitter.is_tracking_inflight` — whether the
186
+ * drain could wait on anything. `cross_drain_effects` refuses `tracked: false`,
187
+ * so a binary whose emitter was built without tracking fails its first audit
188
+ * read instead of racing it. The Rust twin answers the same shape.
189
+ *
182
190
  * `auth` gates on the daemon-token credential, matching `_testing_reset`.
183
191
  */
184
192
  export const testing_drain_effects_action_spec = {
@@ -188,7 +196,13 @@ export const testing_drain_effects_action_spec = {
188
196
  auth: TESTING_ACTION_AUTH,
189
197
  side_effects: false,
190
198
  input: z.void(),
191
- output: z.strictObject({ ok: z.boolean() }),
199
+ output: z.strictObject({
200
+ ok: z.boolean(),
201
+ tracked: z.boolean().meta({
202
+ description: "Whether the backend's audit emitter tracks in-flight writes — false means the drain " +
203
+ 'waited on nothing and is no barrier.'
204
+ })
205
+ }),
192
206
  async: true,
193
207
  description: 'Test-binary only — await in-flight fire-and-forget audit writes so a following audit_log_list read is authoritative.'
194
208
  };
@@ -250,13 +264,14 @@ export const testing_mint_session_action_spec = {
250
264
  * emitter the backend's handlers emit through (`deps.audit`), built with
251
265
  * `track_inflight: true` (see the spec doc). Mount it on any test endpoint
252
266
  * whose suite asserts on audit rows (the spine binary bundles it via
253
- * `create_testing_actions`; in-process suites mount it directly).
267
+ * `create_testing_actions`; in-process suites mount it directly). Answers
268
+ * `{ok: true, tracked}`, `tracked` read from `audit.is_tracking_inflight()`.
254
269
  *
255
270
  * @param audit - the backend's emitter, whose in-flight writes the action awaits
256
271
  */
257
272
  export const create_testing_drain_effects_action = (audit) => rpc_action(testing_drain_effects_action_spec, async () => {
258
273
  await audit.drain_inflight();
259
- return { ok: true };
274
+ return { ok: true, tracked: audit.is_tracking_inflight() };
260
275
  });
261
276
  /**
262
277
  * `_testing_put_fact` — seed an **embedded** fact (`fact.bytes`) for the
@@ -613,8 +628,18 @@ export const create_testing_actions = (deps, options) => {
613
628
  const log = deps.log;
614
629
  return [
615
630
  rpc_action(testing_reset_action_spec, async (input, ctx) => {
616
- log.info('[_testing_reset] resetting auth state + re-seeding keeper');
617
- // 1. Wipe every auth-namespace row. No keeper-preserve filter —
631
+ log.info('[_testing_reset] resetting domain + auth state + re-seeding keeper');
632
+ // 1. Fire domain-state reset (zzz workspaces/terminals/scratch,
633
+ // fuz_forge cell/fact/file truncation, the spine binaries' cell +
634
+ // fact wipe) first: domain rows depend on auth rows, so a domain
635
+ // table with a restricting FK to `actor` would make the auth wipe
636
+ // below fail once it holds a row.
637
+ // Pass the transactional `ctx.db` so DB-domain truncation runs
638
+ // on the same connection — a separate pool connection deadlocks
639
+ // against this open transaction under the in-memory drivers.
640
+ if (reset_state)
641
+ await reset_state(ctx.db);
642
+ // 2. Wipe every auth-namespace row. No keeper-preserve filter —
618
643
  // the fresh-keeper-per-test contract means mutation-cascade
619
644
  // tests (password change, revoke-all) and hardcoded-username
620
645
  // signup-uniqueness tests can't leak between cases.
@@ -631,13 +656,13 @@ export const create_testing_actions = (deps, options) => {
631
656
  await ctx.db.query('DELETE FROM role_grant');
632
657
  await ctx.db.query('DELETE FROM actor');
633
658
  await ctx.db.query('DELETE FROM account');
634
- // 2. Reset singleton `app_settings` to production defaults
659
+ // 3. Reset singleton `app_settings` to production defaults
635
660
  // (matches in-process `_build_test_backend` behavior in
636
661
  // `app_server.ts`). Tests that flipped `open_signup` mid-run
637
662
  // revert.
638
663
  await ctx.db.query('UPDATE app_settings SET open_signup = false, updated_at = NULL, updated_by = NULL ' +
639
664
  'WHERE open_signup = true OR updated_at IS NOT NULL');
640
- // 3. Flip `bootstrap_lock` to its post-bootstrap shape. Production
665
+ // 4. Flip `bootstrap_lock` to its post-bootstrap shape. Production
641
666
  // `bootstrap_account` flips this to `true` on success; the
642
667
  // in-process `bootstrap_test_keeper` mirrors the flip. We're
643
668
  // about to seed the keeper here, so the final state needs to
@@ -645,7 +670,7 @@ export const create_testing_actions = (deps, options) => {
645
670
  // to `false` — nothing reads it between our DELETEs and the
646
671
  // UPDATE.
647
672
  await ctx.db.query('UPDATE bootstrap_lock SET bootstrapped = true WHERE id = 1');
648
- // 4. Seed the fresh keeper inline. Reuses the same primitive
673
+ // 5. Seed the fresh keeper inline. Reuses the same primitive
649
674
  // in-process tests use (`create_test_account_with_credentials`)
650
675
  // so cross-process and in-process write semantics stay in
651
676
  // parity — same hash, same account+actor+role_grants+
@@ -662,7 +687,19 @@ export const create_testing_actions = (deps, options) => {
662
687
  password_value: DEFAULT_TEST_PASSWORD,
663
688
  roles: [ROLE_KEEPER, ROLE_ADMIN, ...(input.extra_keeper_roles ?? [])]
664
689
  });
665
- // 5. Seed any caller-requested extras. These are bootstrap-time
690
+ // 6. Refresh the daemon-token cache so subsequent daemon-token
691
+ // requests resolve to the freshly seeded keeper. The
692
+ // middleware's lazy-refresh path only fires when the cached
693
+ // id is null, so the reset sets it — after the commit, queued as
694
+ // soon as the keeper exists, so a reset that rolls back after it
695
+ // (a failing extra seed, a failed COMMIT) leaves the cache on the
696
+ // keeper still in the database rather than one that never
697
+ // landed, and the next reset can still authenticate.
698
+ const keeper_account_id = keeper.account.id;
699
+ emit_after_commit(ctx, () => {
700
+ daemon_token_state.keeper_account_id = keeper_account_id;
701
+ });
702
+ // 7. Seed any caller-requested extras. These are bootstrap-time
666
703
  // secondaries — the bypass exists in the same cradle the
667
704
  // keeper does, not as a free-form runtime action.
668
705
  const extras = [];
@@ -678,7 +715,7 @@ export const create_testing_actions = (deps, options) => {
678
715
  });
679
716
  extras.push(seeded);
680
717
  }
681
- // 5b. Seed any additional keeper actors. Same bootstrap-cradle
718
+ // 8. Seed any additional keeper actors. Same bootstrap-cradle
682
719
  // bypass as extra_accounts — no production wire path mints a
683
720
  // second actor, so the multi-actor `acting` branches need this
684
721
  // direct insert. Order-preserving so the fixture can address
@@ -691,26 +728,6 @@ export const create_testing_actions = (deps, options) => {
691
728
  const seeded_actor = await create_test_extra_actor(ctx.db, keeper.account.id, name);
692
729
  extra_actors.push({ id: seeded_actor.id, name: seeded_actor.name });
693
730
  }
694
- // 6. Refresh the daemon-token cache so subsequent daemon-token
695
- // requests resolve to the freshly seeded keeper. The
696
- // middleware's lazy-refresh path only fires when the cached
697
- // id is null, so the reset sets it — after the commit, so a
698
- // reset that rolls back (a throwing `reset_state`, a failed
699
- // COMMIT) leaves the cache on the keeper still in the database
700
- // rather than one that never landed, and the next reset can
701
- // still authenticate.
702
- const keeper_account_id = keeper.account.id;
703
- emit_after_commit(ctx, () => {
704
- daemon_token_state.keeper_account_id = keeper_account_id;
705
- });
706
- // 7. Fire domain-state reset (zzz workspaces/terminals/scratch,
707
- // fuz_forge cell/fact/file truncation, the spine binaries' cell +
708
- // fact wipe).
709
- // Pass the transactional `ctx.db` so DB-domain truncation runs
710
- // on the same connection — a separate pool connection deadlocks
711
- // against this open transaction under the in-memory drivers.
712
- if (reset_state)
713
- await reset_state(ctx.db);
714
731
  return { ...keeper, extra_accounts: extras, extra_actors };
715
732
  }),
716
733
  rpc_action(testing_mint_session_action_spec, async (input, ctx) => {
@@ -60,9 +60,9 @@ import '../assert_dev_env.js';
60
60
  * **Multi-actor upgrade.** One more WS row needs a keeper seeded with a
61
61
  * second actor (`extra_actors`), so it runs only given `multi_actor`: a
62
62
  * multi-actor keeper's session upgrading with no `?acting=` → 400
63
- * `actor_required`, the flat body the TS spine's upgrade-time authorization
64
- * phase answers. The upgrade resolves an actor there, and a multi-actor
65
- * account must name one. A backend whose upgrade resolves no actor admits
63
+ * `actor_required`, the flat body both spines' upgrade-time actor resolution
64
+ * answers. The upgrade resolves an actor there, and a multi-actor account
65
+ * must name one. A backend whose upgrade resolves no actor admits
66
66
  * the socket instead; `multi_actor.xfail` runs the row as `xfail_until` for
67
67
  * it.
68
68
  *
@@ -1 +1 @@
1
- {"version":3,"file":"ts_spine_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ts_spine_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AA4EzD,6FAA6F;AAC7F,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,iDAAiD;AACjD,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,gDAAgD;AAChD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,uDAAuD,CAAC;AAEvF,MAAM,WAAW,2BAA2B;IAC3C,mFAAmF;IACnF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;;OASG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,8BAA8B,CAAC,EAAE,MAAM,CAAC;CACjD;AAWD;;;GAGG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAwBF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GACvC,UAAS,2BAAgC,KACvC,aAwBF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAqCF,CAAC"}
1
+ {"version":3,"file":"ts_spine_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ts_spine_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAgFzD,6FAA6F;AAC7F,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,iDAAiD;AACjD,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,gDAAgD;AAChD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,uDAAuD,CAAC;AAEvF,MAAM,WAAW,2BAA2B;IAC3C,mFAAmF;IACnF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;;OASG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,8BAA8B,CAAC,EAAE,MAAM,CAAC;CACjD;AAWD;;;GAGG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAwBF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GACvC,UAAS,2BAAgC,KACvC,aAwBF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAqCF,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import '../assert_dev_env.js';
2
2
  import { build_test_backend_paths } from './build_test_backend_paths.js';
3
3
  import { action_rate_limit_env, make_default_ts_backend_config, ts_default_capabilities } from './default_backend_configs.js';
4
- import { LOGIN_RATE_LIMIT_ENABLED_ENV, SPINE_SSE_PATH, TS_SPINE_DIR_ENV } from './spine_surface_constants.js';
4
+ import { LOGIN_RATE_LIMIT_ENABLED_ENV, SPINE_SSE_PATH, TESTING_BACKDOOR_METHODS, TS_SPINE_DIR_ENV } from './spine_surface_constants.js';
5
5
  /**
6
6
  * Capabilities for the TS spine binary — `ts_default_capabilities` with every `MountFlag` stated, all `true`: `ws`
7
7
  * (the binary mounts `/api/ws` through `create_app_server`'s `ws_endpoints`),
@@ -16,7 +16,9 @@ import { LOGIN_RATE_LIMIT_ENABLED_ENV, SPINE_SSE_PATH, TS_SPINE_DIR_ENV } from '
16
16
  * the account actions among them),
17
17
  * `cell_gated_create` (the binary's `full_spine_mount` mounts the
18
18
  * `test_cell_gated_create_authorize` policy), and `ws_role_gated` (the binary
19
- * mounts the role-gated WS endpoint at `SPINE_ROLE_GATED_WS_PATH`). The Node
19
+ * mounts the role-gated WS endpoint at `SPINE_ROLE_GATED_WS_PATH`), and every
20
+ * `TESTING_BACKDOOR_METHODS` entry as `testing_backdoors` (`full_spine_mount`
21
+ * mounts them all, on `/api/rpc` and `/api/ws`). The Node
20
22
  * binary's preset; `ws_refusal_body` stays `false`, as `@hono/node-ws` drops a
21
23
  * refused upgrade's body.
22
24
  */
@@ -32,7 +34,8 @@ const ts_spine_capabilities = Object.freeze({
32
34
  peer_request: true,
33
35
  ws_account_actions: true,
34
36
  cell_gated_create: true,
35
- ws_role_gated: true
37
+ ws_role_gated: true,
38
+ testing_backdoors: TESTING_BACKDOOR_METHODS
36
39
  });
37
40
  /**
38
41
  * Capabilities for the **Deno** spine binary — `ts_spine_capabilities` with
@@ -6,6 +6,16 @@ export interface RpcAttackSurfaceOptions {
6
6
  build: () => AppSurfaceSpec;
7
7
  /** All roles in the app (e.g. `['admin', 'keeper']`). */
8
8
  roles: Array<string>;
9
+ /**
10
+ * Valid params per method (method name → params) to build its adversarial
11
+ * params cases from, in place of the generated base — for an input whose
12
+ * valid shape the generator can't reach. Each must pass the method's input
13
+ * schema; one that doesn't throws at collection, naming the method. The
14
+ * cases are sent as a single-actor principal, so an override omits
15
+ * `acting`. The cross-process twin is
16
+ * `RpcAdversarialCrossTestOptions.input_overrides`.
17
+ */
18
+ input_overrides?: ReadonlyMap<string, Record<string, unknown>>;
9
19
  }
10
20
  /**
11
21
  * Run the standard RPC attack surface test suite.
@@ -1 +1 @@
1
- {"version":3,"file":"rpc_attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAkB7B,OAAO,KAAK,EAGX,cAAc,EACd,MAAM,oBAAoB,CAAC;AA8B5B,uDAAuD;AACvD,MAAM,WAAW,uBAAuB;IACvC,+FAA+F;IAC/F,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACrB;AA2UD;;;;;;;;;GASG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,uBAAuB,KAAG,IAOpF,CAAC"}
1
+ {"version":3,"file":"rpc_attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAkB7B,OAAO,KAAK,EAGX,cAAc,EACd,MAAM,oBAAoB,CAAC;AA+B5B,uDAAuD;AACvD,MAAM,WAAW,uBAAuB;IACvC,+FAA+F;IAC/F,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC/D;AA2VD;;;;;;;;;GASG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,uBAAuB,KAAG,IAOpF,CAAC"}
@@ -19,6 +19,7 @@ import { create_auth_test_apps, create_test_app_from_specs, create_test_request_
19
19
  import { generate_valid_body } from './schema_generators.js';
20
20
  import { create_rpc_post_init, create_rpc_get_url, assert_jsonrpc_error_response } from './rpc_helpers.js';
21
21
  import { assert_rpc_envelope_refusal, create_rpc_envelope_cases, generate_rpc_params_test_cases, to_rpc_envelope_request } from './rpc_envelope_cases.js';
22
+ import { expected_invalid_reason } from './adversarial_input_cases.js';
22
23
  // --- Helpers ---
23
24
  /** Filter RPC methods that require any form of authentication. */
24
25
  const filter_protected_rpc_methods = (endpoint) => endpoint.methods.filter((m) => !is_public_auth(m.auth));
@@ -201,8 +202,10 @@ const describe_rpc_adversarial_envelopes = (options) => {
201
202
  *
202
203
  * For each method with a non-null input schema, generates test cases
203
204
  * from the schema (wrong types, missing fields, format violations)
204
- * and wraps them in valid JSON-RPC envelopes. Reuses
205
- * `generate_input_test_cases` from `testing/adversarial_input.ts`.
205
+ * and wraps them in valid JSON-RPC envelopes, via `generate_rpc_params_test_cases`;
206
+ * a method's `input_overrides` entry, when given, is the base they're built from.
207
+ * Each owes 400 `invalid_params` with the broken field's declared
208
+ * `invalid_reason` as `data.reason`, or no reason (`expected_invalid_reason`).
206
209
  */
207
210
  const describe_rpc_adversarial_params = (options) => {
208
211
  const { build, roles } = options;
@@ -226,7 +229,10 @@ const describe_rpc_adversarial_params = (options) => {
226
229
  });
227
230
  continue;
228
231
  }
229
- const test_cases = generate_rpc_params_test_cases(action.spec.input);
232
+ const test_cases = generate_rpc_params_test_cases(action.spec.input, {
233
+ base: options.input_overrides?.get(method.name),
234
+ label: method.name
235
+ });
230
236
  if (test_cases.length === 0)
231
237
  continue;
232
238
  total_cases += test_cases.length;
@@ -238,6 +244,12 @@ const describe_rpc_adversarial_params = (options) => {
238
244
  assert.strictEqual(res.status, 400, `Expected 400 for ${method.name} [${tc.label}], got ${res.status}`);
239
245
  const body = await res.json();
240
246
  assert_jsonrpc_error_response(body, JSONRPC_ERROR_CODES.invalid_params);
247
+ // the broken field's declared reason, or none
248
+ const data = body.error.data;
249
+ const reason = typeof data === 'object' && data !== null
250
+ ? data.reason
251
+ : undefined;
252
+ assert.strictEqual(reason, expected_invalid_reason(action.spec.input, tc), `data.reason for ${method.name} [${tc.label}]`);
241
253
  });
242
254
  }
243
255
  });
@@ -2,7 +2,7 @@ import './assert_dev_env.js';
2
2
  import type { z } from 'zod';
3
3
  import type { AppSurfaceRpcEndpoint, AppSurfaceRpcMethod } from '../http/surface.js';
4
4
  import type { JsonrpcErrorCode, JsonrpcRequestId } from '../http/jsonrpc.js';
5
- import { type InputTestCase } from './adversarial_input_cases.js';
5
+ import { type InputTestCase, type InputTestCaseOptions } from './adversarial_input_cases.js';
6
6
  /** The request id every envelope case sends where it sends one. */
7
7
  export declare const RPC_ENVELOPE_PROBE_ID = "envelope_probe";
8
8
  /**
@@ -105,7 +105,10 @@ export declare const assert_rpc_envelope_refusal: (status: number, body: unknown
105
105
  * cover it. Every case returned owes 400 `invalid_params`.
106
106
  *
107
107
  * @param input - the method's input schema
108
+ * @param options - a valid base `params` to seed the cases (`InputTestCaseOptions.base`),
109
+ * and the method name for error messages
108
110
  * @returns the cases, each a `params` object
111
+ * @throws Error if the base `params`, seeded or generated, fails the input schema
109
112
  */
110
- export declare const generate_rpc_params_test_cases: (input: z.ZodType) => Array<InputTestCase>;
113
+ export declare const generate_rpc_params_test_cases: (input: z.ZodType, options?: InputTestCaseOptions) => Array<InputTestCase>;
111
114
  //# sourceMappingURL=rpc_envelope_cases.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rpc_envelope_cases.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_envelope_cases.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAe7B,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAGrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAK7E,OAAO,EAA6B,KAAK,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAI7F,mEAAmE;AACnE,eAAO,MAAM,qBAAqB,mBAAmB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,YAAY,GAAG,WAAW,CAAC;IACxC,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,gEAAgE;IAChE,QAAQ,CAAC,OAAO,EACb;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAChD;QAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,QAAQ,CAAC,MAAM,EAAE;QAChB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;QAChC,mFAAmF;QACnF,QAAQ,CAAC,EAAE,EAAE,gBAAgB,GAAG,IAAI,CAAC;QACrC,mEAAmE;QACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/B,CAAC;CACF;AAED,+CAA+C;AAC/C,MAAM,WAAW,sBAAsB;IACtC,oDAAoD;IACpD,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC9C;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAAC,MAAM,CAAgC,CAAC;AAEzF,wFAAwF;AACxF,eAAO,MAAM,2BAA2B,GAAI,QAAQ,MAAM,KAAG,MACL,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,yBAAyB,GACrC,UAAU,qBAAqB,EAC/B,UAAS,sBAA2B,KAClC,KAAK,CAAC,eAAe,CAmKvB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GACnC,eAAe,MAAM,EACrB,GAAG,eAAe,EAClB,UAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,KAClC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAA;CAUgD,CAAC;AAEpF;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,GACvC,QAAQ,MAAM,EACd,MAAM,OAAO,EACb,GAAG,eAAe,KAChB,IAUF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,GAAI,OAAO,CAAC,CAAC,OAAO,KAAG,KAAK,CAAC,aAAa,CACU,CAAC"}
1
+ {"version":3,"file":"rpc_envelope_cases.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_envelope_cases.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAe7B,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAGrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAK7E,OAAO,EAEN,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,MAAM,8BAA8B,CAAC;AAItC,mEAAmE;AACnE,eAAO,MAAM,qBAAqB,mBAAmB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,YAAY,GAAG,WAAW,CAAC;IACxC,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,gEAAgE;IAChE,QAAQ,CAAC,OAAO,EACb;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAChD;QAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,QAAQ,CAAC,MAAM,EAAE;QAChB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;QAChC,mFAAmF;QACnF,QAAQ,CAAC,EAAE,EAAE,gBAAgB,GAAG,IAAI,CAAC;QACrC,mEAAmE;QACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/B,CAAC;CACF;AAED,+CAA+C;AAC/C,MAAM,WAAW,sBAAsB;IACtC,oDAAoD;IACpD,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC9C;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAAC,MAAM,CAAgC,CAAC;AAEzF,wFAAwF;AACxF,eAAO,MAAM,2BAA2B,GAAI,QAAQ,MAAM,KAAG,MACL,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,yBAAyB,GACrC,UAAU,qBAAqB,EAC/B,UAAS,sBAA2B,KAClC,KAAK,CAAC,eAAe,CAmKvB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GACnC,eAAe,MAAM,EACrB,GAAG,eAAe,EAClB,UAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,KAClC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAA;CAUgD,CAAC;AAEpF;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,GACvC,QAAQ,MAAM,EACd,MAAM,OAAO,EACb,GAAG,eAAe,KAChB,IAUF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,8BAA8B,GAC1C,OAAO,CAAC,CAAC,OAAO,EAChB,UAAU,oBAAoB,KAC5B,KAAK,CAAC,aAAa,CAGpB,CAAC"}
@@ -235,6 +235,9 @@ export const assert_rpc_envelope_refusal = (status, body, c) => {
235
235
  * cover it. Every case returned owes 400 `invalid_params`.
236
236
  *
237
237
  * @param input - the method's input schema
238
+ * @param options - a valid base `params` to seed the cases (`InputTestCaseOptions.base`),
239
+ * and the method name for error messages
238
240
  * @returns the cases, each a `params` object
241
+ * @throws Error if the base `params`, seeded or generated, fails the input schema
239
242
  */
240
- export const generate_rpc_params_test_cases = (input) => generate_input_test_cases(input).filter((tc) => tc.expected_error !== ERROR_INVALID_JSON_BODY);
243
+ export const generate_rpc_params_test_cases = (input, options) => generate_input_test_cases(input, options).filter((tc) => tc.expected_error !== ERROR_INVALID_JSON_BODY);
@@ -28,7 +28,7 @@ import './assert_dev_env.js';
28
28
  import { describe, test, beforeAll, assert } from 'vitest';
29
29
  import { ROLE_ADMIN } from '../auth/role_schema.js';
30
30
  import { JSONRPC_INTERNAL_ERROR, JSONRPC_METHOD_NOT_FOUND, JsonrpcErrorResponse } from '../http/jsonrpc.js';
31
- import { JSONRPC_ERROR_CODE_TO_HTTP_STATUS } from '../http/jsonrpc_errors.js';
31
+ import { jsonrpc_error_code_to_http_status } from '../http/jsonrpc_errors.js';
32
32
  import { generate_valid_body } from './schema_generators.js';
33
33
  import { create_rpc_post_init, create_rpc_get_url, assert_jsonrpc_error_response, assert_jsonrpc_success_response, resolve_rpc_endpoints_for_setup, find_rpc_action, find_rpc_method } from './rpc_helpers.js';
34
34
  import { is_unauthenticated_refusal, pick_auth_principal, round_trip_uses_fresh_account, to_principal_headers } from './integration_helpers.js';
@@ -112,7 +112,7 @@ const read_rpc_body = async (res) => {
112
112
  }
113
113
  };
114
114
  /** The HTTP status an `internal_error` maps to, read off the shared status table. */
115
- const INTERNAL_ERROR_HTTP_STATUS = JSONRPC_ERROR_CODE_TO_HTTP_STATUS[JSONRPC_INTERNAL_ERROR];
115
+ const INTERNAL_ERROR_HTTP_STATUS = jsonrpc_error_code_to_http_status(JSONRPC_INTERNAL_ERROR);
116
116
  /**
117
117
  * Guard against a backend that crashes on schema-valid input. A response is
118
118
  * a crash when any of these holds:
@@ -125,7 +125,7 @@ const INTERNAL_ERROR_HTTP_STATUS = JSONRPC_ERROR_CODE_TO_HTTP_STATUS[JSONRPC_INT
125
125
  *
126
126
  * Any other ≥500 that carries a well-formed envelope with a declared code
127
127
  * passes like a 4xx: `service_unavailable` (503), `timeout` (504), or a
128
- * consumer's own code mapped in `JSONRPC_ERROR_CODE_TO_HTTP_STATUS` (a 502).
128
+ * consumer's own code declared with `register_jsonrpc_error_code` (a 502).
129
129
  * Those are server conditions a handler declares on purpose — a dependency
130
130
  * the test environment doesn't provide — while an undeclared 500 or -32603
131
131
  * is a handler that failed where it owed an answer.
@@ -11,12 +11,109 @@ import './assert_dev_env.js';
11
11
  import { z } from 'zod';
12
12
  import { type ZodFieldInfo } from '../zod.js';
13
13
  /**
14
- * Detect format constraints on a field by converting to JSON Schema.
15
- * Returns format string (e.g. 'uuid', 'email') or null.
14
+ * The JSON Schema of a field's input side — where its format, pattern and
15
+ * bounds live — or `null` when JSON Schema can't render the field or any part
16
+ * of it (`z.date()`, `z.custom()`, a union with a `z.date()` branch).
17
+ *
18
+ * Rendered with `io: 'input'`, so a field carrying a `.transform()` or a pipe
19
+ * (`Username`) still yields the constraints its input is checked against;
20
+ * the default output side throws on a transform. A pipe yields its input
21
+ * side only, so constraints of the stage after it are not here. A
22
+ * `.nullable()` / `.nullish()` field renders as
23
+ * `anyOf: [{…}, {type: 'null'}]`; its one non-null branch is returned, so
24
+ * the bounds and pattern read the same as on the bare field. A union of two
25
+ * or more non-null branches returns the whole schema — its branches' bounds
26
+ * are no single field's bounds.
27
+ *
28
+ * A `.refine()` is never here: JSON Schema can't express one, so a bound a
29
+ * refinement enforces (a code-point length, a cross-field rule) generates no
30
+ * boundary case.
31
+ *
32
+ * @param field_schema - the field to read
33
+ * @returns the field's input-side JSON Schema, or `null` when it can't be rendered
34
+ */
35
+ export declare const to_field_json_schema: (field_schema: z.ZodType) => Record<string, unknown> | null;
36
+ /**
37
+ * The format constraint a field's input-side JSON Schema carries: its
38
+ * `format` (e.g. `'uuid'`, `'email'`), `'pattern'` for a regex, or `null`.
39
+ * A union (`anyOf` of several non-null branches) answers its first branch
40
+ * carrying one — a value violating every branch is what the caller needs,
41
+ * and the generators check the field refuses the candidate.
42
+ *
43
+ * @param json - a field's JSON Schema, as `to_field_json_schema` returns it
44
+ * @returns the format, `'pattern'`, or `null`
45
+ */
46
+ export declare const detect_json_schema_format: (json: Record<string, unknown>) => string | null;
47
+ /**
48
+ * Detect format constraints on a field from its input-side JSON Schema
49
+ * (`to_field_json_schema`). Returns the format string (e.g. `'uuid'`,
50
+ * `'email'`), `'pattern'` for a regex, or `null`.
16
51
  */
17
52
  export declare const detect_format: (field_schema: z.ZodType) => string | null;
18
53
  /** Generate a valid-ish value for a field based on its base type. */
19
54
  export declare const generate_valid_value: (field: ZodFieldInfo, field_schema: z.ZodType) => unknown;
55
+ /**
56
+ * Generate a valid object for an object schema: its required and defaulted
57
+ * fields first; when `schema` still refuses it, each optional field alone on
58
+ * top of those, in declaration order, taking the first that parses; and
59
+ * failing that, the optional fields added one at a time, in declaration
60
+ * order, until it parses. The fill covers a `.refine()` over optional fields
61
+ * (at least one of, exactly one of, both of), which the required-only object
62
+ * can't satisfy, and the single-field pass keeps an unrelated optional field
63
+ * declared ahead of the refined ones (an `acting`, a note) out of the object.
64
+ *
65
+ * Shared by the top-level body generators and `generate_valid_value`'s
66
+ * nested-object case, so a refined object validates the same way at any depth.
67
+ *
68
+ * @param object_schema - the unwrapped object whose fields are generated
69
+ * @param schema - the schema the object must satisfy, refinements included
70
+ * (`object_schema` or a schema wrapping it)
71
+ * @returns the generated object and its last `safeParse` result — the caller
72
+ * decides whether a failure throws
73
+ */
74
+ export declare const generate_valid_object: (object_schema: z.ZodObject, schema: z.ZodType) => {
75
+ value: Record<string, unknown>;
76
+ result: z.ZodSafeParseResult<unknown>;
77
+ };
78
+ /**
79
+ * A valid object that carries `field`, built from `base`, a valid object of
80
+ * `object_schema` — the body a case breaking `field` changes, so the case
81
+ * breaks that field and nothing else. `base` itself when it carries the
82
+ * field. Otherwise the field takes a generated value and joins `base`, with
83
+ * the optional fields `base` omits filled as `generate_valid_object` fills
84
+ * them (a pair rule: adding `scope_kind` needs `scope_id`); failing that, it
85
+ * replaces `base`'s optional fields, refilled the same way around it (an
86
+ * exactly-one-of rule: adding `slug` means dropping `id`).
87
+ *
88
+ * @param object_schema - the unwrapped object `base` is an instance of
89
+ * @param schema - the schema the object must satisfy, refinements included
90
+ * @param base - a valid object, the generated or seeded base
91
+ * @param field - the field the object must carry
92
+ * @returns the object, or `null` when no object the generator can build
93
+ * carries the field and passes `schema`
94
+ */
95
+ export declare const generate_valid_object_with_field: (object_schema: z.ZodObject, schema: z.ZodType, base: Record<string, unknown>, field: ZodFieldInfo) => Record<string, unknown> | null;
96
+ /**
97
+ * A string a URL-sourced field accepts — a route param or a query value, which
98
+ * arrive as strings whatever the schema parses them into. The nil UUID for a
99
+ * UUID field; else the first the field accepts of `test_value`, the generated
100
+ * valid value written as a string, `1`, `0`, `true` and `false`, the field's
101
+ * JSON Schema `default` or lower bound written as a string (a preprocessed
102
+ * integer whose minimum is above one), and a string synthesized from the
103
+ * field's constraints.
104
+ *
105
+ * @param field - the field's info, from `zod_extract_fields`
106
+ * @param field_schema - the field's schema
107
+ * @returns the string, or `null` when no candidate passes
108
+ */
109
+ export declare const generate_valid_string_param: (field: ZodFieldInfo, field_schema: z.ZodType) => string | null;
110
+ /**
111
+ * Format Zod issues as `path: message` pairs for a generator's error message.
112
+ *
113
+ * @param error - the failed parse's error
114
+ * @returns the issues, comma-separated
115
+ */
116
+ export declare const format_zod_issues: (error: z.ZodError) => string;
20
117
  /**
21
118
  * The `Content-Type` a well-formed request to a route sends: `application/json`,
22
119
  * or `application/octet-stream` for a `raw_body` route. Every `POST` route
@@ -1 +1 @@
1
- {"version":3,"file":"schema_generators.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/schema_generators.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAKN,KAAK,YAAY,EACjB,MAAM,WAAW,CAAC;AAInB;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAI,cAAc,CAAC,CAAC,OAAO,KAAG,MAAM,GAAG,IAkBhE,CAAC;AAmHF,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,GAAI,OAAO,YAAY,EAAE,cAAc,CAAC,CAAC,OAAO,KAAG,OA8GnF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,KAAG,MACL,CAAC;AAElE;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,gBAAgB,CAAC,CAAC,SAAS,KAAG,MAiB9E,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAC/B,cAAc,CAAC,CAAC,OAAO,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SA+B5B,CAAC"}
1
+ {"version":3,"file":"schema_generators.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/schema_generators.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAKN,KAAK,YAAY,EACjB,MAAM,WAAW,CAAC;AAInB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,oBAAoB,GAAI,cAAc,CAAC,CAAC,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAcxF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,yBAAyB,GAAI,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,GAAG,IAWlF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,cAAc,CAAC,CAAC,OAAO,KAAG,MAAM,GAAG,IAGhE,CAAC;AAqHF,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,GAAI,OAAO,YAAY,EAAE,cAAc,CAAC,CAAC,OAAO,KAAG,OAkGnF,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,qBAAqB,GACjC,eAAe,CAAC,CAAC,SAAS,EAC1B,QAAQ,CAAC,CAAC,OAAO,KACf;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAA;CASzE,CAAC;AAsCF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,gCAAgC,GAC5C,eAAe,CAAC,CAAC,SAAS,EAC1B,QAAQ,CAAC,CAAC,OAAO,EACjB,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,YAAY,KACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IA0B5B,CAAC;AAIF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,2BAA2B,GACvC,OAAO,YAAY,EACnB,cAAc,CAAC,CAAC,OAAO,KACrB,MAAM,GAAG,IA6BX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,CAAC,CAAC,QAAQ,KAAG,MACkB,CAAC;AAEzE;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,KAAG,MACL,CAAC;AAElE;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,gBAAgB,CAAC,CAAC,SAAS,KAAG,MAiB9E,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAC/B,cAAc,CAAC,CAAC,OAAO,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAY5B,CAAC"}