@fuzdev/fuz_app 0.55.0 → 0.57.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 (333) hide show
  1. package/dist/actions/CLAUDE.md +211 -155
  2. package/dist/actions/action_bridge.d.ts +8 -5
  3. package/dist/actions/action_bridge.d.ts.map +1 -1
  4. package/dist/actions/action_bridge.js +1 -11
  5. package/dist/actions/action_codegen.d.ts +19 -0
  6. package/dist/actions/action_codegen.d.ts.map +1 -1
  7. package/dist/actions/action_codegen.js +20 -14
  8. package/dist/actions/action_registry.d.ts.map +1 -1
  9. package/dist/actions/action_registry.js +5 -2
  10. package/dist/actions/action_rpc.d.ts +110 -44
  11. package/dist/actions/action_rpc.d.ts.map +1 -1
  12. package/dist/actions/action_rpc.js +92 -287
  13. package/dist/actions/action_spec.d.ts +55 -16
  14. package/dist/actions/action_spec.d.ts.map +1 -1
  15. package/dist/actions/action_spec.js +16 -11
  16. package/dist/actions/action_types.d.ts +28 -60
  17. package/dist/actions/action_types.d.ts.map +1 -1
  18. package/dist/actions/action_types.js +13 -5
  19. package/dist/actions/broadcast_api.d.ts +2 -2
  20. package/dist/actions/broadcast_api.js +2 -2
  21. package/dist/actions/compile_action_registry.d.ts +50 -0
  22. package/dist/actions/compile_action_registry.d.ts.map +1 -0
  23. package/dist/actions/compile_action_registry.js +69 -0
  24. package/dist/actions/heartbeat.d.ts +8 -4
  25. package/dist/actions/heartbeat.d.ts.map +1 -1
  26. package/dist/actions/heartbeat.js +5 -4
  27. package/dist/actions/perform_action.d.ts +145 -0
  28. package/dist/actions/perform_action.d.ts.map +1 -0
  29. package/dist/actions/perform_action.js +258 -0
  30. package/dist/actions/register_action_ws.d.ts +44 -38
  31. package/dist/actions/register_action_ws.d.ts.map +1 -1
  32. package/dist/actions/register_action_ws.js +101 -159
  33. package/dist/actions/register_ws_endpoint.d.ts +2 -10
  34. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  35. package/dist/actions/register_ws_endpoint.js +32 -10
  36. package/dist/actions/transports_ws_auth_guard.d.ts +1 -1
  37. package/dist/actions/transports_ws_auth_guard.js +1 -1
  38. package/dist/actions/transports_ws_backend.d.ts +1 -1
  39. package/dist/actions/transports_ws_backend.js +1 -1
  40. package/dist/auth/CLAUDE.md +673 -442
  41. package/dist/auth/account_action_specs.d.ts +28 -7
  42. package/dist/auth/account_action_specs.d.ts.map +1 -1
  43. package/dist/auth/account_action_specs.js +7 -7
  44. package/dist/auth/account_actions.d.ts +8 -14
  45. package/dist/auth/account_actions.d.ts.map +1 -1
  46. package/dist/auth/account_actions.js +26 -32
  47. package/dist/auth/account_queries.d.ts +46 -13
  48. package/dist/auth/account_queries.d.ts.map +1 -1
  49. package/dist/auth/account_queries.js +73 -33
  50. package/dist/auth/account_routes.d.ts +4 -3
  51. package/dist/auth/account_routes.d.ts.map +1 -1
  52. package/dist/auth/account_routes.js +58 -33
  53. package/dist/auth/account_schema.d.ts +46 -54
  54. package/dist/auth/account_schema.d.ts.map +1 -1
  55. package/dist/auth/account_schema.js +21 -48
  56. package/dist/auth/admin_action_specs.d.ts +55 -21
  57. package/dist/auth/admin_action_specs.d.ts.map +1 -1
  58. package/dist/auth/admin_action_specs.js +42 -26
  59. package/dist/auth/admin_actions.d.ts +14 -21
  60. package/dist/auth/admin_actions.d.ts.map +1 -1
  61. package/dist/auth/admin_actions.js +47 -44
  62. package/dist/auth/audit_emitter.d.ts +160 -0
  63. package/dist/auth/audit_emitter.d.ts.map +1 -0
  64. package/dist/auth/audit_emitter.js +83 -0
  65. package/dist/auth/audit_log_queries.d.ts +17 -87
  66. package/dist/auth/audit_log_queries.d.ts.map +1 -1
  67. package/dist/auth/audit_log_queries.js +17 -96
  68. package/dist/auth/audit_log_routes.d.ts +1 -1
  69. package/dist/auth/audit_log_routes.d.ts.map +1 -1
  70. package/dist/auth/audit_log_routes.js +7 -3
  71. package/dist/auth/audit_log_schema.d.ts +48 -42
  72. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  73. package/dist/auth/audit_log_schema.js +56 -43
  74. package/dist/auth/auth_guard_resolver.d.ts +44 -0
  75. package/dist/auth/auth_guard_resolver.d.ts.map +1 -0
  76. package/dist/auth/auth_guard_resolver.js +56 -0
  77. package/dist/auth/bootstrap_account.d.ts +7 -7
  78. package/dist/auth/bootstrap_account.d.ts.map +1 -1
  79. package/dist/auth/bootstrap_account.js +7 -7
  80. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  81. package/dist/auth/bootstrap_routes.js +11 -10
  82. package/dist/auth/cleanup.d.ts +20 -26
  83. package/dist/auth/cleanup.d.ts.map +1 -1
  84. package/dist/auth/cleanup.js +33 -47
  85. package/dist/auth/credential_type_schema.d.ts +115 -0
  86. package/dist/auth/credential_type_schema.d.ts.map +1 -0
  87. package/dist/auth/credential_type_schema.js +127 -0
  88. package/dist/auth/daemon_token_middleware.d.ts +1 -1
  89. package/dist/auth/daemon_token_middleware.js +3 -3
  90. package/dist/auth/ddl.d.ts +2 -2
  91. package/dist/auth/ddl.d.ts.map +1 -1
  92. package/dist/auth/ddl.js +6 -6
  93. package/dist/auth/deps.d.ts +7 -32
  94. package/dist/auth/deps.d.ts.map +1 -1
  95. package/dist/auth/grant_path_schema.d.ts +117 -0
  96. package/dist/auth/grant_path_schema.d.ts.map +1 -0
  97. package/dist/auth/grant_path_schema.js +137 -0
  98. package/dist/auth/invite_queries.d.ts +12 -1
  99. package/dist/auth/invite_queries.d.ts.map +1 -1
  100. package/dist/auth/invite_queries.js +12 -1
  101. package/dist/auth/invite_schema.d.ts +1 -1
  102. package/dist/auth/invite_schema.d.ts.map +1 -1
  103. package/dist/auth/invite_schema.js +1 -1
  104. package/dist/auth/middleware.d.ts.map +1 -1
  105. package/dist/auth/middleware.js +5 -2
  106. package/dist/auth/migrations.d.ts +22 -7
  107. package/dist/auth/migrations.d.ts.map +1 -1
  108. package/dist/auth/migrations.js +64 -25
  109. package/dist/auth/request_context.d.ts +157 -170
  110. package/dist/auth/request_context.d.ts.map +1 -1
  111. package/dist/auth/request_context.js +224 -268
  112. package/dist/auth/{permit_offer_action_specs.d.ts → role_grant_offer_action_specs.d.ts} +130 -100
  113. package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -0
  114. package/dist/auth/role_grant_offer_action_specs.js +262 -0
  115. package/dist/auth/role_grant_offer_actions.d.ts +104 -0
  116. package/dist/auth/role_grant_offer_actions.d.ts.map +1 -0
  117. package/dist/auth/{permit_offer_actions.js → role_grant_offer_actions.js} +153 -140
  118. package/dist/auth/{permit_offer_notifications.d.ts → role_grant_offer_notifications.d.ts} +80 -70
  119. package/dist/auth/role_grant_offer_notifications.d.ts.map +1 -0
  120. package/dist/auth/role_grant_offer_notifications.js +182 -0
  121. package/dist/auth/{permit_offer_queries.d.ts → role_grant_offer_queries.d.ts} +64 -64
  122. package/dist/auth/role_grant_offer_queries.d.ts.map +1 -0
  123. package/dist/auth/{permit_offer_queries.js → role_grant_offer_queries.js} +136 -123
  124. package/dist/auth/role_grant_offer_schema.d.ts +150 -0
  125. package/dist/auth/role_grant_offer_schema.d.ts.map +1 -0
  126. package/dist/auth/{permit_offer_schema.js → role_grant_offer_schema.js} +55 -36
  127. package/dist/auth/role_grant_queries.d.ts +231 -0
  128. package/dist/auth/role_grant_queries.d.ts.map +1 -0
  129. package/dist/auth/role_grant_queries.js +320 -0
  130. package/dist/auth/role_schema.d.ts +150 -40
  131. package/dist/auth/role_schema.d.ts.map +1 -1
  132. package/dist/auth/role_schema.js +144 -45
  133. package/dist/auth/scope_kind_schema.d.ts +96 -0
  134. package/dist/auth/scope_kind_schema.d.ts.map +1 -0
  135. package/dist/auth/scope_kind_schema.js +94 -0
  136. package/dist/auth/self_service_role_action_specs.d.ts +4 -1
  137. package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
  138. package/dist/auth/self_service_role_action_specs.js +2 -2
  139. package/dist/auth/self_service_role_actions.d.ts +35 -29
  140. package/dist/auth/self_service_role_actions.d.ts.map +1 -1
  141. package/dist/auth/self_service_role_actions.js +58 -48
  142. package/dist/auth/session_cookie.d.ts +43 -6
  143. package/dist/auth/session_cookie.d.ts.map +1 -1
  144. package/dist/auth/session_cookie.js +31 -5
  145. package/dist/auth/session_middleware.d.ts +37 -3
  146. package/dist/auth/session_middleware.d.ts.map +1 -1
  147. package/dist/auth/session_middleware.js +33 -7
  148. package/dist/auth/signup_routes.d.ts.map +1 -1
  149. package/dist/auth/signup_routes.js +48 -19
  150. package/dist/auth/standard_action_specs.d.ts +2 -2
  151. package/dist/auth/standard_action_specs.js +4 -4
  152. package/dist/auth/standard_rpc_actions.d.ts +23 -19
  153. package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
  154. package/dist/auth/standard_rpc_actions.js +12 -12
  155. package/dist/db/migrate.d.ts +1 -1
  156. package/dist/db/migrate.js +1 -1
  157. package/dist/dev/setup.d.ts +2 -2
  158. package/dist/dev/setup.d.ts.map +1 -1
  159. package/dist/dev/setup.js +4 -4
  160. package/dist/env/load.d.ts +1 -1
  161. package/dist/env/load.js +1 -1
  162. package/dist/hono_context.d.ts +27 -45
  163. package/dist/hono_context.d.ts.map +1 -1
  164. package/dist/hono_context.js +14 -28
  165. package/dist/http/CLAUDE.md +235 -121
  166. package/dist/http/auth_shape.d.ts +191 -0
  167. package/dist/http/auth_shape.d.ts.map +1 -0
  168. package/dist/http/auth_shape.js +237 -0
  169. package/dist/http/common_routes.js +3 -3
  170. package/dist/http/db_routes.d.ts +4 -0
  171. package/dist/http/db_routes.d.ts.map +1 -1
  172. package/dist/http/db_routes.js +44 -7
  173. package/dist/http/error_schemas.d.ts +72 -39
  174. package/dist/http/error_schemas.d.ts.map +1 -1
  175. package/dist/http/error_schemas.js +81 -33
  176. package/dist/http/pending_effects.d.ts +71 -18
  177. package/dist/http/pending_effects.d.ts.map +1 -1
  178. package/dist/http/pending_effects.js +87 -18
  179. package/dist/http/proxy.d.ts +52 -5
  180. package/dist/http/proxy.d.ts.map +1 -1
  181. package/dist/http/proxy.js +92 -14
  182. package/dist/http/route_spec.d.ts +89 -75
  183. package/dist/http/route_spec.d.ts.map +1 -1
  184. package/dist/http/route_spec.js +54 -72
  185. package/dist/http/schema_helpers.d.ts +3 -14
  186. package/dist/http/schema_helpers.d.ts.map +1 -1
  187. package/dist/http/schema_helpers.js +2 -14
  188. package/dist/http/surface.d.ts +2 -10
  189. package/dist/http/surface.d.ts.map +1 -1
  190. package/dist/http/surface.js +3 -4
  191. package/dist/http/surface_query.d.ts +39 -35
  192. package/dist/http/surface_query.d.ts.map +1 -1
  193. package/dist/http/surface_query.js +79 -36
  194. package/dist/primitive_schemas.d.ts +39 -0
  195. package/dist/primitive_schemas.d.ts.map +1 -0
  196. package/dist/primitive_schemas.js +40 -0
  197. package/dist/realtime/sse_auth_guard.d.ts +5 -5
  198. package/dist/realtime/sse_auth_guard.js +9 -9
  199. package/dist/runtime/mock.d.ts +1 -1
  200. package/dist/runtime/mock.js +1 -1
  201. package/dist/server/app_backend.d.ts +14 -11
  202. package/dist/server/app_backend.d.ts.map +1 -1
  203. package/dist/server/app_backend.js +12 -8
  204. package/dist/server/app_server.d.ts +7 -7
  205. package/dist/server/app_server.d.ts.map +1 -1
  206. package/dist/server/app_server.js +35 -40
  207. package/dist/server/validate_nginx.d.ts +1 -1
  208. package/dist/server/validate_nginx.js +1 -1
  209. package/dist/testing/CLAUDE.md +50 -38
  210. package/dist/testing/admin_integration.d.ts +5 -6
  211. package/dist/testing/admin_integration.d.ts.map +1 -1
  212. package/dist/testing/admin_integration.js +87 -85
  213. package/dist/testing/app_server.d.ts +11 -14
  214. package/dist/testing/app_server.d.ts.map +1 -1
  215. package/dist/testing/app_server.js +16 -15
  216. package/dist/testing/assertions.d.ts.map +1 -1
  217. package/dist/testing/assertions.js +2 -1
  218. package/dist/testing/attack_surface.d.ts.map +1 -1
  219. package/dist/testing/attack_surface.js +15 -9
  220. package/dist/testing/audit_completeness.d.ts +2 -2
  221. package/dist/testing/audit_completeness.d.ts.map +1 -1
  222. package/dist/testing/audit_completeness.js +36 -36
  223. package/dist/testing/auth_apps.d.ts +5 -4
  224. package/dist/testing/auth_apps.d.ts.map +1 -1
  225. package/dist/testing/auth_apps.js +22 -19
  226. package/dist/testing/data_exposure.d.ts.map +1 -1
  227. package/dist/testing/data_exposure.js +5 -5
  228. package/dist/testing/db.d.ts +1 -1
  229. package/dist/testing/db.d.ts.map +1 -1
  230. package/dist/testing/db.js +4 -4
  231. package/dist/testing/db_entities.d.ts +22 -0
  232. package/dist/testing/db_entities.d.ts.map +1 -0
  233. package/dist/testing/db_entities.js +28 -0
  234. package/dist/testing/entities.d.ts +8 -7
  235. package/dist/testing/entities.d.ts.map +1 -1
  236. package/dist/testing/entities.js +21 -18
  237. package/dist/testing/integration.d.ts.map +1 -1
  238. package/dist/testing/integration.js +13 -14
  239. package/dist/testing/integration_helpers.d.ts +4 -4
  240. package/dist/testing/integration_helpers.d.ts.map +1 -1
  241. package/dist/testing/integration_helpers.js +20 -18
  242. package/dist/testing/middleware.d.ts +4 -4
  243. package/dist/testing/middleware.d.ts.map +1 -1
  244. package/dist/testing/middleware.js +12 -11
  245. package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
  246. package/dist/testing/rpc_attack_surface.js +40 -24
  247. package/dist/testing/rpc_round_trip.d.ts +1 -1
  248. package/dist/testing/rpc_round_trip.d.ts.map +1 -1
  249. package/dist/testing/rpc_round_trip.js +14 -13
  250. package/dist/testing/sse_round_trip.d.ts +3 -4
  251. package/dist/testing/sse_round_trip.d.ts.map +1 -1
  252. package/dist/testing/sse_round_trip.js +7 -11
  253. package/dist/testing/standard.d.ts +1 -1
  254. package/dist/testing/stubs.d.ts +25 -0
  255. package/dist/testing/stubs.d.ts.map +1 -1
  256. package/dist/testing/stubs.js +43 -2
  257. package/dist/testing/surface_invariants.d.ts +14 -6
  258. package/dist/testing/surface_invariants.d.ts.map +1 -1
  259. package/dist/testing/surface_invariants.js +119 -43
  260. package/dist/testing/ws_round_trip.d.ts +12 -13
  261. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  262. package/dist/testing/ws_round_trip.js +19 -11
  263. package/dist/ui/AdminAccounts.svelte +23 -20
  264. package/dist/ui/AdminOverview.svelte +15 -13
  265. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
  266. package/dist/ui/{AdminPermitHistory.svelte → AdminRoleGrantHistory.svelte} +12 -12
  267. package/dist/ui/AdminRoleGrantHistory.svelte.d.ts +4 -0
  268. package/dist/ui/AdminRoleGrantHistory.svelte.d.ts.map +1 -0
  269. package/dist/ui/BootstrapForm.svelte +1 -1
  270. package/dist/ui/CLAUDE.md +60 -60
  271. package/dist/ui/{PermitOfferForm.svelte → RoleGrantOfferForm.svelte} +27 -26
  272. package/dist/ui/{PermitOfferForm.svelte.d.ts → RoleGrantOfferForm.svelte.d.ts} +7 -7
  273. package/dist/ui/RoleGrantOfferForm.svelte.d.ts.map +1 -0
  274. package/dist/ui/{PermitOfferHistory.svelte → RoleGrantOfferHistory.svelte} +12 -12
  275. package/dist/ui/{PermitOfferHistory.svelte.d.ts → RoleGrantOfferHistory.svelte.d.ts} +4 -4
  276. package/dist/ui/RoleGrantOfferHistory.svelte.d.ts.map +1 -0
  277. package/dist/ui/{PermitOfferInbox.svelte → RoleGrantOfferInbox.svelte} +14 -14
  278. package/dist/ui/{PermitOfferInbox.svelte.d.ts → RoleGrantOfferInbox.svelte.d.ts} +4 -4
  279. package/dist/ui/RoleGrantOfferInbox.svelte.d.ts.map +1 -0
  280. package/dist/ui/SignupForm.svelte +1 -1
  281. package/dist/ui/SurfaceExplorer.svelte +35 -15
  282. package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
  283. package/dist/ui/account_sessions_state.svelte.d.ts +2 -3
  284. package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
  285. package/dist/ui/account_sessions_state.svelte.js +2 -3
  286. package/dist/ui/admin_accounts_state.svelte.d.ts +18 -18
  287. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
  288. package/dist/ui/admin_accounts_state.svelte.js +16 -16
  289. package/dist/ui/admin_rpc_adapters.d.ts +20 -20
  290. package/dist/ui/admin_rpc_adapters.d.ts.map +1 -1
  291. package/dist/ui/admin_rpc_adapters.js +17 -17
  292. package/dist/ui/admin_sessions_state.svelte.d.ts +2 -2
  293. package/dist/ui/admin_sessions_state.svelte.js +2 -2
  294. package/dist/ui/audit_log_state.svelte.d.ts +7 -7
  295. package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
  296. package/dist/ui/audit_log_state.svelte.js +6 -6
  297. package/dist/ui/auth_state.svelte.d.ts +3 -3
  298. package/dist/ui/auth_state.svelte.d.ts.map +1 -1
  299. package/dist/ui/auth_state.svelte.js +6 -6
  300. package/dist/ui/format_scope.d.ts +2 -2
  301. package/dist/ui/format_scope.js +2 -2
  302. package/dist/ui/{permit_offers_state.svelte.d.ts → role_grant_offers_state.svelte.d.ts} +30 -30
  303. package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -0
  304. package/dist/ui/{permit_offers_state.svelte.js → role_grant_offers_state.svelte.js} +18 -18
  305. package/dist/ui/ui_format.js +2 -2
  306. package/package.json +3 -3
  307. package/dist/auth/permit_offer_action_specs.d.ts.map +0 -1
  308. package/dist/auth/permit_offer_action_specs.js +0 -258
  309. package/dist/auth/permit_offer_actions.d.ts +0 -110
  310. package/dist/auth/permit_offer_actions.d.ts.map +0 -1
  311. package/dist/auth/permit_offer_notifications.d.ts.map +0 -1
  312. package/dist/auth/permit_offer_notifications.js +0 -182
  313. package/dist/auth/permit_offer_queries.d.ts.map +0 -1
  314. package/dist/auth/permit_offer_schema.d.ts +0 -125
  315. package/dist/auth/permit_offer_schema.d.ts.map +0 -1
  316. package/dist/auth/permit_queries.d.ts +0 -222
  317. package/dist/auth/permit_queries.d.ts.map +0 -1
  318. package/dist/auth/permit_queries.js +0 -305
  319. package/dist/auth/require_keeper.d.ts +0 -20
  320. package/dist/auth/require_keeper.d.ts.map +0 -1
  321. package/dist/auth/require_keeper.js +0 -35
  322. package/dist/auth/route_guards.d.ts +0 -27
  323. package/dist/auth/route_guards.d.ts.map +0 -1
  324. package/dist/auth/route_guards.js +0 -38
  325. package/dist/auth/session_lifecycle.d.ts +0 -37
  326. package/dist/auth/session_lifecycle.d.ts.map +0 -1
  327. package/dist/auth/session_lifecycle.js +0 -29
  328. package/dist/ui/AdminPermitHistory.svelte.d.ts +0 -4
  329. package/dist/ui/AdminPermitHistory.svelte.d.ts.map +0 -1
  330. package/dist/ui/PermitOfferForm.svelte.d.ts.map +0 -1
  331. package/dist/ui/PermitOfferHistory.svelte.d.ts.map +0 -1
  332. package/dist/ui/PermitOfferInbox.svelte.d.ts.map +0 -1
  333. package/dist/ui/permit_offers_state.svelte.d.ts.map +0 -1
