@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
@@ -4,7 +4,8 @@ import '../assert_dev_env.js';
4
4
  *
5
5
  * `create_app_server` (TS) and the Rust spine both cap the request body at a
6
6
  * 1 MiB default (`DEFAULT_MAX_BODY_SIZE` / `fuz_http`'s
7
- * `DEFAULT_BODY_LIMIT_BYTES`) and reject oversized payloads with `413` and the
7
+ * `DEFAULT_BODY_LIMIT_BYTES`; a backend capping its RPC path elsewhere passes
8
+ * `rpc_max_body_bytes`) and reject oversized payloads with `413` and the
8
9
  * canonical flat REST body `{error: 'payload_too_large'}`. A declared
9
10
  * `Content-Length` over the cap is refused on the header, *before* auth,
10
11
  * origin, or dispatch run (middleware step 4). Each impl unit-tests this in
@@ -55,7 +56,7 @@ import '../assert_dev_env.js';
55
56
  *
56
57
  * - a **declared** over-cap length is sent as the request head alone —
57
58
  * `Content-Length` one over the cap and not one body byte. The 413 must
58
- * arrive within `RAW_READ_TIMEOUT_MS`, which also pins that the refusal is
59
+ * arrive within `RAW_HTTP_READ_TIMEOUT_MS`, which also pins that the refusal is
59
60
  * made on the header: a backend that reads the body before refusing, or has
60
61
  * no cap, waits for bytes that never come and fails the case.
61
62
  * - the **chunked** body is sent up to one byte past the cap and never
@@ -72,7 +73,7 @@ import '../assert_dev_env.js';
72
73
  * imperative suite, not a `conformance_table` row. Runs both legs via the
73
74
  * shared `setup_test` protocol: the in-process leg
74
75
  * (`auth/body_size_parity.db.test.ts`, plain `gro test`) and the cross-process
75
- * leg (`cross_backend/body_size.cross.test.ts`, the TS spine binaries + Rust
76
+ * leg (`cross_backend/spine_suites.cross.test.ts`, the TS spine binaries + Rust
76
77
  * `testing_spine_stub` over real HTTP). The body-size limit is on every spine,
77
78
  * so the suite is ungated.
78
79
  *
@@ -83,16 +84,8 @@ import '../assert_dev_env.js';
83
84
  import { describe, test, assert } from 'vitest';
84
85
  import { account_verify_action_spec } from '../../auth/account_action_specs.js';
85
86
  import { ERROR_PAYLOAD_TOO_LARGE } from '../../http/error_schemas.js';
86
- import { raw_http_exchange, raw_http_parse_response } from '../transports/raw_http.js';
87
- import { SPINE_RPC_PATH } from './spine_surface_constants.js';
88
- /**
89
- * The shared 1 MiB default both impls ship (`DEFAULT_MAX_BODY_SIZE` in TS,
90
- * `DEFAULT_BODY_LIMIT_BYTES` in `fuz_http`). Kept as a local cross-impl
91
- * contract value rather than imported from either impl — the boundary it
92
- * pins is the shared default, and a change to either is a deliberate
93
- * cross-impl decision that would revisit this suite regardless.
94
- */
95
- const BODY_LIMIT_DEFAULT_BYTES = 1024 * 1024;
87
+ import { raw_http_exchange, raw_http_header_lines, raw_http_parse_json, raw_http_parse_response, RAW_HTTP_READ_TIMEOUT_MS } from '../transports/raw_http.js';
88
+ import { SPINE_BODY_LIMIT_DEFAULT_BYTES, SPINE_RPC_PATH } from './spine_surface_constants.js';
96
89
  /**
97
90
  * Build an `account_verify` JSON-RPC envelope whose serialized length is
98
91
  * *exactly* `target_bytes`, by growing a `params.pad` string to fill. Every
@@ -101,6 +94,10 @@ const BODY_LIMIT_DEFAULT_BYTES = 1024 * 1024;
101
94
  * middleware measures. `account_verify` takes no params, but at/over the cap
102
95
  * the size check fires before input validation, so the padded shape is never
103
96
  * reached on the rejection path.
97
+ *
98
+ * The envelope with an empty pad is already 70-odd bytes (more with a longer
99
+ * `id`), so a smaller target overshoots: the body comes out at that floor, not
100
+ * at `target_bytes`.
104
101
  */
105
102
  const sized_envelope = (id, target_bytes) => {
106
103
  const method = account_verify_action_spec.method;
@@ -131,28 +128,10 @@ const streamed_body = (total_bytes) => {
131
128
  }
132
129
  });
133
130
  };
134
- /**
135
- * How long a raw over-cap exchange waits for the refusal. The server has every
136
- * byte it will get when the wait starts, so a backend that refuses on what it
137
- * has answers at once and one that waits for more runs the whole window.
138
- */
139
- const RAW_READ_TIMEOUT_MS = 2000;
140
131
  const to_sized_response = async (res) => ({
141
132
  status: res.status,
142
133
  body: await res.json().catch(() => undefined)
143
134
  });
