@fuzdev/fuz_app 0.55.0 → 0.56.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 (331) 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 +56 -34
  174. package/dist/http/error_schemas.d.ts.map +1 -1
  175. package/dist/http/error_schemas.js +63 -28
  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 +2 -2
  258. package/dist/testing/ws_round_trip.d.ts +12 -13
  259. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  260. package/dist/testing/ws_round_trip.js +19 -11
  261. package/dist/ui/AdminAccounts.svelte +23 -20
  262. package/dist/ui/AdminOverview.svelte +15 -13
  263. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
  264. package/dist/ui/{AdminPermitHistory.svelte → AdminRoleGrantHistory.svelte} +12 -12
  265. package/dist/ui/AdminRoleGrantHistory.svelte.d.ts +4 -0
  266. package/dist/ui/AdminRoleGrantHistory.svelte.d.ts.map +1 -0
  267. package/dist/ui/BootstrapForm.svelte +1 -1
  268. package/dist/ui/CLAUDE.md +60 -60
  269. package/dist/ui/{PermitOfferForm.svelte → RoleGrantOfferForm.svelte} +27 -26
  270. package/dist/ui/{PermitOfferForm.svelte.d.ts → RoleGrantOfferForm.svelte.d.ts} +7 -7
  271. package/dist/ui/RoleGrantOfferForm.svelte.d.ts.map +1 -0
  272. package/dist/ui/{PermitOfferHistory.svelte → RoleGrantOfferHistory.svelte} +12 -12
  273. package/dist/ui/{PermitOfferHistory.svelte.d.ts → RoleGrantOfferHistory.svelte.d.ts} +4 -4
  274. package/dist/ui/RoleGrantOfferHistory.svelte.d.ts.map +1 -0
  275. package/dist/ui/{PermitOfferInbox.svelte → RoleGrantOfferInbox.svelte} +14 -14
  276. package/dist/ui/{PermitOfferInbox.svelte.d.ts → RoleGrantOfferInbox.svelte.d.ts} +4 -4
  277. package/dist/ui/RoleGrantOfferInbox.svelte.d.ts.map +1 -0
  278. package/dist/ui/SignupForm.svelte +1 -1
  279. package/dist/ui/SurfaceExplorer.svelte +35 -15
  280. package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
  281. package/dist/ui/account_sessions_state.svelte.d.ts +2 -3
  282. package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
  283. package/dist/ui/account_sessions_state.svelte.js +2 -3
  284. package/dist/ui/admin_accounts_state.svelte.d.ts +18 -18
  285. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
  286. package/dist/ui/admin_accounts_state.svelte.js +16 -16
  287. package/dist/ui/admin_rpc_adapters.d.ts +20 -20
  288. package/dist/ui/admin_rpc_adapters.d.ts.map +1 -1
  289. package/dist/ui/admin_rpc_adapters.js +17 -17
  290. package/dist/ui/admin_sessions_state.svelte.d.ts +2 -2
  291. package/dist/ui/admin_sessions_state.svelte.js +2 -2
  292. package/dist/ui/audit_log_state.svelte.d.ts +7 -7
  293. package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
  294. package/dist/ui/audit_log_state.svelte.js +6 -6
  295. package/dist/ui/auth_state.svelte.d.ts +3 -3
  296. package/dist/ui/auth_state.svelte.d.ts.map +1 -1
  297. package/dist/ui/auth_state.svelte.js +6 -6
  298. package/dist/ui/format_scope.d.ts +2 -2
  299. package/dist/ui/format_scope.js +2 -2
  300. package/dist/ui/{permit_offers_state.svelte.d.ts → role_grant_offers_state.svelte.d.ts} +30 -30
  301. package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -0
  302. package/dist/ui/{permit_offers_state.svelte.js → role_grant_offers_state.svelte.js} +18 -18
  303. package/dist/ui/ui_format.js +2 -2
  304. package/package.json +3 -3
  305. package/dist/auth/permit_offer_action_specs.d.ts.map +0 -1
  306. package/dist/auth/permit_offer_action_specs.js +0 -258
  307. package/dist/auth/permit_offer_actions.d.ts +0 -110
  308. package/dist/auth/permit_offer_actions.d.ts.map +0 -1
  309. package/dist/auth/permit_offer_notifications.d.ts.map +0 -1
  310. package/dist/auth/permit_offer_notifications.js +0 -182
  311. package/dist/auth/permit_offer_queries.d.ts.map +0 -1
  312. package/dist/auth/permit_offer_schema.d.ts +0 -125
  313. package/dist/auth/permit_offer_schema.d.ts.map +0 -1
  314. package/dist/auth/permit_queries.d.ts +0 -222
  315. package/dist/auth/permit_queries.d.ts.map +0 -1
  316. package/dist/auth/permit_queries.js +0 -305
  317. package/dist/auth/require_keeper.d.ts +0 -20
  318. package/dist/auth/require_keeper.d.ts.map +0 -1
  319. package/dist/auth/require_keeper.js +0 -35
  320. package/dist/auth/route_guards.d.ts +0 -27
  321. package/dist/auth/route_guards.d.ts.map +0 -1
  322. package/dist/auth/route_guards.js +0 -38
  323. package/dist/auth/session_lifecycle.d.ts +0 -37
  324. package/dist/auth/session_lifecycle.d.ts.map +0 -1
  325. package/dist/auth/session_lifecycle.js +0 -29
  326. package/dist/ui/AdminPermitHistory.svelte.d.ts +0 -4
  327. package/dist/ui/AdminPermitHistory.svelte.d.ts.map +0 -1
  328. package/dist/ui/PermitOfferForm.svelte.d.ts.map +0 -1
  329. package/dist/ui/PermitOfferHistory.svelte.d.ts.map +0 -1
  330. package/dist/ui/PermitOfferInbox.svelte.d.ts.map +0 -1
  331. package/dist/ui/permit_offers_state.svelte.d.ts.map +0 -1
