@fuzdev/fuz_app 0.147.0 → 0.149.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 (303) hide show
  1. package/dist/actions/CLAUDE.md +8 -6
  2. package/dist/actions/action_codegen.d.ts +15 -1
  3. package/dist/actions/action_codegen.d.ts.map +1 -1
  4. package/dist/actions/action_codegen.js +23 -2
  5. package/dist/actions/action_event.d.ts.map +1 -1
  6. package/dist/actions/action_event.js +14 -1
  7. package/dist/actions/perform_action.d.ts +17 -1
  8. package/dist/actions/perform_action.d.ts.map +1 -1
  9. package/dist/actions/perform_action.js +23 -3
  10. package/dist/auth/CLAUDE.md +3 -2
  11. package/dist/auth/account_action_specs.d.ts +12 -0
  12. package/dist/auth/account_action_specs.d.ts.map +1 -1
  13. package/dist/auth/account_action_specs.js +16 -2
  14. package/dist/auth/account_actions.d.ts.map +1 -1
  15. package/dist/auth/account_actions.js +7 -4
  16. package/dist/auth/admin_action_specs.d.ts.map +1 -1
  17. package/dist/auth/admin_action_specs.js +29 -8
  18. package/dist/auth/audit_emitter.d.ts +14 -0
  19. package/dist/auth/audit_emitter.d.ts.map +1 -1
  20. package/dist/auth/audit_emitter.js +5 -1
  21. package/dist/auth/cell_action_specs.d.ts +3 -1
  22. package/dist/auth/cell_action_specs.d.ts.map +1 -1
  23. package/dist/auth/cell_action_specs.js +5 -5
  24. package/dist/auth/cell_actions.d.ts.map +1 -1
  25. package/dist/auth/cell_actions.js +2 -3
  26. package/dist/auth/cell_data_schema.d.ts +1 -1
  27. package/dist/auth/cell_data_schema.js +1 -1
  28. package/dist/auth/cell_field_action_specs.d.ts +4 -3
  29. package/dist/auth/cell_field_action_specs.d.ts.map +1 -1
  30. package/dist/auth/cell_field_action_specs.js +5 -6
  31. package/dist/auth/cell_field_actions.d.ts.map +1 -1
  32. package/dist/auth/cell_field_actions.js +8 -1
  33. package/dist/auth/cell_item_action_specs.d.ts +4 -2
  34. package/dist/auth/cell_item_action_specs.d.ts.map +1 -1
  35. package/dist/auth/cell_item_action_specs.js +5 -5
  36. package/dist/auth/cell_item_actions.d.ts.map +1 -1
  37. package/dist/auth/cell_item_actions.js +8 -1
  38. package/dist/auth/role_grant_offer_action_specs.d.ts +2 -0
  39. package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
  40. package/dist/auth/role_grant_offer_action_specs.js +8 -4
  41. package/dist/http/CLAUDE.md +14 -2
  42. package/dist/http/error_schemas.d.ts +8 -0
  43. package/dist/http/error_schemas.d.ts.map +1 -1
  44. package/dist/http/error_schemas.js +12 -0
  45. package/dist/http/jsonrpc_errors.d.ts +40 -15
  46. package/dist/http/jsonrpc_errors.d.ts.map +1 -1
  47. package/dist/http/jsonrpc_errors.js +90 -35
  48. package/dist/primitive_schemas.d.ts +1 -1
  49. package/dist/primitive_schemas.d.ts.map +1 -1
  50. package/dist/primitive_schemas.js +2 -1
  51. package/dist/schema_meta.d.ts +80 -0
  52. package/dist/schema_meta.d.ts.map +1 -1
  53. package/dist/schema_meta.js +71 -1
  54. package/dist/testing/CLAUDE.md +853 -146
  55. package/dist/testing/admin_integration.d.ts.map +1 -1
  56. package/dist/testing/admin_integration.js +73 -5
  57. package/dist/testing/adversarial_input.d.ts +0 -47
  58. package/dist/testing/adversarial_input.d.ts.map +1 -1
  59. package/dist/testing/adversarial_input.js +4 -340
  60. package/dist/testing/adversarial_input_cases.d.ts +124 -0
  61. package/dist/testing/adversarial_input_cases.d.ts.map +1 -0
  62. package/dist/testing/adversarial_input_cases.js +393 -0
  63. package/dist/testing/audit_completeness.d.ts.map +1 -1
  64. package/dist/testing/audit_completeness.js +24 -20
  65. package/dist/testing/audit_drift_guard.d.ts +30 -3
  66. package/dist/testing/audit_drift_guard.d.ts.map +1 -1
  67. package/dist/testing/audit_drift_guard.js +55 -3
  68. package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
  69. package/dist/testing/cross_backend/account_lifecycle.js +29 -12
  70. package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -1
  71. package/dist/testing/cross_backend/actor_lookup.js +7 -25
  72. package/dist/testing/cross_backend/app_settings.d.ts.map +1 -1
  73. package/dist/testing/cross_backend/app_settings.js +4 -6
  74. package/dist/testing/cross_backend/audit_cross_helpers.d.ts +57 -0
  75. package/dist/testing/cross_backend/audit_cross_helpers.d.ts.map +1 -0
  76. package/dist/testing/cross_backend/audit_cross_helpers.js +89 -0
  77. package/dist/testing/cross_backend/backend_config.d.ts +2 -1
  78. package/dist/testing/cross_backend/backend_config.d.ts.map +1 -1
  79. package/dist/testing/cross_backend/body_size.d.ts +6 -0
  80. package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
  81. package/dist/testing/cross_backend/body_size.js +26 -44
  82. package/dist/testing/cross_backend/body_size_smuggling.d.ts +5 -0
  83. package/dist/testing/cross_backend/body_size_smuggling.d.ts.map +1 -1
  84. package/dist/testing/cross_backend/body_size_smuggling.js +7 -14
  85. package/dist/testing/cross_backend/build_test_backend_paths.d.ts +3 -1
  86. package/dist/testing/cross_backend/build_test_backend_paths.d.ts.map +1 -1
  87. package/dist/testing/cross_backend/capabilities.d.ts +78 -59
  88. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  89. package/dist/testing/cross_backend/capabilities.js +25 -24
  90. package/dist/testing/cross_backend/cargo_binary.d.ts +70 -0
  91. package/dist/testing/cross_backend/cargo_binary.d.ts.map +1 -0
  92. package/dist/testing/cross_backend/cargo_binary.js +148 -0
  93. package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
  94. package/dist/testing/cross_backend/cell_crud.js +174 -3
  95. package/dist/testing/cross_backend/cell_gated_create.d.ts +0 -60
  96. package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
  97. package/dist/testing/cross_backend/cell_gated_create.js +9 -487
  98. package/dist/testing/cross_backend/cell_grant_role.d.ts +14 -2
  99. package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -1
  100. package/dist/testing/cross_backend/cell_grant_role.js +22 -12
  101. package/dist/testing/cross_backend/cell_moderate.d.ts +66 -0
  102. package/dist/testing/cross_backend/cell_moderate.d.ts.map +1 -0
  103. package/dist/testing/cross_backend/cell_moderate.js +511 -0
  104. package/dist/testing/cross_backend/cell_ref_admission.d.ts.map +1 -1
  105. package/dist/testing/cross_backend/cell_ref_admission.js +1 -1
  106. package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
  107. package/dist/testing/cross_backend/cell_relations.js +40 -19
  108. package/dist/testing/cross_backend/conformance_case.d.ts +43 -4
  109. package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -1
  110. package/dist/testing/cross_backend/conformance_case.js +29 -1
  111. package/dist/testing/cross_backend/conformance_rpc_auth.d.ts +93 -0
  112. package/dist/testing/cross_backend/conformance_rpc_auth.d.ts.map +1 -0
  113. package/dist/testing/cross_backend/conformance_rpc_auth.js +125 -0
  114. package/dist/testing/cross_backend/conformance_table.d.ts +144 -4
  115. package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
  116. package/dist/testing/cross_backend/conformance_table.js +297 -65
  117. package/dist/testing/cross_backend/cookie_attributes.d.ts.map +1 -1
  118. package/dist/testing/cross_backend/cookie_attributes.js +3 -2
  119. package/dist/testing/cross_backend/create_cross_backend_global_setup.d.ts +38 -5
  120. package/dist/testing/cross_backend/create_cross_backend_global_setup.d.ts.map +1 -1
  121. package/dist/testing/cross_backend/create_cross_backend_global_setup.js +24 -3
  122. package/dist/testing/cross_backend/create_dual_spawn_global_setup.d.ts +5 -5
  123. package/dist/testing/cross_backend/create_dual_spawn_global_setup.d.ts.map +1 -1
  124. package/dist/testing/cross_backend/credential_deadline.d.ts +10 -6
  125. package/dist/testing/cross_backend/credential_deadline.d.ts.map +1 -1
  126. package/dist/testing/cross_backend/credential_deadline.js +32 -31
  127. package/dist/testing/cross_backend/credential_header_robustness.d.ts.map +1 -1
  128. package/dist/testing/cross_backend/credential_header_robustness.js +4 -6
  129. package/dist/testing/cross_backend/{cell_cross_helpers.d.ts → cross_rpc.d.ts} +14 -2
  130. package/dist/testing/cross_backend/cross_rpc.d.ts.map +1 -0
  131. package/dist/testing/cross_backend/{cell_cross_helpers.js → cross_rpc.js} +31 -7
  132. package/dist/testing/cross_backend/default_backend_configs.d.ts +58 -61
  133. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  134. package/dist/testing/cross_backend/default_backend_configs.js +97 -91
  135. package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
  136. package/dist/testing/cross_backend/default_spine_surface.js +2 -2
  137. package/dist/testing/cross_backend/differential.d.ts +126 -0
  138. package/dist/testing/cross_backend/differential.d.ts.map +1 -0
  139. package/dist/testing/cross_backend/differential.js +201 -0
  140. package/dist/testing/cross_backend/differential_normalize.d.ts +150 -0
  141. package/dist/testing/cross_backend/differential_normalize.d.ts.map +1 -0
  142. package/dist/testing/cross_backend/differential_normalize.js +153 -0
  143. package/dist/testing/cross_backend/fact_serving.d.ts.map +1 -1
  144. package/dist/testing/cross_backend/fact_serving.js +27 -17
  145. package/dist/testing/cross_backend/full_spine_mount.d.ts +15 -0
  146. package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
  147. package/dist/testing/cross_backend/full_spine_mount.js +55 -1
  148. package/dist/testing/cross_backend/identity_parity.d.ts.map +1 -1
  149. package/dist/testing/cross_backend/identity_parity.js +6 -5
  150. package/dist/testing/cross_backend/in_process_setup.d.ts +12 -2
  151. package/dist/testing/cross_backend/in_process_setup.d.ts.map +1 -1
  152. package/dist/testing/cross_backend/in_process_setup.js +10 -7
  153. package/dist/testing/cross_backend/login_security.d.ts.map +1 -1
  154. package/dist/testing/cross_backend/login_security.js +13 -18
  155. package/dist/testing/cross_backend/origin.js +1 -1
  156. package/dist/testing/cross_backend/origin_conformance.js +1 -1
  157. package/dist/testing/cross_backend/origin_scope.d.ts +6 -0
  158. package/dist/testing/cross_backend/origin_scope.d.ts.map +1 -1
  159. package/dist/testing/cross_backend/origin_scope.js +13 -12
  160. package/dist/testing/cross_backend/peer_ping_ws.d.ts +2 -5
  161. package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
  162. package/dist/testing/cross_backend/peer_ping_ws.js +14 -24
  163. package/dist/testing/cross_backend/postgres_database.d.ts +85 -0
  164. package/dist/testing/cross_backend/postgres_database.d.ts.map +1 -0
  165. package/dist/testing/cross_backend/postgres_database.js +140 -0
  166. package/dist/testing/cross_backend/provided_context.d.ts +28 -0
  167. package/dist/testing/cross_backend/provided_context.d.ts.map +1 -0
  168. package/dist/testing/cross_backend/provided_context.js +35 -0
  169. package/dist/testing/cross_backend/query_shape.d.ts.map +1 -1
  170. package/dist/testing/cross_backend/query_shape.js +10 -12
  171. package/dist/testing/cross_backend/ready.js +2 -2
  172. package/dist/testing/cross_backend/revocation_drivers.d.ts +4 -3
  173. package/dist/testing/cross_backend/revocation_drivers.d.ts.map +1 -1
  174. package/dist/testing/cross_backend/revocation_drivers.js +1 -1
  175. package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts +3 -2
  176. package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts.map +1 -1
  177. package/dist/testing/cross_backend/role_grant_offer_lifecycle.d.ts +13 -0
  178. package/dist/testing/cross_backend/role_grant_offer_lifecycle.d.ts.map +1 -0
  179. package/dist/testing/cross_backend/role_grant_offer_lifecycle.js +203 -0
  180. package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts +2 -5
  181. package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
  182. package/dist/testing/cross_backend/role_grant_offer_notification_ws.js +203 -111
  183. package/dist/testing/cross_backend/role_grant_participation.d.ts +13 -0
  184. package/dist/testing/cross_backend/role_grant_participation.d.ts.map +1 -1
  185. package/dist/testing/cross_backend/role_grant_participation.js +32 -21
  186. package/dist/testing/cross_backend/role_grant_role_grammar.d.ts.map +1 -1
  187. package/dist/testing/cross_backend/role_grant_role_grammar.js +6 -9
  188. package/dist/testing/cross_backend/rpc_adversarial.d.ts +47 -0
  189. package/dist/testing/cross_backend/rpc_adversarial.d.ts.map +1 -0
  190. package/dist/testing/cross_backend/rpc_adversarial.js +302 -0
  191. package/dist/testing/cross_backend/rpc_success_fixtures.d.ts +24 -0
  192. package/dist/testing/cross_backend/rpc_success_fixtures.d.ts.map +1 -0
  193. package/dist/testing/cross_backend/rpc_success_fixtures.js +124 -0
  194. package/dist/testing/cross_backend/rust_spine_consumer_surface.d.ts +101 -0
  195. package/dist/testing/cross_backend/rust_spine_consumer_surface.d.ts.map +1 -0
  196. package/dist/testing/cross_backend/rust_spine_consumer_surface.js +113 -0
  197. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +10 -0
  198. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
  199. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +33 -21
  200. package/dist/testing/cross_backend/session_cap.d.ts +9 -8
  201. package/dist/testing/cross_backend/session_cap.d.ts.map +1 -1
  202. package/dist/testing/cross_backend/session_cap.js +9 -4
  203. package/dist/testing/cross_backend/session_cookie_codec.js +1 -1
  204. package/dist/testing/cross_backend/setup.d.ts +58 -56
  205. package/dist/testing/cross_backend/setup.d.ts.map +1 -1
  206. package/dist/testing/cross_backend/setup.js +29 -29
  207. package/dist/testing/cross_backend/simple_request.d.ts.map +1 -1
  208. package/dist/testing/cross_backend/simple_request.js +4 -4
  209. package/dist/testing/cross_backend/spawn_backend.d.ts +59 -10
  210. package/dist/testing/cross_backend/spawn_backend.d.ts.map +1 -1
  211. package/dist/testing/cross_backend/spawn_backend.js +219 -60
  212. package/dist/testing/cross_backend/spine_suites.d.ts +311 -0
  213. package/dist/testing/cross_backend/spine_suites.d.ts.map +1 -0
  214. package/dist/testing/cross_backend/spine_suites.js +897 -0
  215. package/dist/testing/cross_backend/spine_surface_constants.d.ts +44 -0
  216. package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
  217. package/dist/testing/cross_backend/spine_surface_constants.js +53 -0
  218. package/dist/testing/cross_backend/sse_round_trip.d.ts.map +1 -1
  219. package/dist/testing/cross_backend/sse_round_trip.js +3 -4
  220. package/dist/testing/cross_backend/standard.d.ts +18 -1
  221. package/dist/testing/cross_backend/standard.d.ts.map +1 -1
  222. package/dist/testing/cross_backend/standard.js +2 -0
  223. package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts +24 -0
  224. package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts.map +1 -1
  225. package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +15 -0
  226. package/dist/testing/cross_backend/testing_backdoor.d.ts +29 -12
  227. package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
  228. package/dist/testing/cross_backend/testing_backdoor.js +143 -51
  229. package/dist/testing/cross_backend/testing_reset_actions.d.ts +30 -14
  230. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  231. package/dist/testing/cross_backend/testing_reset_actions.js +58 -37
  232. package/dist/testing/cross_backend/testing_server_bun.js +1 -1
  233. package/dist/testing/cross_backend/token_lifetime.js +1 -1
  234. package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
  235. package/dist/testing/cross_backend/token_scope_surface.js +9 -13
  236. package/dist/testing/cross_backend/transport_composition.d.ts +26 -2
  237. package/dist/testing/cross_backend/transport_composition.d.ts.map +1 -1
  238. package/dist/testing/cross_backend/transport_composition.js +81 -51
  239. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +8 -0
  240. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
  241. package/dist/testing/cross_backend/ts_spine_backend_config.js +23 -13
  242. package/dist/testing/cross_backend/ws_action_rate_limit.d.ts +2 -5
  243. package/dist/testing/cross_backend/ws_action_rate_limit.d.ts.map +1 -1
  244. package/dist/testing/cross_backend/ws_action_rate_limit.js +8 -14
  245. package/dist/testing/cross_backend/ws_connection_cap.d.ts +4 -9
  246. package/dist/testing/cross_backend/ws_connection_cap.d.ts.map +1 -1
  247. package/dist/testing/cross_backend/ws_connection_cap.js +11 -12
  248. package/dist/testing/cross_backend/ws_endpoint.d.ts +52 -0
  249. package/dist/testing/cross_backend/ws_endpoint.d.ts.map +1 -0
  250. package/dist/testing/cross_backend/ws_endpoint.js +54 -0
  251. package/dist/testing/cross_backend/ws_inbound_dispatch.d.ts +2 -5
  252. package/dist/testing/cross_backend/ws_inbound_dispatch.d.ts.map +1 -1
  253. package/dist/testing/cross_backend/ws_inbound_dispatch.js +9 -15
  254. package/dist/testing/cross_backend/ws_outbound_overflow.d.ts +2 -7
  255. package/dist/testing/cross_backend/ws_outbound_overflow.d.ts.map +1 -1
  256. package/dist/testing/cross_backend/ws_outbound_overflow.js +5 -9
  257. package/dist/testing/cross_backend/ws_role_gated.d.ts +4 -5
  258. package/dist/testing/cross_backend/ws_role_gated.d.ts.map +1 -1
  259. package/dist/testing/cross_backend/ws_role_gated.js +12 -21
  260. package/dist/testing/cross_backend/ws_round_trip.d.ts +12 -9
  261. package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
  262. package/dist/testing/cross_backend/ws_round_trip.js +29 -34
  263. package/dist/testing/db_entities.d.ts +16 -3
  264. package/dist/testing/db_entities.d.ts.map +1 -1
  265. package/dist/testing/db_entities.js +28 -5
  266. package/dist/testing/integration_helpers.d.ts +61 -2
  267. package/dist/testing/integration_helpers.d.ts.map +1 -1
  268. package/dist/testing/integration_helpers.js +75 -18
  269. package/dist/testing/round_trip.d.ts +17 -0
  270. package/dist/testing/round_trip.d.ts.map +1 -1
  271. package/dist/testing/round_trip.js +67 -24
  272. package/dist/testing/rpc_attack_surface.d.ts +10 -0
  273. package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
  274. package/dist/testing/rpc_attack_surface.js +27 -140
  275. package/dist/testing/rpc_envelope_cases.d.ts +114 -0
  276. package/dist/testing/rpc_envelope_cases.d.ts.map +1 -0
  277. package/dist/testing/rpc_envelope_cases.js +243 -0
  278. package/dist/testing/rpc_helpers.d.ts +31 -0
  279. package/dist/testing/rpc_helpers.d.ts.map +1 -1
  280. package/dist/testing/rpc_helpers.js +35 -14
  281. package/dist/testing/rpc_round_trip.d.ts +62 -6
  282. package/dist/testing/rpc_round_trip.d.ts.map +1 -1
  283. package/dist/testing/rpc_round_trip.js +151 -39
  284. package/dist/testing/schema_generators.d.ts +99 -2
  285. package/dist/testing/schema_generators.d.ts.map +1 -1
  286. package/dist/testing/schema_generators.js +261 -73
  287. package/dist/testing/standard.d.ts +26 -0
  288. package/dist/testing/standard.d.ts.map +1 -1
  289. package/dist/testing/standard.js +4 -1
  290. package/dist/testing/stubs.d.ts +4 -3
  291. package/dist/testing/stubs.d.ts.map +1 -1
  292. package/dist/testing/stubs.js +6 -4
  293. package/dist/testing/transports/raw_http.d.ts +21 -0
  294. package/dist/testing/transports/raw_http.d.ts.map +1 -1
  295. package/dist/testing/transports/raw_http.js +30 -0
  296. package/dist/zod.d.ts +9 -0
  297. package/dist/zod.d.ts.map +1 -1
  298. package/dist/zod.js +20 -0
  299. package/package.json +6 -4
  300. package/dist/testing/cross_backend/actor_search.d.ts +0 -6
  301. package/dist/testing/cross_backend/actor_search.d.ts.map +0 -1
  302. package/dist/testing/cross_backend/actor_search.js +0 -90
  303. package/dist/testing/cross_backend/cell_cross_helpers.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adversarial_input_cases.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/adversarial_input_cases.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAmE/F,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,OAAO,0BAA0B,GAAG,OAAO,uBAAuB,CAAC;CACnF;AAED,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAID,+CAA+C;AAC/C,MAAM,WAAW,oBAAoB;IACpC;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,iFAAiF;IACjF,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,yBAAyB,GACrC,cAAc,CAAC,CAAC,OAAO,EACvB,UAAU,oBAAoB,KAC5B,KAAK,CAAC,aAAa,CAmOrB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,GACnC,cAAc,CAAC,CAAC,OAAO,EACvB,WAAW,aAAa,KACtB,MAAM,GAAG,SAG+C,CAAC;AAkC5D;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,GAAI,eAAe,CAAC,CAAC,SAAS,KAAG,KAAK,CAAC,cAAc,CAoB3F,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,yBAAyB,GAAI,cAAc,CAAC,CAAC,SAAS,KAAG,KAAK,CAAC,aAAa,CAkCxF,CAAC"}
