@fuzdev/fuz_app 0.111.0 → 0.113.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 (212) hide show
  1. package/dist/actions/CLAUDE.md +15 -11
  2. package/dist/actions/action_rpc.d.ts +1 -1
  3. package/dist/actions/action_rpc.d.ts.map +1 -1
  4. package/dist/actions/action_rpc.js +4 -6
  5. package/dist/actions/peer_ping.d.ts +8 -0
  6. package/dist/actions/peer_ping.d.ts.map +1 -1
  7. package/dist/actions/peer_ping.js +8 -0
  8. package/dist/actions/peer_request.d.ts +1 -1
  9. package/dist/actions/peer_request.js +1 -1
  10. package/dist/actions/perform_action.d.ts +16 -9
  11. package/dist/actions/perform_action.d.ts.map +1 -1
  12. package/dist/actions/perform_action.js +30 -21
  13. package/dist/actions/register_action_ws.d.ts +1 -1
  14. package/dist/actions/register_action_ws.d.ts.map +1 -1
  15. package/dist/actions/register_action_ws.js +88 -21
  16. package/dist/auth/CLAUDE.md +43 -8
  17. package/dist/auth/account_actions.d.ts.map +1 -1
  18. package/dist/auth/account_actions.js +10 -1
  19. package/dist/auth/account_queries.d.ts.map +1 -1
  20. package/dist/auth/account_queries.js +36 -16
  21. package/dist/auth/account_schema.d.ts +20 -0
  22. package/dist/auth/account_schema.d.ts.map +1 -1
  23. package/dist/auth/account_schema.js +22 -1
  24. package/dist/auth/actor_search_actions.d.ts +3 -3
  25. package/dist/auth/actor_search_actions.d.ts.map +1 -1
  26. package/dist/auth/actor_search_actions.js +10 -6
  27. package/dist/auth/api_token_queries.d.ts.map +1 -1
  28. package/dist/auth/api_token_queries.js +11 -5
  29. package/dist/auth/app_settings_queries.d.ts.map +1 -1
  30. package/dist/auth/app_settings_queries.js +6 -4
  31. package/dist/auth/audit_log_queries.d.ts +6 -0
  32. package/dist/auth/audit_log_queries.d.ts.map +1 -1
  33. package/dist/auth/audit_log_queries.js +11 -5
  34. package/dist/auth/audit_log_route_schema.d.ts.map +1 -1
  35. package/dist/auth/audit_log_route_schema.js +12 -1
  36. package/dist/auth/cell_actions.d.ts.map +1 -1
  37. package/dist/auth/cell_actions.js +3 -8
  38. package/dist/auth/cell_audit_actions.d.ts.map +1 -1
  39. package/dist/auth/cell_audit_actions.js +1 -1
  40. package/dist/auth/cell_field_actions.js +1 -1
  41. package/dist/auth/cell_grant_actions.js +1 -1
  42. package/dist/auth/cell_item_actions.js +1 -1
  43. package/dist/auth/invite_queries.d.ts.map +1 -1
  44. package/dist/auth/invite_queries.js +10 -8
  45. package/dist/auth/role_grant_offer_action_specs.d.ts +2 -1
  46. package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
  47. package/dist/auth/role_grant_offer_action_specs.js +2 -1
  48. package/dist/auth/role_grant_offer_ddl.d.ts +11 -0
  49. package/dist/auth/role_grant_offer_ddl.d.ts.map +1 -1
  50. package/dist/auth/role_grant_offer_ddl.js +20 -2
  51. package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
  52. package/dist/auth/role_grant_offer_queries.js +29 -15
  53. package/dist/auth/role_grant_queries.d.ts.map +1 -1
  54. package/dist/auth/role_grant_queries.js +21 -7
  55. package/dist/auth/session_queries.d.ts.map +1 -1
  56. package/dist/auth/session_queries.js +9 -4
  57. package/dist/db/CLAUDE.md +21 -2
  58. package/dist/db/cell_audit_queries.js +2 -2
  59. package/dist/db/cell_field_queries.d.ts +1 -1
  60. package/dist/db/cell_field_queries.d.ts.map +1 -1
  61. package/dist/db/cell_field_queries.js +8 -6
  62. package/dist/db/cell_grant_queries.d.ts +1 -1
  63. package/dist/db/cell_grant_queries.d.ts.map +1 -1
  64. package/dist/db/cell_grant_queries.js +12 -10
  65. package/dist/db/cell_item_queries.d.ts +1 -1
  66. package/dist/db/cell_item_queries.d.ts.map +1 -1
  67. package/dist/db/cell_item_queries.js +9 -7
  68. package/dist/db/cell_queries.d.ts +3 -3
  69. package/dist/db/cell_queries.d.ts.map +1 -1
  70. package/dist/db/cell_queries.js +6 -4
  71. package/dist/db/fact_disk_storage.js +1 -1
  72. package/dist/db/fact_queries.d.ts.map +1 -1
  73. package/dist/db/fact_queries.js +9 -3
  74. package/dist/db/sql_columns.d.ts +80 -3
  75. package/dist/db/sql_columns.d.ts.map +1 -1
  76. package/dist/db/sql_columns.js +91 -4
  77. package/dist/http/CLAUDE.md +6 -4
  78. package/dist/http/auth_shape.d.ts +19 -6
  79. package/dist/http/auth_shape.d.ts.map +1 -1
  80. package/dist/http/auth_shape.js +19 -6
  81. package/dist/http/jsonrpc_helpers.d.ts +13 -0
  82. package/dist/http/jsonrpc_helpers.d.ts.map +1 -1
  83. package/dist/http/jsonrpc_helpers.js +13 -0
  84. package/dist/http/surface_query.d.ts +29 -4
  85. package/dist/http/surface_query.d.ts.map +1 -1
  86. package/dist/http/surface_query.js +30 -5
  87. package/dist/realtime/sse_auth_guard.d.ts +9 -3
  88. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  89. package/dist/realtime/sse_auth_guard.js +9 -3
  90. package/dist/server/serve_fact_route.d.ts.map +1 -1
  91. package/dist/server/serve_fact_route.js +7 -2
  92. package/dist/testing/CLAUDE.md +54 -28
  93. package/dist/testing/admin_integration.d.ts +0 -3
  94. package/dist/testing/admin_integration.d.ts.map +1 -1
  95. package/dist/testing/admin_integration.js +0 -1
  96. package/dist/testing/adversarial_404.d.ts.map +1 -1
  97. package/dist/testing/adversarial_404.js +4 -1
  98. package/dist/testing/adversarial_input.d.ts.map +1 -1
  99. package/dist/testing/adversarial_input.js +5 -4
  100. package/dist/testing/app_server.d.ts +4 -3
  101. package/dist/testing/app_server.d.ts.map +1 -1
  102. package/dist/testing/app_server.js +11 -5
  103. package/dist/testing/attack_surface.d.ts +21 -2
  104. package/dist/testing/attack_surface.d.ts.map +1 -1
  105. package/dist/testing/attack_surface.js +78 -27
  106. package/dist/testing/audit_completeness.d.ts +0 -3
  107. package/dist/testing/audit_completeness.d.ts.map +1 -1
  108. package/dist/testing/audit_completeness.js +0 -1
  109. package/dist/testing/auth_apps.d.ts +34 -2
  110. package/dist/testing/auth_apps.d.ts.map +1 -1
  111. package/dist/testing/auth_apps.js +43 -7
  112. package/dist/testing/cross_backend/account_lifecycle.d.ts +5 -4
  113. package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
  114. package/dist/testing/cross_backend/account_lifecycle.js +7 -0
  115. package/dist/testing/cross_backend/action_manifest.d.ts +19 -3
  116. package/dist/testing/cross_backend/action_manifest.d.ts.map +1 -1
  117. package/dist/testing/cross_backend/action_manifest.js +10 -4
  118. package/dist/testing/cross_backend/action_manifest_parity.d.ts +9 -3
  119. package/dist/testing/cross_backend/action_manifest_parity.d.ts.map +1 -1
  120. package/dist/testing/cross_backend/action_manifest_parity.js +6 -0
  121. package/dist/testing/cross_backend/actor_lookup.d.ts +3 -2
  122. package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -1
  123. package/dist/testing/cross_backend/body_size.d.ts +4 -2
  124. package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
  125. package/dist/testing/cross_backend/body_size.js +1 -1
  126. package/dist/testing/cross_backend/cell_crud.d.ts +2 -2
  127. package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
  128. package/dist/testing/cross_backend/cell_crud.js +10 -0
  129. package/dist/testing/cross_backend/cell_gated_create.d.ts +3 -3
  130. package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
  131. package/dist/testing/cross_backend/cell_grant_role.d.ts +2 -2
  132. package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -1
  133. package/dist/testing/cross_backend/cell_relations.d.ts +2 -2
  134. package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
  135. package/dist/testing/cross_backend/fact_serving.d.ts +2 -2
  136. package/dist/testing/cross_backend/fact_serving.d.ts.map +1 -1
  137. package/dist/testing/cross_backend/make_cross_backend_project.d.ts +17 -2
  138. package/dist/testing/cross_backend/make_cross_backend_project.d.ts.map +1 -1
  139. package/dist/testing/cross_backend/make_cross_backend_project.js +23 -3
  140. package/dist/testing/cross_backend/origin.d.ts +4 -2
  141. package/dist/testing/cross_backend/origin.d.ts.map +1 -1
  142. package/dist/testing/cross_backend/origin.js +1 -1
  143. package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
  144. package/dist/testing/cross_backend/peer_ping_ws.js +11 -7
  145. package/dist/testing/cross_backend/query_shape.d.ts +6 -5
  146. package/dist/testing/cross_backend/query_shape.d.ts.map +1 -1
  147. package/dist/testing/cross_backend/ready.d.ts +2 -2
  148. package/dist/testing/cross_backend/ready.d.ts.map +1 -1
  149. package/dist/testing/cross_backend/role_grant_offer_enumeration.d.ts.map +1 -1
  150. package/dist/testing/cross_backend/role_grant_offer_enumeration.js +7 -1
  151. package/dist/testing/cross_backend/setup.d.ts +26 -6
  152. package/dist/testing/cross_backend/setup.d.ts.map +1 -1
  153. package/dist/testing/cross_backend/standard.d.ts +5 -1
  154. package/dist/testing/cross_backend/standard.d.ts.map +1 -1
  155. package/dist/testing/cross_backend/standard.js +1 -5
  156. package/dist/testing/cross_backend/testing_backdoor.js +1 -1
  157. package/dist/testing/cross_backend/testing_reset_actions.d.ts +5 -0
  158. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  159. package/dist/testing/cross_backend/testing_reset_actions.js +3 -2
  160. package/dist/testing/cross_backend/token_lifetime.d.ts.map +1 -1
  161. package/dist/testing/cross_backend/token_lifetime.js +6 -0
  162. package/dist/testing/cross_backend/token_scope_surface.d.ts +2 -2
  163. package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
  164. package/dist/testing/cross_backend/token_scope_surface.js +36 -7
  165. package/dist/testing/cross_backend/wire_shapes.d.ts +22 -0
  166. package/dist/testing/cross_backend/wire_shapes.d.ts.map +1 -0
  167. package/dist/testing/cross_backend/wire_shapes.js +41 -0
  168. package/dist/testing/data_exposure.d.ts +0 -3
  169. package/dist/testing/data_exposure.d.ts.map +1 -1
  170. package/dist/testing/data_exposure.js +0 -1
  171. package/dist/testing/db.d.ts +44 -7
  172. package/dist/testing/db.d.ts.map +1 -1
  173. package/dist/testing/db.js +62 -27
  174. package/dist/testing/integration.d.ts.map +1 -1
  175. package/dist/testing/integration.js +15 -2
  176. package/dist/testing/round_trip.d.ts +0 -3
  177. package/dist/testing/round_trip.d.ts.map +1 -1
  178. package/dist/testing/round_trip.js +0 -4
  179. package/dist/testing/rpc_attack_surface.js +2 -2
  180. package/dist/testing/rpc_round_trip.d.ts +0 -3
  181. package/dist/testing/rpc_round_trip.d.ts.map +1 -1
  182. package/dist/testing/rpc_round_trip.js +0 -1
  183. package/dist/testing/schema_generators.d.ts +15 -0
  184. package/dist/testing/schema_generators.d.ts.map +1 -1
  185. package/dist/testing/schema_generators.js +45 -11
  186. package/dist/testing/standard.d.ts +5 -1
  187. package/dist/testing/standard.d.ts.map +1 -1
  188. package/dist/testing/standard.js +2 -7
  189. package/dist/testing/surface_invariants.d.ts +7 -1
  190. package/dist/testing/surface_invariants.d.ts.map +1 -1
  191. package/dist/testing/surface_invariants.js +11 -5
  192. package/dist/timestamp.d.ts +58 -0
  193. package/dist/timestamp.d.ts.map +1 -0
  194. package/dist/timestamp.js +57 -0
  195. package/dist/ui/AdminAccounts.svelte +6 -4
  196. package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
  197. package/dist/ui/AdminInvites.svelte +3 -3
  198. package/dist/ui/AdminInvites.svelte.d.ts.map +1 -1
  199. package/dist/ui/AdminOverview.svelte +10 -10
  200. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
  201. package/dist/ui/AdminRoleGrantHistory.svelte +7 -7
  202. package/dist/ui/AdminRoleGrantHistory.svelte.d.ts.map +1 -1
  203. package/dist/ui/AdminSettings.svelte +1 -1
  204. package/dist/ui/RoleGrantOfferHistory.svelte +3 -1
  205. package/dist/ui/RoleGrantOfferHistory.svelte.d.ts.map +1 -1
  206. package/dist/ui/RoleGrantOfferInbox.svelte +4 -1
  207. package/dist/ui/RoleGrantOfferInbox.svelte.d.ts.map +1 -1
  208. package/dist/ui/SurfaceExplorer.svelte +33 -17
  209. package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
  210. package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -1
  211. package/dist/ui/role_grant_offers_state.svelte.js +6 -2
  212. package/package.json +10 -10
