@fuzdev/fuz_app 0.54.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 (348) hide show
  1. package/dist/actions/CLAUDE.md +214 -103
  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 +32 -0
  6. package/dist/actions/action_codegen.d.ts.map +1 -1
  7. package/dist/actions/action_codegen.js +35 -15
  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 +141 -22
  11. package/dist/actions/action_rpc.d.ts.map +1 -1
  12. package/dist/actions/action_rpc.js +106 -187
  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 +46 -40
  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 +15 -10
  34. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  35. package/dist/actions/register_ws_endpoint.js +54 -7
  36. package/dist/actions/transports.d.ts.map +1 -1
  37. package/dist/actions/transports.js +0 -4
  38. package/dist/actions/transports_ws_auth_guard.d.ts +1 -1
  39. package/dist/actions/transports_ws_auth_guard.js +1 -1
  40. package/dist/actions/transports_ws_backend.d.ts +1 -1
  41. package/dist/actions/transports_ws_backend.js +1 -1
  42. package/dist/auth/CLAUDE.md +794 -410
  43. package/dist/auth/account_action_specs.d.ts +28 -7
  44. package/dist/auth/account_action_specs.d.ts.map +1 -1
  45. package/dist/auth/account_action_specs.js +7 -7
  46. package/dist/auth/account_actions.d.ts +7 -13
  47. package/dist/auth/account_actions.d.ts.map +1 -1
  48. package/dist/auth/account_actions.js +26 -35
  49. package/dist/auth/account_queries.d.ts +52 -16
  50. package/dist/auth/account_queries.d.ts.map +1 -1
  51. package/dist/auth/account_queries.js +87 -38
  52. package/dist/auth/account_routes.d.ts +9 -11
  53. package/dist/auth/account_routes.d.ts.map +1 -1
  54. package/dist/auth/account_routes.js +118 -46
  55. package/dist/auth/account_schema.d.ts +46 -35
  56. package/dist/auth/account_schema.d.ts.map +1 -1
  57. package/dist/auth/account_schema.js +21 -28
  58. package/dist/auth/admin_action_specs.d.ts +100 -32
  59. package/dist/auth/admin_action_specs.d.ts.map +1 -1
  60. package/dist/auth/admin_action_specs.js +64 -33
  61. package/dist/auth/admin_actions.d.ts +13 -19
  62. package/dist/auth/admin_actions.d.ts.map +1 -1
  63. package/dist/auth/admin_actions.js +37 -41
  64. package/dist/auth/audit_emitter.d.ts +160 -0
  65. package/dist/auth/audit_emitter.d.ts.map +1 -0
  66. package/dist/auth/audit_emitter.js +83 -0
  67. package/dist/auth/audit_log_queries.d.ts +17 -48
  68. package/dist/auth/audit_log_queries.d.ts.map +1 -1
  69. package/dist/auth/audit_log_queries.js +20 -56
  70. package/dist/auth/audit_log_routes.d.ts +1 -1
  71. package/dist/auth/audit_log_routes.d.ts.map +1 -1
  72. package/dist/auth/audit_log_routes.js +7 -3
  73. package/dist/auth/audit_log_schema.d.ts +92 -32
  74. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  75. package/dist/auth/audit_log_schema.js +75 -46
  76. package/dist/auth/auth_guard_resolver.d.ts +44 -0
  77. package/dist/auth/auth_guard_resolver.d.ts.map +1 -0
  78. package/dist/auth/auth_guard_resolver.js +56 -0
  79. package/dist/auth/bearer_auth.d.ts +9 -7
  80. package/dist/auth/bearer_auth.d.ts.map +1 -1
  81. package/dist/auth/bearer_auth.js +13 -21
  82. package/dist/auth/bootstrap_account.d.ts +7 -7
  83. package/dist/auth/bootstrap_account.d.ts.map +1 -1
  84. package/dist/auth/bootstrap_account.js +7 -7
  85. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  86. package/dist/auth/bootstrap_routes.js +11 -10
  87. package/dist/auth/cleanup.d.ts +20 -26
  88. package/dist/auth/cleanup.d.ts.map +1 -1
  89. package/dist/auth/cleanup.js +33 -42
  90. package/dist/auth/credential_type_schema.d.ts +115 -0
  91. package/dist/auth/credential_type_schema.d.ts.map +1 -0
  92. package/dist/auth/credential_type_schema.js +127 -0
  93. package/dist/auth/daemon_token_middleware.d.ts +23 -11
  94. package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
  95. package/dist/auth/daemon_token_middleware.js +28 -22
  96. package/dist/auth/ddl.d.ts +2 -2
  97. package/dist/auth/ddl.d.ts.map +1 -1
  98. package/dist/auth/ddl.js +6 -6
  99. package/dist/auth/deps.d.ts +7 -18
  100. package/dist/auth/deps.d.ts.map +1 -1
  101. package/dist/auth/grant_path_schema.d.ts +117 -0
  102. package/dist/auth/grant_path_schema.d.ts.map +1 -0
  103. package/dist/auth/grant_path_schema.js +137 -0
  104. package/dist/auth/invite_queries.d.ts +12 -1
  105. package/dist/auth/invite_queries.d.ts.map +1 -1
  106. package/dist/auth/invite_queries.js +12 -1
  107. package/dist/auth/invite_schema.d.ts +1 -1
  108. package/dist/auth/invite_schema.d.ts.map +1 -1
  109. package/dist/auth/invite_schema.js +1 -1
  110. package/dist/auth/middleware.d.ts.map +1 -1
  111. package/dist/auth/middleware.js +9 -4
  112. package/dist/auth/migrations.d.ts +37 -14
  113. package/dist/auth/migrations.d.ts.map +1 -1
  114. package/dist/auth/migrations.js +79 -32
  115. package/dist/auth/request_context.d.ts +331 -61
  116. package/dist/auth/request_context.d.ts.map +1 -1
  117. package/dist/auth/request_context.js +378 -95
  118. package/dist/auth/{permit_offer_action_specs.d.ts → role_grant_offer_action_specs.d.ts} +163 -94
  119. package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -0
  120. package/dist/auth/role_grant_offer_action_specs.js +262 -0
  121. package/dist/auth/role_grant_offer_actions.d.ts +104 -0
  122. package/dist/auth/role_grant_offer_actions.d.ts.map +1 -0
  123. package/dist/auth/role_grant_offer_actions.js +473 -0
  124. package/dist/auth/{permit_offer_notifications.d.ts → role_grant_offer_notifications.d.ts} +90 -70
  125. package/dist/auth/role_grant_offer_notifications.d.ts.map +1 -0
  126. package/dist/auth/role_grant_offer_notifications.js +182 -0
  127. package/dist/auth/role_grant_offer_queries.d.ts +242 -0
  128. package/dist/auth/role_grant_offer_queries.d.ts.map +1 -0
  129. package/dist/auth/role_grant_offer_queries.js +533 -0
  130. package/dist/auth/role_grant_offer_schema.d.ts +150 -0
  131. package/dist/auth/role_grant_offer_schema.d.ts.map +1 -0
  132. package/dist/auth/{permit_offer_schema.js → role_grant_offer_schema.js} +60 -36
  133. package/dist/auth/role_grant_queries.d.ts +231 -0
  134. package/dist/auth/role_grant_queries.d.ts.map +1 -0
  135. package/dist/auth/role_grant_queries.js +320 -0
  136. package/dist/auth/role_schema.d.ts +150 -40
  137. package/dist/auth/role_schema.d.ts.map +1 -1
  138. package/dist/auth/role_schema.js +144 -45
  139. package/dist/auth/scope_kind_schema.d.ts +96 -0
  140. package/dist/auth/scope_kind_schema.d.ts.map +1 -0
  141. package/dist/auth/scope_kind_schema.js +94 -0
  142. package/dist/auth/self_service_role_action_specs.d.ts +6 -1
  143. package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
  144. package/dist/auth/self_service_role_action_specs.js +3 -1
  145. package/dist/auth/self_service_role_actions.d.ts +34 -27
  146. package/dist/auth/self_service_role_actions.d.ts.map +1 -1
  147. package/dist/auth/self_service_role_actions.js +68 -48
  148. package/dist/auth/session_cookie.d.ts +43 -6
  149. package/dist/auth/session_cookie.d.ts.map +1 -1
  150. package/dist/auth/session_cookie.js +31 -5
  151. package/dist/auth/session_middleware.d.ts +37 -3
  152. package/dist/auth/session_middleware.d.ts.map +1 -1
  153. package/dist/auth/session_middleware.js +33 -7
  154. package/dist/auth/signup_routes.d.ts.map +1 -1
  155. package/dist/auth/signup_routes.js +48 -19
  156. package/dist/auth/standard_action_specs.d.ts +2 -2
  157. package/dist/auth/standard_action_specs.js +4 -4
  158. package/dist/auth/standard_rpc_actions.d.ts +23 -19
  159. package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
  160. package/dist/auth/standard_rpc_actions.js +12 -12
  161. package/dist/db/migrate.d.ts +12 -8
  162. package/dist/db/migrate.d.ts.map +1 -1
  163. package/dist/db/migrate.js +10 -7
  164. package/dist/dev/setup.d.ts +2 -2
  165. package/dist/dev/setup.d.ts.map +1 -1
  166. package/dist/dev/setup.js +9 -7
  167. package/dist/env/load.d.ts +1 -1
  168. package/dist/env/load.js +1 -1
  169. package/dist/hono_context.d.ts +64 -5
  170. package/dist/hono_context.d.ts.map +1 -1
  171. package/dist/hono_context.js +38 -2
  172. package/dist/http/CLAUDE.md +264 -87
  173. package/dist/http/auth_shape.d.ts +191 -0
  174. package/dist/http/auth_shape.d.ts.map +1 -0
  175. package/dist/http/auth_shape.js +237 -0
  176. package/dist/http/common_routes.js +3 -3
  177. package/dist/http/db_routes.d.ts +4 -0
  178. package/dist/http/db_routes.d.ts.map +1 -1
  179. package/dist/http/db_routes.js +44 -7
  180. package/dist/http/error_schemas.d.ts +132 -19
  181. package/dist/http/error_schemas.d.ts.map +1 -1
  182. package/dist/http/error_schemas.js +132 -40
  183. package/dist/http/jsonrpc_errors.d.ts +27 -2
  184. package/dist/http/jsonrpc_errors.d.ts.map +1 -1
  185. package/dist/http/jsonrpc_errors.js +26 -2
  186. package/dist/http/pending_effects.d.ts +71 -18
  187. package/dist/http/pending_effects.d.ts.map +1 -1
  188. package/dist/http/pending_effects.js +87 -18
  189. package/dist/http/proxy.d.ts +52 -5
  190. package/dist/http/proxy.d.ts.map +1 -1
  191. package/dist/http/proxy.js +92 -14
  192. package/dist/http/route_spec.d.ts +113 -41
  193. package/dist/http/route_spec.d.ts.map +1 -1
  194. package/dist/http/route_spec.js +130 -52
  195. package/dist/http/schema_helpers.d.ts +3 -2
  196. package/dist/http/schema_helpers.d.ts.map +1 -1
  197. package/dist/http/schema_helpers.js +9 -2
  198. package/dist/http/surface.d.ts +2 -1
  199. package/dist/http/surface.d.ts.map +1 -1
  200. package/dist/http/surface.js +1 -2
  201. package/dist/http/surface_query.d.ts +39 -35
  202. package/dist/http/surface_query.d.ts.map +1 -1
  203. package/dist/http/surface_query.js +79 -36
  204. package/dist/primitive_schemas.d.ts +39 -0
  205. package/dist/primitive_schemas.d.ts.map +1 -0
  206. package/dist/primitive_schemas.js +40 -0
  207. package/dist/realtime/sse_auth_guard.d.ts +5 -5
  208. package/dist/realtime/sse_auth_guard.js +9 -9
  209. package/dist/runtime/mock.d.ts +1 -1
  210. package/dist/runtime/mock.js +1 -1
  211. package/dist/server/app_backend.d.ts +14 -11
  212. package/dist/server/app_backend.d.ts.map +1 -1
  213. package/dist/server/app_backend.js +12 -8
  214. package/dist/server/app_server.d.ts +7 -7
  215. package/dist/server/app_server.d.ts.map +1 -1
  216. package/dist/server/app_server.js +36 -31
  217. package/dist/server/validate_nginx.d.ts +1 -1
  218. package/dist/server/validate_nginx.js +1 -1
  219. package/dist/testing/CLAUDE.md +73 -55
  220. package/dist/testing/admin_integration.d.ts +5 -6
  221. package/dist/testing/admin_integration.d.ts.map +1 -1
  222. package/dist/testing/admin_integration.js +100 -96
  223. package/dist/testing/adversarial_headers.js +1 -1
  224. package/dist/testing/app_server.d.ts +11 -14
  225. package/dist/testing/app_server.d.ts.map +1 -1
  226. package/dist/testing/app_server.js +18 -17
  227. package/dist/testing/assertions.d.ts.map +1 -1
  228. package/dist/testing/assertions.js +2 -1
  229. package/dist/testing/attack_surface.d.ts.map +1 -1
  230. package/dist/testing/attack_surface.js +15 -9
  231. package/dist/testing/audit_completeness.d.ts +2 -2
  232. package/dist/testing/audit_completeness.d.ts.map +1 -1
  233. package/dist/testing/audit_completeness.js +53 -39
  234. package/dist/testing/auth_apps.d.ts +5 -4
  235. package/dist/testing/auth_apps.d.ts.map +1 -1
  236. package/dist/testing/auth_apps.js +28 -22
  237. package/dist/testing/data_exposure.d.ts.map +1 -1
  238. package/dist/testing/data_exposure.js +5 -5
  239. package/dist/testing/db.d.ts +1 -1
  240. package/dist/testing/db.d.ts.map +1 -1
  241. package/dist/testing/db.js +4 -4
  242. package/dist/testing/db_entities.d.ts +22 -0
  243. package/dist/testing/db_entities.d.ts.map +1 -0
  244. package/dist/testing/db_entities.js +28 -0
  245. package/dist/testing/entities.d.ts +10 -8
  246. package/dist/testing/entities.d.ts.map +1 -1
  247. package/dist/testing/entities.js +22 -18
  248. package/dist/testing/integration.d.ts.map +1 -1
  249. package/dist/testing/integration.js +13 -14
  250. package/dist/testing/integration_helpers.d.ts +8 -6
  251. package/dist/testing/integration_helpers.d.ts.map +1 -1
  252. package/dist/testing/integration_helpers.js +29 -23
  253. package/dist/testing/middleware.d.ts +15 -11
  254. package/dist/testing/middleware.d.ts.map +1 -1
  255. package/dist/testing/middleware.js +75 -32
  256. package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
  257. package/dist/testing/rpc_attack_surface.js +40 -24
  258. package/dist/testing/rpc_helpers.d.ts.map +1 -1
  259. package/dist/testing/rpc_helpers.js +3 -1
  260. package/dist/testing/rpc_round_trip.d.ts +1 -1
  261. package/dist/testing/rpc_round_trip.d.ts.map +1 -1
  262. package/dist/testing/rpc_round_trip.js +14 -13
  263. package/dist/testing/sse_round_trip.d.ts +3 -4
  264. package/dist/testing/sse_round_trip.d.ts.map +1 -1
  265. package/dist/testing/sse_round_trip.js +7 -11
  266. package/dist/testing/standard.d.ts +1 -1
  267. package/dist/testing/stubs.d.ts +25 -0
  268. package/dist/testing/stubs.d.ts.map +1 -1
  269. package/dist/testing/stubs.js +43 -2
  270. package/dist/testing/surface_invariants.d.ts +2 -2
  271. package/dist/testing/ws_round_trip.d.ts +12 -13
  272. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  273. package/dist/testing/ws_round_trip.js +24 -12
  274. package/dist/ui/AdminAccounts.svelte +23 -20
  275. package/dist/ui/AdminOverview.svelte +15 -13
  276. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
  277. package/dist/ui/{AdminPermitHistory.svelte → AdminRoleGrantHistory.svelte} +12 -12
  278. package/dist/ui/AdminRoleGrantHistory.svelte.d.ts +4 -0
  279. package/dist/ui/AdminRoleGrantHistory.svelte.d.ts.map +1 -0
  280. package/dist/ui/BootstrapForm.svelte +1 -1
  281. package/dist/ui/CLAUDE.md +65 -59
  282. package/dist/ui/{PermitOfferForm.svelte → RoleGrantOfferForm.svelte} +37 -22
  283. package/dist/ui/RoleGrantOfferForm.svelte.d.ts +20 -0
  284. package/dist/ui/RoleGrantOfferForm.svelte.d.ts.map +1 -0
  285. package/dist/ui/{PermitOfferHistory.svelte → RoleGrantOfferHistory.svelte} +12 -12
  286. package/dist/ui/{PermitOfferHistory.svelte.d.ts → RoleGrantOfferHistory.svelte.d.ts} +4 -4
  287. package/dist/ui/RoleGrantOfferHistory.svelte.d.ts.map +1 -0
  288. package/dist/ui/{PermitOfferInbox.svelte → RoleGrantOfferInbox.svelte} +14 -14
  289. package/dist/ui/{PermitOfferInbox.svelte.d.ts → RoleGrantOfferInbox.svelte.d.ts} +4 -4
  290. package/dist/ui/RoleGrantOfferInbox.svelte.d.ts.map +1 -0
  291. package/dist/ui/SignupForm.svelte +1 -1
  292. package/dist/ui/SurfaceExplorer.svelte +35 -15
  293. package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
  294. package/dist/ui/account_sessions_state.svelte.d.ts +2 -3
  295. package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
  296. package/dist/ui/account_sessions_state.svelte.js +2 -3
  297. package/dist/ui/admin_accounts_state.svelte.d.ts +25 -18
  298. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
  299. package/dist/ui/admin_accounts_state.svelte.js +28 -17
  300. package/dist/ui/admin_rpc_adapters.d.ts +20 -20
  301. package/dist/ui/admin_rpc_adapters.d.ts.map +1 -1
  302. package/dist/ui/admin_rpc_adapters.js +17 -17
  303. package/dist/ui/admin_sessions_state.svelte.d.ts +2 -2
  304. package/dist/ui/admin_sessions_state.svelte.js +2 -2
  305. package/dist/ui/audit_log_state.svelte.d.ts +7 -7
  306. package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
  307. package/dist/ui/audit_log_state.svelte.js +6 -6
  308. package/dist/ui/auth_state.svelte.d.ts +3 -3
  309. package/dist/ui/auth_state.svelte.d.ts.map +1 -1
  310. package/dist/ui/auth_state.svelte.js +6 -6
  311. package/dist/ui/format_scope.d.ts +2 -2
  312. package/dist/ui/format_scope.js +2 -2
  313. package/dist/ui/{permit_offers_state.svelte.d.ts → role_grant_offers_state.svelte.d.ts} +39 -31
  314. package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -0
  315. package/dist/ui/{permit_offers_state.svelte.js → role_grant_offers_state.svelte.js} +25 -19
  316. package/dist/ui/ui_format.js +2 -2
  317. package/package.json +3 -3
  318. package/dist/auth/permit_offer_action_specs.d.ts.map +0 -1
  319. package/dist/auth/permit_offer_action_specs.js +0 -227
  320. package/dist/auth/permit_offer_actions.d.ts +0 -110
  321. package/dist/auth/permit_offer_actions.d.ts.map +0 -1
  322. package/dist/auth/permit_offer_actions.js +0 -452
  323. package/dist/auth/permit_offer_notifications.d.ts.map +0 -1
  324. package/dist/auth/permit_offer_notifications.js +0 -182
  325. package/dist/auth/permit_offer_queries.d.ts +0 -183
  326. package/dist/auth/permit_offer_queries.d.ts.map +0 -1
  327. package/dist/auth/permit_offer_queries.js +0 -408
  328. package/dist/auth/permit_offer_schema.d.ts +0 -103
  329. package/dist/auth/permit_offer_schema.d.ts.map +0 -1
  330. package/dist/auth/permit_queries.d.ts +0 -210
  331. package/dist/auth/permit_queries.d.ts.map +0 -1
  332. package/dist/auth/permit_queries.js +0 -294
  333. package/dist/auth/require_keeper.d.ts +0 -20
  334. package/dist/auth/require_keeper.d.ts.map +0 -1
  335. package/dist/auth/require_keeper.js +0 -35
  336. package/dist/auth/route_guards.d.ts +0 -21
  337. package/dist/auth/route_guards.d.ts.map +0 -1
  338. package/dist/auth/route_guards.js +0 -32
  339. package/dist/auth/session_lifecycle.d.ts +0 -37
  340. package/dist/auth/session_lifecycle.d.ts.map +0 -1
  341. package/dist/auth/session_lifecycle.js +0 -29
  342. package/dist/ui/AdminPermitHistory.svelte.d.ts +0 -4
  343. package/dist/ui/AdminPermitHistory.svelte.d.ts.map +0 -1
  344. package/dist/ui/PermitOfferForm.svelte.d.ts +0 -14
  345. package/dist/ui/PermitOfferForm.svelte.d.ts.map +0 -1
  346. package/dist/ui/PermitOfferHistory.svelte.d.ts.map +0 -1
  347. package/dist/ui/PermitOfferInbox.svelte.d.ts.map +0 -1
  348. package/dist/ui/permit_offers_state.svelte.d.ts.map +0 -1