@@ -0,0 +1,393 @@
1
+ import './assert_dev_env.js';
2
+ /**
3
+ * Schema-walk generation of adversarial inputs — payloads that must fail
4
+ * validation.
5
+ *
6
+ * Focused cases: one representative wrong-type value per field, one format
7
+ * violation per constrained field, one null and one missing case per required
8
+ * field, plus whole-body structural attacks (non-object body, extra unknown
9
+ * keys). Hono-free, so the cross-process suites reuse the generators the
10
+ * in-process `describe_adversarial_input` and `describe_rpc_attack_surface_tests`
11
+ * run.
12
+ *
13
+ * @module
14
+ */
15
+ import { z } from 'zod';
16
+ import { zod_unwrap_to_object, zod_extract_fields } from '../zod.js';
17
+ import { resolve_invalid_reason } from '../schema_meta.js';
18
+ import { is_null_schema } from '../http/schema_helpers.js';
19
+ import { ERROR_INVALID_REQUEST_BODY, ERROR_INVALID_JSON_BODY } from '../http/error_schemas.js';
20
+ import { detect_format, detect_json_schema_format, format_zod_issues, generate_valid_object, generate_valid_object_with_field, generate_valid_string_param, to_field_json_schema } from './schema_generators.js';
21
+ // --- Payload generation ---
22
+ /** One wrong-type value per base type — one representative is sufficient. */
23
+ const wrong_type_for = (base_type) => {
24
+ switch (base_type) {
25
+ case 'string':
26
+ case 'uuid':
27
+ case 'email':
28
+ return { label: 'number instead of string', value: 42 };
29
+ case 'number':
30
+ case 'int':
31
+ return { label: 'string instead of number', value: 'not_a_number' };
32
+ case 'boolean':
33
+ return { label: 'number instead of boolean', value: 42 };
34
+ case 'array':
35
+ return { label: 'string instead of array', value: 'not_an_array' };
36
+ case 'object':
37
+ return { label: 'string instead of object', value: 'not_an_object' };
38
+ case 'enum':
39
+ return { label: 'number instead of enum', value: 42 };
40
+ default:
41
+ return null;
42
+ }
43
+ };
44
+ /** Format violation payloads — one per constraint type. */
45
+ const format_violation = (format) => {
46
+ switch (format) {
47
+ case 'uuid':
48
+ return { label: 'malformed uuid', value: 'not-a-uuid' };
49
+ case 'email':
50
+ return { label: 'malformed email', value: 'not-an-email' };
51
+ case 'date-time':
52
+ return { label: 'malformed datetime', value: 'not-a-date' };
53
+ case 'pattern':
54
+ return { label: 'pattern violation', value: "'; DROP TABLE --" };
55
+ default:
56
+ return null;
57
+ }
58
+ };
59
+ /**
60
+ * `value` moved by `step`, up when that stays inside the field's numeric range
61
+ * and down otherwise — a number off the integer or `multipleOf` grid that
62
+ * still meets the range, so the grid check is the one a refusal names.
63
+ */
64
+ const off_grid_in_range = (json, value, step) => {
65
+ const up = value + step;
66
+ const over = (typeof json.maximum === 'number' && up > json.maximum) ||
67
+ (typeof json.exclusiveMaximum === 'number' && up >= json.exclusiveMaximum);
68
+ return over ? value - step : up;
69
+ };
70
+ /**
71
+ * Generate adversarial test cases for a route's input schema.
72
+ *
73
+ * Produces focused, non-redundant cases:
74
+ * - Whole-body: send array instead of object, extra unknown key
75
+ * - Missing required fields (without defaults)
76
+ * - One wrong-type value per field
77
+ * - Null for required non-nullable fields
78
+ * - One format violation per constrained field
79
+ * - String length (empty, one under `minLength`, one over `maxLength`), numeric
80
+ * (out of range, non-integer, off a `multipleOf` grid) and array-length
81
+ * (empty, one under `minItems`, one over `maxItems`) boundaries
82
+ *
83
+ * Format and boundaries come from the field's input-side JSON Schema
84
+ * (`to_field_json_schema`), so a field carrying a `.transform()` or pipe and a
85
+ * `.nullable()` / `.nullish()` field get them too. A bound only a `.refine()`
86
+ * enforces generates nothing — JSON Schema can't express it.
87
+ *
88
+ * Each per-field case is single-fault: valid except for the field it breaks.
89
+ * It changes one field of a valid base body — `options.base` when given, else
90
+ * one built by `generate_valid_object` — or, for a field that base omits, of
91
+ * the valid body `generate_valid_object_with_field` builds around it, so a case
92
+ * can't also break an object-level rule (adding `scope_kind` without its
93
+ * paired `scope_id`, or `slug` beside an exactly-one-of `id`). A field no valid
94
+ * body carries keeps its wrong-type and null cases on the base and gets no
95
+ * format or boundary cases, which would test no bound. A per-field case is
96
+ * emitted only when the field refuses its value, so no case is accidentally
97
+ * valid.
98
+ *
99
+ * @param input_schema - the input schema to attack
100
+ * @param options - a base body to seed the cases, and a label for errors
101
+ * @throws Error if the base body fails `input_schema.safeParse` — a seeded
102
+ * base the schema refuses, or a generated one that surfaces broken
103
+ * generation logic — naming the issues rather than producing nonsense
104
+ * adversarial cases.
105
+ */
106
+ export const generate_input_test_cases = (input_schema, options) => {
107
+ if (is_null_schema(input_schema))
108
+ return [];
109
+ const object_schema = zod_unwrap_to_object(input_schema);
110
+ if (!object_schema)
111
+ return [];
112
+ const fields = zod_extract_fields(object_schema);
113
+ const cases = [];
114
+ const for_label = options?.label === undefined ? '' : ` for '${options.label}'`;
115
+ // the base object every case breaks one field of — must pass validation itself
116
+ let base;
117
+ if (options?.base) {
118
+ base = { ...options.base };
119
+ const base_result = input_schema.safeParse(base);
120
+ if (!base_result.success) {
121
+ throw new Error(`adversarial_input: the seeded base${for_label} fails validation — ` +
122
+ `fix the base for: ${format_zod_issues(base_result.error)}`);
123
+ }
124
+ }
125
+ else {
126
+ const generated = generate_valid_object(object_schema, input_schema);
127
+ if (!generated.result.success) {
128
+ throw new Error(`adversarial_input: generated base object fails validation${for_label} — ` +
129
+ `fix generate_valid_value for: ${format_zod_issues(generated.result.error)}`);
130
+ }
131
+ base = generated.value;
132
+ }
133
+ // whole-body structural: send array instead of object
134
+ cases.push({
135
+ label: 'non-object body (array)',
136
+ body: [1, 2, 3],
137
+ expected_error: ERROR_INVALID_JSON_BODY
138
+ });
139
+ // whole-body structural: extra unknown key (enforces strictObject)
140
+ // only emit if the schema rejects unknown keys (i.e. uses z.strictObject)
141
+ const extra_key_result = input_schema.safeParse({ ...base, __adversarial_extra: 'rejected' });
142
+ if (!extra_key_result.success) {
143
+ cases.push({
144
+ label: 'extra unknown key',
145
+ body: { ...base, __adversarial_extra: 'should_be_rejected' },
146
+ expected_error: ERROR_INVALID_REQUEST_BODY
147
+ });
148
+ }
149
+ for (const field of fields) {
150
+ const field_schema = object_schema.shape[field.name];
151
+ // the valid body every case on this field changes only the field of: the
152
+ // base, or for a field the base omits, one that carries it — so the case
153
+ // can't also break an object-level rule (exactly one of, both or neither)
154
+ const field_base = generate_valid_object_with_field(object_schema, input_schema, base, field);
155
+ // every per-field case breaks the field, so the field itself must refuse
156
+ // its value — a candidate it accepts (a `z.coerce` field given a wrong
157
+ // type, a pattern that admits the violation string) is no invalid case.
158
+ // A type case (wrong type, null) on a field no valid body carries falls
159
+ // back to the base; a value case — a format or a bound — is built only on
160
+ // a body that is valid but for the field, or it tests no bound at all
161
+ const push_field_case = (label, value, kind) => {
162
+ if (field_schema.safeParse(value).success)
163
+ return;
164
+ const body_base = field_base ?? (kind === 'type' ? base : null);
165
+ if (!body_base)
166
+ return;
167
+ cases.push({
168
+ label,
169
+ body: { ...body_base, [field.name]: value },
170
+ field: field.name,
171
+ expected_error: ERROR_INVALID_REQUEST_BODY
172
+ });
173
+ };
174
+ // the field's valid value the value cases start from
175
+ const valid_value = field_base?.[field.name];
176
+ // missing required field (skip fields with defaults — Zod fills them)
177
+ if (field.required && !field.has_default && !field_schema.safeParse(undefined).success) {
178
+ const without = {};
179
+ for (const [k, v] of Object.entries(base)) {
180
+ if (k !== field.name)
181
+ without[k] = v;
182
+ }
183
+ cases.push({
184
+ label: `missing: ${field.name}`,
185
+ body: without,
186
+ field: field.name,
187
+ expected_error: ERROR_INVALID_REQUEST_BODY
188
+ });
189
+ }
190
+ // one wrong-type value
191
+ const wrong = wrong_type_for(field.base_type);
192
+ if (wrong)
193
+ push_field_case(`wrong type: ${field.name} (${wrong.label})`, wrong.value, 'type');
194
+ // null for required non-nullable fields
195
+ if (field.required && !field.nullable && field.base_type !== 'null') {
196
+ push_field_case(`null: ${field.name}`, null, 'type');
197
+ }
198
+ // the field's input-side JSON Schema — format, pattern and bounds, read
199
+ // through a transform or pipe and under a nullable's `anyOf`
200
+ const json = to_field_json_schema(field_schema);
201
+ if (!json || !field_base)
202
+ continue; // no constraints, or no body to break them on
203
+ // format violation for constrained string fields
204
+ const format = detect_json_schema_format(json);
205
+ if (format) {
206
+ const violation = format_violation(format);
207
+ if (violation) {
208
+ push_field_case(`format: ${field.name} (${violation.label})`, violation.value, 'value');
209
+ }
210
+ }
211
+ // string length boundaries — `x`s are one UTF-16 unit and one code point
212
+ // each, so the over-`maxLength` string is over in either count
213
+ if (field.base_type === 'string' || field.base_type === 'uuid' || field.base_type === 'email') {
214
+ if (typeof json.minLength === 'number' && json.minLength > 0) {
215
+ push_field_case(`empty string: ${field.name}`, '', 'value');
216
+ }
217
+ // one short of the minimum, distinct from the empty string — a prefix of
218
+ // the valid value where there is one, so a pattern it met still holds
219
+ if (typeof json.minLength === 'number' && json.minLength > 1) {
220
+ const under = json.minLength - 1;
221
+ push_field_case(`under minLength: ${field.name} (${under} chars)`, typeof valid_value === 'string' && valid_value.length >= under
222
+ ? valid_value.slice(0, under)
223
+ : 'x'.repeat(under), 'value');
224
+ }
225
+ if (typeof json.maxLength === 'number') {
226
+ push_field_case(`over maxLength: ${field.name} (${json.maxLength + 1} chars)`, 'x'.repeat(json.maxLength + 1), 'value');
227
+ }
228
+ }
229
+ // numeric boundaries
230
+ if (field.base_type === 'number' || field.base_type === 'int') {
231
+ if (typeof json.minimum === 'number') {
232
+ push_field_case(`below minimum: ${field.name} (${json.minimum - 1})`, json.minimum - 1, 'value');
233
+ }
234
+ if (typeof json.maximum === 'number') {
235
+ push_field_case(`above maximum: ${field.name} (${json.maximum + 1})`, json.maximum + 1, 'value');
236
+ }
237
+ if (typeof json.exclusiveMinimum === 'number') {
238
+ push_field_case(`at exclusive minimum: ${field.name} (${json.exclusiveMinimum})`, json.exclusiveMinimum, 'value');
239
+ }
240
+ if (typeof json.exclusiveMaximum === 'number') {
241
+ push_field_case(`at exclusive maximum: ${field.name} (${json.exclusiveMaximum})`, json.exclusiveMaximum, 'value');
242
+ }
243
+ // 0 and negative for positive-only fields
244
+ if (typeof json.minimum === 'number' && json.minimum > 0) {
245
+ push_field_case(`zero for positive-only: ${field.name}`, 0, 'value');
246
+ push_field_case(`negative for positive-only: ${field.name}`, -1, 'value');
247
+ }
248
+ // off the grid by half a step from the valid value, inside the range,
249
+ // so the integer or `multipleOf` check is the one that refuses
250
+ const valid_number = typeof valid_value === 'number' ? valid_value : 1;
251
+ if (json.type === 'integer') {
252
+ const value = off_grid_in_range(json, valid_number, 0.5);
253
+ push_field_case(`non-integer: ${field.name} (${value})`, value, 'value');
254
+ }
255
+ if (typeof json.multipleOf === 'number' && json.multipleOf > 0) {
256
+ // a whole step for an integer field, so the integer check still holds
257
+ const step = json.type === 'integer' && json.multipleOf > 1 ? 1 : json.multipleOf / 2;
258
+ const value = off_grid_in_range(json, valid_number, step);
259
+ push_field_case(`not multipleOf: ${field.name} (${value}, multipleOf ${json.multipleOf})`, value, 'value');
260
+ }
261
+ }
262
+ // array length boundaries
263
+ if (field.base_type === 'array') {
264
+ if (typeof json.minItems === 'number' && json.minItems > 0) {
265
+ push_field_case(`empty array for minItems > 0: ${field.name}`, [], 'value');
266
+ }
267
+ // one short of the minimum, distinct from the empty array — the valid
268
+ // value's own items, so only the count is wrong
269
+ if (typeof json.minItems === 'number' &&
270
+ json.minItems > 1 &&
271
+ Array.isArray(valid_value) &&
272
+ valid_value.length >= json.minItems - 1) {
273
+ push_field_case(`under minItems: ${field.name} (${json.minItems - 1} items)`, valid_value.slice(0, json.minItems - 1), 'value');
274
+ }
275
+ if (typeof json.maxItems === 'number') {
276
+ // one item over the max (items are null — schema-agnostic)
277
+ push_field_case(`over maxItems: ${field.name} (${json.maxItems + 1} items)`, Array.from({ length: json.maxItems + 1 }, () => null), 'value');
278
+ }
279
+ }
280
+ }
281
+ return cases;
282
+ };
283
+ /**
284
+ * The `error.data.reason` an RPC refusal of `test_case` owes: the declared
285
+ * `invalid_reason` of the field it breaks, resolved as the dispatcher resolves
286
+ * a failure on that field (`resolve_invalid_reason`), or `undefined` — no
287
+ * reason — for an undeclared field or a whole-body case.
288
+ *
289
+ * @param input_schema - the input schema the case was generated from
290
+ * @param test_case - a case `generate_input_test_cases` produced from it
291
+ * @returns the owed reason, or `undefined` when the refusal carries none
292
+ */
293
+ export const expected_invalid_reason = (input_schema, test_case) => test_case.field === undefined
294
+ ? undefined
295
+ : resolve_invalid_reason(input_schema, [test_case.field]);
296
+ // --- Params and query test case generation ---
297
+ /**
298
+ * A valid string record for a route's params or query schema — each field the
299
+ * string `generate_valid_string_param` finds for it — checked against the
300
+ * whole schema, so a case changing one field breaks that field only.
301
+ *
302
+ * @throws Error if a field has no string it accepts, or the record fails the
303
+ * schema, naming the issues
304
+ */
305
+ const generate_string_record_base = (schema, kind) => {
306
+ const base = {};
307
+ for (const field of zod_extract_fields(schema)) {
308
+ const value = generate_valid_string_param(field, schema.shape[field.name]);
309
+ if (value === null) {
310
+ throw new Error(`adversarial_input: no generated ${kind} value passes field '${field.name}'`);
311
+ }
312
+ base[field.name] = value;
313
+ }
314
+ const result = schema.safeParse(base);
315
+ if (!result.success) {
316
+ throw new Error(`adversarial_input: generated ${kind} base fails validation — ` +
317
+ `fix generate_valid_string_param for: ${format_zod_issues(result.error)}`);
318
+ }
319
+ return base;
320
+ };
321
+ /**
322
+ * Generate adversarial test cases for a route's params schema.
323
+ *
324
+ * Params are always strings from URL segments. Only generates cases for
325
+ * format-constrained fields (uuid, pattern) since unconstrained string
326
+ * params accept any string value, and only a violation the field refuses.
327
+ * Each case breaks one field of a valid params record.
328
+ *
329
+ * @throws Error if no valid params record can be generated
330
+ */
331
+ export const generate_params_test_cases = (params_schema) => {
332
+ const fields = zod_extract_fields(params_schema);
333
+ const cases = [];
334
+ const base_params = generate_string_record_base(params_schema, 'params');
335
+ for (const field of fields) {
336
+ const field_schema = params_schema.shape[field.name];
337
+ const format = detect_format(field_schema);
338
+ if (!format)
339
+ continue; // unconstrained string — any value passes
340
+ const violation = format_violation(format);
341
+ if (violation && !field_schema.safeParse(violation.value).success) {
342
+ cases.push({
343
+ label: `${violation.label}: param ${field.name}`,
344
+ params: { ...base_params, [field.name]: violation.value }
345
+ });
346
+ }
347
+ }
348
+ return cases;
349
+ };
350
+ /**
351
+ * Generate adversarial test cases for a route's query schema.
352
+ *
353
+ * Query params are always strings from the URL. Generates cases for:
354
+ * - Missing required fields the field refuses to go without (a preprocessed
355
+ * field with a default accepts its absence)
356
+ * - Format violations on constrained fields (uuid, pattern) the field refuses
357
+ *
358
+ * Each case breaks one field of a valid query record.
359
+ *
360
+ * @throws Error if no valid query record can be generated
361
+ */
362
+ export const generate_query_test_cases = (query_schema) => {
363
+ const fields = zod_extract_fields(query_schema);
364
+ const cases = [];
365
+ const base_query = generate_string_record_base(query_schema, 'query');
366
+ for (const field of fields) {
367
+ const field_schema = query_schema.shape[field.name];
368
+ // missing required field
369
+ if (field.required && !field.has_default && !field_schema.safeParse(undefined).success) {
370
+ const without = {};
371
+ for (const [k, v] of Object.entries(base_query)) {
372
+ if (k !== field.name)
373
+ without[k] = v;
374
+ }
375
+ cases.push({
376
+ label: `missing query: ${field.name}`,
377
+ query: without
378
+ });
379
+ }
380
+ // format violation for constrained string fields
381
+ const format = detect_format(field_schema);
382
+ if (!format)
383
+ continue;
384
+ const violation = format_violation(format);
385
+ if (violation && !field_schema.safeParse(violation.value).success) {
386
+ cases.push({
387
+ label: `${violation.label}: query ${field.name}`,
388
+ query: { ...base_query, [field.name]: violation.value }
389
+ });
390
+ }
391
+ }
392
+ return cases;
393
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA4B7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAUhE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AA2B1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,0BAA0B,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;OAMG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,yEAAyE;IACzE,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;CACvC;AAgGD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAgnBzF,CAAC"}
1
+ {"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA4B7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAUhE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AA2B1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,0BAA0B,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;OAMG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,yEAAyE;IACzE,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;CACvC;AAoGD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAgnBzF,CAAC"}
@@ -54,8 +54,12 @@ const create_admin_observer = (fixture) => fixture.create_account({ username: 'a
54
54
  * `observer` is a dedicated admin account (see `create_admin_observer`)
55
55
  * — its credentials are never the subject of the mutation under test, so the
56
56
  * read works uniformly across every flow including session-revoking ones.
57
+ *
58
+ * In-process only, so no drain precedes the read (`create_test_app` settles a
59
+ * request's effects before it responds) — the cross-process reader is
60
+ * `cross_backend/audit_cross_helpers.ts`'s `list_audit_events`.
57
61
  */
58
- const list_audit_events = async (app, rpc_path, observer, params = {}) => {
62
+ const read_audit_log = async (app, rpc_path, observer, params = {}) => {
59
63
  const res = await rpc_call_for_spec({
60
64
  app,
61
65
  path: rpc_path,
@@ -128,7 +132,7 @@ export const describe_audit_completeness_tests = (options) => {
128
132
  })
129
133
  });
130
134
  assert.strictEqual(res.status, 200);
131
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
135
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
132
136
  assert_has_event(events, 'login', 'POST /login (success)');
133
137
  });
134
138
  test('login failure produces login event with failure outcome', async () => {
@@ -145,7 +149,7 @@ export const describe_audit_completeness_tests = (options) => {
145
149
  })
146
150
  });
147
151
  assert.strictEqual(res.status, 401);
148
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
152
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
149
153
  assert_has_event(events, 'login', 'POST /login (failure)');
150
154
  });