@@ -1 +1 @@
1
- {"version":3,"file":"token_lifetime.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/token_lifetime.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA+C9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC7C,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAaD,eAAO,MAAM,mCAAmC,GAC/C,SAAS,6BAA6B,KACpC,IAiHF,CAAC"}
1
+ {"version":3,"file":"token_lifetime.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/token_lifetime.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAgD9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC7C,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAaD,eAAO,MAAM,mCAAmC,GAC/C,SAAS,6BAA6B,KACpC,IAsHF,CAAC"}
@@ -37,6 +37,7 @@ import '../assert_dev_env.js';
37
37
  import { describe, test, assert } from 'vitest';
38
38
  import { account_token_create_action_spec, account_token_list_action_spec, TokenCreateOutput, TokenListOutput } from '../../auth/account_action_specs.js';
39
39
  import { cross_rpc_call, error_reason, expect_output } from './cell_cross_helpers.js';
40
+ import { assert_iso8601_seconds } from './wire_shapes.js';
40
41
  import { SPINE_RPC_PATH } from './spine_surface_constants.js';
41
42
  /** TTL used by the bounded-mint case. */
42
43
  const TTL_DAYS = 30;
@@ -61,6 +62,9 @@ export const describe_token_lifetime_cross_tests = (options) => {
61
62
  }, fixture.create_session_headers());
