@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,20 +12,24 @@
12
12
  */
13
13
  import { z } from 'zod';
14
14
  import { RateLimitKey } from '../http/error_schemas.js';
15
+ import { RouteAuth } from '../http/auth_shape.js';
15
16
  export const ActionKind = z.enum(['request_response', 'remote_notification', 'local_call']);
16
17
  export const ActionInitiator = z.enum(['frontend', 'backend', 'both']);
17
- export const ActionAuth = z.union([
18
- z.literal('public'),
19
- z.literal('authenticated'),
20
- z.literal('keeper'),
21
- z.strictObject({ role: z.string() }),
22
- ]);
23
18
  export const ActionSideEffects = z.boolean();
24
19
  export const ActionSpec = z.strictObject({
25
20
  method: z.string(),
26
21
  kind: ActionKind,
27
22
  initiator: ActionInitiator,
28
- auth: ActionAuth.nullable(),
23
+ /**
24
+ * The four-axis auth shape (canonical schema in `http/auth_shape.ts`).
25
+ * `null` for `remote_notification` and `local_call` — those don't
26
+ * dispatch through the request/response auth gate.
27
+ *
28
+ * See `../http/auth_shape.ts` for the design rationale (orthogonal
29
+ * authentication / account-resolution / actor-resolution / role-and-
30
+ * credential authorization axes).
31
+ */
32
+ auth: RouteAuth.nullable(),
29
33
  side_effects: ActionSideEffects,
30
34
  input: z.custom((v) => v instanceof z.ZodType),
31
35
  output: z.custom((v) => v instanceof z.ZodType),
@@ -44,7 +48,7 @@ export const ActionSpec = z.strictObject({
44
48
  * failure. Declarative metadata mirroring the `streams` precedent —
45
49
  * codegen, UI form-state matching, and docs read it off the spec instead
46
50
  * of scanning handler implementations. Reuses the same `as const` string
47
- * constants the handler throws (e.g. `ERROR_OFFER_*`) so call sites can
51
+ * constants the handler throws (e.g. `ERROR_ROLE_GRANT_OFFER_*`) so call sites can
48
52
  * import either side. Optional — actions that surface only standard
49
53
  * transport errors leave it unset.
50
54
  */
@@ -54,8 +58,9 @@ export const ActionSpec = z.strictObject({
54
58
  * actions without it skip the rate-limit hook entirely.
55
59
  *
56
60
  * - `'ip'` — keyed on the resolved client IP (`get_client_ip(c)`).
57
- * - `'account'` — keyed on the post-auth actor id (`request_context.actor.id`).
58
- * Registration-time error if paired with `auth: 'public'` (no actor).
61
+ * - `'account'` — keyed on the post-auth account id (`request_context.account.id`).
62
+ * Registration-time error if paired with `auth.account !== 'required'`
63
+ * (no account to key on).
59
64
  * - `'both'` — both checks run; either can block.
60
65
  *
61
66
  * Throttle-requests semantics — every invocation records, regardless of
@@ -72,7 +77,7 @@ export const ActionSpec = z.strictObject({
72
77
  });
73
78
  export const RequestResponseActionSpec = ActionSpec.extend({
74
79
  kind: z.literal('request_response').default('request_response'),
75
- auth: ActionAuth,
80
+ auth: RouteAuth,
76
81
  async: z.literal(true).default(true),
77
82
  });
78
83
  export const RemoteNotificationActionSpec = ActionSpec.extend({
@@ -1,72 +1,40 @@
1
1
  /**
2
- * Shared type surface for the action system — context, handler, composable Action tuple.
2
+ * Shared type surface for the action system — `Action` (the composable
3
+ * `{spec, handler?}` tuple) and re-exports of the canonical `ActionContext`
4
+ * + `ActionHandler` shapes.
3
5
  *
4
- * These types sit above `actions/action_spec.ts` (pure Zod schemas) and below the
5
- * dispatchers (`actions/register_action_ws.ts`, `actions/action_rpc.ts`). Extracted so the
6
- * shared protocol actions (e.g. `heartbeat_action`) can name them without
7
- * pulling in server-only modules.
6
+ * Sits above `actions/action_spec.ts` (pure Zod schemas) and below the
7
+ * dispatchers (`actions/register_action_ws.ts`, `actions/action_rpc.ts`,
8
+ * `actions/perform_action.ts`). Extracted so the shared protocol actions
9
+ * (e.g. `heartbeat_action`) can name them without pulling in server-only
10
+ * modules.
11
+ *
12
+ * HTTP RPC and WebSocket dispatchers both call into `perform_action`,
13
+ * and both pass the same `ActionContext` to the handler. Consumers
14
+ * inject domain deps via factory closures the same way HTTP RPC
15
+ * factories do (see `auth/standard_rpc_actions.ts`).
8
16
  *
9
17
  * @module
10
18
  */
11
- import type { Uuid } from '@fuzdev/fuz_util/id.js';
12
- import type { JsonrpcRequestId } from '../http/jsonrpc.js';
13
19
  import type { ActionSpecUnion } from './action_spec.js';
14
- /**
15
- * Minimum per-request context every server-side WS handler receives.
16
- *
17
- * Consumers extend this with domain-specific fields via the dispatcher's
18
- * `extend_context` option. Mirrors the HTTP-side `ActionContext` and Rust's
19
- * `Ctx<'a>` shape (`request_id` + `NotifyFn` + `CancellationToken`).
20
- */
21
- export interface BaseHandlerContext {
22
- /** JSON-RPC envelope request id — echoed back on the response. */
23
- request_id: JsonrpcRequestId;
24
- /**
25
- * Stable per-socket connection id assigned by
26
- * `BackendWebsocketTransport.add_connection` — same reference across every
27
- * message on this socket, also passed to `on_socket_open` /
28
- * `on_socket_close`. Consumers key per-connection domain state on this
29
- * directly instead of trying to derive it from signals (which are
30
- * per-message composites of `AbortSignal.any([socket, request])`).
31
- */
32
- connection_id: Uuid;
33
- /**
34
- * Send a request-scoped JSON-RPC notification to the originating socket.
35
- * Not a broadcast — the message only reaches the client whose request
36
- * triggered this handler.
37
- */
38
- notify: (method: string, params: unknown) => void;
39
- /**
40
- * Fires on socket close OR on a client-initiated `cancel` notification
41
- * matching this request's id. Streaming handlers poll for early
42
- * termination; per-message composite (`AbortSignal.any([socket, request])`)
43
- * — not stable across messages.
44
- */
45
- signal: AbortSignal;
46
- }
47
- /**
48
- * Handler signature — receives validated input and per-request context.
49
- *
50
- * Named to disambiguate from `actions/action_rpc.ts`'s `ActionHandler`
51
- * (HTTP-side, `ActionContext` + two generic slots). The WS variant is
52
- * single-slotted on the context and returns `unknown`.
53
- */
54
- export type WsActionHandler<TCtx extends BaseHandlerContext = BaseHandlerContext> = (input: unknown, ctx: TCtx) => unknown;
20
+ import type { ActionHandler } from './action_rpc.js';
55
21
  /**
56
22
  * A spec paired with its optional handler — the composable unit passed to
57
- * `register_action_ws` and `create_rpc_client`. The server uses
58
- * both fields; the client reads only `spec` (the `handler` is
59
- * ignored, harmless). Shared fuz_app primitives (e.g. `heartbeat_action`)
60
- * export a complete tuple so consumers spread them into both sides'
61
- * `actions` array without inventing per-repo ping plumbing.
23
+ * `register_action_ws` and `create_rpc_client`. The server uses both
24
+ * fields; the client reads only `spec` (the `handler` is ignored,
25
+ * harmless). Shared fuz_app primitives (e.g. `heartbeat_action`) export a
26
+ * complete tuple so consumers spread them into both sides' `actions`
27
+ * arrays without inventing per-repo ping plumbing.
62
28
  *
63
- * Left open for future fields (`rate_limit`, ACL, middleware hooks) so
64
- * additions attach to the action itself instead of scattering across
65
- * parallel arrays.
29
+ * Polymorphic on `kind`: `request_response` specs require a handler for
30
+ * dispatch; `remote_notification` specs may declare a stub handler for
31
+ * symmetry but are dispatcher-handled (e.g. `cancel`); `local_call` specs
32
+ * never reach a network dispatcher. The WS dispatcher only invokes
33
+ * handlers on `request_response` actions; everything else is registry-only.
66
34
  */
67
- export interface Action<TCtx extends BaseHandlerContext = BaseHandlerContext> {
68
- spec: ActionSpecUnion;
69
- /** Server-side handler. Ignored by the client. Omit for client-only specs. */
70
- handler?: WsActionHandler<TCtx>;
35
+ export interface Action<TSpec extends ActionSpecUnion = ActionSpecUnion> {
36
+ spec: TSpec;
37
+ /** Server-side handler invoked by dispatchers on `request_response` actions. Ignored for client-only specs and dispatcher-handled notifications. */
38
+ handler?: ActionHandler;
71
39
  }
72
40
  //# sourceMappingURL=action_types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"action_types.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAEjD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAEtD;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IAClC,kEAAkE;IAClE,UAAU,EAAE,gBAAgB,CAAC;IAC7B;;;;;;;OAOG;IACH,aAAa,EAAE,IAAI,CAAC;IACpB;;;;OAIG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD;;;;;OAKG;IACH,MAAM,EAAE,WAAW,CAAC;CACpB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,CAAC,IAAI,SAAS,kBAAkB,GAAG,kBAAkB,IAAI,CACnF,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,IAAI,KACL,OAAO,CAAC;AAEb;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,MAAM,CAAC,IAAI,SAAS,kBAAkB,GAAG,kBAAkB;IAC3E,IAAI,EAAE,eAAe,CAAC;IACtB,8EAA8E;IAC9E,OAAO,CAAC,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC;CAChC"}
1
+ {"version":3,"file":"action_types.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAEnD;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,MAAM,CAAC,KAAK,SAAS,eAAe,GAAG,eAAe;IACtE,IAAI,EAAE,KAAK,CAAC;IACZ,sJAAsJ;IACtJ,OAAO,CAAC,EAAE,aAAa,CAAC;CACxB"}
@@ -1,10 +1,18 @@
1
1
  /**
2
- * Shared type surface for the action system — context, handler, composable Action tuple.
2
+ * Shared type surface for the action system — `Action` (the composable
3
+ * `{spec, handler?}` tuple) and re-exports of the canonical `ActionContext`
4
+ * + `ActionHandler` shapes.
3
5
  *
4
- * These types sit above `actions/action_spec.ts` (pure Zod schemas) and below the
5
- * dispatchers (`actions/register_action_ws.ts`, `actions/action_rpc.ts`). Extracted so the
6
- * shared protocol actions (e.g. `heartbeat_action`) can name them without
7
- * pulling in server-only modules.
6
+ * Sits above `actions/action_spec.ts` (pure Zod schemas) and below the
7
+ * dispatchers (`actions/register_action_ws.ts`, `actions/action_rpc.ts`,
8
+ * `actions/perform_action.ts`). Extracted so the shared protocol actions
9
+ * (e.g. `heartbeat_action`) can name them without pulling in server-only
10
+ * modules.
11
+ *
12
+ * HTTP RPC and WebSocket dispatchers both call into `perform_action`,
13
+ * and both pass the same `ActionContext` to the handler. Consumers
14
+ * inject domain deps via factory closures the same way HTTP RPC
15
+ * factories do (see `auth/standard_rpc_actions.ts`).
8
16
  *
9
17
  * @module
10
18
  */
@@ -9,11 +9,11 @@
9
9
  * Counterpart to `register_action_ws`: that handles request-scoped dispatch
10
10
  * (frontend-initiated), this handles broadcast (backend-initiated). Together
11
11
  * they cover the two primitives fuz_app consumers share. Request-scoped
12
- * streaming (`completion_progress`, `tx_apply` events) stays on
12
+ * streaming (`completion_progress`, `zap_apply` events) stays on
13
13
  * `ctx.notify` inside a handler — it's socket-scoped, not broadcast.
14
14
  *
15
15
  * Extracted from zzz's `backend_actions_api.ts` to stop the pattern from
16
- * drifting across zzz, tx, and undying.
16
+ * drifting across zzz, zap, and undying.
17
17
  *
18
18
  * @module
19
19
  */
@@ -9,11 +9,11 @@
9
9
  * Counterpart to `register_action_ws`: that handles request-scoped dispatch
10
10
  * (frontend-initiated), this handles broadcast (backend-initiated). Together
11
11
  * they cover the two primitives fuz_app consumers share. Request-scoped
12
- * streaming (`completion_progress`, `tx_apply` events) stays on
12
+ * streaming (`completion_progress`, `zap_apply` events) stays on
13
13
  * `ctx.notify` inside a handler — it's socket-scoped, not broadcast.
14
14
  *
15
15
  * Extracted from zzz's `backend_actions_api.ts` to stop the pattern from
16
- * drifting across zzz, tx, and undying.
16
+ * drifting across zzz, zap, and undying.
17
17
  *
18
18
  * @module
19
19
  */
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Shared registration loop for action-dispatcher endpoints.
3
+ *
4
+ * `create_rpc_endpoint` (HTTP RPC) and `register_action_ws` (WebSocket)
5
+ * both build a `Map<method, RpcAction>` from a list of action specs and
6
+ * gate the build on the same registry-time invariants:
7
+ *
8
+ * 1. **Auth-shape biconditional** — per `assert_route_auth_acting_biconditional`
9
+ * in `http/auth_shape.ts`: `auth.actor !== 'none' ⟺ input declares
10
+ * acting?: ActingActor`. Fires for every spec with non-null auth.
11
+ * 2. **Rate-limit / account axis** — `rate_limit: 'account' | 'both'`
12
+ * requires `auth.account === 'required'`; without an account on the
13
+ * request there is no key for the per-account bucket.
14
+ * 3. **JSON-RPC §4.2 wire validity** — `request_response` specs whose
15
+ * handler will reach the dispatch map must not use `z.null()` for
16
+ * input (the wire format forbids `"params": null`; use `z.void()`
17
+ * for parameterless methods).
18
+ * 4. **Duplicate method names** — JSON-RPC keys on `method`, so every
19
+ * spec in the array must declare a unique `method` regardless of
20
+ * kind / handler presence.
21
+ *
22
+ * Pre-consolidation each dispatcher inlined these checks; the comment
23
+ * in `register_action_ws.ts` literally said "mirrors the HTTP RPC
24
+ * registration check" but nothing kept them mirrored. Centralizing the
25
+ * loop closes the most likely future drift surface.
26
+ *
27
+ * @module
28
+ */
29
+ import type { Action } from './action_types.js';
30
+ import type { RpcAction } from './action_rpc.js';
31
+ /** Result returned by `compile_action_registry`. */
32
+ export interface ActionRegistryCompileResult {
33
+ /**
34
+ * Method → `RpcAction` lookup for dispatch. Only `request_response`
35
+ * specs with a handler land here — kind-polymorphic input arrays
36
+ * (the WebSocket dispatcher's `actions: ReadonlyArray<Action>`)
37
+ * pass `remote_notification` / handler-less specs through unchanged.
38
+ */
39
+ action_map: Map<string, RpcAction>;
40
+ }
41
+ /**
42
+ * Validate registry-time invariants and build the dispatcher's
43
+ * method → action lookup.
44
+ *
45
+ * @param actions - polymorphic action array; HTTP RPC passes `RpcAction[]` (narrower), WebSocket passes `Action[]` (kind-polymorphic — handler-less notification specs are accepted)
46
+ * @param ctx_label - per-spec error-message prefix, e.g. `'RPC action'` or `'WS action'`. Combined with the spec method as `${ctx_label} "${method}"`.
47
+ * @throws Error on biconditional violation, rate-limit/account-axis mismatch, JSON-RPC null-input, or duplicate method.
48
+ */
49
+ export declare const compile_action_registry: (actions: ReadonlyArray<Action>, ctx_label: string) => ActionRegistryCompileResult;
50
+ //# sourceMappingURL=compile_action_registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile_action_registry.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/compile_action_registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mBAAmB,CAAC;AAC9C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAI/C,oDAAoD;AACpD,MAAM,WAAW,2BAA2B;IAC3C;;;;;OAKG;IACH,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GACnC,SAAS,aAAa,CAAC,MAAM,CAAC,EAC9B,WAAW,MAAM,KACf,2BAoCF,CAAC"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Shared registration loop for action-dispatcher endpoints.
3
+ *
4
+ * `create_rpc_endpoint` (HTTP RPC) and `register_action_ws` (WebSocket)
5
+ * both build a `Map<method, RpcAction>` from a list of action specs and
6
+ * gate the build on the same registry-time invariants:
7
+ *
8
+ * 1. **Auth-shape biconditional** — per `assert_route_auth_acting_biconditional`
9
+ * in `http/auth_shape.ts`: `auth.actor !== 'none' ⟺ input declares
10
+ * acting?: ActingActor`. Fires for every spec with non-null auth.
11
+ * 2. **Rate-limit / account axis** — `rate_limit: 'account' | 'both'`
12
+ * requires `auth.account === 'required'`; without an account on the
13
+ * request there is no key for the per-account bucket.
14
+ * 3. **JSON-RPC §4.2 wire validity** — `request_response` specs whose
15
+ * handler will reach the dispatch map must not use `z.null()` for
16
+ * input (the wire format forbids `"params": null`; use `z.void()`
17
+ * for parameterless methods).
18
+ * 4. **Duplicate method names** — JSON-RPC keys on `method`, so every
19
+ * spec in the array must declare a unique `method` regardless of
20
+ * kind / handler presence.
21
+ *
22
+ * Pre-consolidation each dispatcher inlined these checks; the comment
23
+ * in `register_action_ws.ts` literally said "mirrors the HTTP RPC
24
+ * registration check" but nothing kept them mirrored. Centralizing the
25
+ * loop closes the most likely future drift surface.
26
+ *
27
+ * @module
28
+ */
29
+ import { assert_route_auth_acting_biconditional } from '../http/auth_shape.js';
30
+ import { is_null_schema } from '../http/schema_helpers.js';
31
+ /**
32
+ * Validate registry-time invariants and build the dispatcher's
33
+ * method → action lookup.
34
+ *
35
+ * @param actions - polymorphic action array; HTTP RPC passes `RpcAction[]` (narrower), WebSocket passes `Action[]` (kind-polymorphic — handler-less notification specs are accepted)
36
+ * @param ctx_label - per-spec error-message prefix, e.g. `'RPC action'` or `'WS action'`. Combined with the spec method as `${ctx_label} "${method}"`.
37
+ * @throws Error on biconditional violation, rate-limit/account-axis mismatch, JSON-RPC null-input, or duplicate method.
38
+ */
39
+ export const compile_action_registry = (actions, ctx_label) => {
40
+ const action_map = new Map();
41
+ const seen_methods = new Set();
42
+ for (const action of actions) {
43
+ const { spec } = action;
44
+ const ctx = `${ctx_label} "${spec.method}"`;
45
+ if (seen_methods.has(spec.method)) {
46
+ throw new Error(`Duplicate ${ctx_label} method: ${spec.method}`);
47
+ }
48
+ seen_methods.add(spec.method);
49
+ // Auth-shape invariants apply to any spec with non-null auth (which
50
+ // per the spec union means `kind === 'request_response'`).
51
+ if (spec.auth !== null) {
52
+ assert_route_auth_acting_biconditional(spec.auth, { input: spec.input }, ctx);
53
+ if ((spec.rate_limit === 'account' || spec.rate_limit === 'both') &&
54
+ spec.auth.account !== 'required') {
55
+ throw new Error(`${ctx} declares rate_limit: '${spec.rate_limit}' but auth.account !== 'required' — no account guaranteed for account-keyed limiting. Use 'ip' or set auth.account: 'required'.`);
56
+ }
57
+ }
58
+ // Only request_response specs with a handler reach the dispatch
59
+ // map. Notifications (e.g. WS `cancel`) and handler-less specs
60
+ // stay registry-only and bypass JSON-RPC wire-validity checks.
61
+ if (spec.kind === 'request_response' && action.handler) {
62
+ if (is_null_schema(spec.input)) {
63
+ throw new Error(`${ctx} uses z.null() for input — JSON-RPC 2.0 §4.2 forbids "params": null on the wire. Use z.void() for parameterless methods.`);
64
+ }
65
+ action_map.set(spec.method, { spec, handler: action.handler });
66
+ }
67
+ }
68
+ return { action_map };
69
+ };
@@ -20,15 +20,19 @@
20
20
  import { z } from 'zod';
21
21
  import type { Action } from './action_types.js';
22
22
  /**
23
- * `ActionSpec` for the shared heartbeat. `authenticated` auth upgrade-time
24
- * auth has already admitted the socket; heartbeats don't need role gating.
25
- * `side_effects: false` keeps it orthogonal to state changes.
23
+ * `ActionSpec` for the shared heartbeat. Account-required, actor-none
24
+ * upgrade-time auth has already admitted the socket; heartbeats don't
25
+ * need role gating or actor resolution. `side_effects: false` keeps it
26
+ * orthogonal to state changes.
26
27
  */
27
28
  export declare const heartbeat_action_spec: {
28
29
  method: string;
29
30
  kind: "request_response";
30
31
  initiator: "frontend";
31
- auth: "authenticated";
32
+ auth: {
33
+ account: "required";
34
+ actor: "none";
35
+ };
32
36
  side_effects: false;
33
37
  input: z.ZodObject<{}, z.core.$strict>;
34
38
  output: z.ZodObject<{}, z.core.$strict>;
@@ -1 +1 @@
1
- {"version":3,"file":"heartbeat.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/heartbeat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mBAAmB,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;CAUG,CAAC;AAEtC,4EAA4E;AAC5E,eAAO,MAAM,iBAAiB,QAAO,MAAM,CAAC,MAAM,EAAE,KAAK,CAAS,CAAC;AAEnE;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAG9B,CAAC"}
1
+ {"version":3,"file":"heartbeat.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/heartbeat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mBAAmB,CAAC;AAE9C;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;CAUG,CAAC;AAEtC,4EAA4E;AAC5E,eAAO,MAAM,iBAAiB,QAAO,MAAM,CAAC,MAAM,EAAE,KAAK,CAAS,CAAC;AAEnE;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAG9B,CAAC"}
@@ -19,15 +19,16 @@
19
19
  */
20
20
  import { z } from 'zod';
21
21
  /**
22
- * `ActionSpec` for the shared heartbeat. `authenticated` auth upgrade-time
23
- * auth has already admitted the socket; heartbeats don't need role gating.
24
- * `side_effects: false` keeps it orthogonal to state changes.
22
+ * `ActionSpec` for the shared heartbeat. Account-required, actor-none
23
+ * upgrade-time auth has already admitted the socket; heartbeats don't
24
+ * need role gating or actor resolution. `side_effects: false` keeps it
25
+ * orthogonal to state changes.
25
26
  */
26
27
  export const heartbeat_action_spec = {
27
28
  method: 'heartbeat',
28
29
  kind: 'request_response',
29
30
  initiator: 'frontend',
30
- auth: 'authenticated',
31
+ auth: { account: 'required', actor: 'none' },
31
32
  side_effects: false,
32
33
  input: z.strictObject({}),
33
34
  output: z.strictObject({}),
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Transport-agnostic dispatch core shared by HTTP RPC and WebSocket
3
+ * action dispatchers.
4
+ *
5
+ * `perform_action` runs the post-parse pipeline that every action-spec
6
+ * handler must traverse:
7
+ *
8
+ * 1. **Pre-validation auth (401)** — short-circuits unauthenticated callers
9
+ * on `'required'` axes before input validation runs, so callers never
10
+ * see `invalid_params` for methods with required input.
11
+ * 2. **Validate params (400)** — `spec.input.safeParse(raw_params)` with
12
+ * `z.void()` / `?? {}` rules. The validated input lands inside the
13
+ * function so the authorization phase reads `acting` as a typed Zod
14
+ * field.
15
+ * 3. **Authorization phase** — when `auth.actor !== 'none'` (or
16
+ * `auth.account !== 'none' && actor === 'none'`), resolves the actor
17
+ * via `apply_authorization_phase` against the supplied `account_id`
18
+ * plus `validated_input.acting`. Failures fold into a JSON-RPC error
19
+ * envelope. The test-harness escape hatch lives in the caller — pass
20
+ * `preset.request_context` to skip the live phase and use a pre-baked
21
+ * context instead.
22
+ * 4. **Post-authorization auth (403)** — gates `auth.credential_types` and
23
+ * `auth.roles` against the resolved context.
24
+ * 5. **Rate limit (429)** — per-action IP / account throttling, throttle-
25
+ * requests semantics (every invocation records, regardless of outcome).
26
+ * 6. **Dispatch + DEV-only output validation + error normalization** —
27
+ * `spec.side_effects` picks transaction (`deps.db.transaction`) vs
28
+ * pool. Handler throws roll back the transaction; the catch sits
29
+ * outside the transaction boundary. Handler outputs are validated
30
+ * against `spec.output` under DEV (logs an error on mismatch, never
31
+ * throws, never mutates the result).
32
+ *
33
+ * The function is pure data — it never touches a Hono context, so HTTP
34
+ * RPC, REST bridge (when on the action surface), and WS dispatch all
35
+ * call into it the same way and bind the discriminated
36
+ * `PerformActionResult` to their wire shape.
37
+ *
38
+ * @module
39
+ */
40
+ import type { Logger } from '@fuzdev/fuz_util/log.js';
41
+ import type { Uuid } from '@fuzdev/fuz_util/id.js';
42
+ import { type RequestContext } from '../auth/request_context.js';
43
+ import { type CredentialType } from '../hono_context.js';
44
+ import type { Db } from '../db/db.js';
45
+ import { type JsonrpcRequestId, type JsonrpcErrorObject } from '../http/jsonrpc.js';
46
+ import type { RateLimiter } from '../rate_limiter.js';
47
+ import type { RpcAction } from './action_rpc.js';
48
+ /**
49
+ * Per-call inputs to `perform_action`. Each transport assembles this from
50
+ * its wire envelope + connection identity.
51
+ */
52
+ export interface PerformActionInput {
53
+ /** The resolved spec + handler (transport does method lookup). */
54
+ action: RpcAction;
55
+ /** Raw params from the wire envelope (post-`JsonrpcRequest.parse`, pre-`spec.input.safeParse`). */
56
+ raw_params: unknown;
57
+ /** JSON-RPC request id — echoed onto the response. */
58
+ request_id: JsonrpcRequestId;
59
+ /** Authenticated account id, or `null` for anonymous. */
60
+ account_id: string | null;
61
+ /** Credential type the request arrived on, or `null` for anonymous. */
62
+ credential_type: CredentialType | null;
63
+ /** Resolved client IP (`'unknown'` if upstream couldn't resolve). */
64
+ client_ip: string;
65
+ /** Per-request abort signal. HTTP: `c.req.raw.signal`. WS: `AbortSignal.any([socket, request])`. */
66
+ signal: AbortSignal;
67
+ /** Send a request-scoped notification. HTTP: DEV-warn-and-drop. WS: socket-scoped. */
68
+ notify: (method: string, params: unknown) => void;
69
+ /** Stable per-socket id on WS; `undefined` on HTTP. */
70
+ connection_id?: Uuid;
71
+ /**
72
+ * Test-harness escape hatch. When set, the live authorization phase is
73
+ * skipped and `request_context` is used directly for post-authorization
74
+ * checks + handler dispatch. Production callers leave this `undefined`.
75
+ */
76
+ preset?: {
77
+ request_context: RequestContext | null;
78
+ };
79
+ }
80
+ /**
81
+ * Per-deps inputs to `perform_action`. Each transport supplies its own
82
+ * pool-level `Db` and rate limiters; the dispatcher wraps in a transaction
83
+ * iff `spec.side_effects` is true.
84
+ *
85
+ * Pool-resilient fire-and-forget effects (audit writes) run through
86
+ * `AppDeps.audit.emit` from the action factory's closure — the dispatcher
87
+ * never sees the audit emitter. The bound emitter owns the pool.
88
+ */
89
+ export interface PerformActionDeps {
90
+ /** Pool-level DB. The dispatcher wraps in `db.transaction` for `side_effects: true` actions. */
91
+ db: Db;
92
+ /**
93
+ * Eager fire-and-forget pool-write queue, flushed by the transport's
94
+ * `try/finally` via `flush_pending_effects`.
95
+ */
96
+ pending_effects: Array<Promise<void>>;
97
+ /**
98
+ * Deferred post-commit thunks pushed via `emit_after_commit`, flushed
99
+ * by the transport's `try/finally` after the handler returns.
100
+ */
101
+ post_commit_effects: Array<() => void | Promise<void>>;
102
+ /** Logger threaded into `ActionContext.log`. */
103
+ log: Logger;
104
+ /** Per-IP limiter (shared across transports). `null` disables. */
105
+ action_ip_rate_limiter: RateLimiter | null;
106
+ /** Per-account limiter (shared across transports). `null` disables. */
107
+ action_account_rate_limiter: RateLimiter | null;
108
+ }
109
+ /**
110
+ * Discriminated result of `perform_action`. Each transport binds this to
111
+ * its wire shape: HTTP RPC folds the error into a JSON-RPC envelope and
112
+ * returns via `c.json`; WS sends the response over the socket.
113
+ */
114
+ export type PerformActionResult = {
115
+ kind: 'ok';
116
+ result: unknown;
117
+ } | {
118
+ kind: 'error';
119
+ error: JsonrpcErrorObject;
120
+ status: number;
121
+ };
122
+ /**
123
+ * The shared dispatch core. Pure data — no Hono context, no socket. Each
124
+ * transport calls into this with pre-parsed inputs and binds the result
125
+ * to its wire shape.
126
+ *
127
+ * Phase order: 401 → 400 → 403 → handler. On the test-preset path the
128
+ * dispatcher skips the live authorization phase and uses the supplied
129
+ * pre-baked context for post-authorization checks; pre-validation 401
130
+ * still fires when the harness omits `account_id`.
131
+ */
132
+ export declare const perform_action: (input: PerformActionInput, deps: PerformActionDeps) => Promise<PerformActionResult>;
133
+ /**
134
+ * Build a JSON-RPC response envelope from a `PerformActionResult` for
135
+ * transports that wire over the JSON-RPC 2.0 message shape (HTTP RPC + WS).
136
+ */
137
+ export declare const perform_action_result_to_envelope: (id: JsonrpcRequestId, result: PerformActionResult) => {
138
+ jsonrpc: string;
139
+ id: JsonrpcRequestId;
140
+ } & ({
141
+ result: unknown;
142
+ } | {
143
+ error: JsonrpcErrorObject;
144
+ });
145
+ //# sourceMappingURL=perform_action.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perform_action.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/perform_action.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAGH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAGN,KAAK,cAAc,EACnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAC,KAAK,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAEpC,OAAO,EAEN,KAAK,gBAAgB,EAErB,KAAK,kBAAkB,EACvB,MAAM,oBAAoB,CAAC;AAW5B,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAEpD,OAAO,KAAK,EAA+B,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAE7E;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IAClC,kEAAkE;IAClE,MAAM,EAAE,SAAS,CAAC;IAClB,mGAAmG;IACnG,UAAU,EAAE,OAAO,CAAC;IACpB,sDAAsD;IACtD,UAAU,EAAE,gBAAgB,CAAC;IAC7B,yDAAyD;IACzD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,uEAAuE;IACvE,eAAe,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAC;IAClB,oGAAoG;IACpG,MAAM,EAAE,WAAW,CAAC;IACpB,sFAAsF;IACtF,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,uDAAuD;IACvD,aAAa,CAAC,EAAE,IAAI,CAAC;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE;QAAC,eAAe,EAAE,cAAc,GAAG,IAAI,CAAA;KAAC,CAAC;CAClD;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IACjC,gGAAgG;IAChG,EAAE,EAAE,EAAE,CAAC;IACP;;;OAGG;IACH,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC;;;OAGG;IACH,mBAAmB,EAAE,KAAK,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACvD,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,kEAAkE;IAClE,sBAAsB,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3C,uEAAuE;IACvE,2BAA2B,EAAE,WAAW,GAAG,IAAI,CAAC;CAChD;AAED;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAC5B;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAC,GAC7B;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAAC;AAE9D;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAC1B,OAAO,kBAAkB,EACzB,MAAM,iBAAiB,KACrB,OAAO,CAAC,mBAAmB,CAuJ7B,CAAC;AA4EF;;;GAGG;AACH,eAAO,MAAM,iCAAiC,GAC7C,IAAI,gBAAgB,EACpB,QAAQ,mBAAmB,KACzB;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,gBAAgB,CAAA;CAAC,GAAG,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAC,GAAG;IAAC,KAAK,EAAE,kBAAkB,CAAA;CAAC,CAK5F,CAAC"}