@@ -12,7 +12,7 @@
12
12
  * @module
13
13
  */
14
14
  import { z } from 'zod';
15
- import type { RouteAuth } from './route_spec.js';
15
+ import { type RouteAuth } from './auth_shape.js';
16
16
  /** Request body failed Zod validation. */
17
17
  export declare const ERROR_INVALID_REQUEST_BODY: "invalid_request_body";
18
18
  /** Request body is not valid JSON or not an object. */
@@ -25,6 +25,16 @@ export declare const ERROR_INVALID_QUERY_PARAMS: "invalid_query_params";
25
25
  export declare const ERROR_AUTHENTICATION_REQUIRED: "authentication_required";
26
26
  /** Authenticated but missing required role. */
27
27
  export declare const ERROR_INSUFFICIENT_PERMISSIONS: "insufficient_permissions";
28
+ /**
29
+ * Route requires a credential type the request didn't arrive on.
30
+ * Symmetric with `ERROR_INSUFFICIENT_PERMISSIONS` + `required_roles`:
31
+ * the body carries `required_credential_types: ReadonlyArray<string>`
32
+ * — what the route demanded, not what arrived. Today the only
33
+ * credential gate is keeper (`['daemon_token']`); future gates
34
+ * (`agent_token`, `group_actor_token`) reuse the same literal and
35
+ * label themselves through the array.
36
+ */
37
+ export declare const ERROR_CREDENTIAL_TYPE_REQUIRED: "credential_type_required";
28
38
  /** Rate limiter rejected the request. */
29
39
  export declare const ERROR_RATE_LIMIT_EXCEEDED: "rate_limit_exceeded";
30
40
  /** Username or password is wrong (intentionally vague for enumeration prevention). */
@@ -74,8 +84,6 @@ export declare const ERROR_NO_ACTORS_ON_ACCOUNT: "no_actors_on_account";
74
84
  * `ERROR_NO_ACTORS_ON_ACCOUNT` (the actor list enumerated empty).
75
85
  */
76
86
  export declare const ERROR_ACCOUNT_VANISHED: "account_vanished";
77
- /** Keeper routes require daemon_token credential type. */
78
- export declare const ERROR_KEEPER_REQUIRES_DAEMON_TOKEN: "keeper_requires_daemon_token";
79
87
  /** Daemon token header present but malformed or not matching current/previous token. */
80
88
  export declare const ERROR_INVALID_DAEMON_TOKEN: "invalid_daemon_token";
81
89
  /** Daemon token valid but keeper account not yet resolved (pre-bootstrap). */
@@ -104,8 +112,8 @@ export declare const ERROR_INVITE_ACCOUNT_EXISTS_USERNAME: "invite_account_exist
104
112
  export declare const ERROR_INVITE_ACCOUNT_EXISTS_EMAIL: "invite_account_exists_email";
105
113
  /** Admin tried to grant a role that is not web-grantable. */
106
114
  export declare const ERROR_ROLE_NOT_WEB_GRANTABLE: "role_not_web_grantable";
107
- /** Permit ID not found or not owned by the target actor. */
108
- export declare const ERROR_PERMIT_NOT_FOUND: "permit_not_found";
115
+ /** Role grant ID not found or not owned by the target actor. */
116
+ export declare const ERROR_ROLE_GRANT_NOT_FOUND: "role_grant_not_found";
109
117
  /** Query parameter `event_type` is not a valid audit event type. */
110
118
  export declare const ERROR_INVALID_EVENT_TYPE: "invalid_event_type";
111
119
  /** DELETE blocked by a foreign key constraint. */
@@ -137,18 +145,37 @@ export declare const ValidationError: z.ZodObject<{
137
145
  }, z.core.$loose>>;
138
146
  }, z.core.$loose>;
139
147
  export type ValidationError = z.infer<typeof ValidationError>;