144
- const parse_json = (text) => {
145
- try {
146
- return JSON.parse(text);
147
- }
148
- catch {
149
- return undefined;
150
- }
151
- };
152
- /** Render request headers as raw header lines. */
153
- const to_header_lines = (headers) => Object.entries(headers)
154
- .map(([name, value]) => `${name}: ${value}\r\n`)
155
- .join('');
156
135
  /**
157
136
  * Write `request_bytes` to the backend's socket and read the one response it
158
137
  * must answer with — without being sent anything more.
@@ -161,17 +140,17 @@ const to_header_lines = (headers) => Object.entries(headers)
161
140
  */
162
141
  const exchange_withholding = async (base_url, request_bytes, withheld) => {
163
142
  const { received, closed_by_server } = await raw_http_exchange(base_url, request_bytes, {
164
- read_timeout_ms: RAW_READ_TIMEOUT_MS,
143
+ read_timeout_ms: RAW_HTTP_READ_TIMEOUT_MS,
165
144
  until: 'response'
166
145
  });
167
146
  const response = raw_http_parse_response(received);
168
147
  assert.ok(response, `expected a whole response with ${withheld} withheld; ` +
169
148
  (closed_by_server
170
149
  ? `the server closed the connection first`
171
- : `none arrived within ${RAW_READ_TIMEOUT_MS}ms — the backend is waiting for bytes ` +
150
+ : `none arrived within ${RAW_HTTP_READ_TIMEOUT_MS}ms — the backend is waiting for bytes ` +
172
151
  `it should refuse without`) +
173
152
  `. Received: ${received ? received.slice(0, 120) : 'nothing'}`);
174
- return { status: response.status, body: parse_json(response.body) };
153
+ return { status: response.status, body: raw_http_parse_json(response.body) };
175
154
  };
176
155
  /** Assert a response is the canonical over-cap refusal. */