62
63
  const created = expect_output(create_res, TokenCreateOutput);
63
64
  assert.ok(created.expires_at !== null, 'a ttl mint must return a non-null expires_at — null means the spine dropped the field');
65
+ // Byte-shape gate: `z.string()` accepts a millisecond stamp too, so
66
+ // `expect_output` above can't tell the two spines apart.
67
+ assert_iso8601_seconds(created.expires_at, 'token_create.expires_at');
64
68
  const expires_ms = Date.parse(created.expires_at);
65
69
  assert.ok(!Number.isNaN(expires_ms), `expires_at must parse as a date: ${created.expires_at}`);
66
70
  const expected = before + TTL_DAYS * 86_400_000;
@@ -71,6 +75,8 @@ export const describe_token_lifetime_cross_tests = (options) => {
71
75
  const row = listed.tokens.find((t) => t.id === created.id);
72
76
  assert.ok(row, 'the minted token must appear in account_token_list');
73
77
  assert.ok(row.expires_at !== null, 'account_token_list must report the stored expiry, not null');
78
+ assert_iso8601_seconds(row.expires_at, 'token_list.expires_at');
79
+ assert_iso8601_seconds(row.created_at, 'token_list.created_at');
74
80
  });
75
81
  test('an eternal mint stores NULL expiry', async () => {
76
82
  const fixture = await setup_test();
@@ -1,7 +1,7 @@
1
1
  import '../assert_dev_env.js';
2
- import type { RpcPathCrossSuiteOptions } from './setup.js';
2
+ import type { RpcPathCapabilityGatedCrossSuiteOptions } from './setup.js';
3
3
  /** Options for the token-scope surface parity suite. */
4
- export interface TokenScopeSurfaceCrossTestOptions extends RpcPathCrossSuiteOptions {
4
+ export interface TokenScopeSurfaceCrossTestOptions extends RpcPathCapabilityGatedCrossSuiteOptions {
5
5
  /** Audit-log SSE path. Default `/api/admin/audit/stream`. */
6
6
  readonly sse_path?: string;
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"token_scope_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/token_scope_surface.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAiE9B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAkB3D,wDAAwD;AACxD,MAAM,WAAW,iCAAkC,SAAQ,wBAAwB;IAClF,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AA2DD,eAAO,MAAM,wCAAwC,GACpD,SAAS,iCAAiC,KACxC,IAwHF,CAAC"}
1
+ {"version":3,"file":"token_scope_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/token_scope_surface.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA8E9B,OAAO,KAAK,EAAE,uCAAuC,EAAE,MAAM,YAAY,CAAC;AAkB1E,wDAAwD;AACxD,MAAM,WAAW,iCAAkC,SAAQ,uCAAuC;IACjG,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAwFD,eAAO,MAAM,wCAAwC,GACpD,SAAS,iCAAiC,KACxC,IAwHF,CAAC"}
@@ -26,6 +26,15 @@ import '../assert_dev_env.js';
26
26
  * `required_scope` capability string, per surface. A spine that renamed a
27
27
  * surface, dropped the `surface:` prefix, or emitted the JSON-RPC envelope
28
28
  * from a REST route fails here.
29
+ * - **The audit stream's channel denial** — that surface is gated to
30
+ * `credential_types: ['session']` on both spines, so a bearer never reaches
31
+ * its scope gate and the observable is `credential_type_required` instead.
32
+ * The case is kept here rather than moved: it is still the bearer-vs-surface
33
+ * probe, and what it now pins is the coarser-gate-first ordering both spines
34
+ * run (a spine that checked scope first would answer `token_scope_required`).
35
+ * `surface:audit_stream`'s own capability string is consequently unreachable
36
+ * on the default mount and has no cross-impl pin — each spine's census holds
37
+ * its declaration, the way the WS upgrade's is held.
29
38
  * - **Scope outranks role** — a narrowed token whose account *also* lacks the
30
39
  * gating role must hear `token_scope_required`, not
31
40
  * `insufficient_permissions`. This is the observable the TS↔Rust ordering
@@ -37,16 +46,17 @@ import '../assert_dev_env.js';
37
46
  * Two non-vacuity controls hold the negatives honest: the narrowed token must
38
47
  * still reach the RPC method it *does* name (otherwise a spine that rejected
39
48
  * the credential outright would pass every denial), and a **full** bearer must
40
- * reach the same surface (otherwise a route broken for all bearers would pass
41
- * too).
49
+ * reach `fact_bare` (otherwise a route broken for all bearers would pass too).
50
+ * The second has no audit-stream twin by construction — refusing every bearer
51
+ * is exactly what that route's channel gate is for.
42
52
  *
43
53
  * ## Why the WS upgrade isn't here
44
54
  *
45
55
  * Rule 3's third probeable surface is the WebSocket upgrade, and it is
46
56
  * deliberately omitted: `create_ws_transport` threads cookies, not bearer
47
57
  * headers, and a refused upgrade surfaces as a thrown connection error whose
48
- * body — the thing this suite exists to compare — isn't readable. The two HTTP
49
- * surfaces are where the denial body is observable, which is what the parity
58
+ * body — the thing this suite exists to compare — isn't readable. The HTTP
59
+ * surfaces are where a denial body is observable, which is what the parity
50
60
  * gate needs. The WS gate keeps its per-spine coverage (fuz_app's surface
51
61
  * census pins the call site; the Rust census pins its own).
52
62
  *
@@ -57,7 +67,7 @@ import '../assert_dev_env.js';
57
67
  */
58
68
  import { describe, assert } from 'vitest';
59
69
  import { account_token_create_action_spec } from '../../auth/account_action_specs.js';
60
- import { ERROR_TOKEN_SCOPE_REQUIRED } from '../../http/error_schemas.js';
70
+ import { ERROR_CREDENTIAL_TYPE_REQUIRED, ERROR_TOKEN_SCOPE_REQUIRED } from '../../http/error_schemas.js';
61
71
  import { test_if } from './capabilities.js';
62
72
  import { cross_rpc_call } from './cell_cross_helpers.js';
63
73
  import { SPINE_RPC_PATH } from './spine_surface_constants.js';
@@ -107,6 +117,25 @@ const assert_surface_denied = async (fresh_transport, path, token, surface) => {
107
117
  const body = await res.json();
108
118
  assert.deepStrictEqual(body, surface_denial_body(surface), `${path} must return the canonical flat surface denial`);
109
119
  };
120
+ /**
121
+ * Assert a bearer GET is refused by the *channel* gate rather than the scope
122
+ * gate — the shape a surface takes once it declares
123
+ * `credential_types: ['session']`.
124
+ *
125
+ * Distinct from `assert_surface_denied` on purpose: a channel-gated surface
126
+ * refuses **every** bearer, narrow or full, so the denial cannot name a scope.
127
+ * A narrowed token is still the right probe — it proves the channel gate runs
128
+ * *first*, since a scope-gated route would have answered
129
+ * `token_scope_required` instead.
130
+ */
131
+ const assert_channel_denied = async (fresh_transport, path, token, credential_types) => {
132
+ const res = await fresh_transport({ origin: null })(path, {
133
+ headers: { authorization: `Bearer ${token}` }
134
+ });
135
+ assert.strictEqual(res.status, 403, `${path} must refuse a bearer with 403`);
136
+ const body = await res.json();
137
+ assert.deepStrictEqual(body, { error: ERROR_CREDENTIAL_TYPE_REQUIRED, required_credential_types: credential_types }, `${path} must return the canonical flat credential-type denial`);
138
+ };
110
139
  export const describe_token_scope_surface_cross_tests = (options) => {
111
140
  const { setup_test, capabilities } = options;
112
141
  const rpc_path = options.rpc_path ?? SPINE_RPC_PATH;
@@ -123,10 +152,10 @@ export const describe_token_scope_surface_cross_tests = (options) => {
123
152
  const res = await cross_rpc_call(fixture.fresh_transport({ origin: null }), rpc_path, ADMITTED_METHOD, undefined, { authorization: `Bearer ${token}` });
124
153
  assert.ok(res.ok, `admitted method must succeed: ${JSON.stringify(res.error)}`);
125
154
  });
126
- test_if(capabilities.sse, 'a narrowed token is refused the audit stream (surface:audit_stream)', async () => {
155
+ test_if(capabilities.sse, 'the audit stream refuses a bearer at the channel gate (credential_types)', async () => {
127
156
  const fixture = await setup_test();
128
157
  const token = await mint_narrowed_token(fixture.transport, rpc_path, fixture.create_session_headers());
129
- await assert_surface_denied(fixture.fresh_transport, sse_path, token, 'audit_stream');
158
+ await assert_channel_denied(fixture.fresh_transport, sse_path, token, ['session']);
130
159
  });
131
160
  test_if(capabilities.fact_serving, 'a narrowed token is refused the bare-hash fact read (surface:fact_bare)', async () => {
132
161
  const fixture = await setup_test();
@@ -0,0 +1,22 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Assert `value` is a timestamp in the spine's canonical wire shape:
4
+ * second-precision UTC, exactly 20 characters. The TS spine emits it from
5
+ * the `iso8601_timestamp_column` SQL projection (`db/sql_columns.ts`) and
6
+ * `to_iso8601_seconds` (`timestamp.ts`); the Rust spine from
7
+ * `fuz_db::iso8601_timestamp_column` and `fuz_sys::rfc3339_now`.
8
+ *
9
+ * @param value - the field read off a wire response
10
+ * @param label - the field's name, for the failure message
11
+ */
12
+ export declare const assert_iso8601_seconds: (value: unknown, label: string) => void;
13
+ /**
14
+ * Assert `value` is either `null` or a canonical second-precision UTC
15
+ * timestamp — the shape of every nullable timestamp on the wire
16
+ * (`expires_at`, `deleted_at`, `updated_at`, …).
17
+ *
18
+ * @param value - the field read off a wire response
19
+ * @param label - the field's name, for the failure message
20
+ */
21
+ export declare const assert_iso8601_seconds_nullable: (value: unknown, label: string) => void;
22
+ //# sourceMappingURL=wire_shapes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire_shapes.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/wire_shapes.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAkB9B;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,GAAI,OAAO,OAAO,EAAE,OAAO,MAAM,KAAG,IAMtE,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,GAAI,OAAO,OAAO,EAAE,OAAO,MAAM,KAAG,IAG/E,CAAC"}
@@ -0,0 +1,41 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Wire-shape assertions shared by the cross-backend suites.
4
+ *
5
+ * A field can carry the right value on both backends and still be a parity
6
+ * defect if the two serialize it differently — a millisecond
7
+ * `2026-05-17T12:34:56.789Z` and a second-precision `2026-05-17T12:34:56Z`
8
+ * both parse as the same instant, so a Zod `z.string()` check and an
9
+ * `assert.ok(value)` pass on either. These helpers pin the bytes.
10
+ *
11
+ * @module
12
+ */
13
+ import { assert } from 'vitest';
14
+ import { ISO8601_SECONDS } from '../../timestamp.js';
15
+ /**
16
+ * Assert `value` is a timestamp in the spine's canonical wire shape:
17
+ * second-precision UTC, exactly 20 characters. The TS spine emits it from
18
+ * the `iso8601_timestamp_column` SQL projection (`db/sql_columns.ts`) and
19
+ * `to_iso8601_seconds` (`timestamp.ts`); the Rust spine from
20
+ * `fuz_db::iso8601_timestamp_column` and `fuz_sys::rfc3339_now`.
21
+ *
22
+ * @param value - the field read off a wire response
23
+ * @param label - the field's name, for the failure message
24
+ */
25
+ export const assert_iso8601_seconds = (value, label) => {
26
+ assert.isTrue(typeof value === 'string', `${label} must be a string, got ${JSON.stringify(value)}`);
27
+ assert.match(value, ISO8601_SECONDS, `${label} must be second-precision UTC`);
28
+ };
29
+ /**
30
+ * Assert `value` is either `null` or a canonical second-precision UTC
31
+ * timestamp — the shape of every nullable timestamp on the wire
32
+ * (`expires_at`, `deleted_at`, `updated_at`, …).
33
+ *
34
+ * @param value - the field read off a wire response
35
+ * @param label - the field's name, for the failure message
36
+ */
37
+ export const assert_iso8601_seconds_nullable = (value, label) => {
38
+ if (value === null)
39
+ return;
40
+ assert_iso8601_seconds(value, label);
41
+ };
@@ -1,6 +1,5 @@
1
1
  import './assert_dev_env.js';
2
2
  import type { AppSurface, AppSurfaceSpec } from '../http/surface.js';
3
- import type { BackendCapabilities } from './cross_backend/capabilities.js';
4
3
  import type { SetupTest } from './cross_backend/setup.js';
5
4
  /**
6
5
  * Recursively collect all property names from a JSON Schema.
@@ -26,8 +25,6 @@ export interface DataExposureTestOptions {
26
25
  * TS by the consumer; same shape for in-process and cross-process tests.
27
26
  */
28
27
  surface_source: AppSurfaceSpec;
29
- /** Backend capability declarations. */
30
- capabilities: BackendCapabilities;
31
28
  /** Fields that must never appear in any response. Default: `sensitive_field_blocklist`. */
32
29
  sensitive_fields?: ReadonlyArray<string>;
33
30
  /** Fields that must not appear in non-admin responses. Default: `admin_only_field_blocklist`. */
@@ -1 +1 @@
1
- {"version":3,"file":"data_exposure.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/data_exposure.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAqB7B,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAYrE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,0BAA0B,CAAC;AAIvE;;;;;GAKG;AACH,eAAO,MAAM,kCAAkC,GAAI,QAAQ,OAAO,KAAG,GAAG,CAAC,MAAM,CAuB9E,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,UAAU,EACnB,mBAAkB,aAAa,CAAC,MAAM,CAA6B,KACjE,IAWF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,wCAAwC,GACpD,SAAS,UAAU,EACnB,oBAAmB,aAAa,CAAC,MAAM,CAA8B,KACnE,IAgBF,CAAC;AAIF,kDAAkD;AAClD,MAAM,WAAW,uBAAuB;IACvC,kEAAkE;IAClE,UAAU,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,2FAA2F;IAC3F,gBAAgB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACzC,iGAAiG;IACjG,iBAAiB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC1C,kDAAkD;IAClD,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,GAAI,SAAS,uBAAuB,KAAG,IA2L/E,CAAC"}
1
+ {"version":3,"file":"data_exposure.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/data_exposure.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAqB7B,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAYrE,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,0BAA0B,CAAC;AAIvE;;;;;GAKG;AACH,eAAO,MAAM,kCAAkC,GAAI,QAAQ,OAAO,KAAG,GAAG,CAAC,MAAM,CAuB9E,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,UAAU,EACnB,mBAAkB,aAAa,CAAC,MAAM,CAA6B,KACjE,IAWF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,wCAAwC,GACpD,SAAS,UAAU,EACnB,oBAAmB,aAAa,CAAC,MAAM,CAA8B,KACnE,IAgBF,CAAC;AAIF,kDAAkD;AAClD,MAAM,WAAW,uBAAuB;IACvC,kEAAkE;IAClE,UAAU,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,2FAA2F;IAC3F,gBAAgB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACzC,iGAAiG;IACjG,iBAAiB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC1C,kDAAkD;IAClD,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,GAAI,SAAS,uBAAuB,KAAG,IA0L/E,CAAC"}
@@ -96,7 +96,6 @@ export const describe_data_exposure_tests = (options) => {
96
96
  const { surface, route_specs } = options.surface_source;
97
97
  const { sensitive_fields = sensitive_field_blocklist, admin_only_fields = admin_only_field_blocklist } = options;
98
98
  const skip_set = new Set(options.skip_routes);
99
- void options.capabilities;
100
99
  describe('data exposure — schema-level', () => {
101
100
  test('no sensitive fields in any output schema', () => {
102
101
  assert_output_schemas_no_sensitive_fields(surface, sensitive_fields);
@@ -15,27 +15,64 @@ export interface DbFactory {
15
15
  skip_reason?: string;
16
16
  }
17
17
  /**
18
- * Reset a PGlite database to a clean state by dropping and recreating the public schema.
18
+ * Reset a test database to a clean state by dropping and recreating the public schema.
19
19
  *
20
20
  * Removes all tables, sequences, indexes, types, and functions.
21
- * The database instance remains usable after reset.
21
+ * The database instance remains usable after reset. Despite the name it issues
22
+ * plain SQL, so it also resets the other drivers the test factories hand out.
22
23
  *
23
24
  * @mutates db - drops the `public` schema and recreates it; all rows in all
24
25
  * tables are gone after this returns.
25
26
  */
26
27
  export declare const reset_pglite: (db: Db) => Promise<void>;
28
+ /**
29
+ * Builds a `DbFactory` from a schema initializer — `create_pglite_factory`'s own shape.
30
+ */
31
+ export type DbFactoryBuilder = (init_schema: (db: Db) => Promise<void>) => DbFactory;
32
+ /**
33
+ * Options for `create_pglite_factory`.
34
+ */
35
+ export interface CreatePgliteFactoryOptions {
36
+ /**
37
+ * Whether an installed substitute (`set_substitute_db_factory`) may stand in
38
+ * for the PGlite factory at this call site. Defaults to `true`; pass `false`
39
+ * where PGlite itself is the subject under test.
40
+ */
41
+ substitutable?: boolean;
42
+ }
43
+ /**
44
+ * Install a builder that stands in for `create_pglite_factory`, so suites
45
+ * written against PGlite can run against another driver without being edited.
46
+ *
47
+ * Injected rather than selected here: the drivers a substitute wraps live in
48
+ * test-side code this shipped module cannot import. A call site that passes
49
+ * `{substitutable: false}` keeps PGlite regardless. Pass `null` to uninstall.
50
+ *
51
+ * The slot is read when a factory is built, not when it creates a database,
52
+ * so install before the first `create_pglite_factory` call — a vitest
53
+ * `setupFiles` entry, which runs ahead of every test file's imports, is the
54
+ * shape fuz_app's own `db` project uses. A factory built earlier (including
55
+ * the module-level fallback in `testing/app_server.ts`) keeps the driver it
56
+ * was built with, and uninstalling does not revert it.
57
+ *
58
+ * @param create_factory - the builder to install, or `null` to remove the installed one
59
+ */
60
+ export declare const set_substitute_db_factory: (create_factory: DbFactoryBuilder | null) => void;
27
61
  /**
28
62
  * Create a pglite (in-memory) database factory for tests.
29
63
  *
30
64
  * Always enabled — no external dependencies required.
31
- * Shares a single PGlite WASM instance across all factories in the same
32
- * vitest worker thread (one test file). Subsequent `create()` calls reset
33
- * the schema via `DROP SCHEMA public CASCADE` instead of paying the WASM
34
- * cold-start cost again.
65
+ * Shares a single PGlite WASM instance across the PGlite factories this module builds.
66
+ * Subsequent `create()` calls reset the schema via `DROP SCHEMA public CASCADE`
67
+ * instead of paying the WASM cold-start cost again.
68
+ *
69
+ * Returns the installed substitute's factory instead when one is installed and
70
+ * `options.substitutable` is not `false` — see `set_substitute_db_factory`.
35
71
  *
36
72
  * @param init_schema - callback to initialize the database schema
73
+ * @param options - `substitutable: false` pins this call site to PGlite
37
74
  */
38
- export declare const create_pglite_factory: (init_schema: (db: Db) => Promise<void>) => DbFactory;
75
+ export declare const create_pglite_factory: (init_schema: (db: Db) => Promise<void>, options?: CreatePgliteFactoryOptions) => DbFactory;
39
76
  /**
40
77
  * Create a pg (PostgreSQL) database factory for tests.
41
78
  *
@@ -1 +1 @@
1
- {"version":3,"file":"db.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/db.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA8B7B,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAMtC;;GAEG;AACH,eAAO,MAAM,KAAK,SAA4B,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAU,IAAI,EAAE,KAAG,OAAO,CAAC,IAAI,CAGvD,CAAC;AAMF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,GAAI,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,KAAG,SAkB7E,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,iBAAiB,GAC7B,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,EACtC,WAAW,MAAM,KACf,SAgEF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,UAQhC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,UAAyC,CAAC;AAEvF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gBAAgB,GAAU,IAAI,EAAE,KAAG,OAAO,CAAC,IAAI,CAG3D,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,GAC9B,WAAW,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,EACvC,iBAAiB,KAAK,CAAC,MAAM,CAAC,KAC5B,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,KAAK,IAAI,CAwBzD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,WAAW,KAAK,CAAC,SAAS,CAAC,KAAG,IAQnE,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,yBAAyB,GACrC,IAAI,EAAE,EACN,OAAO,MAAM,EACb,SAAS,aAAa,CAAC,MAAM,CAAC,KAC5B,OAAO,CAAC,IAAI,CAId,CAAC"}
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/db.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA8B7B,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAMtC;;GAEG;AACH,eAAO,MAAM,KAAK,SAA4B,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,GAAU,IAAI,EAAE,KAAG,OAAO,CAAC,IAAI,CAGvD,CAAC;AAQF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC;AAErF;;GAEG;AACH,MAAM,WAAW,0BAA0B;IAC1C;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAKD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,yBAAyB,GAAI,gBAAgB,gBAAgB,GAAG,IAAI,KAAG,IAEnF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qBAAqB,GACjC,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,EACtC,UAAU,0BAA0B,KAClC,SAyBF,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,iBAAiB,GAC7B,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,EACtC,WAAW,MAAM,KACf,SAgEF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,UAQhC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,UAAyC,CAAC;AAEvF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gBAAgB,GAAU,IAAI,EAAE,KAAG,OAAO,CAAC,IAAI,CAG3D,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,GAC9B,WAAW,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,EACvC,iBAAiB,KAAK,CAAC,MAAM,CAAC,KAC5B,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,KAAK,IAAI,CAwBzD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,WAAW,KAAK,CAAC,SAAS,CAAC,KAAG,IAQnE,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,yBAAyB,GACrC,IAAI,EAAE,EACN,OAAO,MAAM,EACb,SAAS,aAAa,CAAC,MAAM,CAAC,KAC5B,OAAO,CAAC,IAAI,CAId,CAAC"}
@@ -33,10 +33,11 @@ import { query_public_columns } from '../db/schema_ready.js';
33
33
  */
34
34
  export const IS_CI = process.env.CI === 'true';
35
35
  /**
36
- * Reset a PGlite database to a clean state by dropping and recreating the public schema.
36
+ * Reset a test database to a clean state by dropping and recreating the public schema.
37
37
  *
38
38
  * Removes all tables, sequences, indexes, types, and functions.
39
- * The database instance remains usable after reset.
39
+ * The database instance remains usable after reset. Despite the name it issues
40
+ * plain SQL, so it also resets the other drivers the test factories hand out.
40
41
  *
41
42
  * @mutates db - drops the `public` schema and recreates it; all rows in all
42
43
  * tables are gone after this returns.
@@ -45,40 +46,74 @@ export const reset_pglite = async (db) => {
45
46
  await db.query('DROP SCHEMA public CASCADE');
46
47
  await db.query('CREATE SCHEMA public');
47
48
  };
48
- // Module-level PGlite cache — shared across all factories in the same vitest worker (one test file).
49
- // Each vitest file runs in its own worker thread, so no cross-file contamination.
49
+ // Module-level PGlite cache — one instance shared by the PGlite factories this
50
+ // module builds, for as long as the module is loaded. Under the `db` project's
51
+ // `isolate: false` + `fileParallelism: false` that is the whole run; a project
52
+ // that isolates its files gets one per worker instead.
50
53
  let module_db = null;
54
+ // The installed stand-in for `create_pglite_factory`, `null` when none is installed.
55
+ let substitute_db_factory = null;
56
+ /**
57
+ * Install a builder that stands in for `create_pglite_factory`, so suites
58
+ * written against PGlite can run against another driver without being edited.
59
+ *
60
+ * Injected rather than selected here: the drivers a substitute wraps live in
61
+ * test-side code this shipped module cannot import. A call site that passes
62
+ * `{substitutable: false}` keeps PGlite regardless. Pass `null` to uninstall.
63
+ *
64
+ * The slot is read when a factory is built, not when it creates a database,
65
+ * so install before the first `create_pglite_factory` call — a vitest
66
+ * `setupFiles` entry, which runs ahead of every test file's imports, is the
67
+ * shape fuz_app's own `db` project uses. A factory built earlier (including
68
+ * the module-level fallback in `testing/app_server.ts`) keeps the driver it
69
+ * was built with, and uninstalling does not revert it.
70
+ *
71
+ * @param create_factory - the builder to install, or `null` to remove the installed one
72
+ */
73
+ export const set_substitute_db_factory = (create_factory) => {
74
+ substitute_db_factory = create_factory;
75
+ };
51
76
  /**
52
77
  * Create a pglite (in-memory) database factory for tests.
53
78
  *
54
79
  * Always enabled — no external dependencies required.
55
- * Shares a single PGlite WASM instance across all factories in the same
56
- * vitest worker thread (one test file). Subsequent `create()` calls reset
57
- * the schema via `DROP SCHEMA public CASCADE` instead of paying the WASM
58
- * cold-start cost again.
80
+ * Shares a single PGlite WASM instance across the PGlite factories this module builds.
81
+ * Subsequent `create()` calls reset the schema via `DROP SCHEMA public CASCADE`
82
+ * instead of paying the WASM cold-start cost again.
83
+ *
84
+ * Returns the installed substitute's factory instead when one is installed and
85
+ * `options.substitutable` is not `false` — see `set_substitute_db_factory`.
59
86
  *
60
87
  * @param init_schema - callback to initialize the database schema
88
+ * @param options - `substitutable: false` pins this call site to PGlite
61
89
  */
62
- export const create_pglite_factory = (init_schema) => ({
63
- name: 'pglite',
64
- skip: false,
65
- async create() {
66
- if (module_db) {
67
- await reset_pglite(module_db);
68
- }
69
- else {
70
- const { PGlite } = await import('@electric-sql/pglite');
71
- const pglite = new PGlite();
72
- module_db = create_pglite_db(pglite).db;
73
- }
74
- await init_schema(module_db);
75
- return module_db;
76
- },
77
- async close() {
78
- // No-op: shared instance lives for the worker thread's lifetime.
79
- // PGlite is cleaned up when the vitest worker exits.
90
+ export const create_pglite_factory = (init_schema, options) => {
91
+ if (substitute_db_factory && options?.substitutable !== false) {
92
+ return substitute_db_factory(init_schema);
80
93
  }
81
- });
94
+ return {
95
+ name: 'pglite',
96
+ skip: false,
97
+ async create() {
98
+ if (module_db) {
99
+ await reset_pglite(module_db);
100
+ }
101
+ else {
102
+ const { PGlite } = await import('@electric-sql/pglite');
103
+ const pglite = new PGlite();
104
+ module_db = create_pglite_db(pglite).db;
105
+ }
106
+ await init_schema(module_db);
107
+ return module_db;
108
+ },
109
+ async close() {
110
+ // No-op: the instance is the module-level cache above, so it lives as long
111
+ // as this module stays loaded — under the `db` project's `isolate: false` +
112
+ // `fileParallelism: false` that is the whole run. Nothing frees the PGlite
113
+ // instance earlier.
114
+ }
115
+ };
116
+ };
82
117
  /**
83
118
  * Create a pg (PostgreSQL) database factory for tests.
84
119
  *
@@ -1 +1 @@
1
- {"version":3,"file":"integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAsB7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAMhE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAkB1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAW,KAAK,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAG1D;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC9C;;;;OAIG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;;;;OASG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,kEAAkE;IAClE,YAAY,EAAE,mBAAmB,CAAC;IAClC;;;;OAIG;IACH,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;;;;;;;;OAYG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,mCAAmC,GAC/C,SAAS,8BAA8B,KACrC,IA07CF,CAAC"}
1
+ {"version":3,"file":"integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAsB7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAMhE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAkB1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAW,KAAK,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAKpF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAG1D;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC9C;;;;OAIG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;;;;OASG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,kEAAkE;IAClE,YAAY,EAAE,mBAAmB,CAAC;IAClC;;;;OAIG;IACH,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;;;;;;;;OAYG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,mCAAmC,GAC/C,SAAS,8BAA8B,KACrC,IAs8CF,CAAC"}
@@ -25,6 +25,7 @@ import { account_verify_action_spec, account_session_list_action_spec, account_s
25
25
  import { invite_create_action_spec } from '../auth/admin_action_specs.js';
26
26
  import { LoginOutput, AccountStatusOutput } from '../auth/account_route_schema.js';
27
27
  import { test_if } from './cross_backend/capabilities.js';
28
+ import { assert_iso8601_seconds, assert_iso8601_seconds_nullable } from './cross_backend/wire_shapes.js';
28
29
  import { DEFAULT_TEST_PASSWORD } from './test_credentials.js';
29
30
  /**
30
31
  * Standard integration test suite for fuz_app auth routes.
@@ -343,6 +344,13 @@ export const describe_standard_integration_tests = (options) => {
343
344
  // Single-actor keeper: actor resolved, role_grants populated.
344
345
  assert.ok(parsed.actor, 'single-actor keeper must resolve a non-null actor');
345
346
  assert.ok(parsed.role_grants.length > 0, 'single-actor keeper must have populated role_grants');
347
+ // Byte-shape gate: `z.string()` accepts a millisecond stamp too,
348
+ // so the schema parse above can't tell the two spines apart.
349
+ assert_iso8601_seconds(parsed.account.created_at, 'account.created_at');
350
+ for (const grant of parsed.role_grants) {
351
+ assert_iso8601_seconds(grant.created_at, 'role_grant.created_at');
352
+ assert_iso8601_seconds_nullable(grant.expires_at, 'role_grant.expires_at');
353
+ }
346
354
  });
347
355
  });
348
356
  // --- 2. Cookie attributes ---
@@ -419,7 +427,12 @@ export const describe_standard_integration_tests = (options) => {
419
427
  });
420
428
  assert.ok(list_res.ok, 'account_session_list should succeed');
421
429
  assert.ok(list_res.result.sessions.length >= 1);
422
- const session_id = list_res.result.sessions[0].id;
430
+ const session = list_res.result.sessions[0];
431
+ // Byte-shape gate on the session timestamps — see the account-status
432
+ // case for why the output schema alone can't catch the divergence.
433
+ assert_iso8601_seconds(session.created_at, 'session.created_at');
434
+ assert_iso8601_seconds(session.expires_at, 'session.expires_at');
435
+ const session_id = session.id;
423
436
  // Revoke that session by ID
424
437
  const revoke_res = await rpc_call_for_spec({
425
438
  app: { request: fixture.transport },
@@ -691,7 +704,7 @@ export const describe_standard_integration_tests = (options) => {
691
704
  // phase doesn't first demand an acting actor → `actor_required`)
692
705
  // and input/param/query-free (params and query still validate ahead
693
706
  // of the auth phase — only body validation moved behind the gates,
694
- // per the 401 → authz → 403 → 400 order). Admin surfaces are
707
+ // per the 401 → authz → 403 → 429 → 400 order). Admin surfaces are
695
708
  // RPC-first now; a consumer may expose no such REST route (fuz_app
696
709
  // itself, zzz) — then this REST-era check is a no-op and RPC denial
697
710
  // is covered by `describe_rpc_attack_surface_tests`.
@@ -1,5 +1,4 @@
1
1
  import './assert_dev_env.js';
2
- import type { BackendCapabilities } from './cross_backend/capabilities.js';
3
2
  import type { SetupTest, TestFixture } from './cross_backend/setup.js';
4
3
  import type { AppSurfaceSpec } from '../http/surface.js';
5
4
  /** Options for `describe_round_trip_validation`. */
@@ -16,8 +15,6 @@ export interface RoundTripTestOptions {
16
15
  * TS by the consumer; same shape for in-process and cross-process tests.
17
16
  */
18
17
  surface_source: AppSurfaceSpec;
19
- /** Backend capability declarations — see `cross_backend/capabilities.ts`. */
20
- capabilities: BackendCapabilities;
21
18
  /** Routes to skip, in `'METHOD /path'` format. */
22
19
  skip_routes?: Array<string>;
23
20
  /** Override generated bodies for specific routes (`'METHOD /path'` → body). */
@@ -1 +1 @@
1
- {"version":3,"file":"round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA2B7B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGzD,oDAAoD;AACpD,MAAM,WAAW,oBAAoB;IACpC;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,6EAA6E;IAC7E,YAAY,EAAE,mBAAmB,CAAC;IAClC,kDAAkD;IAClD,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5B,+EAA+E;IAC/E,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACvD;;;;;;;;;;;;;OAaG;IACH,gBAAgB,CAAC,EAAE,GAAG,CACrB,MAAM,EACN,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CACnF,CAAC;CACF;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,8BAA8B,GAAI,SAAS,oBAAoB,KAAG,IA6I9E,CAAC"}
1
+ {"version":3,"file":"round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA2B7B,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGzD,oDAAoD;AACpD,MAAM,WAAW,oBAAoB;IACpC;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,kDAAkD;IAClD,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5B,+EAA+E;IAC/E,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACvD;;;;;;;;;;;;;OAaG;IACH,gBAAgB,CAAC,EAAE,GAAG,CACrB,MAAM,EACN,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CACnF,CAAC;CACF;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,8BAA8B,GAAI,SAAS,oBAAoB,KAAG,IAyI9E,CAAC"}
@@ -36,10 +36,6 @@ import { resolve_valid_path, generate_valid_body } from './schema_generators.js'
36
36
  export const describe_round_trip_validation = (options) => {
37
37
  const describe_time_specs = options.surface_source.route_specs;
38
38
  const skip_set = new Set(options.skip_routes);
39
- // `capabilities` is currently unused by this suite (no in-process-only
40
- // reads, no transport-gated cases) but stays on the options for
41
- // uniformity with the other Tier 1 suites.
42
- void options.capabilities;
43
39
  describe('round-trip validation', () => {
44
40
  let fixture;
45
41
  let authed_account;
@@ -84,8 +84,8 @@ const describe_rpc_auth = (options) => {
84
84
  if (!app)
85
85
  throw new Error(`No test app for role '${wrong_role}'`);
86
86
  // Send valid params so the probe varies only the role. Dispatcher
87
- // ordering is 401 → authz → 403 → 400, so the role gate trips
88
- // ahead of validation regardless; valid params also keep the
87
+ // ordering is 401 → authz → 403 → 429 → 400, so the role gate
88
+ // trips ahead of validation regardless; valid params also keep the
89
89
  // authorization phase from answering `actor_required` first.
90
90
  const action = find_rpc_action(rpc_endpoint_specs, endpoint.path, method.name);
91
91
  const params = action ? generate_valid_body(action.spec.input) : undefined;
@@ -1,7 +1,6 @@
1
1
  import './assert_dev_env.js';
2
2
  import type { AppSurfaceSpec } from '../http/surface.js';
3
3
  import { type RpcEndpointsSuiteOption } from './rpc_helpers.js';
4
- import type { BackendCapabilities } from './cross_backend/capabilities.js';
5
4
  import type { SetupTest, TestFixture } from './cross_backend/setup.js';
6
5
  import type { SessionOptions } from '../auth/session_cookie.js';
7
6
  /** Options for `describe_rpc_round_trip_tests`. */
@@ -14,8 +13,6 @@ export interface RpcRoundTripTestOptions {
14
13
  * in-process and cross-process tests.
15
14
  */
16
15
  surface_source: AppSurfaceSpec;
17
- /** Backend capability declarations. */
18
- capabilities: BackendCapabilities;
19
16
  /**
20
17
  * Session config — only needed to resolve factory-form `rpc_endpoints`
21
18
  * against a stub `AppServerContext` at setup time (the actions' input
@@ -1 +1 @@
1
- {"version":3,"file":"rpc_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAyB7B,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,oBAAoB,CAAC;AAE9E,OAAO,EAQN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEhE,mDAAmD;AACnD,MAAM,WAAW,uBAAuB;IACvC,kEAAkE;IAClE,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC;;;;;OAKG;IACH,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;OAKG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC,qDAAqD;IACrD,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,6EAA6E;IAC7E,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACvD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CAC3F;AAoDD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,6BAA6B,GAAI,SAAS,uBAAuB,KAAG,IAgKhF,CAAC"}
1
+ {"version":3,"file":"rpc_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAyB7B,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,oBAAoB,CAAC;AAE9E,OAAO,EAQN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEhE,mDAAmD;AACnD,MAAM,WAAW,uBAAuB;IACvC,kEAAkE;IAClE,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B;;;;;OAKG;IACH,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;OAKG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC,qDAAqD;IACrD,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,6EAA6E;IAC7E,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACvD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CAC3F;AAoDD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,6BAA6B,GAAI,SAAS,uBAAuB,KAAG,IA+JhF,CAAC"}
@@ -88,7 +88,6 @@ export const describe_rpc_round_trip_tests = (options) => {
88
88
  // match what the running backend serves.
89
89
  const rpc_endpoints_for_setup = resolve_rpc_endpoints_for_setup(options.rpc_endpoints, options.session_options);
90
90
  const surface_rpc_endpoints = options.surface_source.surface.rpc_endpoints;
91
- void options.capabilities;
92
91
  describe('RPC round-trip validation', () => {
93
92
  let fixture;
94
93
  let authed_account;
@@ -20,6 +20,21 @@ export declare const generate_valid_value: (field: ZodFieldInfo, field_schema: z
20
20
  /**
21
21
  * Resolve a route path with valid-ish param values so params validation passes.
22
22
  * Used when testing input on routes that also have params.
23
+ *
24
+ * Without a params schema (or for a param the schema doesn't name) each
25
+ * `:name` becomes `test_<name>` — fine for an unconstrained segment. With
26
+ * one, `test_<name>` is still preferred wherever the field schema accepts
27
+ * it, because the param name in the URL is what makes a failure message
28
+ * readable. Only a segment that refuses it falls through: a `uuid` param
29
+ * takes the nil UUID (the fast path — by far the most common shape), and
30
+ * anything else is synthesized by `generate_valid_string`, so a
31
+ * format-constrained segment (a `blake3:…` fact hash, a `tok_…` id)
32
+ * resolves to a value its schema accepts instead of a `test_<name>` that
33
+ * 400s before the route's real gates run.
34
+ *
35
+ * The synthesized value is percent-encoded, and a value containing `/` is
36
+ * refused (it would silently add a path segment and route elsewhere) —
37
+ * both fall back to `test_<name>`.
23
38
  */
24
39
  export declare const resolve_valid_path: (path: string, params_schema?: z.ZodObject) => string;
25
40
  /**
@@ -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,yBAAyB,CAAC;AAIjC;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAI,cAAc,CAAC,CAAC,OAAO,KAAG,MAAM,GAAG,IAkBhE,CAAC;AAsGF,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,GAAI,OAAO,YAAY,EAAE,cAAc,CAAC,CAAC,OAAO,KAAG,OAgGnF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,gBAAgB,CAAC,CAAC,SAAS,KAAG,MAa9E,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,yBAAyB,CAAC;AAIjC;;;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,OAgGnF,CAAC;AAEF;;;;;;;;;;;;;;;;;;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"}