151
155
  test('logout produces logout event', async () => {
@@ -158,7 +162,7 @@ export const describe_audit_completeness_tests = (options) => {
158
162
  headers: fixture.create_session_headers({ 'content-type': 'application/json' })
159
163
  });
160
164
  assert.strictEqual(res.status, 200);
161
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
165
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
162
166
  assert_has_event(events, 'logout', 'POST /logout');
163
167
  });
164
168
  test('token create produces token_create event', async () => {
@@ -176,7 +180,7 @@ export const describe_audit_completeness_tests = (options) => {
176
180
  headers: fixture.create_session_headers()
177
181
  });
178
182
  assert.ok(res.ok, `account_token_create failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
179
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
183
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
180
184
  assert_has_event(events, 'token_create', 'account_token_create RPC');
181
185
  assert_event_credential_type(events, 'token_create', 'session', 'account_token_create RPC');
182
186
  });
@@ -202,7 +206,7 @@ export const describe_audit_completeness_tests = (options) => {
202
206
  headers: fixture.create_session_headers()
203
207
  });
204
208
  assert.ok(res.ok, `account_token_revoke failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
205
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
209
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
206
210
  assert_has_event(events, 'token_revoke', 'account_token_revoke RPC');
207
211
  assert_event_credential_type(events, 'token_revoke', 'session', 'account_token_revoke RPC');
208
212
  });