140
- /** Permission error — returned by `require_role()` when the required role is missing. */
148
+ /**
149
+ * Permission error — returned by `require_role()` and the dispatcher's
150
+ * post-authorization role gate when the actor's role_grants don't include any
151
+ * of the route's `auth.roles`.
152
+ *
153
+ * `required_roles` carries the full disjunction the route declared
154
+ * (`auth.roles` from the new flat-record shape). Single-role specs surface
155
+ * as a one-element array; multi-role disjunctions show every admittable
156
+ * role so clients can render targeted copy ("requires admin or steward").
157
+ */
141
158
  export declare const PermissionError: z.ZodObject<{
142
159
  error: z.ZodLiteral<"insufficient_permissions">;
143
- required_role: z.ZodString;
160
+ required_roles: z.ZodReadonly<z.ZodArray<z.ZodString>>;
144
161
  }, z.core.$loose>;
145
162
  export type PermissionError = z.infer<typeof PermissionError>;
146
- /** Keeper credential error — returned by `require_keeper` when credential type is wrong. */
147
- export declare const KeeperError: z.ZodObject<{
148
- error: z.ZodLiteral<"keeper_requires_daemon_token">;
149
- credential_type: z.ZodString;
163
+ /**
164
+ * Credential-type error — returned by the dispatcher's post-authorization
165
+ * credential gate (and the `require_credential_types` REST middleware) when
166
+ * the request's credential type isn't in the route's
167
+ * `auth.credential_types` allowlist.
168
+ *
169
+ * `required_credential_types` carries what the route declared
170
+ * (`['daemon_token']` for keeper; future gates carry their own labels).
171
+ * Symmetric with `PermissionError`'s `required_roles`: clients see what
172
+ * the route demanded, not what their credential is.
173
+ */
174
+ export declare const CredentialTypeRequiredError: z.ZodObject<{
175
+ error: z.ZodLiteral<"credential_type_required">;
176
+ required_credential_types: z.ZodReadonly<z.ZodArray<z.ZodString>>;
150
177
  }, z.core.$loose>;
151
- export type KeeperError = z.infer<typeof KeeperError>;
178
+ export type CredentialTypeRequiredError = z.infer<typeof CredentialTypeRequiredError>;
152
179
  /** Rate limit error — returned when a rate limiter rejects the request. */
153
180
  export declare const RateLimitError: z.ZodObject<{
154
181
  error: z.ZodLiteral<"rate_limit_exceeded">;
@@ -169,7 +196,7 @@ export type ForeignKeyError = z.infer<typeof ForeignKeyError>;
169
196
  * Authorization-phase failure shapes. Surfaced when the dispatcher's
170
197
  * `apply_authorization_phase` rejects a request before the handler runs —
171
198
  * the route is acting-aware (input declares `acting?: ActingActor` or
172
- * auth requires permits), but actor resolution failed.
199
+ * auth requires role_grants), but actor resolution failed.
173
200
  *
174
201
  * 400: `actor_required` (with `available[]`) for unspecified-actor on
175
202
  * a multi-actor account; `actor_not_on_account` for a supplied actor
@@ -180,7 +207,7 @@ export type ForeignKeyError = z.infer<typeof ForeignKeyError>;
180
207
  * race (account/actor row deleted between credential validation and
181
208
  * the dispatcher's follow-up read).
182
209
  *
183
- * Used by `derive_error_schemas` when `acting_aware` is true so the
210
+ * Used by `derive_error_schemas` when `auth.actor !== 'none'` so the
184
211
  * merged error surface matches what the dispatcher actually emits.
185
212
  */
186
213
  export declare const ActorRequiredError: z.ZodObject<{
@@ -232,24 +259,20 @@ export type RateLimitKey = z.infer<typeof RateLimitKey>;
232
259
  * Route handlers can declare additional error schemas via `RouteSpec.errors`;
233
260
  * explicit entries override auto-derived ones for the same status code.
234
261
  *
235
- * Derivation rules:
236
- * - **Has input schema** (non-null) or **has params schema** or **has query schema**: 400 (validation error with issues)
237
- * - **auth: authenticated**: 401
238
- * - **auth: role**: 401 + 403 (with `required_role`)
239
- * - **auth: keeper**: 401 + 403 (keeper-specific)
240
- * - **rate_limit**: 429 (rate limit exceeded with `retry_after`)
241
- * - **acting_aware**: extends 400 with `ActorRequiredError` / `ActorNotOnAccountError`
242
- * and adds 500 union of `NoActorsOnAccountError` / `AccountVanishedError`. The
243
- * dispatcher's authorization phase emits these on routes whose input declares
244
- * `acting?: ActingActor` or whose auth requires permits (`role` / `keeper`); the
245
- * route's surface must reflect them so DEV-mode error-schema validation in
246
- * `wrap_output_validation` doesn't fail when the auth phase fires before the
247
- * handler. See `http/CLAUDE.md` § Three-layer error-schema merge.
248
- *
249
- * `acting_aware` is computed at the merge call site (it requires inspecting
250
- * the input schema for `acting?: ActingActor`, which lives in `auth/`). This
251
- * keeps `http/` auth-agnostic — the per-route flag flows in via the optional
252
- * `is_acting_aware` callback on `apply_route_specs` / `generate_app_surface`.
262
+ * Derivation rules under the new flat-record auth shape:
263
+ * - **Has input / params / query schema**: 400 (`ValidationError`).
264
+ * - **`auth.account === 'required'`** or **`auth.actor === 'required'`**: 401
265
+ * (`ApiError`) — pre-validation 401 fires when the credential isn't there.
266
+ * `'optional'` does not derive 401.
267
+ * - **`auth.roles?.length`**: 403 (`PermissionError` carrying `required_roles`).
268
+ * - **`auth.credential_types?.length`**: 403 (`CredentialTypeRequiredError`
269
+ * carrying `required_credential_types` symmetric with `PermissionError`).
270
+ * Today the only credential gate is keeper; future gates reuse the literal.
271
+ * - **`auth.actor !== 'none'`** (`'optional'` or `'required'`): extends 400
272
+ * with `ActorRequiredError` / `ActorNotOnAccountError` and adds 500 union
273
+ * of `NoActorsOnAccountError` / `AccountVanishedError`. The dispatcher's
274
+ * authorization phase emits these whenever it tries to resolve an actor.
275
+ * - **rate_limit**: 429 (`RateLimitError` with `retry_after`).
253
276
  */
254
277
  export interface DeriveErrorSchemasOptions {
255
278
  auth: RouteAuth;
@@ -257,7 +280,6 @@ export interface DeriveErrorSchemasOptions {
257
280
  has_params?: boolean;
258
281
  has_query?: boolean;
259
282
  rate_limit?: RateLimitKey;
260
- acting_aware?: boolean;
261
283
  }
262
- export declare const derive_error_schemas: ({ auth, has_input, has_params, has_query, rate_limit, acting_aware, }: DeriveErrorSchemasOptions) => RouteErrorSchemas;
284
+ export declare const derive_error_schemas: ({ auth, has_input, has_params, has_query, rate_limit, }: DeriveErrorSchemasOptions) => RouteErrorSchemas;
263
285
  //# sourceMappingURL=error_schemas.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"error_schemas.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/error_schemas.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"error_schemas.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/error_schemas.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,7 @@
12
12
  * @module
13
13
  */
14
14
  import { z } from 'zod';
15
+ import { needs_actor } from './auth_shape.js';
15
16
  // --- Core: Validation (auto-derived by route spec middleware) ---
16
17
  /** Request body failed Zod validation. */
17
18
  export const ERROR_INVALID_REQUEST_BODY = 'invalid_request_body';
@@ -26,6 +27,16 @@ export const ERROR_INVALID_QUERY_PARAMS = 'invalid_query_params';
26
27
  export const ERROR_AUTHENTICATION_REQUIRED = 'authentication_required';
27
28
  /** Authenticated but missing required role. */
28
29
  export const ERROR_INSUFFICIENT_PERMISSIONS = 'insufficient_permissions';
30
+ /**
31
+ * Route requires a credential type the request didn't arrive on.
32
+ * Symmetric with `ERROR_INSUFFICIENT_PERMISSIONS` + `required_roles`:
33
+ * the body carries `required_credential_types: ReadonlyArray<string>`
34
+ * — what the route demanded, not what arrived. Today the only
35
+ * credential gate is keeper (`['daemon_token']`); future gates
36
+ * (`agent_token`, `group_actor_token`) reuse the same literal and
37
+ * label themselves through the array.
38
+ */
39
+ export const ERROR_CREDENTIAL_TYPE_REQUIRED = 'credential_type_required';
29
40
  /** Rate limiter rejected the request. */
30
41
  export const ERROR_RATE_LIMIT_EXCEEDED = 'rate_limit_exceeded';
31
42
  /** Username or password is wrong (intentionally vague for enumeration prevention). */
@@ -77,8 +88,6 @@ export const ERROR_NO_ACTORS_ON_ACCOUNT = 'no_actors_on_account';
77
88
  */
78
89
  export const ERROR_ACCOUNT_VANISHED = 'account_vanished';
79
90
  // --- Keeper / daemon token ---
80
- /** Keeper routes require daemon_token credential type. */
81
- export const ERROR_KEEPER_REQUIRES_DAEMON_TOKEN = 'keeper_requires_daemon_token';
82
91
  /** Daemon token header present but malformed or not matching current/previous token. */
83
92
  export const ERROR_INVALID_DAEMON_TOKEN = 'invalid_daemon_token';
84
93
  /** Daemon token valid but keeper account not yet resolved (pre-bootstrap). */
@@ -110,8 +119,8 @@ export const ERROR_INVITE_ACCOUNT_EXISTS_EMAIL = 'invite_account_exists_email';
110
119
  // --- Admin routes ---
111
120
  /** Admin tried to grant a role that is not web-grantable. */
112
121
  export const ERROR_ROLE_NOT_WEB_GRANTABLE = 'role_not_web_grantable';
113
- /** Permit ID not found or not owned by the target actor. */
114
- export const ERROR_PERMIT_NOT_FOUND = 'permit_not_found';
122
+ /** Role grant ID not found or not owned by the target actor. */
123
+ export const ERROR_ROLE_GRANT_NOT_FOUND = 'role_grant_not_found';
115
124
  /** Query parameter `event_type` is not a valid audit event type. */
116
125
  export const ERROR_INVALID_EVENT_TYPE = 'invalid_event_type';
117
126
  // --- DB table browser ---
@@ -142,15 +151,34 @@ export const ValidationError = z.looseObject({
142
151
  path: z.array(z.union([z.string(), z.number()])),
143
152
  })),
144
153
  });
145
- /** Permission error — returned by `require_role()` when the required role is missing. */
154
+ /**
155
+ * Permission error — returned by `require_role()` and the dispatcher's
156
+ * post-authorization role gate when the actor's role_grants don't include any
157
+ * of the route's `auth.roles`.
158
+ *
159
+ * `required_roles` carries the full disjunction the route declared
160
+ * (`auth.roles` from the new flat-record shape). Single-role specs surface
161
+ * as a one-element array; multi-role disjunctions show every admittable
162
+ * role so clients can render targeted copy ("requires admin or steward").
163
+ */
146
164
  export const PermissionError = z.looseObject({
147
165
  error: z.literal(ERROR_INSUFFICIENT_PERMISSIONS),
148
- required_role: z.string(),
166
+ required_roles: z.array(z.string()).readonly(),
149
167
  });
150
- /** Keeper credential error — returned by `require_keeper` when credential type is wrong. */
151
- export const KeeperError = z.looseObject({
152
- error: z.literal(ERROR_KEEPER_REQUIRES_DAEMON_TOKEN),
153
- credential_type: z.string(),
168
+ /**
169
+ * Credential-type error — returned by the dispatcher's post-authorization
170
+ * credential gate (and the `require_credential_types` REST middleware) when
171
+ * the request's credential type isn't in the route's
172
+ * `auth.credential_types` allowlist.
173
+ *
174
+ * `required_credential_types` carries what the route declared
175
+ * (`['daemon_token']` for keeper; future gates carry their own labels).
176
+ * Symmetric with `PermissionError`'s `required_roles`: clients see what
177
+ * the route demanded, not what their credential is.
178
+ */
179
+ export const CredentialTypeRequiredError = z.looseObject({
180
+ error: z.literal(ERROR_CREDENTIAL_TYPE_REQUIRED),
181
+ required_credential_types: z.array(z.string()).readonly(),
154
182
  });
155
183
  /** Rate limit error — returned when a rate limiter rejects the request. */
156
184
  export const RateLimitError = z.looseObject({
@@ -169,7 +197,7 @@ export const ForeignKeyError = z.looseObject({
169
197
  * Authorization-phase failure shapes. Surfaced when the dispatcher's
170
198
  * `apply_authorization_phase` rejects a request before the handler runs —
171
199
  * the route is acting-aware (input declares `acting?: ActingActor` or
172
- * auth requires permits), but actor resolution failed.
200
+ * auth requires role_grants), but actor resolution failed.
173
201
  *
174
202
  * 400: `actor_required` (with `available[]`) for unspecified-actor on
175
203
  * a multi-actor account; `actor_not_on_account` for a supplied actor
@@ -180,7 +208,7 @@ export const ForeignKeyError = z.looseObject({
180
208
  * race (account/actor row deleted between credential validation and
181
209
  * the dispatcher's follow-up read).
182
210
  *
183
- * Used by `derive_error_schemas` when `acting_aware` is true so the
211
+ * Used by `derive_error_schemas` when `auth.actor !== 'none'` so the
184
212
  * merged error surface matches what the dispatcher actually emits.
185
213
  */
186
214
  export const ActorRequiredError = z.looseObject({
@@ -207,10 +235,10 @@ export const AccountVanishedError = z.looseObject({
207
235
  * - `'both'` — both keys.
208
236
  */
209
237
  export const RateLimitKey = z.enum(['ip', 'account', 'both']);
210
- export const derive_error_schemas = ({ auth, has_input = false, has_params = false, has_query = false, rate_limit, acting_aware = false, }) => {
238
+ export const derive_error_schemas = ({ auth, has_input = false, has_params = false, has_query = false, rate_limit, }) => {
211
239
  const errors = {};
212
240
  const has_validation = has_input || has_params || has_query;
213
- if (acting_aware) {
241
+ if (needs_actor(auth)) {
214
242
  errors[400] = has_validation
215
243
  ? z.union([ValidationError, ActorRequiredError, ActorNotOnAccountError])
216
244
  : z.union([ActorRequiredError, ActorNotOnAccountError]);
@@ -219,20 +247,27 @@ export const derive_error_schemas = ({ auth, has_input = false, has_params = fal
219
247
  else if (has_validation) {
220
248
  errors[400] = ValidationError;
221
249
  }
222
- switch (auth.type) {
223
- case 'none':
224
- break;
225
- case 'authenticated':
226
- errors[401] = ApiError;
227
- break;
228
- case 'role':
229
- errors[401] = ApiError;
230
- errors[403] = PermissionError;
231
- break;
232
- case 'keeper':
233
- errors[401] = ApiError;
234
- errors[403] = KeeperError;
235
- break;
250
+ // 401 fires when the dispatcher's pre-validation gate rejects an
251
+ // unauthenticated caller — `account === 'required'` (no credential) or
252
+ // `actor === 'required'` (no credential to resolve an actor against,
253
+ // per registry-time invariant 3 forbidding accountless actors in v1).
254
+ if (auth.account === 'required' || auth.actor === 'required') {
255
+ errors[401] = ApiError;
256
+ }
257
+ // 403 fires when `auth.roles` or `auth.credential_types` rejects a
258
+ // resolved request context. With both axes set, the 403 body could be
259
+ // either shape — emit the union so DEV-mode error-schema validation
260
+ // accepts whichever the dispatcher produced.
261
+ const has_role_gate = !!auth.roles?.length;
262
+ const has_credential_gate = !!auth.credential_types?.length;
263
+ if (has_role_gate && has_credential_gate) {
264
+ errors[403] = z.union([PermissionError, CredentialTypeRequiredError]);
265
+ }
266
+ else if (has_role_gate) {
267
+ errors[403] = PermissionError;
268
+ }
269
+ else if (has_credential_gate) {
270
+ errors[403] = CredentialTypeRequiredError;
236
271
  }
237
272
  if (rate_limit) {
238
273
  errors[429] = RateLimitError;
@@ -1,33 +1,86 @@
1
1
  /**
2
- * Shared post-commit side-effect helper.
2
+ * Two-queue side-effect machinery for request handlers.
3
3
  *
4
- * WS sends and `on_audit_event` SSE broadcasts must never fire mid-transaction —
5
- * a rollback would leak state that never existed. Anything pushed onto
6
- * `pending_effects` runs after the response is sent (see the request-context
7
- * middleware), so this helper is the canonical home for post-commit fan-out.
4
+ * Handlers register fire-and-forget work in one of two queues, distinguished
5
+ * by their timing contract:
8
6
  *
9
- * Satisfied by both `RouteContext` (HTTP routes) and `ActionContext` (RPC
10
- * actions) they share `{log, pending_effects}` by convention, so this
11
- * module stays in `http/` (both depend on it).
7
+ * - `pending_effects: Array<Promise<void>>` eager. Producers push pool
8
+ * writes that are already in flight (audit emits, session-touch UPDATE,
9
+ * api-token usage tracking). The pool write is rollback-resilient by
10
+ * virtue of running outside the request transaction; pushing the
11
+ * in-flight handle lets test mode (`await_pending_effects: true`) await
12
+ * it.
13
+ * - `post_commit_effects: Array<() => void | Promise<void>>` — deferred.
14
+ * Producers go through `emit_after_commit(ctx, fn)`; the flush
15
+ * middleware is the only site that ever invokes the thunk, and it does
16
+ * so after the request handler (and its wrapping `db.transaction`)
17
+ * returns. Used for WS sends and any work that must observe a committed
18
+ * transaction.
19
+ *
20
+ * The split exists because the two shapes encode different contracts:
21
+ * eager pushers are saying "wait for this work that's already started";
22
+ * thunk pushers are saying "run this after the handler returns." Burying
23
+ * both behind one `Array<PendingEffect>` made `c.var.pending_effects.push(x)`
24
+ * ambiguous at the call site. With separate queues, the field name is
25
+ * the contract.
26
+ *
27
+ * Both `RouteContext` (HTTP routes) and `ActionContext` (RPC + WS
28
+ * actions) carry both queues by convention, so this module stays in
29
+ * `http/` (every transport depends on it).
12
30
  *
13
31
  * @module
14
32
  */
15
33
  import type { Logger } from '@fuzdev/fuz_util/log.js';
16
- /** Minimal structural context required by `emit_after_commit`. */
17
- export interface PendingEffectsContext {
34
+ /**
35
+ * Minimal structural context required by `emit_after_commit`. Both
36
+ * `RouteContext` and `ActionContext` satisfy this — they each carry
37
+ * `log` and `post_commit_effects`.
38
+ */
39
+ export interface EmitAfterCommitContext {
18
40
  log: Logger;
19
- pending_effects: Array<Promise<void>>;
41
+ post_commit_effects: Array<() => void | Promise<void>>;
20
42
  }
21
43
  /**
22
44
  * Defer a side effect until after the handler's transaction commits.
23
45
  *
24
- * Exceptions thrown by `fn` are caught and logged via `ctx.log.error`, so one
25
- * failed send cannot corrupt the already-committed response or starve other
26
- * queued effects in the same tick.
46
+ * Pushes a raw thunk onto `ctx.post_commit_effects` the flush
47
+ * middleware (in `server/app_server.ts` and the per-message WS dispatcher)
48
+ * is the only site that ever invokes `fn`. This is load-bearing: a
49
+ * previous implementation queued `Promise.resolve().then(fn)`, which
50
+ * JavaScript's microtask scheduler drains before the wrapping
51
+ * `await db.query('COMMIT')` resumes — `fn` fired mid-transaction and a
52
+ * rollback would leak a notification for state that never landed.
53
+ *
54
+ * The thunk shape closes that gap by deferring the work to flush time.
55
+ * The flush owns the per-thunk `try/catch` + `log.error` so any
56
+ * directly-pushed thunk (tests included) cannot escape the safety net.
57
+ *
58
+ * @param ctx - context carrying `log` and the `post_commit_effects` queue
59
+ * @param fn - side effect to run after commit; may return `void` or `Promise<void>`
60
+ * @mutates `ctx.post_commit_effects` - appends `fn` verbatim
61
+ */
62
+ export declare const emit_after_commit: (ctx: EmitAfterCommitContext, fn: () => void | Promise<void>) => void;
63
+ /**
64
+ * Drain an eager `pending_effects` queue: `Promise.allSettled` the
65
+ * in-flight handles, route every rejection through `log.error`, and
66
+ * fan out to `on_rejection` when supplied (production wires this to
67
+ * `on_effect_error` for monitoring).
68
+ *
69
+ * Returned promise resolves once every effect has settled. Never
70
+ * rejects. No-op when `effects` is empty (common on read-only
71
+ * requests).
72
+ *
73
+ * Symmetric with `flush_post_commit_effects` for the deferred queue.
74
+ */
75
+ export declare const flush_pending_effects: (effects: ReadonlyArray<Promise<void>>, log: Logger, on_rejection?: (reason: unknown) => void) => Promise<void>;
76
+ /**
77
+ * Drain a `post_commit_effects` queue: invoke each thunk under
78
+ * `try/catch`, collect any returned promises, and `Promise.allSettled`
79
+ * them. Synchronous throws and async rejections are routed through
80
+ * `log.error` so one failing effect cannot starve siblings.
27
81
  *
28
- * @param ctx - context carrying `log` and the `pending_effects` queue
29
- * @param fn - synchronous side effect to run after commit
30
- * @mutates `ctx.pending_effects` - appends a never-rejecting promise wrapping `fn`
82
+ * Returned promise resolves once every thunk has finished. Never
83
+ * rejects.
31
84
  */
32
- export declare const emit_after_commit: (ctx: PendingEffectsContext, fn: () => void) => void;
85
+ export declare const flush_post_commit_effects: (effects: ReadonlyArray<() => void | Promise<void>>, log: Logger) => Promise<void>;
33
86
  //# sourceMappingURL=pending_effects.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pending_effects.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/pending_effects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,kEAAkE;AAClE,MAAM,WAAW,qBAAqB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;CACtC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,qBAAqB,EAAE,IAAI,MAAM,IAAI,KAAG,IAU9E,CAAC"}
1
+ {"version":3,"file":"pending_effects.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/pending_effects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,mBAAmB,EAAE,KAAK,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;CACvD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,iBAAiB,GAC7B,KAAK,sBAAsB,EAC3B,IAAI,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAC5B,IAEF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,GACjC,SAAS,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EACrC,KAAK,MAAM,EACX,eAAe,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,KACtC,OAAO,CAAC,IAAI,CASd,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,GACrC,SAAS,aAAa,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,EAClD,KAAK,MAAM,KACT,OAAO,CAAC,IAAI,CAiBd,CAAC"}
@@ -1,35 +1,104 @@
1
1
  /**
2
- * Shared post-commit side-effect helper.
2
+ * Two-queue side-effect machinery for request handlers.
3
3
  *
4
- * WS sends and `on_audit_event` SSE broadcasts must never fire mid-transaction —
5
- * a rollback would leak state that never existed. Anything pushed onto
6
- * `pending_effects` runs after the response is sent (see the request-context
7
- * middleware), so this helper is the canonical home for post-commit fan-out.
4
+ * Handlers register fire-and-forget work in one of two queues, distinguished
5
+ * by their timing contract:
8
6
  *
9
- * Satisfied by both `RouteContext` (HTTP routes) and `ActionContext` (RPC
10
- * actions) they share `{log, pending_effects}` by convention, so this
11
- * module stays in `http/` (both depend on it).
7
+ * - `pending_effects: Array<Promise<void>>` eager. Producers push pool
8
+ * writes that are already in flight (audit emits, session-touch UPDATE,
9
+ * api-token usage tracking). The pool write is rollback-resilient by
10
+ * virtue of running outside the request transaction; pushing the
11
+ * in-flight handle lets test mode (`await_pending_effects: true`) await
12
+ * it.
13
+ * - `post_commit_effects: Array<() => void | Promise<void>>` — deferred.
14
+ * Producers go through `emit_after_commit(ctx, fn)`; the flush
15
+ * middleware is the only site that ever invokes the thunk, and it does
16
+ * so after the request handler (and its wrapping `db.transaction`)
17
+ * returns. Used for WS sends and any work that must observe a committed
18
+ * transaction.
19
+ *
20
+ * The split exists because the two shapes encode different contracts:
21
+ * eager pushers are saying "wait for this work that's already started";
22
+ * thunk pushers are saying "run this after the handler returns." Burying
23
+ * both behind one `Array<PendingEffect>` made `c.var.pending_effects.push(x)`
24
+ * ambiguous at the call site. With separate queues, the field name is
25
+ * the contract.
26
+ *
27
+ * Both `RouteContext` (HTTP routes) and `ActionContext` (RPC + WS
28
+ * actions) carry both queues by convention, so this module stays in
29
+ * `http/` (every transport depends on it).
12
30
  *
13
31
  * @module
14
32
  */
15
33
  /**
16
34
  * Defer a side effect until after the handler's transaction commits.
17
35
  *
18
- * Exceptions thrown by `fn` are caught and logged via `ctx.log.error`, so one
19
- * failed send cannot corrupt the already-committed response or starve other
20
- * queued effects in the same tick.
36
+ * Pushes a raw thunk onto `ctx.post_commit_effects` the flush
37
+ * middleware (in `server/app_server.ts` and the per-message WS dispatcher)
38
+ * is the only site that ever invokes `fn`. This is load-bearing: a
39
+ * previous implementation queued `Promise.resolve().then(fn)`, which
40
+ * JavaScript's microtask scheduler drains before the wrapping
41
+ * `await db.query('COMMIT')` resumes — `fn` fired mid-transaction and a
42
+ * rollback would leak a notification for state that never landed.
21
43
  *
22
- * @param ctx - context carrying `log` and the `pending_effects` queue
23
- * @param fn - synchronous side effect to run after commit
24
- * @mutates `ctx.pending_effects` - appends a never-rejecting promise wrapping `fn`
44
+ * The thunk shape closes that gap by deferring the work to flush time.
45
+ * The flush owns the per-thunk `try/catch` + `log.error` so any
46
+ * directly-pushed thunk (tests included) cannot escape the safety net.
47
+ *
48
+ * @param ctx - context carrying `log` and the `post_commit_effects` queue
49
+ * @param fn - side effect to run after commit; may return `void` or `Promise<void>`
50
+ * @mutates `ctx.post_commit_effects` - appends `fn` verbatim
25
51
  */
26
52
  export const emit_after_commit = (ctx, fn) => {
27
- ctx.pending_effects.push(Promise.resolve().then(() => {
53
+ ctx.post_commit_effects.push(fn);
54
+ };
55
+ /**
56
+ * Drain an eager `pending_effects` queue: `Promise.allSettled` the
57
+ * in-flight handles, route every rejection through `log.error`, and
58
+ * fan out to `on_rejection` when supplied (production wires this to
59
+ * `on_effect_error` for monitoring).
60
+ *
61
+ * Returned promise resolves once every effect has settled. Never
62
+ * rejects. No-op when `effects` is empty (common on read-only
63
+ * requests).
64
+ *
65
+ * Symmetric with `flush_post_commit_effects` for the deferred queue.
66
+ */
67
+ export const flush_pending_effects = async (effects, log, on_rejection) => {
68
+ if (effects.length === 0)
69
+ return;
70
+ const results = await Promise.allSettled(effects);
71
+ for (const result of results) {
72
+ if (result.status === 'rejected') {
73
+ log.error('pending effect rejected:', result.reason);
74
+ on_rejection?.(result.reason);
75
+ }
76
+ }
77
+ };
78
+ /**
79
+ * Drain a `post_commit_effects` queue: invoke each thunk under
80
+ * `try/catch`, collect any returned promises, and `Promise.allSettled`
81
+ * them. Synchronous throws and async rejections are routed through
82
+ * `log.error` so one failing effect cannot starve siblings.
83
+ *
84
+ * Returned promise resolves once every thunk has finished. Never
85
+ * rejects.
86
+ */
87
+ export const flush_post_commit_effects = async (effects, log) => {
88
+ const promises = [];
89
+ for (const fn of effects) {
28
90
  try {
29
- fn();
91
+ const result = fn();
92
+ if (result instanceof Promise) {
93
+ promises.push(result.catch((err) => {
94
+ log.error('post-commit side effect failed:', err);
95
+ }));
96
+ }
30
97
  }
31
98
  catch (err) {
32
- ctx.log.error('post-commit side effect failed:', err);
99
+ log.error('post-commit side effect failed:', err);
33
100
  }
34
- }));
101
+ }
102
+ if (promises.length)
103
+ await Promise.allSettled(promises);
35
104
  };