@@ -54,23 +54,70 @@ export type ParsedProxy = {
54
54
  * @throws Error on invalid IP, invalid CIDR network, or NaN/negative/over-range prefix
55
55
  */
56
56
  export declare const parse_proxy_entry: (entry: string) => ParsedProxy;
57
+ /**
58
+ * Strict IP validity check.
59
+ *
60
+ * Defense in depth around Hono's `hono/utils/ipaddr` helpers, which are
61
+ * lax in two ways:
62
+ *
63
+ * 1. `distinctRemoteAddr` classifies anything-with-a-colon as `'IPv6'`,
64
+ * including `'host:port'`, `'attacker:controlled'`, `'203.0.113.1:8080'`.
65
+ * 2. `convertIPv6ToBinary` silently accepts malformed forms like
66
+ * `'[::1]:8080'` and `'::1\n'`, parsing them as inconsistent binary
67
+ * values that would still serve as distinct rate-limit keys for an
68
+ * attacker rotating the suffix.
69
+ *
70
+ * Strict validation here is two-layered: a character-set pre-filter
71
+ * (`IP_LITERAL_CHARS`), then a round-trip through `convertIPv*ToBinary`
72
+ * to confirm the input parses cleanly. Either layer alone has holes;
73
+ * together they reject every input form we've seen Hono mis-handle.
74
+ *
75
+ * Used as the security primitive for any code path that takes an IP
76
+ * string from an untrusted source (XFF, query params) and uses it as a
77
+ * key (rate limiting, audit subject) or compares it against trusted
78
+ * proxies via CIDR (where the latent throw would otherwise bubble out).
79
+ *
80
+ * @returns the address family on success, `undefined` if the string is
81
+ * not a strictly-valid IP
82
+ */
83
+ export declare const validate_ip_strict: (ip: string) => "IPv4" | "IPv6" | undefined;
57
84
  /**
58
85
  * Check whether `ip` matches any entry in the trusted proxy list.
59
86
  *
60
87
  * Normalizes `ip` before matching (lowercase, IPv4-mapped IPv6 stripped).
88
+ * Uses `validate_ip_strict` to reject malformed input — without strict
89
+ * validation, Hono's lax `distinctRemoteAddr` would let an entry like
90
+ * `'203.0.113.1:8080'` (false-positive `'IPv6'`) reach
91
+ * `convertIPv6ToBinary` in the CIDR-match branch and throw.
61
92
  */
62
93
  export declare const is_trusted_ip: (ip: string, proxies: Array<ParsedProxy>) => boolean;
63
94
  /**
64
95
  * Resolve the real client IP from an `X-Forwarded-For` header value.
65
96
  *
66
- * Walks right-to-left, skipping trusted proxy entries. The first
67
- * non-trusted entry is the client IP. If all entries are trusted,
68
- * returns the leftmost entry. All entries are normalized before
69
- * matching and in the returned value.
97
+ * Walks right-to-left, skipping trusted proxy entries AND any entry
98
+ * that fails strict IP validation (`validate_ip_strict`). The first
99
+ * untrusted, strictly-valid entry is the client IP. If every walked
100
+ * entry is trusted or malformed, returns the leftmost strictly-valid
101
+ * (trusted) entry (likely-misconfigured all-trusted case) or
102
+ * `undefined` (everything was malformed — middleware falls back to
103
+ * the connection IP). All entries are normalized before matching and
104
+ * in the returned value.
105
+ *
106
+ * Skipping malformed entries is the rate-limit-key fix for the
107
+ * "attacker controls XFF and the proxy passes it through" surface —
108
+ * without the skip, an attacker could rotate arbitrary strings (incl.
109
+ * `'attacker:controlled'`, which Hono's lax `distinctRemoteAddr`
110
+ * misclassifies as IPv6) as XFF values to get fresh per-IP rate-limit
111
+ * buckets. Tradeoff: legitimate non-standard proxies that include
112
+ * ports in XFF entries (e.g. `203.0.113.1:8080`) also fail strict
113
+ * validation, so those entries get skipped and the rate-limit bucket
114
+ * collapses to the proxy's connection IP (one bucket for everyone
115
+ * behind that proxy). Standard proxies (nginx, cloud LBs) don't
116
+ * include ports.
70
117
  *
71
118
  * @param forwarded_for - the `X-Forwarded-For` header value
72
119
  * @param proxies - parsed trusted proxy entries
73
- * @returns the normalized client IP, or `undefined` if the header is empty
120
+ * @returns the normalized client IP, or `undefined` if the header is empty / all entries malformed
74
121
  */
75
122
  export declare const resolve_client_ip: (forwarded_for: string, proxies: Array<ParsedProxy>) => string | undefined;
76
123
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAE,iBAAiB,EAAC,MAAM,MAAM,CAAC;AAErD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,sBAAsB,CAAC;AAEzD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAI,IAAI,MAAM,KAAG,MAQzC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,sFAAsF;IACtF,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,+DAA+D;IAC/D,iBAAiB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;IACtD,wDAAwD;IACxD,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GACpB;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GAC7B;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAA;CAAC,CAAC;AAElF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,KAAG,WA6CjD,CAAC;AAiBF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,IAAI,MAAM,EAAE,SAAS,KAAK,CAAC,WAAW,CAAC,KAAG,OAqBvE,CAAC;AAQF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,GAC7B,eAAe,MAAM,EACrB,SAAS,KAAK,CAAC,WAAW,CAAC,KACzB,MAAM,GAAG,SAiBX,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,uBAAuB,GAAI,SAAS,YAAY,KAAG,iBAyC/D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,GAAI,SAAS,YAAY,KAAG,cAInE,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GAAI,GAAG,OAAO,KAAG,MAAyC,CAAC"}
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAE,iBAAiB,EAAC,MAAM,MAAM,CAAC;AAErD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,sBAAsB,CAAC;AAEzD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAI,IAAI,MAAM,KAAG,MAQzC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,sFAAsF;IACtF,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,+DAA+D;IAC/D,iBAAiB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;IACtD,wDAAwD;IACxD,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GACpB;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GAC7B;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAA;CAAC,CAAC;AAElF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,KAAG,WA6CjD,CAAC;AA2BF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,kBAAkB,GAAI,IAAI,MAAM,KAAG,MAAM,GAAG,MAAM,GAAG,SAWjE,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,GAAI,IAAI,MAAM,EAAE,SAAS,KAAK,CAAC,WAAW,CAAC,KAAG,OAqBvE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,iBAAiB,GAC7B,eAAe,MAAM,EACrB,SAAS,KAAK,CAAC,WAAW,CAAC,KACzB,MAAM,GAAG,SA0BX,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,uBAAuB,GAAI,SAAS,YAAY,KAAG,iBAyC/D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,GAAI,SAAS,YAAY,KAAG,cAInE,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GAAI,GAAG,OAAO,KAAG,MAAyC,CAAC"}
@@ -91,14 +91,70 @@ const cidr_contains = (ip_binary, network, prefix, total_bits) => {
91
91
  const shift = BigInt(total_bits - prefix);
92
92
  return ip_binary >> shift === network >> shift;
93
93
  };
94
+ /**
95
+ * Allowed character set for a bare IP literal.
96
+ *
97
+ * Covers the union of IPv4 (digits + `.`), IPv6 (hex digits + `:`), and
98
+ * IPv4-mapped IPv6 forms (`::ffff:127.0.0.1`). Anything outside this
99
+ * set — brackets, whitespace, control bytes, letters g-z — disqualifies
100
+ * the input regardless of what Hono's parser does with it.
101
+ */
102
+ const IP_LITERAL_CHARS = /^[0-9a-fA-F.:]+$/;
103
+ /**
104
+ * Strict IP validity check.
105
+ *
106
+ * Defense in depth around Hono's `hono/utils/ipaddr` helpers, which are
107
+ * lax in two ways:
108
+ *
109
+ * 1. `distinctRemoteAddr` classifies anything-with-a-colon as `'IPv6'`,
110
+ * including `'host:port'`, `'attacker:controlled'`, `'203.0.113.1:8080'`.
111
+ * 2. `convertIPv6ToBinary` silently accepts malformed forms like
112
+ * `'[::1]:8080'` and `'::1\n'`, parsing them as inconsistent binary
113
+ * values that would still serve as distinct rate-limit keys for an
114
+ * attacker rotating the suffix.
115
+ *
116
+ * Strict validation here is two-layered: a character-set pre-filter
117
+ * (`IP_LITERAL_CHARS`), then a round-trip through `convertIPv*ToBinary`
118
+ * to confirm the input parses cleanly. Either layer alone has holes;
119
+ * together they reject every input form we've seen Hono mis-handle.
120
+ *
121
+ * Used as the security primitive for any code path that takes an IP
122
+ * string from an untrusted source (XFF, query params) and uses it as a
123
+ * key (rate limiting, audit subject) or compares it against trusted
124
+ * proxies via CIDR (where the latent throw would otherwise bubble out).
125
+ *
126
+ * @returns the address family on success, `undefined` if the string is
127
+ * not a strictly-valid IP
128
+ */
129
+ export const validate_ip_strict = (ip) => {
130
+ if (!IP_LITERAL_CHARS.test(ip))
131
+ return undefined;
132
+ const type = distinctRemoteAddr(ip);
133
+ if (!type)
134
+ return undefined;
135
+ try {
136
+ if (type === 'IPv4')
137
+ convertIPv4ToBinary(ip);
138
+ else
139
+ convertIPv6ToBinary(ip);
140
+ return type;
141
+ }
142
+ catch {
143
+ return undefined;
144
+ }
145
+ };
94
146
  /**
95
147
  * Check whether `ip` matches any entry in the trusted proxy list.
96
148
  *
97
149
  * Normalizes `ip` before matching (lowercase, IPv4-mapped IPv6 stripped).
150
+ * Uses `validate_ip_strict` to reject malformed input — without strict
151
+ * validation, Hono's lax `distinctRemoteAddr` would let an entry like
152
+ * `'203.0.113.1:8080'` (false-positive `'IPv6'`) reach
153
+ * `convertIPv6ToBinary` in the CIDR-match branch and throw.
98
154
  */
99
155
  export const is_trusted_ip = (ip, proxies) => {
100
156
  const normalized = normalize_ip(ip);
101
- const address_type = distinctRemoteAddr(normalized);
157
+ const address_type = validate_ip_strict(normalized);
102
158
  if (!address_type)
103
159
  return false;
104
160
  for (const proxy of proxies) {
@@ -121,22 +177,33 @@ export const is_trusted_ip = (ip, proxies) => {
121
177
  }
122
178
  return false;
123
179
  };
124
- // NOTE: some non-standard proxies include ports in XFF entries (e.g.
125
- // 203.0.113.1:8080). The entry fails distinctRemoteAddr and is treated as
126
- // untrusted (safe default), but rate limiting keys on the port-suffixed
127
- // string instead of the bare IP. Low risk — nginx and cloud LBs don't
128
- // include ports.
129
180
  /**
130
181
  * Resolve the real client IP from an `X-Forwarded-For` header value.
131
182
  *
132
- * Walks right-to-left, skipping trusted proxy entries. The first
133
- * non-trusted entry is the client IP. If all entries are trusted,
134
- * returns the leftmost entry. All entries are normalized before
135
- * matching and in the returned value.
183
+ * Walks right-to-left, skipping trusted proxy entries AND any entry
184
+ * that fails strict IP validation (`validate_ip_strict`). The first
185
+ * untrusted, strictly-valid entry is the client IP. If every walked
186
+ * entry is trusted or malformed, returns the leftmost strictly-valid
187
+ * (trusted) entry (likely-misconfigured all-trusted case) or
188
+ * `undefined` (everything was malformed — middleware falls back to
189
+ * the connection IP). All entries are normalized before matching and
190
+ * in the returned value.
191
+ *
192
+ * Skipping malformed entries is the rate-limit-key fix for the
193
+ * "attacker controls XFF and the proxy passes it through" surface —
194
+ * without the skip, an attacker could rotate arbitrary strings (incl.
195
+ * `'attacker:controlled'`, which Hono's lax `distinctRemoteAddr`
196
+ * misclassifies as IPv6) as XFF values to get fresh per-IP rate-limit
197
+ * buckets. Tradeoff: legitimate non-standard proxies that include
198
+ * ports in XFF entries (e.g. `203.0.113.1:8080`) also fail strict
199
+ * validation, so those entries get skipped and the rate-limit bucket
200
+ * collapses to the proxy's connection IP (one bucket for everyone
201
+ * behind that proxy). Standard proxies (nginx, cloud LBs) don't
202
+ * include ports.
136
203
  *
137
204
  * @param forwarded_for - the `X-Forwarded-For` header value
138
205
  * @param proxies - parsed trusted proxy entries
139
- * @returns the normalized client IP, or `undefined` if the header is empty
206
+ * @returns the normalized client IP, or `undefined` if the header is empty / all entries malformed
140
207
  */
141
208
  export const resolve_client_ip = (forwarded_for, proxies) => {
142
209
  const entries = [];
@@ -147,15 +214,26 @@ export const resolve_client_ip = (forwarded_for, proxies) => {
147
214
  }
148
215
  if (entries.length === 0)
149
216
  return undefined;
150
- // Walk from right to left, skip trusted proxies
217
+ // Walk from right to left, skip trusted proxies and malformed entries.
218
+ // Returning a malformed entry as the client IP would let an attacker
219
+ // who controls XFF poison the per-IP rate-limit key.
151
220
  for (let i = entries.length - 1; i >= 0; i--) {
152
221
  const entry = entries[i];
222
+ if (!validate_ip_strict(entry))
223
+ continue;
153
224
  if (!is_trusted_ip(entry, proxies)) {
154
225
  return entry;
155
226
  }
156
227
  }
157
- // All entries are trusted return leftmost (edge case, likely misconfigured)
158
- return entries[0];
228
+ // Every entry was trusted or malformed. Prefer the leftmost
229
+ // strictly-valid (trusted) entry — the misconfiguration warn in
230
+ // the middleware fires on it. If none, fall through to undefined
231
+ // and let the middleware fall back to the connection IP.
232
+ for (const entry of entries) {
233
+ if (validate_ip_strict(entry))
234
+ return entry;
235
+ }
236
+ return undefined;
159
237
  };
160
238
  /**
161
239
  * Create a Hono middleware that resolves the client IP from trusted proxies.
@@ -18,22 +18,7 @@ import type { Logger } from '@fuzdev/fuz_util/log.js';
18
18
  import type { Db } from '../db/db.js';
19
19
  import { type RouteErrorSchemas, type RateLimitKey } from './error_schemas.js';
20
20
  import type { MiddlewareSpec } from './middleware_spec.js';
21
- /**
22
- * Auth requirement for a route — `none`, `authenticated`, a specific role, or `keeper`.
23
- *
24
- * `{type: 'none'}` means the route is open to all clients — including non-browser
25
- * callers (CLI, API tokens, scripts). No session or auth middleware guards are applied.
26
- */
27
- export type RouteAuth = {
28
- type: 'none';
29
- } | {
30
- type: 'authenticated';
31
- } | {
32
- type: 'role';
33
- role: string;
34
- } | {
35
- type: 'keeper';
36
- };
21
+ import { type RouteAuth } from './auth_shape.js';
37
22
  /**
38
23
  * Two-phase auth guard set returned by `AuthGuardResolver`.
39
24
  *
@@ -41,7 +26,7 @@ export type RouteAuth = {
41
26
  * so unauthenticated callers never see route-shape information from
42
27
  * input parsing failures. `post_authorization` runs after the
43
28
  * authorization phase has populated `RequestContext` — role / keeper
44
- * checks live here because they read `c.var.request_context.permits`.
29
+ * checks live here because they read `c.var.request_context.role_grants`.
45
30
  */
46
31
  export interface AuthGuards {
47
32
  pre_validation: Array<MiddlewareHandler>;
@@ -52,17 +37,16 @@ export interface AuthGuards {
52
37
  *
53
38
  * Injected into `apply_route_specs` to decouple route registration
54
39
  * from auth-specific middleware. See `fuz_auth_guard_resolver` in
55
- * `auth/route_guards.ts` for the standard implementation.
40
+ * `auth/auth_guard_resolver.ts` for the standard implementation.
56
41
  */
57
42
  export type AuthGuardResolver = (auth: RouteAuth) => AuthGuards;
58
43
  /**
59
- * Per-route authorization phase. Runs after the pre-validation auth guards
60
- * and before input validation; resolves the acting actor (when the route's
61
- * input declares `acting?: ActingActor` or auth requires permits) and sets
62
- * the request context on the Hono context. Per-route order in
63
- * `apply_route_specs`: params query → pre-validation auth (401) →
64
- * authorization → post-authorization auth (403) → input validation →
65
- * handler.
44
+ * Per-route authorization phase. Runs after pre-validation auth guards
45
+ * AND input validation; resolves the acting actor (when `auth.actor !== 'none'`)
46
+ * by reading `c.var.validated_input.acting` and sets the request context on
47
+ * the Hono context. Per-route order in `apply_route_specs`: params → query
48
+ * → pre-validation auth (401) → input validation (400) → authorization
49
+ * phase → post-authorization auth (403) → handler.
66
50
  *
67
51
  * Returns a `Response` to short-circuit (resolution failure → 400 / 500),
68
52
  * or `void` to continue. The http framework stays auth-agnostic — fuz_app
@@ -70,37 +54,46 @@ export type AuthGuardResolver = (auth: RouteAuth) => AuthGuards;
70
54
  * `auth/request_context.ts`.
71
55
  */
72
56
  export type AuthorizationHandler = (c: Context, spec: RouteSpec) => Promise<Response | void>;
73
- /**
74
- * Predicate that decides whether a route is "acting-aware" — i.e. whether
75
- * the dispatcher's authorization phase may emit `actor_required` /
76
- * `actor_not_on_account` (400) or `no_actors_on_account` /
77
- * `account_vanished` (500) on this spec. When the predicate returns true
78
- * the merged error schema is widened to accept those shapes so DEV-mode
79
- * `wrap_output_validation` doesn't reject them.
80
- *
81
- * Computed at the call site because the canonical "input declares
82
- * `acting?: ActingActor`" check lives in `auth/request_context.ts` (it
83
- * uses reference equality with the canonical `ActingActor` schema). The
84
- * `http/` framework receives the predicate via this callback so it stays
85
- * auth-agnostic. See `http/CLAUDE.md` § Three-layer error-schema merge.
86
- */
87
- export type IsActingAware = (spec: Pick<RouteSpec, 'auth' | 'input'>) => boolean;
88
57
  /** HTTP methods supported by route specs. */
89
58
  export type RouteMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
90
59
  /**
91
60
  * Per-request deps provided by the framework to route handlers.
61
+ *
62
+ * Audit writes and other rollback-resilient fire-and-forget calls run
63
+ * through `AppDeps.audit.emit(ctx, input)` (see `auth/audit_emitter.ts`),
64
+ * which captures the pool inside its closure — handlers can never
65
+ * accidentally write audits against the request transaction.
66
+ *
67
+ * Routes whose body manages its own transaction (signup, bootstrap)
68
+ * declare `transaction: false` on the spec, which makes `route.db` the
69
+ * pool — they reach for it directly.
92
70
  */
93
71
  export interface RouteContext {
94
- /** Transaction-scoped for mutations, pool-level for reads. */
72
+ /**
73
+ * Transaction-scoped when `RouteSpec.transaction` is true (the default
74
+ * for non-GET); pool-level otherwise.
75
+ */
95
76
  db: Db;
96
- /** Always pool-level — for fire-and-forget effects that must outlive the transaction. */
97
- background_db: Db;
98
- /** Fire-and-forget side effects push here for post-response flushing. */
77
+ /**
78
+ * Eager fire-and-forget queue — push the in-flight `Promise<void>` for
79
+ * pool writes already running (audit emits, session touch, api-token
80
+ * usage tracking). The flush middleware drains via
81
+ * `flush_pending_effects` after the handler returns.
82
+ */
99
83
  pending_effects: Array<Promise<void>>;
84
+ /**
85
+ * Deferred post-commit thunks — do not push directly; reach for
86
+ * `emit_after_commit(ctx, fn)` from `pending_effects.ts`. The flush
87
+ * middleware invokes each thunk after the handler (and any wrapping
88
+ * `db.transaction`) returns, closing the microtask-ordering window
89
+ * that an eager `Promise.resolve().then(fn)` leaves open inside the
90
+ * transaction.
91
+ */
92
+ post_commit_effects: Array<() => void | Promise<void>>;
100
93
  }
101
94
  /**
102
95
  * Route handler function — receives the Hono context and a `RouteContext`
103
- * with per-request deps (db, background_db, pending_effects).
96
+ * with per-request deps (db, pending_effects).
104
97
  *
105
98
  * TypeScript allows fewer params, so handlers that don't need `route`
106
99
  * can use `(c) => ...` without changes.
@@ -164,27 +157,39 @@ export interface RouteSpec {
164
157
  /**
165
158
  * Get validated input from the Hono context.
166
159
  *
167
- * Call after the input validation middleware has run. The type parameter
168
- * should match the route's `input` schema.
160
+ * Call after the input validation middleware has run. Pass the route's
161
+ * `input` Zod schema to infer the typed shape directly:
162
+ *
163
+ * ```ts
164
+ * const input = get_route_input(c, my_route_spec.input);
165
+ * ```
166
+ *
167
+ * Or pass an explicit type argument when the schema isn't in scope:
168
+ *
169
+ * ```ts
170
+ * const input = get_route_input<MyInput>(c);
171
+ * ```
169
172
  */
170
- export declare const get_route_input: <T>(c: Context) => T;
173
+ export declare function get_route_input<S extends z.ZodType>(c: Context, schema: S): z.infer<S>;
174
+ export declare function get_route_input<T = unknown>(c: Context): T;
171
175
  /**
172
176
  * Get validated URL path params from the Hono context.
173
177
  *
174
- * Call after the params validation middleware has run. The type parameter
175
- * should match the route's `params` schema.
176
- *
177
- * TODO derive `T` from the route spec so the type parameter isn't manually
178
- * specified — same applies to `get_route_input` / `get_route_query`.
178
+ * Call after the params validation middleware has run. Pass the route's
179
+ * `params` schema to infer the typed shape, or supply an explicit type
180
+ * argument. See `get_route_input` for the two call shapes.
179
181
  */
180
- export declare const get_route_params: <T>(c: Context) => T;
182
+ export declare function get_route_params<S extends z.ZodType>(c: Context, schema: S): z.infer<S>;
183
+ export declare function get_route_params<T = unknown>(c: Context): T;
181
184
  /**
182
185
  * Get validated URL query params from the Hono context.
183
186
  *
184
- * Call after the query validation middleware has run. The type parameter
185
- * should match the route's `query` schema.
187
+ * Call after the query validation middleware has run. Pass the route's
188
+ * `query` schema to infer the typed shape, or supply an explicit type
189
+ * argument. See `get_route_input` for the two call shapes.
186
190
  */
187
- export declare const get_route_query: <T>(c: Context) => T;
191
+ export declare function get_route_query<S extends z.ZodType>(c: Context, schema: S): z.infer<S>;
192
+ export declare function get_route_query<T = unknown>(c: Context): T;
188
193
  /**
189
194
  * Apply named middleware specs to a Hono app.
190
195
  *
@@ -202,29 +207,38 @@ export declare const apply_middleware_specs: (app: Hono, specs: Array<Middleware
202
207
  * and generic errors), and registers the route.
203
208
  *
204
209
  * Per-route middleware order: params → query → pre-validation auth
205
- * guards (401) → authorization phasepost-authorization auth guards
206
- * (403) → input validation → handler. The 401 check runs before any
207
- * body parsing so unauthenticated callers never see route-shape
208
- * information from parse failures. The authorization phase runs before
209
- * input validation (matches the RPC dispatcher's order) so role /
210
- * keeper denials surface 403 before 400 invalid_params; it extracts
211
- * `acting` from raw query (GET) or pre-parsed JSON body (POST/PUT/...)
212
- * Hono caches the parsed body internally so the subsequent input-
213
- * validation step does not re-parse. The role / keeper guards consume
214
- * the `RequestContext` populated by the authorization phase.
210
+ * guards (401) → input validation (400) → authorization phase
211
+ * post-authorization auth guards (403) → handler. The 401 check runs
212
+ * before any body parsing so unauthenticated callers never see
213
+ * route-shape information from parse failures. Input validation runs
214
+ * before the authorization phase (validate first, authorize after) so
215
+ * the authorization phase reads `c.var.validated_input.acting` as a
216
+ * typed Zod field instead of pre-parsing the raw body. Role /
217
+ * credential-type denials still surface 403 last; trade-off is that
218
+ * authenticated-but-unauthorized callers can distinguish 400
219
+ * (validation) from 403 (authorization), a defense-in-depth concession
220
+ * deemed acceptable because the route surface is public via
221
+ * spec/codegen JSON.
215
222
  *
216
223
  * Each handler receives a `RouteContext` with:
217
- * - `db`: transaction-scoped (for non-GET) or pool-level (for GET)
218
- * - `background_db`: always pool-level
219
- * - `pending_effects`: fire-and-forget effect queue
220
- *
221
- * @param resolve_auth_guards - maps `RouteAuth` to middleware use `fuz_auth_guard_resolver` from `auth/route_guards.ts`
222
- * @param authorize - optional authorization phase; runs between guards and input validation
224
+ * - `db`: transaction-scoped when `RouteSpec.transaction` is true; pool-level otherwise
225
+ * - `pending_effects`: eager fire-and-forget pool-write queue
226
+ * - `post_commit_effects`: deferred-thunk queue (push via `emit_after_commit`)
227
+ *
228
+ * Also enforces registry-time invariant 2 from the auth-shape design:
229
+ * `auth.actor !== 'none' input or query declares acting?: ActingActor`.
230
+ * REST is bi-located (GETs declare `acting` on `query`, mutations on
231
+ * `input`), so the check passes both slots; the action-dispatcher
232
+ * registries (`compile_action_registry`) share the same helper with
233
+ * `input` only — `ActionSpec` has no `query` shape.
234
+ *
235
+ * @param resolve_auth_guards - maps `RouteAuth` to middleware — use `fuz_auth_guard_resolver` from `auth/auth_guard_resolver.ts`
236
+ * @param authorize - optional authorization phase; runs after input validation
223
237
  * @param db - used for transaction wrapping and `RouteContext`
224
238
  * @mutates `app`
225
- * @throws Error if two specs share the same `method` + `path` (each combination must be unique)
239
+ * @throws Error if two specs share the same `method` + `path` (each combination must be unique), or if any spec violates the actor-acting biconditional
226
240
  */
227
- export declare const apply_route_specs: (app: Hono, specs: Array<RouteSpec>, resolve_auth_guards: AuthGuardResolver, log: Logger, db: Db, authorize?: AuthorizationHandler, is_acting_aware?: IsActingAware) => void;
241
+ export declare const apply_route_specs: (app: Hono, specs: Array<RouteSpec>, resolve_auth_guards: AuthGuardResolver, log: Logger, db: Db, authorize?: AuthorizationHandler) => void;
228
242
  /**
229
243
  * Prepend a prefix to all route spec paths.
230
244
  *
@@ -1 +1 @@
1
- {"version":3,"file":"route_spec.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/route_spec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"route_spec.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/route_spec.ts"],"names":[],"mappings":"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"}