@@ -242,7 +246,7 @@ export const describe_audit_completeness_tests = (options) => {
242
246
  headers: fixture.create_session_headers()
243
247
  });
244
248
  assert.ok(res.ok, `account_session_revoke failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
245
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
249
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
246
250
  assert_has_event(events, 'session_revoke', 'account_session_revoke RPC');
247
251
  assert_event_credential_type(events, 'session_revoke', 'session', 'account_session_revoke RPC');
248
252
  });
@@ -257,7 +261,7 @@ export const describe_audit_completeness_tests = (options) => {
257
261
  headers: fixture.create_session_headers()
258
262
  });
259
263
  assert.ok(res.ok, `account_session_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
260
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
264
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
261
265
  assert_has_event(events, 'session_revoke_all', 'account_session_revoke_all RPC');
262
266
  assert_event_credential_type(events, 'session_revoke_all', 'session', 'account_session_revoke_all RPC');
263
267
  });
@@ -275,7 +279,7 @@ export const describe_audit_completeness_tests = (options) => {
275
279
  })
276
280
  });
277
281
  assert.strictEqual(res.status, 200);
278
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
282
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
279
283
  assert_has_event(events, 'password_change', 'POST /password');
280
284
  assert_event_credential_type(events, 'password_change', 'session', 'POST /password');