@@ -1,452 +0,0 @@
1
- /**
2
- * Permit offer RPC action handlers — the consentful-permits action surface.
3
- *
4
- * Seven actions: six offer-lifecycle methods (create / accept / decline /
5
- * retract / list / history) plus `permit_revoke` (admin-only). All mount
6
- * on a consumer's JSON-RPC endpoint via `create_rpc_endpoint`. The action
7
- * specs themselves live in `auth/permit_offer_action_specs.ts`. Mutations
8
- * declare `side_effects: true` so the RPC dispatcher wraps the handler in
9
- * a DB transaction; `permit_offer_list` and `permit_offer_history` declare
10
- * `side_effects: false` so they are addressable via GET.
11
- *
12
- * Authorization:
13
- * - `permit_offer_create` — the grantor must hold an active permit for the
14
- * role being offered, and that role must be `web_grantable`. Consumers
15
- * needing a richer policy (e.g., "teacher may offer student in *their*
16
- * classroom") pass an `authorize` callback that overrides the default.
17
- * - `permit_offer_accept` / `permit_offer_decline` — keyed to the caller's
18
- * account; `query_*` helpers enforce the IDOR guard.
19
- * - `permit_offer_retract` — keyed to the caller's actor.
20
- * - `permit_offer_list` / `permit_offer_history` — self by default;
21
- * `{account_id}` is admin-only.
22
- * - `permit_revoke` — spec-level `auth: {role: 'admin'}`; the RPC
23
- * dispatcher rejects non-admin callers before the handler runs.
24
- * `web_grantable` gate prevents revoking keeper/daemon-scoped roles
25
- * via this surface. Keys on `actor_id` to survive multi-actor accounts.
26
- *
27
- * Audit events are emitted in-transaction by the query layer (atomic with
28
- * the permit write on accept/revoke) or by the handler via
29
- * `audit_log_fire_and_forget` for single-event lifecycle transitions.
30
- * `on_audit_event` (SSE broadcast) fires post-commit in both paths.
31
- *
32
- * WS notifications fan out post-commit via `emit_after_commit` when a
33
- * `notification_sender` is wired: offer lifecycle transitions notify the
34
- * counterparty, `permit_revoke` notifies the revokee plus each superseded
35
- * pending offer's grantor.
36
- *
37
- * @module
38
- */
39
- import { rpc_action } from '../actions/action_rpc.js';
40
- import { jsonrpc_errors } from '../http/jsonrpc_errors.js';
41
- import { emit_after_commit } from '../http/pending_effects.js';
42
- import { BUILTIN_ROLE_OPTIONS, ROLE_ADMIN } from './role_schema.js';
43
- import { PERMIT_OFFER_DEFAULT_TTL_MS, to_permit_offer_json } from './permit_offer_schema.js';
44
- import { query_permit_offer_create, query_permit_offer_decline, query_permit_offer_retract, query_permit_offer_list, query_permit_offer_history_for_account, query_accept_offer, PermitOfferAlreadyTerminalError, PermitOfferExpiredError, PermitOfferNotFoundError, PermitOfferSelfTargetError, } from './permit_offer_queries.js';
45
- import { query_permit_find_active_role_for_actor, query_revoke_permit } from './permit_queries.js';
46
- import { query_actor_by_id } from './account_queries.js';
47
- import { audit_log_fire_and_forget } from './audit_log_queries.js';
48
- import { has_role, has_scoped_role } from './request_context.js';
49
- import { build_permit_offer_accepted_notification, build_permit_offer_declined_notification, build_permit_offer_received_notification, build_permit_offer_retracted_notification, build_permit_offer_supersede_notification, build_permit_revoke_notification, } from './permit_offer_notifications.js';
50
- import { ERROR_ACCOUNT_NOT_FOUND, ERROR_PERMIT_NOT_FOUND, ERROR_ROLE_NOT_WEB_GRANTABLE, } from '../http/error_schemas.js';
51
- import { ERROR_OFFER_EXPIRED, ERROR_OFFER_NOT_AUTHORIZED, ERROR_OFFER_NOT_FOUND, ERROR_OFFER_ROLE_NOT_GRANTABLE, ERROR_OFFER_SELF_TARGET, ERROR_OFFER_TERMINAL, permit_offer_create_action_spec, permit_offer_accept_action_spec, permit_offer_decline_action_spec, permit_offer_retract_action_spec, permit_offer_list_action_spec, permit_offer_history_action_spec, permit_revoke_action_spec, } from './permit_offer_action_specs.js';
52
- // -- Helpers ----------------------------------------------------------------
53
- /** Fire `on_audit_event` for each event — used by accept, whose events were written in-transaction. */
54
- const fan_out_audit_events = (events, on_audit_event, log) => {
55
- for (const event of events) {
56
- try {
57
- on_audit_event(event);
58
- }
59
- catch (err) {
60
- log.error('on_audit_event callback failed:', err);
61
- }
62
- }
63
- };
64
- // eslint-disable-next-line @typescript-eslint/require-await
65
- const default_authorize = async (auth, input, _deps, _ctx) => {
66
- // Caller must hold an active permit for the offered role. Global (no scope)
67
- // check — the scope-aware "only in this classroom" policy is consumer-level.
68
- // Reads from the in-memory `auth.permits` snapshot loaded once per request
69
- // by `create_request_context_middleware`; no DB roundtrip needed.
70
- return has_scoped_role(auth, input.role, null);
71
- };
72
- /**
73
- * Authorization callback that admits any admin and otherwise falls back to
74
- * the symmetric default (caller must hold the offered role globally).
75
- *
76
- * The `web_grantable` filter in `create_handler` runs **before** the
77
- * `authorize` callback, so this never sees non-web-grantable roles. Drop
78
- * into `create_permit_offer_actions({authorize: authorize_admin_or_holder})`
79
- * (or any factory that forwards `authorize`, e.g. `create_standard_rpc_actions`)
80
- * for the common "admins offer anything; users offer what they hold"
81
- * pattern. Scope-aware policies (e.g. classroom_teacher offering
82
- * classroom_student in their own scope) wrap this and short-circuit `true`
83
- * before delegating.
84
- */
85
- export const authorize_admin_or_holder = async (auth, input, _deps, _ctx) => {
86
- if (has_role(auth, ROLE_ADMIN))
87
- return true;
88
- return has_scoped_role(auth, input.role, null);
89
- };
90
- /**
91
- * Narrow `ctx.auth` to non-null. The RPC dispatcher has already enforced
92
- * `auth: 'authenticated'` before the handler runs — this is a type narrow,
93
- * not a runtime check that would otherwise fail.
94
- */
95
- const require_request_auth = (auth) => {
96
- if (!auth)
97
- throw new Error('unreachable: action auth guard did not enforce authentication');
98
- return auth;
99
- };
100
- /**
101
- * Create the seven permit-offer RPC actions (six offer-lifecycle methods
102
- * plus `permit_revoke`).
103
- *
104
- * @param deps - `PermitOfferActionDeps` — `log`, `on_audit_event`, optional `audit_log_config` (slice of `AppDeps`); optional `notification_sender` for WS fan-out
105
- * @param options - role schema, default TTL, authorization override
106
- * @returns the `RpcAction` array to spread into a `create_rpc_endpoint` call
107
- */
108
- export const create_permit_offer_actions = (deps, options = {}) => {
109
- const { on_audit_event, log, notification_sender = null } = deps;
110
- const role_options = options.roles?.role_options ?? BUILTIN_ROLE_OPTIONS;
111
- const default_ttl_ms = options.default_ttl_ms ?? PERMIT_OFFER_DEFAULT_TTL_MS;
112
- const authorize = options.authorize ?? default_authorize;
113
- // Three denial paths (web_grantable, authorize, self-target) all emit the
114
- // same failure-outcome audit event. Local closure over `log` + `on_audit_event`.
115
- const emit_create_failure_audit = (ctx, auth, input) => {
116
- void audit_log_fire_and_forget(ctx, {
117
- event_type: 'permit_offer_create',
118
- outcome: 'failure',
119
- actor_id: auth.actor.id,
120
- account_id: auth.account.id,
121
- target_account_id: input.to_account_id,
122
- ip: ctx.client_ip,
123
- metadata: {
124
- role: input.role,
125
- scope_id: input.scope_id ?? null,
126
- to_account_id: input.to_account_id,
127
- },
128
- }, deps);
129
- };
130
- // Returns {offer} only — no auto-accept. Recipient must call
131
- // permit_offer_accept; admin tests materialize permits via
132
- // query_accept_offer (see testing/admin_integration.ts `offer_and_accept`).
133
- const create_handler = async (input, ctx) => {
134
- const auth = require_request_auth(ctx.auth);
135
- // Role must be web_grantable — same gate as admin direct-grant.
136
- const rc = role_options.get(input.role);
137
- if (!rc?.web_grantable) {
138
- emit_create_failure_audit(ctx, auth, input);
139
- throw jsonrpc_errors.forbidden('role not grantable', {
140
- reason: ERROR_OFFER_ROLE_NOT_GRANTABLE,
141
- });
142
- }
143
- const authorized = await authorize(auth, {
144
- to_account_id: input.to_account_id,
145
- role: input.role,
146
- scope_id: input.scope_id ?? null,
147
- }, { log }, ctx);
148
- if (!authorized) {
149
- emit_create_failure_audit(ctx, auth, input);
150
- throw jsonrpc_errors.forbidden('not authorized to offer this role', {
151
- reason: ERROR_OFFER_NOT_AUTHORIZED,
152
- });
153
- }
154
- let offer;
155
- try {
156
- offer = await query_permit_offer_create(ctx, {
157
- from_actor_id: auth.actor.id,
158
- to_account_id: input.to_account_id,
159
- role: input.role,
160
- scope_id: input.scope_id ?? null,
161
- message: input.message ?? null,
162
- expires_at: new Date(Date.now() + default_ttl_ms),
163
- });
164
- }
165
- catch (err) {
166
- if (err instanceof PermitOfferSelfTargetError) {
167
- emit_create_failure_audit(ctx, auth, input);
168
- throw jsonrpc_errors.invalid_params('cannot offer to self', {
169
- reason: ERROR_OFFER_SELF_TARGET,
170
- });
171
- }
172
- throw err;
173
- }
174
- void audit_log_fire_and_forget(ctx, {
175
- event_type: 'permit_offer_create',
176
- actor_id: auth.actor.id,
177
- account_id: auth.account.id,
178
- target_account_id: input.to_account_id,
179
- ip: ctx.client_ip,
180
- metadata: {
181
- offer_id: offer.id,
182
- role: offer.role,
183
- scope_id: offer.scope_id,
184
- to_account_id: offer.to_account_id,
185
- },
186
- }, deps);
187
- const offer_json = to_permit_offer_json(offer);
188
- if (notification_sender) {
189
- emit_after_commit(ctx, () => {
190
- notification_sender.send_to_account(offer.to_account_id, build_permit_offer_received_notification({ offer: offer_json }));
191
- });
192
- }
193
- return { offer: offer_json };
194
- };
195
- const accept_handler = async (input, ctx) => {
196
- const auth = require_request_auth(ctx.auth);
197
- let result;
198
- try {
199
- result = await query_accept_offer(ctx, {
200
- offer_id: input.offer_id,
201
- to_account_id: auth.account.id,
202
- ip: ctx.client_ip,
203
- });
204
- }
205
- catch (err) {
206
- if (err instanceof PermitOfferNotFoundError) {
207
- throw jsonrpc_errors.not_found('offer', { reason: ERROR_OFFER_NOT_FOUND });
208
- }
209
- if (err instanceof PermitOfferAlreadyTerminalError) {
210
- throw jsonrpc_errors.invalid_request({ reason: ERROR_OFFER_TERMINAL });
211
- }
212
- if (err instanceof PermitOfferExpiredError) {
213
- throw jsonrpc_errors.invalid_request({ reason: ERROR_OFFER_EXPIRED });
214
- }
215
- throw err;
216
- }
217
- // Look up the grantor's account_id inside the transaction so the
218
- // post-commit notification has a valid target. One cheap SELECT by
219
- // PK — the alternative (widening `query_accept_offer` again) would
220
- // bleed transport concerns into the query layer.
221
- const grantor_actor = notification_sender
222
- ? await query_actor_by_id(ctx, result.offer.from_actor_id)
223
- : null;
224
- const grantor_account_id = grantor_actor?.account_id ?? null;
225
- const offer_json = to_permit_offer_json(result.offer);
226
- const supersede_payloads = result.superseded_offers.map((sib) => ({
227
- offer: to_permit_offer_json(sib),
228
- from_account_id: sib.from_account_id,
229
- }));
230
- // Audit events are written in-transaction by query_accept_offer; wire
231
- // them through on_audit_event post-commit so SSE broadcasts fire.
232
- // WS notifications piggyback on the same post-commit microtask so the
233
- // grantor sees "accepted" and each superseded grantor sees
234
- // "supersede" only once the accept has durably committed.
235
- emit_after_commit(ctx, () => {
236
- fan_out_audit_events(result.audit_events, on_audit_event, ctx.log);
237
- if (notification_sender && grantor_account_id) {
238
- notification_sender.send_to_account(grantor_account_id, build_permit_offer_accepted_notification({ offer: offer_json }));
239
- }
240
- if (notification_sender) {
241
- for (const sib of supersede_payloads) {
242
- notification_sender.send_to_account(sib.from_account_id, build_permit_offer_supersede_notification({
243
- offer: sib.offer,
244
- reason: 'sibling_accepted',
245
- cause_id: result.offer.id,
246
- }));
247
- }
248
- }
249
- });
250
- return {
251
- permit_id: result.permit.id,
252
- offer: offer_json,
253
- superseded_offer_ids: result.superseded_offers.map((o) => o.id),
254
- };
255
- };
256
- const decline_handler = async (input, ctx) => {
257
- const auth = require_request_auth(ctx.auth);
258
- let declined;
259
- try {
260
- declined = await query_permit_offer_decline(ctx, input.offer_id, auth.account.id, input.reason ?? null);
261
- }
262
- catch (err) {
263
- if (err instanceof PermitOfferAlreadyTerminalError) {
264
- throw jsonrpc_errors.invalid_request({ reason: ERROR_OFFER_TERMINAL });
265
- }
266
- throw err;
267
- }
268
- if (!declined) {
269
- throw jsonrpc_errors.not_found('offer', { reason: ERROR_OFFER_NOT_FOUND });
270
- }
271
- void audit_log_fire_and_forget(ctx, {
272
- event_type: 'permit_offer_decline',
273
- actor_id: auth.actor.id,
274
- account_id: auth.account.id,
275
- ip: ctx.client_ip,
276
- metadata: {
277
- offer_id: declined.id,
278
- role: declined.role,
279
- scope_id: declined.scope_id,
280
- reason: input.reason ?? undefined,
281
- },
282
- }, deps);
283
- if (notification_sender) {
284
- // Look up the grantor's account (SELECT by PK, same tx) for the
285
- // notification target. The decline reason rides along on
286
- // `offer.decline_reason` — the DB set it in the RETURNING above.
287
- const grantor_actor = await query_actor_by_id(ctx, declined.from_actor_id);
288
- const grantor_account_id = grantor_actor?.account_id ?? null;
289
- if (grantor_account_id) {
290
- const offer_json = to_permit_offer_json(declined);
291
- emit_after_commit(ctx, () => {
292
- notification_sender.send_to_account(grantor_account_id, build_permit_offer_declined_notification({ offer: offer_json }));
293
- });
294
- }
295
- }
296
- return { ok: true };
297
- };
298
- const retract_handler = async (input, ctx) => {
299
- const auth = require_request_auth(ctx.auth);
300
- let retracted;
301
- try {
302
- retracted = await query_permit_offer_retract(ctx, input.offer_id, auth.actor.id);
303
- }
304
- catch (err) {
305
- if (err instanceof PermitOfferAlreadyTerminalError) {
306
- throw jsonrpc_errors.invalid_request({ reason: ERROR_OFFER_TERMINAL });
307
- }
308
- throw err;
309
- }
310
- if (!retracted) {
311
- throw jsonrpc_errors.not_found('offer', { reason: ERROR_OFFER_NOT_FOUND });
312
- }
313
- void audit_log_fire_and_forget(ctx, {
314
- event_type: 'permit_offer_retract',
315
- actor_id: auth.actor.id,
316
- account_id: auth.account.id,
317
- ip: ctx.client_ip,
318
- metadata: {
319
- offer_id: retracted.id,
320
- role: retracted.role,
321
- scope_id: retracted.scope_id,
322
- },
323
- }, deps);
324
- if (notification_sender) {
325
- const offer_json = to_permit_offer_json(retracted);
326
- emit_after_commit(ctx, () => {
327
- notification_sender.send_to_account(retracted.to_account_id, build_permit_offer_retracted_notification({ offer: offer_json }));
328
- });
329
- }
330
- return { ok: true };
331
- };
332
- const list_handler = async (input, ctx) => {
333
- const auth = require_request_auth(ctx.auth);
334
- const target = input.account_id ?? auth.account.id;
335
- if (target !== auth.account.id && !has_role(auth, ROLE_ADMIN)) {
336
- throw jsonrpc_errors.forbidden('admin required to inspect another account');
337
- }
338
- const offers = await query_permit_offer_list(ctx, target);
339
- return { offers: offers.map(to_permit_offer_json) };
340
- };
341
- const history_handler = async (input, ctx) => {
342
- const auth = require_request_auth(ctx.auth);
343
- const target = input.account_id ?? auth.account.id;
344
- if (target !== auth.account.id && !has_role(auth, ROLE_ADMIN)) {
345
- throw jsonrpc_errors.forbidden('admin required to inspect another account');
346
- }
347
- const offers = await query_permit_offer_history_for_account(ctx, target, input.limit ?? undefined, input.offset ?? undefined);
348
- return { offers: offers.map(to_permit_offer_json) };
349
- };
350
- const revoke_handler = async (input, ctx) => {
351
- const auth = require_request_auth(ctx.auth);
352
- // IDOR guard + role lookup. One SELECT — returns null when the
353
- // permit is revoked, missing, or belongs to a different actor.
354
- const permit_row = await query_permit_find_active_role_for_actor(ctx, input.permit_id, input.actor_id);
355
- if (!permit_row) {
356
- throw jsonrpc_errors.not_found('permit', { reason: ERROR_PERMIT_NOT_FOUND });
357
- }
358
- // Resolve the target actor's account once — drives both the audit
359
- // `target_account_id` and the post-commit notification target.
360
- const target_actor = await query_actor_by_id(ctx, input.actor_id);
361
- if (!target_actor) {
362
- // The IDOR guard above already matched, so a missing actor here
363
- // indicates a race (account deleted between the two SELECTs).
364
- // Treat as account-not-found for the caller.
365
- throw jsonrpc_errors.not_found('account', { reason: ERROR_ACCOUNT_NOT_FOUND });
366
- }
367
- const target_account_id = target_actor.account_id;
368
- // web_grantable gate — keeper/daemon-scoped roles stay CLI-only.
369
- const rc = role_options.get(permit_row.role);
370
- if (!rc?.web_grantable) {
371
- void audit_log_fire_and_forget(ctx, {
372
- event_type: 'permit_revoke',
373
- outcome: 'failure',
374
- actor_id: auth.actor.id,
375
- account_id: auth.account.id,
376
- target_account_id,
377
- ip: ctx.client_ip,
378
- metadata: { role: permit_row.role, permit_id: input.permit_id },
379
- }, deps);
380
- throw jsonrpc_errors.forbidden('role not web-grantable', {
381
- reason: ERROR_ROLE_NOT_WEB_GRANTABLE,
382
- });
383
- }
384
- const result = await query_revoke_permit(ctx, input.permit_id, input.actor_id, auth.actor.id, input.reason ?? null);
385
- if (!result) {
386
- // Raced with another revoker or the permit was revoked between
387
- // the IDOR check and the UPDATE.
388
- throw jsonrpc_errors.not_found('permit', { reason: ERROR_PERMIT_NOT_FOUND });
389
- }
390
- void audit_log_fire_and_forget(ctx, {
391
- event_type: 'permit_revoke',
392
- actor_id: auth.actor.id,
393
- account_id: auth.account.id,
394
- target_account_id,
395
- ip: ctx.client_ip,
396
- metadata: {
397
- role: result.role,
398
- permit_id: result.id,
399
- scope_id: result.scope_id,
400
- reason: input.reason ?? undefined,
401
- },
402
- }, deps);
403
- for (const offer of result.superseded_offers) {
404
- void audit_log_fire_and_forget(ctx, {
405
- event_type: 'permit_offer_supersede',
406
- actor_id: auth.actor.id,
407
- account_id: offer.to_account_id,
408
- ip: ctx.client_ip,
409
- metadata: {
410
- offer_id: offer.id,
411
- role: offer.role,
412
- scope_id: offer.scope_id,
413
- reason: 'permit_revoked',
414
- cause_id: result.id,
415
- },
416
- }, deps);
417
- }
418
- if (notification_sender) {
419
- const superseded = result.superseded_offers.map((o) => ({
420
- offer: to_permit_offer_json(o),
421
- from_account_id: o.from_account_id,
422
- }));
423
- const cause_id = result.id;
424
- const reason = input.reason ?? null;
425
- emit_after_commit(ctx, () => {
426
- notification_sender.send_to_account(target_account_id, build_permit_revoke_notification({
427
- permit_id: result.id,
428
- role: result.role,
429
- scope_id: result.scope_id,
430
- reason,
431
- }));
432
- for (const sib of superseded) {
433
- notification_sender.send_to_account(sib.from_account_id, build_permit_offer_supersede_notification({
434
- offer: sib.offer,
435
- reason: 'permit_revoked',
436
- cause_id,
437
- }));
438
- }
439
- });
440
- }
441
- return { ok: true, revoked: true };
442
- };
443
- return [
444
- rpc_action(permit_offer_create_action_spec, create_handler),
445
- rpc_action(permit_offer_accept_action_spec, accept_handler),
446
- rpc_action(permit_offer_decline_action_spec, decline_handler),
447
- rpc_action(permit_offer_retract_action_spec, retract_handler),
448
- rpc_action(permit_offer_list_action_spec, list_handler),
449
- rpc_action(permit_offer_history_action_spec, history_handler),
450
- rpc_action(permit_revoke_action_spec, revoke_handler),
451
- ];
452
- };
@@ -1 +0,0 @@
1
- {"version":3,"file":"permit_offer_notifications.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/permit_offer_notifications.ts"],"names":[],"mappings":"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"}
@@ -1,182 +0,0 @@
1
- /**
2
- * Permit offer WebSocket notification specs, builders, and the narrow
3
- * `NotificationSender` interface that decouples offer/revoke send sites
4
- * from `BackendWebsocketTransport`.
5
- *
6
- * Six `RemoteNotificationActionSpec`s cover the consentful-permits
7
- * lifecycle events the server pushes to affected accounts:
8
- *
9
- * - `permit_offer_received` → recipient's sockets when an offer is created
10
- * - `permit_offer_retracted` → recipient's sockets when a grantor retracts
11
- * - `permit_offer_accepted` → grantor's sockets when the recipient accepts
12
- * - `permit_offer_declined` → grantor's sockets when the recipient declines
13
- * - `permit_offer_supersede` → grantor's sockets when a sibling accept,
14
- * a revoke of the resulting permit, or destruction of the parent scope
15
- * row obsoletes their pending offer
16
- * - `permit_revoke` → revokee's sockets when one of their active permits
17
- * is revoked (companion to the `permit_revoke` audit event)
18
- *
19
- * Payloads are flat and normalized — `PermitOfferJson` for the offer-lifecycle
20
- * notifications (decline reason rides on `offer.decline_reason`, not a
21
- * sibling field), and `{permit_id, role, scope_id, reason?}` for `permit_revoke`. The
22
- * revokee/grantor/recipient account id travels via the send target (the
23
- * `NotificationSender.send_to_account` argument), not in the payload.
24
- *
25
- * The specs surface as `EventSpec`s via `create_action_event_spec` — callers
26
- * append `PERMIT_OFFER_NOTIFICATION_SPECS` to their `event_specs` on
27
- * `create_app_server` so the surface reflects them and DEV-mode broadcast
28
- * validation catches payload drift.
29
- *
30
- * @module
31
- */
32
- import { z } from 'zod';
33
- import { Uuid as UuidSchema } from '@fuzdev/fuz_util/id.js';
34
- import { create_action_event_spec } from '../actions/action_bridge.js';
35
- import { create_jsonrpc_notification } from '../http/jsonrpc_helpers.js';
36
- import { RoleName } from './role_schema.js';
37
- import { PermitOfferJson } from './permit_offer_schema.js';
38
- import { PERMIT_REVOKED_REASON_LENGTH_MAX } from './account_schema.js';
39
- // -- Method constants -------------------------------------------------------
40
- export const PERMIT_OFFER_RECEIVED_NOTIFICATION_METHOD = 'permit_offer_received';
41
- export const PERMIT_OFFER_RETRACTED_NOTIFICATION_METHOD = 'permit_offer_retracted';
42
- export const PERMIT_OFFER_ACCEPTED_NOTIFICATION_METHOD = 'permit_offer_accepted';
43
- export const PERMIT_OFFER_DECLINED_NOTIFICATION_METHOD = 'permit_offer_declined';
44
- export const PERMIT_OFFER_SUPERSEDE_NOTIFICATION_METHOD = 'permit_offer_supersede';
45
- export const PERMIT_REVOKE_NOTIFICATION_METHOD = 'permit_revoke';
46
- // -- Params schemas ---------------------------------------------------------
47
- /** Params for `permit_offer_received` — offer delivered to its recipient. */
48
- export const PermitOfferReceivedParams = z.strictObject({
49
- offer: PermitOfferJson,
50
- });
51
- /** Params for `permit_offer_retracted` — grantor-side retraction. */
52
- export const PermitOfferRetractedParams = z.strictObject({
53
- offer: PermitOfferJson,
54
- });
55
- /** Params for `permit_offer_accepted` — recipient accepted the offer. */
56
- export const PermitOfferAcceptedParams = z.strictObject({
57
- offer: PermitOfferJson,
58
- });
59
- /**
60
- * Params for `permit_offer_declined`. The decline reason (if any) rides along
61
- * inside `offer.decline_reason` — the DB stamps it on the offer row during
62
- * decline, so a sibling `reason` field would just duplicate it.
63
- */
64
- export const PermitOfferDeclinedParams = z.strictObject({
65
- offer: PermitOfferJson,
66
- });
67
- /**
68
- * Params for `permit_offer_supersede`. Fires to the grantor's sockets when
69
- * their pending offer is obsoleted — either by a sibling accept
70
- * (`reason: 'sibling_accepted'`), by revoke of the resulting permit
71
- * (`reason: 'permit_revoked'`), or by deletion of the parent scope row
72
- * the offer was bound to (`reason: 'scope_destroyed'`). `cause_id` points
73
- * at the accepted offer id, the revoked permit id, or the destroyed scope
74
- * row id respectively.
75
- */
76
- export const PermitOfferSupersedeParams = z.strictObject({
77
- offer: PermitOfferJson,
78
- reason: z.enum(['sibling_accepted', 'permit_revoked', 'scope_destroyed']),
79
- cause_id: UuidSchema,
80
- });
81
- /**
82
- * Params for `permit_revoke`. Delivered to the revokee's sockets when one
83
- * of their active permits is revoked. Flat wire shape — `revoked_by` is
84
- * admin-UI-visible but deliberately omitted here (the revokee doesn't need
85
- * to learn the admin's identity). Target account is implicit in the send
86
- * target.
87
- */
88
- export const PermitRevokeParams = z.strictObject({
89
- permit_id: UuidSchema,
90
- role: RoleName,
91
- scope_id: UuidSchema.nullable(),
92
- reason: z.string().max(PERMIT_REVOKED_REASON_LENGTH_MAX).nullable(),
93
- });
94
- // -- Action specs -----------------------------------------------------------
95
- export const permit_offer_received_notification_spec = {
96
- method: PERMIT_OFFER_RECEIVED_NOTIFICATION_METHOD,
97
- kind: 'remote_notification',
98
- initiator: 'backend',
99
- auth: null,
100
- side_effects: true,
101
- input: PermitOfferReceivedParams,
102
- output: z.void(),
103
- async: true,
104
- description: 'A new permit offer arrived in the recipient’s inbox.',
105
- };
106
- export const permit_offer_retracted_notification_spec = {
107
- method: PERMIT_OFFER_RETRACTED_NOTIFICATION_METHOD,
108
- kind: 'remote_notification',
109
- initiator: 'backend',
110
- auth: null,
111
- side_effects: true,
112
- input: PermitOfferRetractedParams,
113
- output: z.void(),
114
- async: true,
115
- description: 'A pending permit offer was retracted by its grantor.',
116
- };
117
- export const permit_offer_accepted_notification_spec = {
118
- method: PERMIT_OFFER_ACCEPTED_NOTIFICATION_METHOD,
119
- kind: 'remote_notification',
120
- initiator: 'backend',
121
- auth: null,
122
- side_effects: true,
123
- input: PermitOfferAcceptedParams,
124
- output: z.void(),
125
- async: true,
126
- description: 'A pending permit offer was accepted by its recipient.',
127
- };
128
- export const permit_offer_declined_notification_spec = {
129
- method: PERMIT_OFFER_DECLINED_NOTIFICATION_METHOD,
130
- kind: 'remote_notification',
131
- initiator: 'backend',
132
- auth: null,
133
- side_effects: true,
134
- input: PermitOfferDeclinedParams,
135
- output: z.void(),
136
- async: true,
137
- description: 'A pending permit offer was declined by its recipient.',
138
- };
139
- export const permit_offer_supersede_notification_spec = {
140
- method: PERMIT_OFFER_SUPERSEDE_NOTIFICATION_METHOD,
141
- kind: 'remote_notification',
142
- initiator: 'backend',
143
- auth: null,
144
- side_effects: true,
145
- input: PermitOfferSupersedeParams,
146
- output: z.void(),
147
- async: true,
148
- description: 'A grantor’s pending permit offer was obsoleted by a sibling accept, by revoke of the resulting permit, or by destruction of the parent scope row.',
149
- };
150
- export const permit_revoke_notification_spec = {
151
- method: PERMIT_REVOKE_NOTIFICATION_METHOD,
152
- kind: 'remote_notification',
153
- initiator: 'backend',
154
- auth: null,
155
- side_effects: true,
156
- input: PermitRevokeParams,
157
- output: z.void(),
158
- async: true,
159
- description: 'An active permit on the revokee’s account was revoked.',
160
- };
161
- // -- EventSpec surface ------------------------------------------------------
162
- /**
163
- * SSE/WS event specs for the consentful-permits notification surface.
164
- *
165
- * Pass to `create_app_server`'s `event_specs` so the attack surface reflects
166
- * them and DEV-mode `create_validated_broadcaster` catches payload drift.
167
- */
168
- export const PERMIT_OFFER_NOTIFICATION_SPECS = [
169
- create_action_event_spec(permit_offer_received_notification_spec),
170
- create_action_event_spec(permit_offer_retracted_notification_spec),
171
- create_action_event_spec(permit_offer_accepted_notification_spec),
172
- create_action_event_spec(permit_offer_declined_notification_spec),
173
- create_action_event_spec(permit_offer_supersede_notification_spec),
174
- create_action_event_spec(permit_revoke_notification_spec),
175
- ];
176
- // -- Notification builders --------------------------------------------------
177
- export const build_permit_offer_received_notification = (params) => create_jsonrpc_notification(PERMIT_OFFER_RECEIVED_NOTIFICATION_METHOD, params);
178
- export const build_permit_offer_retracted_notification = (params) => create_jsonrpc_notification(PERMIT_OFFER_RETRACTED_NOTIFICATION_METHOD, params);
179
- export const build_permit_offer_accepted_notification = (params) => create_jsonrpc_notification(PERMIT_OFFER_ACCEPTED_NOTIFICATION_METHOD, params);
180
- export const build_permit_offer_declined_notification = (params) => create_jsonrpc_notification(PERMIT_OFFER_DECLINED_NOTIFICATION_METHOD, params);
181
- export const build_permit_offer_supersede_notification = (params) => create_jsonrpc_notification(PERMIT_OFFER_SUPERSEDE_NOTIFICATION_METHOD, params);
182
- export const build_permit_revoke_notification = (params) => create_jsonrpc_notification(PERMIT_REVOKE_NOTIFICATION_METHOD, params);