177
156
  const assert_payload_too_large = (res, message) => {
@@ -181,6 +160,7 @@ const assert_payload_too_large = (res, message) => {
181
160
  export const describe_body_size_cross_tests = (options) => {
182
161
  const { setup_test, base_url } = options;
183
162
  const rpc_path = options.rpc_path ?? SPINE_RPC_PATH;
163
+ const cap = options.rpc_max_body_bytes ?? SPINE_BODY_LIMIT_DEFAULT_BYTES;
184
164
  /** POST `body` to the RPC path as the keeper. */
185
165
  const post = (fixture, body) => fixture.transport(rpc_path, {
186
166
  method: 'POST',
@@ -194,7 +174,7 @@ export const describe_body_size_cross_tests = (options) => {
194
174
  */
195
175
  const post_over_limit_declared = async (fixture, id) => {
196
176
  const headers = fixture.create_session_headers();
197
- const content_length = BODY_LIMIT_DEFAULT_BYTES + 1;
177
+ const content_length = cap + 1;
198
178
  if (base_url === null) {
199
179
  return to_sized_response(await fixture.transport(rpc_path, {
200
180
  method: 'POST',
@@ -204,7 +184,7 @@ export const describe_body_size_cross_tests = (options) => {
204
184
  }
205
185
  const url = new URL(base_url);
206
186
  return exchange_withholding(base_url, `POST ${rpc_path} HTTP/1.1\r\n` +
207
- to_header_lines({
187
+ raw_http_header_lines({
208
188
  host: url.host,
209
189
  origin: url.origin,
210
190
  ...headers,
@@ -223,18 +203,19 @@ export const describe_body_size_cross_tests = (options) => {
223
203
  return to_sized_response(await fixture.transport(rpc_path, {
224
204
  method: 'POST',
225
205
  headers: { ...fixture.create_session_headers(), 'content-type': 'application/json' },
226
- body: streamed_body(BODY_LIMIT_DEFAULT_BYTES + 1024),
206
+ body: streamed_body(cap + 1024),
227
207
  // a streamed request body needs half-duplex `fetch`
228
208
  duplex: 'half'
229
209
  }));
230
210
  }
231
211
  const url = new URL(base_url);
232
- // whole chunks up to exactly the cap, then a one-byte chunk's size line
233
- // and its byte — and neither that chunk's closing CRLF nor the
234
- // terminating chunk, so the last byte sent is the first one past the cap
235
- const whole_chunk = `${STREAM_CHUNK_BYTES.toString(16)}\r\n${'x'.repeat(STREAM_CHUNK_BYTES)}\r\n`;
212
+ // chunks up to exactly the cap, then a one-byte chunk's size line and its
213
+ // byte — and neither that chunk's closing CRLF nor the terminating chunk,
214
+ // so the last byte sent is the first one past the cap
215
+ const to_chunk = (size) => `${size.toString(16)}\r\n${'x'.repeat(size)}\r\n`;
216
+ const remainder = cap % STREAM_CHUNK_BYTES;
236
217
  return exchange_withholding(base_url, `POST ${rpc_path} HTTP/1.1\r\n` +
237
- to_header_lines({
218
+ raw_http_header_lines({
238
219
  host: url.host,
239
220
  origin: url.origin,
240
221
  ...fixture.create_session_headers(),
@@ -242,7 +223,8 @@ export const describe_body_size_cross_tests = (options) => {
242
223
  'transfer-encoding': 'chunked'
243
224
  }) +
244
225
  `\r\n` +
245
- whole_chunk.repeat(BODY_LIMIT_DEFAULT_BYTES / STREAM_CHUNK_BYTES) +
226
+ to_chunk(STREAM_CHUNK_BYTES).repeat(Math.floor(cap / STREAM_CHUNK_BYTES)) +
227
+ (remainder === 0 ? '' : to_chunk(remainder)) +
246
228
  `1\r\nx`, `the chunked body's end`);
247
229
  };
248
230
  describe('body-size limit parity', () => {
@@ -259,7 +241,7 @@ export const describe_body_size_cross_tests = (options) => {
259
241
  // Exactly at the cap is one byte under the `>` threshold, so it clears
260
242
  // the size gate and reaches the dispatcher. Whatever the dispatcher
261
243
  // then returns, it must not be the 413 — that's the lower boundary.
262
- const res = await post(fixture, sized_envelope('at-limit', BODY_LIMIT_DEFAULT_BYTES));
244
+ const res = await post(fixture, sized_envelope('at-limit', cap));
263
245
  assert.notStrictEqual(res.status, 413, 'a body exactly at the cap must not be size-rejected');
264
246
  });
265
247
  test('under-limit POST (small) → 200', async () => {
@@ -5,6 +5,11 @@ export interface BodySizeSmugglingCrossTestOptions {
5
5
  readonly base_url: string;
6
6
  /** RPC endpoint path to target. Default `/api/rpc`. */
7
7
  readonly rpc_path?: string;
8
+ /**
9
+ * The backend's request-body cap on `rpc_path`, in bytes; the oversized
10
+ * `POST` declares a length past it. Default `SPINE_BODY_LIMIT_DEFAULT_BYTES`.
11
+ */
12
+ readonly rpc_max_body_bytes?: number;
8
13
  }
9
14
  export declare const describe_body_size_smuggling_cross_tests: (options: BodySizeSmugglingCrossTestOptions) => void;
10
15
  //# sourceMappingURL=body_size_smuggling.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"body_size_smuggling.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/body_size_smuggling.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmE9B,4EAA4E;AAC5E,MAAM,WAAW,iCAAiC;IACjD,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,uDAAuD;IACvD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAWD,eAAO,MAAM,wCAAwC,GACpD,SAAS,iCAAiC,KACxC,IA4GF,CAAC"}
1
+ {"version":3,"file":"body_size_smuggling.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/body_size_smuggling.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAuE9B,4EAA4E;AAC5E,MAAM,WAAW,iCAAiC;IACjD,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,uDAAuD;IACvD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,eAAO,MAAM,wCAAwC,GACpD,SAAS,iCAAiC,KACxC,IA4GF,CAAC"}
@@ -59,15 +59,8 @@ import '../assert_dev_env.js';
59
59
  * @module
60
60
  */
61
61
  import { describe, test, assert } from 'vitest';
62
- import { raw_http_count_responses, raw_http_exchange } from '../transports/raw_http.js';
63
- import { SPINE_RPC_PATH } from './spine_surface_constants.js';
64
- /** The shared 1 MiB cap (see `body_size.ts` for why it's a local constant). */
65
- const BODY_LIMIT_DEFAULT_BYTES = 1024 * 1024;
66
- /**
67
- * How long each raw exchange waits for the server to finish answering. An
68
- * exchange the backend does not end by closing runs the whole window.
69
- */
70
- const RAW_READ_TIMEOUT_MS = 2000;
62
+ import { raw_http_count_responses, raw_http_exchange, RAW_HTTP_READ_TIMEOUT_MS } from '../transports/raw_http.js';
63
+ import { SPINE_BODY_LIMIT_DEFAULT_BYTES, SPINE_RPC_PATH } from './spine_surface_constants.js';
71
64
  export const describe_body_size_smuggling_cross_tests = (options) => {
72
65
  const { base_url } = options;
73
66
  const rpc_path = options.rpc_path ?? SPINE_RPC_PATH;
@@ -81,7 +74,7 @@ export const describe_body_size_smuggling_cross_tests = (options) => {
81
74
  const two_requests = `GET ${rpc_path} HTTP/1.1\r\nHost: ${host}\r\n\r\n` +
82
75
  `GET ${rpc_path} HTTP/1.1\r\nHost: ${host}\r\nConnection: close\r\n\r\n`;
83
76
  const { received: response } = await raw_http_exchange(base_url, two_requests, {
84
- read_timeout_ms: RAW_READ_TIMEOUT_MS
77
+ read_timeout_ms: RAW_HTTP_READ_TIMEOUT_MS
85
78
  });
86
79
  const n = raw_http_count_responses(response);
87
80
  assert.ok(n >= 2, `expected ≥2 responses on one keep-alive connection (got ${n}); without ` +
@@ -89,7 +82,7 @@ export const describe_body_size_smuggling_cross_tests = (options) => {
89
82
  `assertion below is not a real signal. Raw head: ${response.slice(0, 120)}`);
90
83
  });
91
84
  test('oversized POST + pipelined GET → no request smuggled', async () => {
92
- const oversized_len = BODY_LIMIT_DEFAULT_BYTES + 1024;
85
+ const oversized_len = (options.rpc_max_body_bytes ?? SPINE_BODY_LIMIT_DEFAULT_BYTES) + 1024;
93
86
  const pipelined_get = `GET ${rpc_path} HTTP/1.1\r\nHost: ${host}\r\n\r\n`;
94
87
  // One write: an over-cap POST (rejected on Content-Length) immediately
95
88
  // followed by a GET.
@@ -101,7 +94,7 @@ export const describe_body_size_smuggling_cross_tests = (options) => {
101
94
  'x'.repeat(oversized_len) +
102
95
  pipelined_get;
103
96
  const { received: response } = await raw_http_exchange(base_url, payload, {
104
- read_timeout_ms: RAW_READ_TIMEOUT_MS
97
+ read_timeout_ms: RAW_HTTP_READ_TIMEOUT_MS
105
98
  });
106
99
  const n = raw_http_count_responses(response);
107
100
  // The reject closes the connection, so the pipelined GET is never
@@ -140,7 +133,7 @@ export const describe_body_size_smuggling_cross_tests = (options) => {
140
133
  chunked_envelope +
141
134
  `GET ${rpc_path} HTTP/1.1\r\nHost: ${host}\r\n\r\n`;
142
135
  const { received, closed_by_server } = await raw_http_exchange(base_url, payload, {
143
- read_timeout_ms: RAW_READ_TIMEOUT_MS
136
+ read_timeout_ms: RAW_HTTP_READ_TIMEOUT_MS
144
137
  });
145
138
  const n = raw_http_count_responses(received);
146
139
  // Refused with 400 or answered — the status is the implementation's.
@@ -151,7 +144,7 @@ export const describe_body_size_smuggling_cross_tests = (options) => {
151
144
  `Raw head: ${received.slice(0, 120)}`);
152
145
  assert.ok(closed_by_server, `expected the server to close the connection after a request carrying both ` +
153
146
  `Content-Length and Transfer-Encoding; it was still open after ` +
154
- `${RAW_READ_TIMEOUT_MS}ms. Raw head: ${received.slice(0, 120)}`);
147
+ `${RAW_HTTP_READ_TIMEOUT_MS}ms. Raw head: ${received.slice(0, 120)}`);
155
148
  });
156
149
  }
157
150
  });
@@ -8,7 +8,9 @@ import '../assert_dev_env.js';
8
8
  * - `bootstrap_token_path` — `FUZ_BOOTSTRAP_TOKEN_PATH`; harness writes
9
9
  * the bootstrap token here before spawn.
10
10
  * - `daemon_token_path` — where `init_daemon_token` (Rust) and the TS
11
- * server's daemon-token writer land the token (under `{root}/run/`).
11
+ * server's daemon-token writer land the token (under `{root}/run/`);
12
+ * `spawn_backend` deletes it before each spawn, so a token a previous run
13
+ * left is never read.
12
14
  */
13
15
  export interface TestBackendPaths {
14
16
  readonly root: string;
@@ -1 +1 @@
1
- {"version":3,"file":"build_test_backend_paths.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/build_test_backend_paths.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAqB9B;;;;;;;;;;GAUG;AACH,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACnC;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GAAI,QAAQ,MAAM,KAAG,gBASzD,CAAC"}
1
+ {"version":3,"file":"build_test_backend_paths.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/build_test_backend_paths.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAqB9B;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACnC;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GAAI,QAAQ,MAAM,KAAG,gBASzD,CAAC"}
@@ -1,43 +1,10 @@
1
1
  import '../assert_dev_env.js';
2
- /**
3
- * Backend wiring facts recorded for documentation — **not** gating flags.
4
- *
5
- * The companion to `BackendCapabilities`: where each capability flag is read
6
- * by a suite (a `test_if(capabilities.X, ...)` gate, or a fork of one case's
7
- * assertion), nothing reads these. They record middleware / limiter wiring that
8
- * differs between the TS and Rust families (a backend-shape record) but gates
9
- * no cross test today. They live in their own type precisely so
10
- * `BackendCapabilities` stops claiming gating power it doesn't have — fold a
11
- * flag in here the moment it has no `test_if` reader, and promote it back the
12
- * day a suite genuinely gates on it.
13
- */
14
- export interface BackendShapeNotes {
15
- /**
16
- * Bearer-token auth (`Authorization: Bearer <token>`) is wired through the
17
- * backend's middleware stack. `true` on every spine — the bearer-token cases
18
- * in `describe_standard_integration_tests` / `describe_rate_limiting_tests`
19
- * run unconditionally.
20
- */
21
- readonly bearer_auth: boolean;
22
- /**
23
- * Trusted-proxy XFF parsing (`X-Forwarded-For` etc.) is wired. Records the
24
- * proxy-default difference between the TS family (`false` — the test binary
25
- * leaves proxy parsing off) and the Rust family (`true` — the client-IP
26
- * middleware is always wired; the env-gate only chooses XFF vs the TCP peer).
27
- */
28
- readonly trusted_proxy: boolean;
29
- /**
30
- * Per-account login rate limiting is wired. `false` for the TS family (the
31
- * canonical path leaves the limiter null in test mode), `true` for the Rust
32
- * family (env-gated bucket on `/login` + `/password`).
33
- */
34
- readonly login_rate_limit: boolean;
35
- }
2
+ import type { TestingBackdoorMethod } from './spine_surface_constants.js';
36
3
  /**
37
4
  * Optional behaviors a backend may support. Each flag's TSDoc names the
38
5
  * tests that gate on it; add a new flag here before referencing it from
39
- * a suite body, and document the gating tests inline. Wiring facts that gate
40
- * nothing belong in `BackendShapeNotes`, not here.
6
+ * a suite body, and document the gating tests inline. A wiring fact no
7
+ * suite reads doesn't belong here.
41
8
  */
42
9
  export interface BackendCapabilities {
43
10
  /**
@@ -93,15 +60,28 @@ export interface BackendCapabilities {
93
60
  */
94
61
  readonly account_lifecycle: boolean;
95
62
  /**
96
- * The cell-gated fact-serving routes (`GET /api/cells/:cell_id/facts/:hash`
97
- * + the admin-only `GET /api/facts/:hash`) are live-mounted on the backend,
98
- * its DB carries the `fuz_facts` migration namespace, and it registers the
99
- * `_testing_put_fact` seeder. Gates `describe_fact_serving_cross_tests` —
100
- * the per-reference read model (cell-scoped admit via a viewable cell,
101
- * cross-owner-dedup-no-leak, 404-mask, bare-hash admin-only). Like cells,
102
- * the serve routes stay off the standard declared surface.
63
+ * The cell-gated fact-serving route (`GET /api/cells/:cell_id/facts/:hash`)
64
+ * is live-mounted on the backend, its DB carries the `fuz_facts` migration
65
+ * namespace, and it mounts the `_testing_put_fact` seeder — declared in
66
+ * `testing_backdoors`, which `describe_spine_cross_suites` requires. Gates
67
+ * `describe_fact_serving_cross_tests`' per-reference read model (cell-scoped
68
+ * admit via a viewable cell, cross-owner-dedup-no-leak, 404-mask),
69
+ * `describe_cell_ref_admission_cross_tests`, and the fact cases of the
70
+ * moderation suite. Like cells, the serve route stays off the standard
71
+ * declared surface. The admin bare-hash route is its own flag,
72
+ * `fact_serving_admin_hash`.
103
73
  */
104
74
  readonly fact_serving: boolean;
75
+ /**
76
+ * The admin-only bare-hash fact route (`GET /api/facts/:hash`) is
77
+ * live-mounted. Gates the bare-hash cases of
78
+ * `describe_fact_serving_cross_tests` (with `fact_serving`, whose seeder
79
+ * they use), `describe_query_shape_cross_tests` and
80
+ * `describe_token_scope_surface_cross_tests`. A backend serving only the
81
+ * cell-gated route states `fact_serving: true, fact_serving_admin_hash:
82
+ * false`.
83
+ */
84
+ readonly fact_serving_admin_hash: boolean;
105
85
  /**
106
86
  * The `/ready` readiness deploy gate (`GET /ready`) is live-mounted on the
107
87
  * backend — the public column-presence schema-drift probe over the committed
@@ -130,13 +110,12 @@ export interface BackendCapabilities {
130
110
  * rejection, per-connection id isolation, never-replying `Timeout`,
131
111
  * wrong-shape reply rejection).
132
112
  *
133
- * `true` for both the Rust spine (server-initiated requests landed
134
- * Rust-first canonical) and the TS spine
135
- * (`BackendWebsocketTransport.request_connection` drives the round-trip,
136
- * correlated by `register_action_ws`). The conservative
137
- * `ts_default_capabilities` keeps it `false` like sse/ready — a TS backend
138
- * opts in once it mounts `peer/ping` on a WS endpoint **and** the HTTP RPC
139
- * endpoint (the no-transport refusal). Like ws/sse/cells, `peer/ping` stays
113
+ * `true` on fuz_app's own spine presets — the Rust `testing_spine_stub` and
114
+ * the TS spine (`BackendWebsocketTransport.request_connection` drives the
115
+ * round-trip, correlated by `register_action_ws`). A `MountFlag`: a backend
116
+ * states `true` once it mounts `peer/ping` on a WS endpoint **and** the HTTP
117
+ * RPC endpoint (the no-transport refusal).
118
+ * Like ws/sse/cells, `peer/ping` stays
140
119
  * off the standard declared surface (it's a protocol action,
141
120
  * manifest-excluded), so this flag is the only opt-in into the peer parity
142
121
  * coverage.
@@ -171,9 +150,9 @@ export interface BackendCapabilities {
171
150
  *
172
151
  * `true` on fuz_app's own spine presets — the TS spine binary mounts its
173
152
  * full action set on `/api/ws`, and the Rust `testing_spine_stub` serves
174
- * one action registry on RPC and WS. `false` in the family defaults:
175
- * a consumer's WS endpoint carries whatever its domain mounts, so it opts in
176
- * once the account actions are there.
153
+ * one action registry on RPC and WS. A `MountFlag`: a consumer's WS endpoint
154
+ * carries whatever its domain mounts, so it states `true` once the account
155
+ * actions are there.
177
156
  */
178
157
  readonly ws_account_actions: boolean;
179
158
  /**
@@ -214,7 +193,53 @@ export interface BackendCapabilities {
214
193
  * Deno, Bun, and the Rust spine, which send the response as built.
215
194
  */
216
195
  readonly ws_refusal_body: boolean;
196
+ /**
197
+ * The `_testing_*` backdoors the backend mounts on its RPC endpoint, from
198
+ * `TESTING_BACKDOOR_METHODS` (a consumer's own seams stay off it). Not a
199
+ * flag, but declared like a `MountFlag`: no family default, so every config
200
+ * states it. `describe_testing_backdoor_cross_tests` checks the declaration
201
+ * against the backend — a declared method must be mounted, an undeclared one
202
+ * must answer `method_not_found` — and runs its credential-gate rows over
203
+ * the declared ones; `describe_spine_cross_suites` requires `_testing_reset`
204
+ * and `_testing_drain_effects` (the per-test reset and the audit barrier),
205
+ * skips a suite whose backdoor is undeclared, naming it — `credential_deadline`
206
+ * (`_testing_shorten_credential_expiry`), `origin_conformance`
207
+ * (`_testing_origin_check`), `ws_outbound_overflow`
208
+ * (`_testing_flood_notifications`) — and requires `_testing_put_fact` of a
209
+ * backend declaring `fact_serving`.
210
+ *
211
+ * fuz_app's spine presets declare every one; the in-process transport none
212
+ * (the backdoor suites are cross-process only).
213
+ */
214
+ readonly testing_backdoors: ReadonlyArray<TestingBackdoorMethod>;
217
215
  }
216
+ /**
217
+ * The capabilities that describe a backend's own mounts, never a runtime
218
+ * trait: what a consumer chooses to serve. They have no family default —
219
+ * state each flag in your backend config's `capabilities`; a missing one
220
+ * would silently skip its suites, so leaving one out is a type error.
221
+ *
222
+ * `Pick` keeps the list honest: a name that is not a `BackendCapabilities`
223
+ * flag fails to compile.
224
+ */
225
+ export type MountFlag = keyof Pick<BackendCapabilities, 'ws' | 'sse' | 'cell_crud' | 'cell_relations' | 'cell_gated_create' | 'fact_serving' | 'fact_serving_admin_hash' | 'ready' | 'peer_request' | 'ws_account_actions' | 'ws_role_gated'>;
226
+ /**
227
+ * A family's default capabilities: every flag but the `MountFlag`s and the
228
+ * `testing_backdoors` list, which each backend config states itself. Spread
229
+ * one and add every mount flag and the backdoor list:
230
+ *
231
+ * ```ts
232
+ * capabilities: {
233
+ * ...rust_default_capabilities,
234
+ * ws: true, sse: true, ready: true, peer_request: true,
235
+ * cell_crud: false, cell_relations: false, cell_gated_create: false,
236
+ * fact_serving: false, fact_serving_admin_hash: false,
237
+ * ws_account_actions: false, ws_role_gated: false,
238
+ * testing_backdoors: ['_testing_reset', '_testing_drain_effects'],
239
+ * }
240
+ * ```
241
+ */
242
+ export type FamilyDefaultCapabilities = Omit<BackendCapabilities, MountFlag | 'testing_backdoors'>;
218
243
  /**
219
244
  * Capability declarations for the in-process Hono transport. Nearly every
220
245
  * flag is `true` because in-process testing exercises the full backend
@@ -229,12 +254,6 @@ export interface BackendCapabilities {
229
254
  * per backend.
230
255
  */
231
256
  export declare const in_process_capabilities: BackendCapabilities;
232
- /**
233
- * Shape notes for the in-process Hono transport — every wiring fact present
234
- * (the in-process app exercises the full middleware stack). Documentation
235
- * only, like every `BackendShapeNotes` (nothing reads it).
236
- */
237
- export declare const in_process_shape_notes: BackendShapeNotes;
238
257
  /**
239
258
  * Conditional `test()` wrapper — registers a vitest case only when
240
259
  * `cond` is true; otherwise registers it as `.skip` so the run still
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA2C9B;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IACjC;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;CACnC;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC;;;;;;;;OAQG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B;;;;;;;;OAQG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB;;;;;;;;OAQG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,uBAAuB,EAAE,OAAO,CAAC;IAC1C;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;IACrC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC;;;;;;;;;OASG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CAClC;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAwBpC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,iBAInC,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,EAAE,IAAI,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAG,IAMrF,CAAC"}
1
+ {"version":3,"file":"capabilities.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAkD9B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B;;;;;;;;OAQG;IACH,QAAQ,CAAC,uBAAuB,EAAE,OAAO,CAAC;IAC1C;;;;;;;;OAQG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB;;;;;;;;OAQG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;;;;;;;;;;;;;;;OAiBG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,uBAAuB,EAAE,OAAO,CAAC;IAC1C;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;IACrC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC;;;;;;;;;OASG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC;;;;;;;;;;;;;;;;;OAiBG;IACH,QAAQ,CAAC,iBAAiB,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;CACjE;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,IAAI,CACjC,mBAAmB,EACjB,IAAI,GACJ,KAAK,GACL,WAAW,GACX,gBAAgB,GAChB,mBAAmB,GACnB,cAAc,GACd,yBAAyB,GACzB,OAAO,GACP,cAAc,GACd,oBAAoB,GACpB,eAAe,CACjB,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAAC,mBAAmB,EAAE,SAAS,GAAG,mBAAmB,CAAC,CAAC;AAEnG;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBA4BpC,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,EAAE,IAAI,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAG,IAMrF,CAAC"}
@@ -18,22 +18,29 @@ import '../assert_dev_env.js';
18
18
  * - `in_process_capabilities` — here; every flag `true` except the
19
19
  * cross-process-only ones.
20
20
  * - `ts_default_capabilities` / `rust_default_capabilities` — consumer-facing
21
- * family defaults, in `default_backend_configs.ts` (full literals, so adding
22
- * a capability is a compile error until each family declares it).
21
+ * family defaults, in `default_backend_configs.ts` — every flag but the
22
+ * `MountFlag`s (`FamilyDefaultCapabilities`), as full literals, so adding a
23
+ * non-mount capability is a compile error until each family declares it.
23
24
  * - `ts_spine_capabilities` / `ts_spine_bun_capabilities` — fuz_app's own TS
24
- * spine presets, in `ts_spine_backend_config.ts` (deltas off the family
25
- * default; Bun flips `ws_handshake_pipelining`).
25
+ * spine presets, in `ts_spine_backend_config.ts` (the family
26
+ * default plus every mount flag; Bun flips `ws_handshake_pipelining`).
26
27
  * - `rust_spine_stub_capabilities` — fuz_app's Rust spine-stub preset, in
27
- * `rust_spine_stub_backend_config.ts` (delta off the rust family default).
28
+ * `rust_spine_stub_backend_config.ts` (the rust family default plus every mount flag).
28
29
  *
29
- * **Gating flags vs shape notes.** `BackendCapabilities` holds only flags a
30
- * suite actually gates on (each has a `test_if(capabilities.X, ...)` reader,
31
- * or — `ws_refusal_body` — picks how a case reads a refusal).
32
- * Wiring facts that gate nothing — `bearer_auth` / `trusted_proxy` /
33
- * `login_rate_limit` — live in the parallel `BackendShapeNotes` record
34
- * (`in_process_shape_notes` here, `ts_default_shape_notes` /
35
- * `rust_default_shape_notes` in `default_backend_configs.ts`) so the
36
- * capability type never claims gating power it doesn't have.
30
+ * **Mount flags have no default.** A flag naming something a backend mounts
31
+ * (a `MountFlag`) is never inherited, nor is the `testing_backdoors` list: the
32
+ * family defaults are `FamilyDefaultCapabilities`, which omits them, so every
33
+ * backend config states each one. The family defaults carry only what the family guarantees — the
34
+ * declared spine surface's mounts (`account_lifecycle`, `account_status`) and
35
+ * the runtime behavior of its reference runtime (`ws_handshake_pipelining`,
36
+ * `ws_refusal_body`: Node for TS, hyper for Rust), which a backend on another
37
+ * runtime overrides.
38
+ *
39
+ * **Gating flags only.** `BackendCapabilities` holds only flags a suite
40
+ * actually gates on (each has a `test_if(capabilities.X, ...)` reader, or —
41
+ * `ws_refusal_body` — picks how a case reads a refusal), plus the
42
+ * `testing_backdoors` list the backdoor suite and `describe_spine_cross_suites`
43
+ * read. A wiring fact no suite reads is not a capability.
37
44
  *
38
45
  * @module
39
46
  */
@@ -58,6 +65,7 @@ export const in_process_capabilities = Object.freeze({
58
65
  cell_relations: true,
59
66
  account_lifecycle: true,
60
67
  fact_serving: true,
68
+ fact_serving_admin_hash: true,
61
69
  ready: true,
62
70
  account_status: true,
63
71
  peer_request: false,
@@ -74,17 +82,10 @@ export const in_process_capabilities = Object.freeze({
74
82
  ws_role_gated: false,
75
83
  // Read only by the front-door table's WS rows, which skip in-process (no
76
84
  // socket to upgrade); Hono's own refusals carry their bodies.
77
- ws_refusal_body: true
78
- });
79
- /**
80
- * Shape notes for the in-process Hono transport — every wiring fact present
81
- * (the in-process app exercises the full middleware stack). Documentation
82
- * only, like every `BackendShapeNotes` (nothing reads it).
83
- */
84
- export const in_process_shape_notes = Object.freeze({
85
- bearer_auth: true,
86
- trusted_proxy: true,
87
- login_rate_limit: true
85
+ ws_refusal_body: true,
86
+ // The in-process app mounts no `_testing_*` action; the suites reading this
87
+ // are cross-process only.
88
+ testing_backdoors: Object.freeze([])
88
89
  });
89
90
  /**
90
91
  * Conditional `test()` wrapper — registers a vitest case only when
@@ -0,0 +1,70 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Env var skipping the build, for iteration on a known-current binary: `1` or
4
+ * `true` skips it, any other value (`0`, `false`, empty) builds.
5
+ */
6
+ export declare const NO_REBUILD_ENV = "FUZ_TESTING_NO_REBUILD";
7
+ /**
8
+ * Whether an `NO_REBUILD_ENV` value asks to skip the build — only an explicit
9
+ * `1` / `true`, so `FUZ_TESTING_NO_REBUILD=0` still builds.
10
+ *
11
+ * @param value - the variable's value, `undefined` when unset
12
+ */
13
+ export declare const is_no_rebuild_requested: (value: string | undefined) => boolean;
14
+ /** Options naming one Cargo-built binary. */
15
+ export interface CargoBinaryOptions {
16
+ /** The Cargo workspace (or package) directory the build runs in. */
17
+ readonly workspace_dir: string;
18
+ /** The Cargo package holding the binary (`cargo build -p`). */
19
+ readonly package: string;
20
+ /** The binary target (`cargo build --bin`). Default the package name. */
21
+ readonly bin?: string;
22
+ /** The Cargo profile. Default `'dev'`, whose output is `debug/`. */
23
+ readonly profile?: string;
24
+ }
25
+ /**
26
+ * The directory under the target directory a profile's output lands in —
27
+ * `debug` for `dev`, the profile's own name otherwise (`release`, a custom
28
+ * `cross-test`).
29
+ *
30
+ * @param profile - the Cargo profile
31
+ */
32
+ export declare const to_cargo_profile_dir: (profile: string) => string;
33
+ /**
34
+ * The binary path `cargo build --profile <profile> --bin <bin>` writes, from
35
+ * `cargo metadata --no-deps --format-version 1` output.
36
+ *
37
+ * @param metadata_json - the `cargo metadata` output
38
+ * @param bin - the binary's name
39
+ * @param profile - the Cargo profile
40
+ * @returns the binary's absolute path
41
+ * @throws Error when the output carries no `target_directory`
42
+ */
43
+ export declare const to_cargo_binary_path: (metadata_json: string, bin: string, profile: string) => string;
44
+ /**
45
+ * The path of a Cargo-built binary, without building it — what a config's
46
+ * `start_command` names. Runs `cargo metadata` once per process per workspace,
47
+ * so it needs cargo on PATH.
48
+ *
49
+ * @param options - the workspace, package, binary and profile
50
+ * @returns the binary's absolute path
51
+ * @throws Error when the workspace is missing or `cargo metadata` fails
52
+ */
53
+ export declare const resolve_cargo_binary_path: (options: CargoBinaryOptions) => string;
54
+ /**
55
+ * Build a Cargo binary before its backend spawns, unless `NO_REBUILD_ENV`
56
+ * asks to skip it, and return its path. The build's output is inherited, so a
57
+ * cold build shows its progress, and a failed build throws — the project fails
58
+ * loudly rather than spawning a stale binary. `cargo build` is incremental, so
59
+ * a build with nothing changed is quick.
60
+ *
61
+ * Synchronous: vitest awaits each project's `globalSetup` in turn, so one
62
+ * invocation selecting several projects builds once, then finds the build
63
+ * current.
64
+ *
65
+ * @param options - the workspace, package, binary and profile
66
+ * @returns the binary's absolute path (`resolve_cargo_binary_path`)
67
+ * @throws Error when the workspace is missing, the build fails, or `cargo metadata` fails
68
+ */
69
+ export declare const prepare_cargo_binary: (options: CargoBinaryOptions) => string;
70
+ //# sourceMappingURL=cargo_binary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cargo_binary.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cargo_binary.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAyB9B;;;GAGG;AACH,eAAO,MAAM,cAAc,2BAA2B,CAAC;AAEvD;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAAI,OAAO,MAAM,GAAG,SAAS,KAAG,OACnB,CAAC;AAElD,6CAA6C;AAC7C,MAAM,WAAW,kBAAkB;IAClC,oEAAoE;IACpE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,KAAG,MACjB,CAAC;AAEvC;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,GAChC,eAAe,MAAM,EACrB,KAAK,MAAM,EACX,SAAS,MAAM,KACb,MAMF,CAAC;AA2DF;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,kBAAkB,KAAG,MAGvE,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,kBAAkB,KAAG,MAUlE,CAAC"}