281
285
  });
@@ -298,7 +302,7 @@ export const describe_audit_completeness_tests = (options) => {
298
302
  // Admin offer emits `role_grant_offer_create` only — the role_grant doesn't
299
303
  // exist yet. Drive the accept to confirm `role_grant_offer_accept` and
300
304
  // `role_grant_create` both fire on the downstream consent transition.
301
- const events_after_offer = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
305
+ const events_after_offer = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
302
306
  assert_has_event(events_after_offer, 'role_grant_offer_create', 'role_grant_offer_create RPC');
303
307
  const accept_res = await rpc_call_for_spec({
304
308
  app: { request: fixture.transport },
@@ -308,7 +312,7 @@ export const describe_audit_completeness_tests = (options) => {
308
312
  headers: target.create_session_headers()
309
313
  });
310
314
  assert.ok(accept_res.ok, `role_grant_offer_accept failed: ${accept_res.ok ? '' : JSON.stringify(accept_res.error)}`);
311
- const events_after_accept = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
315
+ const events_after_accept = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
312
316
  assert_has_event(events_after_accept, 'role_grant_offer_accept', 'offer accept RPC');
313
317
  assert_has_event(events_after_accept, 'role_grant_create', 'offer accept RPC');
314
318
  });
@@ -335,7 +339,7 @@ export const describe_audit_completeness_tests = (options) => {
335
339
  headers: fixture.create_session_headers()
336
340
  });
337
341
  assert.ok(revoke_res.ok, `role_grant_revoke failed: ${revoke_res.ok ? '' : JSON.stringify(revoke_res.error)}`);
338
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
342
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
339
343
  assert_has_event(events, 'role_grant_revoke', 'role_grant_revoke RPC');
340
344
  // Audit envelope must populate both target columns —
341
345
  // `role_grant_revoke` is the canonical actor-bound-subject event.
@@ -357,7 +361,7 @@ export const describe_audit_completeness_tests = (options) => {
357
361
  headers: fixture.create_session_headers()
358
362
  });
359
363
  assert.ok(res.ok, `admin_session_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
360
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
364
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
361
365
  // admin session revoke-all also produces session_revoke_all
362
366
  assert_has_event(events, 'session_revoke_all', 'admin_session_revoke_all RPC');
363
367
  });
@@ -373,7 +377,7 @@ export const describe_audit_completeness_tests = (options) => {
373
377
  headers: fixture.create_session_headers()
374
378
  });
375
379
  assert.ok(res.ok, `admin_token_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
376
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
380
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
377
381
  assert_has_event(events, 'token_revoke_all', 'admin_token_revoke_all RPC');
378
382
  });
379
383
  });
@@ -399,7 +403,7 @@ export const describe_audit_completeness_tests = (options) => {
399
403
  headers: fixture.create_session_headers()
400
404
  });
401
405
  assert.ok(delete_res.ok, `invite_delete failed: ${delete_res.ok ? '' : JSON.stringify(delete_res.error)}`);
402
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
406
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
403
407
  assert_has_event(events, 'invite_create', 'invite_create RPC');
404
408
  assert_has_event(events, 'invite_delete', 'invite_delete RPC');
405
409
  });
@@ -417,7 +421,7 @@ export const describe_audit_completeness_tests = (options) => {
417
421
  headers: fixture.create_session_headers()
418
422
  });
419
423
  assert.ok(res.ok, `app_settings_update failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
420
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
424
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
421
425
  assert_has_event(events, 'app_settings_update', 'app_settings_update RPC');
422
426
  });
423
427
  });
@@ -435,7 +439,7 @@ export const describe_audit_completeness_tests = (options) => {
435
439
  headers: fixture.create_session_headers()
436
440
  });
437
441
  assert.ok(res.ok, `account_delete failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
438
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
442
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
439
443
  assert_has_event(events, 'account_delete', 'account_delete RPC');
440
444
  assert_has_event(events, 'actor_delete', 'account_delete RPC (per-actor cascade)');
441
445
  });
@@ -455,7 +459,7 @@ export const describe_audit_completeness_tests = (options) => {
455
459
  headers: fixture.create_daemon_token_headers()
456
460
  });
457
461
  assert.ok(res.ok, `account_purge failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
458
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
462
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
459
463
  assert_has_event(events, 'account_purge', 'account_purge RPC');
460
464
  assert_has_event(events, 'actor_purge', 'account_purge RPC (per-actor cascade)');
461
465
  });
@@ -479,7 +483,7 @@ export const describe_audit_completeness_tests = (options) => {
479
483
  headers: fixture.create_session_headers()
480
484
  });
481
485
  assert.ok(res.ok, `account_undelete failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
482
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
486
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
483
487
  assert_has_event(events, 'account_undelete', 'account_undelete RPC');
484
488
  assert_has_event(events, 'actor_undelete', 'account_undelete RPC (per-actor cascade)');
485
489
  });
@@ -514,7 +518,7 @@ export const describe_audit_completeness_tests = (options) => {
514
518
  })
515
519
  });
516
520
  assert.strictEqual(res.status, 200);
517
- const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
521
+ const events = await read_audit_log({ request: fixture.transport }, rpc_path, observer);
518
522
  assert_has_event(events, 'signup', 'POST /signup');
519
523
  });
520
524
  });
@@ -1,6 +1,6 @@
1
1
  import './assert_dev_env.js';
2
2
  import { type AuditEmitter } from '../auth/audit_emitter.js';
3
- import type { AuditLogInput } from '../auth/audit_log_schema.js';
3
+ import { type AuditLogEventJson, type AuditLogInput } from '../auth/audit_log_schema.js';
4
4
  /**
5
5
  * Register per-test `beforeEach` + `afterEach` hooks that catch any audit
6
6
  * emission with a metadata shape that fails its `audit_metadata_schemas`
@@ -61,8 +61,9 @@ export interface RecordingAuditEmitter {
61
61
  * `notify` is a no-op; `add_listener` records into a local array that
62
62
  * `listener_count` reports, and returns a remover with the production
63
63
  * semantics (per registration, idempotent) — registered listeners never fire,
64
- * since this emitter captures `emit` shapes, not fan-out. `drain_inflight` resolves at once:
65
- * a recorded call writes nothing, so nothing is ever in flight.
64
+ * since this emitter captures `emit` shapes, not fan-out. `drain_inflight` resolves at once
65
+ * and `is_tracking_inflight` answers `false`: a recorded call writes nothing, so nothing is
66
+ * ever in flight.
66
67
  *
67
68
  * `emit` AND `emit_pool` both append to `calls`, so a test reads either
68
69
  * entry point's writes off the same array.
@@ -81,4 +82,30 @@ export interface RecordingAuditEmitter {
81
82
  * so the freeze isn't load-bearing here.
82
83
  */
83
84
  export declare const create_recording_audit_emitter: (calls_ref?: Array<AuditLogInput>) => RecordingAuditEmitter;
85
+ /** Options for `assert_audit_event_metadata`. */
86
+ export interface AssertAuditEventMetadataOptions {
87
+ /**
88
+ * Refuse metadata keys the event type doesn't declare. Off by default: the
89
+ * builtin schemas admit consumer-added fields. For a suite whose backend
90
+ * adds none — a spine's own — so an undeclared key on any spine fails.
91
+ */
92
+ readonly reject_unknown_keys?: boolean;
93
+ }
94
+ /**
95
+ * Assert an audit row read off the wire carries the metadata its event type
96
+ * declares — parsed against `audit_metadata_schemas[event_type]` as declared,
97
+ * so a mistyped, `null`, or missing field fails, and with
98
+ * `reject_unknown_keys` so does a key the event doesn't declare. The wire
99
+ * schema (`AuditLogEventJson.metadata`) is a loose record, and only the TS
100
+ * spine validates metadata, at insert and fail-open, so this is the check that
101
+ * holds a spine's rows to their event's contract.
102
+ *
103
+ * Builtin event types only: a consumer-registered type has no entry here, and
104
+ * passes unexamined.
105
+ *
106
+ * @param event - an audit row from `audit_log_list` or an audit read like it
107
+ * @param options - `reject_unknown_keys` for the closed-world check
108
+ * @throws AssertionError naming the event type and the schema issues
109
+ */
110
+ export declare const assert_audit_event_metadata: (event: Pick<AuditLogEventJson, "event_type" | "outcome" | "metadata">, options?: AssertAuditEventMetadataOptions) => void;
84
111
  //# sourceMappingURL=audit_drift_guard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"audit_drift_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_drift_guard.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAU7B,OAAO,EAGN,KAAK,YAAY,EACjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAEhF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,yBAAyB,QAAO,IAO5C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,QAAO,IAgBxC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,8BAA8B,GAC1C,YAAY,KAAK,CAAC,aAAa,CAAC,KAC9B,qBAqCF,CAAC"}
1
+ {"version":3,"file":"audit_drift_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_drift_guard.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAW7B,OAAO,EAGN,KAAK,YAAY,EACjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAGN,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,MAAM,6BAA6B,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,yBAAyB,QAAO,IAO5C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,QAAO,IAgBxC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,8BAA8B,GAC1C,YAAY,KAAK,CAAC,aAAa,CAAC,KAC9B,qBAsCF,CAAC;AA2BF,iDAAiD;AACjD,MAAM,WAAW,+BAA+B;IAC/C;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CACvC;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,2BAA2B,GACvC,OAAO,IAAI,CAAC,iBAAiB,EAAE,YAAY,GAAG,SAAS,GAAG,UAAU,CAAC,EACrE,UAAU,+BAA+B,KACvC,IAcF,CAAC"}