@fuzdev/fuz_app 0.30.0 → 0.32.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 (222) hide show
  1. package/dist/actions/CLAUDE.md +630 -0
  2. package/dist/actions/action_rpc.d.ts +29 -0
  3. package/dist/actions/action_rpc.d.ts.map +1 -1
  4. package/dist/actions/action_rpc.js +42 -6
  5. package/dist/actions/action_types.d.ts +2 -2
  6. package/dist/actions/cancel.d.ts +12 -13
  7. package/dist/actions/cancel.d.ts.map +1 -1
  8. package/dist/actions/cancel.js +10 -13
  9. package/dist/actions/heartbeat.d.ts +8 -13
  10. package/dist/actions/heartbeat.d.ts.map +1 -1
  11. package/dist/actions/heartbeat.js +5 -8
  12. package/dist/actions/register_action_ws.d.ts +3 -3
  13. package/dist/actions/register_action_ws.js +2 -2
  14. package/dist/actions/register_ws_endpoint.d.ts +4 -4
  15. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  16. package/dist/actions/register_ws_endpoint.js +3 -3
  17. package/dist/actions/rpc_client.d.ts +29 -0
  18. package/dist/actions/rpc_client.d.ts.map +1 -1
  19. package/dist/actions/rpc_client.js +31 -0
  20. package/dist/actions/socket.svelte.d.ts +16 -16
  21. package/dist/actions/socket.svelte.d.ts.map +1 -1
  22. package/dist/actions/socket.svelte.js +15 -15
  23. package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
  24. package/dist/auth/CLAUDE.md +945 -0
  25. package/dist/auth/account_action_specs.d.ts +216 -0
  26. package/dist/auth/account_action_specs.d.ts.map +1 -0
  27. package/dist/auth/account_action_specs.js +159 -0
  28. package/dist/auth/account_actions.d.ts +51 -0
  29. package/dist/auth/account_actions.d.ts.map +1 -0
  30. package/dist/auth/account_actions.js +119 -0
  31. package/dist/auth/account_queries.d.ts +6 -2
  32. package/dist/auth/account_queries.d.ts.map +1 -1
  33. package/dist/auth/account_queries.js +40 -4
  34. package/dist/auth/account_routes.d.ts +94 -16
  35. package/dist/auth/account_routes.d.ts.map +1 -1
  36. package/dist/auth/account_routes.js +108 -180
  37. package/dist/auth/account_schema.d.ts +85 -30
  38. package/dist/auth/account_schema.d.ts.map +1 -1
  39. package/dist/auth/account_schema.js +40 -8
  40. package/dist/auth/admin_action_specs.d.ts +674 -0
  41. package/dist/auth/admin_action_specs.d.ts.map +1 -0
  42. package/dist/auth/admin_action_specs.js +287 -0
  43. package/dist/auth/admin_actions.d.ts +69 -0
  44. package/dist/auth/admin_actions.d.ts.map +1 -0
  45. package/dist/auth/admin_actions.js +256 -0
  46. package/dist/auth/admin_rpc_actions.d.ts +49 -0
  47. package/dist/auth/admin_rpc_actions.d.ts.map +1 -0
  48. package/dist/auth/admin_rpc_actions.js +32 -0
  49. package/dist/auth/api_token.d.ts +10 -0
  50. package/dist/auth/api_token.d.ts.map +1 -1
  51. package/dist/auth/api_token.js +9 -0
  52. package/dist/auth/api_token_queries.d.ts +3 -3
  53. package/dist/auth/api_token_queries.js +3 -3
  54. package/dist/auth/app_settings_schema.d.ts +4 -3
  55. package/dist/auth/app_settings_schema.d.ts.map +1 -1
  56. package/dist/auth/app_settings_schema.js +2 -1
  57. package/dist/auth/audit_log_routes.d.ts +14 -6
  58. package/dist/auth/audit_log_routes.d.ts.map +1 -1
  59. package/dist/auth/audit_log_routes.js +22 -79
  60. package/dist/auth/audit_log_schema.d.ts +100 -29
  61. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  62. package/dist/auth/audit_log_schema.js +83 -11
  63. package/dist/auth/bootstrap_routes.d.ts +14 -0
  64. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  65. package/dist/auth/bootstrap_routes.js +10 -3
  66. package/dist/auth/cleanup.d.ts +63 -0
  67. package/dist/auth/cleanup.d.ts.map +1 -0
  68. package/dist/auth/cleanup.js +80 -0
  69. package/dist/auth/invite_schema.d.ts +11 -10
  70. package/dist/auth/invite_schema.d.ts.map +1 -1
  71. package/dist/auth/invite_schema.js +4 -3
  72. package/dist/auth/migrations.d.ts +6 -0
  73. package/dist/auth/migrations.d.ts.map +1 -1
  74. package/dist/auth/migrations.js +28 -0
  75. package/dist/auth/permit_offer_action_specs.d.ts +364 -0
  76. package/dist/auth/permit_offer_action_specs.d.ts.map +1 -0
  77. package/dist/auth/permit_offer_action_specs.js +216 -0
  78. package/dist/auth/permit_offer_actions.d.ts +96 -0
  79. package/dist/auth/permit_offer_actions.d.ts.map +1 -0
  80. package/dist/auth/permit_offer_actions.js +428 -0
  81. package/dist/auth/permit_offer_notifications.d.ts +361 -0
  82. package/dist/auth/permit_offer_notifications.d.ts.map +1 -0
  83. package/dist/auth/permit_offer_notifications.js +179 -0
  84. package/dist/auth/permit_offer_queries.d.ts +165 -0
  85. package/dist/auth/permit_offer_queries.d.ts.map +1 -0
  86. package/dist/auth/permit_offer_queries.js +390 -0
  87. package/dist/auth/permit_offer_schema.d.ts +103 -0
  88. package/dist/auth/permit_offer_schema.d.ts.map +1 -0
  89. package/dist/auth/permit_offer_schema.js +142 -0
  90. package/dist/auth/permit_queries.d.ts +77 -14
  91. package/dist/auth/permit_queries.d.ts.map +1 -1
  92. package/dist/auth/permit_queries.js +119 -24
  93. package/dist/auth/session_queries.d.ts +4 -2
  94. package/dist/auth/session_queries.d.ts.map +1 -1
  95. package/dist/auth/session_queries.js +4 -2
  96. package/dist/auth/signup_routes.d.ts +13 -0
  97. package/dist/auth/signup_routes.d.ts.map +1 -1
  98. package/dist/auth/signup_routes.js +14 -7
  99. package/dist/http/CLAUDE.md +584 -0
  100. package/dist/http/pending_effects.d.ts +29 -0
  101. package/dist/http/pending_effects.d.ts.map +1 -0
  102. package/dist/http/pending_effects.js +31 -0
  103. package/dist/http/route_spec.d.ts.map +1 -1
  104. package/dist/http/route_spec.js +4 -3
  105. package/dist/rate_limiter.d.ts +30 -0
  106. package/dist/rate_limiter.d.ts.map +1 -1
  107. package/dist/rate_limiter.js +25 -2
  108. package/dist/realtime/sse_auth_guard.d.ts +2 -0
  109. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  110. package/dist/realtime/sse_auth_guard.js +5 -3
  111. package/dist/server/app_server.d.ts +13 -2
  112. package/dist/server/app_server.d.ts.map +1 -1
  113. package/dist/server/app_server.js +12 -1
  114. package/dist/testing/CLAUDE.md +668 -1
  115. package/dist/testing/admin_integration.d.ts +10 -7
  116. package/dist/testing/admin_integration.d.ts.map +1 -1
  117. package/dist/testing/admin_integration.js +382 -482
  118. package/dist/testing/app_server.d.ts +7 -6
  119. package/dist/testing/app_server.d.ts.map +1 -1
  120. package/dist/testing/attack_surface.d.ts +9 -3
  121. package/dist/testing/attack_surface.d.ts.map +1 -1
  122. package/dist/testing/attack_surface.js +4 -4
  123. package/dist/testing/audit_completeness.d.ts +11 -0
  124. package/dist/testing/audit_completeness.d.ts.map +1 -1
  125. package/dist/testing/audit_completeness.js +169 -134
  126. package/dist/testing/auth_apps.d.ts.map +1 -1
  127. package/dist/testing/auth_apps.js +4 -33
  128. package/dist/testing/db.d.ts +1 -1
  129. package/dist/testing/db.d.ts.map +1 -1
  130. package/dist/testing/db.js +2 -0
  131. package/dist/testing/entities.d.ts +35 -13
  132. package/dist/testing/entities.d.ts.map +1 -1
  133. package/dist/testing/entities.js +17 -0
  134. package/dist/testing/integration.d.ts +10 -0
  135. package/dist/testing/integration.d.ts.map +1 -1
  136. package/dist/testing/integration.js +352 -340
  137. package/dist/testing/integration_helpers.d.ts +16 -5
  138. package/dist/testing/integration_helpers.d.ts.map +1 -1
  139. package/dist/testing/integration_helpers.js +24 -4
  140. package/dist/testing/rate_limiting.d.ts +7 -0
  141. package/dist/testing/rate_limiting.d.ts.map +1 -1
  142. package/dist/testing/rate_limiting.js +41 -10
  143. package/dist/testing/rpc_helpers.d.ts +153 -1
  144. package/dist/testing/rpc_helpers.d.ts.map +1 -1
  145. package/dist/testing/rpc_helpers.js +184 -8
  146. package/dist/testing/sse_round_trip.d.ts +8 -0
  147. package/dist/testing/sse_round_trip.d.ts.map +1 -1
  148. package/dist/testing/sse_round_trip.js +10 -3
  149. package/dist/testing/standard.d.ts +9 -1
  150. package/dist/testing/standard.d.ts.map +1 -1
  151. package/dist/testing/standard.js +6 -2
  152. package/dist/testing/stubs.d.ts +10 -2
  153. package/dist/testing/stubs.d.ts.map +1 -1
  154. package/dist/testing/stubs.js +17 -2
  155. package/dist/testing/surface_invariants.d.ts +7 -3
  156. package/dist/testing/surface_invariants.d.ts.map +1 -1
  157. package/dist/testing/surface_invariants.js +5 -4
  158. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  159. package/dist/testing/ws_round_trip.js +9 -38
  160. package/dist/ui/AccountSessions.svelte +8 -4
  161. package/dist/ui/AccountSessions.svelte.d.ts.map +1 -1
  162. package/dist/ui/AdminAccounts.svelte +61 -33
  163. package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
  164. package/dist/ui/AdminAuditLog.svelte +3 -2
  165. package/dist/ui/AdminAuditLog.svelte.d.ts.map +1 -1
  166. package/dist/ui/AdminInvites.svelte +3 -2
  167. package/dist/ui/AdminInvites.svelte.d.ts.map +1 -1
  168. package/dist/ui/AdminOverview.svelte +14 -9
  169. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
  170. package/dist/ui/AdminPermitHistory.svelte +3 -2
  171. package/dist/ui/AdminPermitHistory.svelte.d.ts.map +1 -1
  172. package/dist/ui/AdminSessions.svelte +29 -25
  173. package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
  174. package/dist/ui/CLAUDE.md +363 -0
  175. package/dist/ui/OpenSignupToggle.svelte +6 -3
  176. package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -1
  177. package/dist/ui/PermitOfferForm.svelte +141 -0
  178. package/dist/ui/PermitOfferForm.svelte.d.ts +14 -0
  179. package/dist/ui/PermitOfferForm.svelte.d.ts.map +1 -0
  180. package/dist/ui/PermitOfferHistory.svelte +109 -0
  181. package/dist/ui/PermitOfferHistory.svelte.d.ts +11 -0
  182. package/dist/ui/PermitOfferHistory.svelte.d.ts.map +1 -0
  183. package/dist/ui/PermitOfferInbox.svelte +121 -0
  184. package/dist/ui/PermitOfferInbox.svelte.d.ts +12 -0
  185. package/dist/ui/PermitOfferInbox.svelte.d.ts.map +1 -0
  186. package/dist/ui/account_sessions_state.svelte.d.ts +53 -3
  187. package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
  188. package/dist/ui/account_sessions_state.svelte.js +39 -16
  189. package/dist/ui/admin_accounts_state.svelte.d.ts +118 -2
  190. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
  191. package/dist/ui/admin_accounts_state.svelte.js +99 -23
  192. package/dist/ui/admin_invites_state.svelte.d.ts +47 -1
  193. package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -1
  194. package/dist/ui/admin_invites_state.svelte.js +38 -26
  195. package/dist/ui/admin_rpc_adapters.d.ts +94 -0
  196. package/dist/ui/admin_rpc_adapters.d.ts.map +1 -0
  197. package/dist/ui/admin_rpc_adapters.js +100 -0
  198. package/dist/ui/admin_sessions_state.svelte.d.ts +26 -0
  199. package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -1
  200. package/dist/ui/admin_sessions_state.svelte.js +35 -21
  201. package/dist/ui/app_settings_state.svelte.d.ts +39 -0
  202. package/dist/ui/app_settings_state.svelte.d.ts.map +1 -1
  203. package/dist/ui/app_settings_state.svelte.js +34 -18
  204. package/dist/ui/audit_log_state.svelte.d.ts +40 -3
  205. package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
  206. package/dist/ui/audit_log_state.svelte.js +36 -42
  207. package/dist/ui/auth_state.svelte.d.ts +4 -3
  208. package/dist/ui/auth_state.svelte.d.ts.map +1 -1
  209. package/dist/ui/auth_state.svelte.js +4 -1
  210. package/dist/ui/permit_offers_state.svelte.d.ts +125 -0
  211. package/dist/ui/permit_offers_state.svelte.d.ts.map +1 -0
  212. package/dist/ui/permit_offers_state.svelte.js +197 -0
  213. package/package.json +3 -3
  214. package/dist/auth/admin_routes.d.ts +0 -29
  215. package/dist/auth/admin_routes.d.ts.map +0 -1
  216. package/dist/auth/admin_routes.js +0 -226
  217. package/dist/auth/app_settings_routes.d.ts +0 -27
  218. package/dist/auth/app_settings_routes.d.ts.map +0 -1
  219. package/dist/auth/app_settings_routes.js +0 -66
  220. package/dist/auth/invite_routes.d.ts +0 -18
  221. package/dist/auth/invite_routes.d.ts.map +0 -1
  222. package/dist/auth/invite_routes.js +0 -129
@@ -15,17 +15,28 @@ import type { TestApp, TestAccount } from './app_server.js';
15
15
  */
16
16
  export declare const find_route_spec: (specs: Array<RouteSpec>, method: string, path: string) => RouteSpec | undefined;
17
17
  /**
18
- * Find an auth route by suffix and method.
18
+ * REST auth route suffixes still on the account/bootstrap surface after the
19
+ * 2026-04-22 RPC migration. `find_auth_route` rejects any other suffix at
20
+ * runtime — session/token CRUD, admin operations, and permit flows live on
21
+ * the RPC surface and should be reached via `rpc_call`.
22
+ */
23
+ export declare const REST_AUTH_ROUTE_SUFFIXES: readonly ["/login", "/logout", "/password", "/verify", "/signup", "/bootstrap"];
24
+ export type RestAuthRouteSuffix = (typeof REST_AUTH_ROUTE_SUFFIXES)[number];
25
+ /**
26
+ * Find a REST auth route by suffix and method.
19
27
  *
20
- * Useful for discovering login/logout/verify/revoke paths regardless
21
- * of consumer prefix (`/api/account/login`, `/api/auth/login`, etc.).
28
+ * Decouples tests from consumer route prefix (`/api/account/login`,
29
+ * `/api/auth/login`, etc.). `suffix` must be one of
30
+ * `REST_AUTH_ROUTE_SUFFIXES` — throws otherwise so a post-migration RPC
31
+ * method name (e.g. `/sessions/revoke-all`) fails loudly at the call site
32
+ * instead of silently returning `undefined`.
22
33
  *
23
34
  * @param specs - route specs to search
24
- * @param suffix - path suffix to match (e.g. `'/login'`)
35
+ * @param suffix - REST auth path suffix
25
36
  * @param method - HTTP method
26
37
  * @returns matching route spec, or `undefined`
27
38
  */
28
- export declare const find_auth_route: (specs: Array<RouteSpec>, suffix: string, method: RouteMethod) => RouteSpec | undefined;
39
+ export declare const find_auth_route: (specs: Array<RouteSpec>, suffix: RestAuthRouteSuffix, method: RouteMethod) => RouteSpec | undefined;
29
40
  /**
30
41
  * Validate a response body against the route spec's declared schemas.
31
42
  *
@@ -1 +1 @@
1
- {"version":3,"file":"integration_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/integration_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAU7B,OAAO,KAAK,EAAC,SAAS,EAAE,WAAW,EAAC,MAAM,uBAAuB,CAAC;AAElE,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAA8B,KAAK,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAE3F,OAAO,KAAK,EAAC,OAAO,EAAE,WAAW,EAAC,MAAM,iBAAiB,CAAC;AAE1D;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAC3B,OAAO,KAAK,CAAC,SAAS,CAAC,EACvB,QAAQ,MAAM,EACd,MAAM,MAAM,KACV,SAAS,GAAG,SAad,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,GAC3B,OAAO,KAAK,CAAC,SAAS,CAAC,EACvB,QAAQ,MAAM,EACd,QAAQ,WAAW,KACjB,SAAS,GAAG,SAEd,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,4BAA4B,GACxC,aAAa,KAAK,CAAC,SAAS,CAAC,EAC7B,QAAQ,MAAM,EACd,MAAM,MAAM,EACZ,UAAU,QAAQ,KAChB,OAAO,CAAC,IAAI,CAmDd,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,GACtC,SAAS,OAAO,EAChB,iBAAiB,cAAc,CAAC,MAAM,CAAC,KACrC,OAAO,CAAC,MAAM,CAGhB,CAAC;AAuCF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,GAAI,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,KAAK,CAAC,MAAM,CAQvF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,SAAS,MAAM,KACb,IAkBF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oCAAoC,GAChD,UAAU,QAAQ,EAClB,MAAM;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,KACzB,IAUF,CAAC;AAIF,oEAAoE;AACpE,eAAO,MAAM,yBAAyB,EAAE,aAAa,CAAC,MAAM,CAAmC,CAAC;AAEhG,0EAA0E;AAC1E,eAAO,MAAM,0BAA0B,EAAE,aAAa,CAAC,MAAM,CAAgC,CAAC;AAE9F;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,GAAI,OAAO,OAAO,KAAG,GAAG,CAAC,MAAM,CAetE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,kCAAkC,GAC9C,MAAM,OAAO,EACb,WAAW,aAAa,CAAC,MAAM,CAAC,EAChC,SAAS,MAAM,KACb,IAKF,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iBAAiB,GAC7B,MAAM,SAAS,EACf,UAAU,OAAO,EACjB,gBAAgB,WAAW,EAC3B,eAAe,WAAW,KACxB,MAAM,CAAC,MAAM,EAAE,MAAM,CAcvB,CAAC"}
1
+ {"version":3,"file":"integration_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/integration_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAU7B,OAAO,KAAK,EAAC,SAAS,EAAE,WAAW,EAAC,MAAM,uBAAuB,CAAC;AAElE,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAA8B,KAAK,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAE3F,OAAO,KAAK,EAAC,OAAO,EAAE,WAAW,EAAC,MAAM,iBAAiB,CAAC;AAE1D;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAC3B,OAAO,KAAK,CAAC,SAAS,CAAC,EACvB,QAAQ,MAAM,EACd,MAAM,MAAM,KACV,SAAS,GAAG,SAad,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,iFAO3B,CAAC;AACX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,GAC3B,OAAO,KAAK,CAAC,SAAS,CAAC,EACvB,QAAQ,mBAAmB,EAC3B,QAAQ,WAAW,KACjB,SAAS,GAAG,SAOd,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,4BAA4B,GACxC,aAAa,KAAK,CAAC,SAAS,CAAC,EAC7B,QAAQ,MAAM,EACd,MAAM,MAAM,EACZ,UAAU,QAAQ,KAChB,OAAO,CAAC,IAAI,CAmDd,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,GACtC,SAAS,OAAO,EAChB,iBAAiB,cAAc,CAAC,MAAM,CAAC,KACrC,OAAO,CAAC,MAAM,CAGhB,CAAC;AAuCF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,GAAI,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,KAAK,CAAC,MAAM,CAQvF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,SAAS,MAAM,KACb,IAkBF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oCAAoC,GAChD,UAAU,QAAQ,EAClB,MAAM;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,KACzB,IAUF,CAAC;AAIF,oEAAoE;AACpE,eAAO,MAAM,yBAAyB,EAAE,aAAa,CAAC,MAAM,CAAmC,CAAC;AAEhG,0EAA0E;AAC1E,eAAO,MAAM,0BAA0B,EAAE,aAAa,CAAC,MAAM,CAAgC,CAAC;AAE9F;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,GAAI,OAAO,OAAO,KAAG,GAAG,CAAC,MAAM,CAetE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,kCAAkC,GAC9C,MAAM,OAAO,EACb,WAAW,aAAa,CAAC,MAAM,CAAC,EAChC,SAAS,MAAM,KACb,IAKF,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iBAAiB,GAC7B,MAAM,SAAS,EACf,UAAU,OAAO,EACjB,gBAAgB,WAAW,EAC3B,eAAe,WAAW,KACxB,MAAM,CAAC,MAAM,EAAE,MAAM,CAcvB,CAAC"}
@@ -35,17 +35,37 @@ export const find_route_spec = (specs, method, path) => {
35
35
  });
36
36
  };
37
37
  /**
38
- * Find an auth route by suffix and method.
38
+ * REST auth route suffixes still on the account/bootstrap surface after the
39
+ * 2026-04-22 RPC migration. `find_auth_route` rejects any other suffix at
40
+ * runtime — session/token CRUD, admin operations, and permit flows live on
41
+ * the RPC surface and should be reached via `rpc_call`.
42
+ */
43
+ export const REST_AUTH_ROUTE_SUFFIXES = [
44
+ '/login',
45
+ '/logout',
46
+ '/password',
47
+ '/verify',
48
+ '/signup',
49
+ '/bootstrap',
50
+ ];
51
+ /**
52
+ * Find a REST auth route by suffix and method.
39
53
  *
40
- * Useful for discovering login/logout/verify/revoke paths regardless
41
- * of consumer prefix (`/api/account/login`, `/api/auth/login`, etc.).
54
+ * Decouples tests from consumer route prefix (`/api/account/login`,
55
+ * `/api/auth/login`, etc.). `suffix` must be one of
56
+ * `REST_AUTH_ROUTE_SUFFIXES` — throws otherwise so a post-migration RPC
57
+ * method name (e.g. `/sessions/revoke-all`) fails loudly at the call site
58
+ * instead of silently returning `undefined`.
42
59
  *
43
60
  * @param specs - route specs to search
44
- * @param suffix - path suffix to match (e.g. `'/login'`)
61
+ * @param suffix - REST auth path suffix
45
62
  * @param method - HTTP method
46
63
  * @returns matching route spec, or `undefined`
47
64
  */
48
65
  export const find_auth_route = (specs, suffix, method) => {
66
+ if (!REST_AUTH_ROUTE_SUFFIXES.includes(suffix)) {
67
+ throw new Error(`find_auth_route: unknown suffix ${JSON.stringify(suffix)} — expected one of ${REST_AUTH_ROUTE_SUFFIXES.join(', ')}. Use rpc_call for RPC methods.`);
68
+ }
49
69
  return specs.find((s) => s.method === method && s.path.endsWith(suffix));
50
70
  };
51
71
  /**
@@ -3,6 +3,7 @@ import type { SessionOptions } from '../auth/session_cookie.js';
3
3
  import type { AppServerContext, AppServerOptions } from '../server/app_server.js';
4
4
  import type { RouteSpec } from '../http/route_spec.js';
5
5
  import { type DbFactory } from './db.js';
6
+ import type { RpcEndpointSpec } from '../http/surface.js';
6
7
  /**
7
8
  * Configuration for `describe_rate_limiting_tests`.
8
9
  */
@@ -22,6 +23,12 @@ export interface RateLimitingTestOptions {
22
23
  * Default: `2` (tight limit for fast tests).
23
24
  */
24
25
  max_attempts?: number;
26
+ /**
27
+ * RPC endpoint specs — required so the bearer-auth rate limiting test
28
+ * can probe an authenticated method via the `account_verify` RPC
29
+ * action. Hard-fails via `require_rpc_endpoint_path` on setup.
30
+ */
31
+ rpc_endpoints: Array<RpcEndpointSpec>;
25
32
  }
26
33
  /**
27
34
  * Standard rate limiting integration test suite.
@@ -1 +1 @@
1
- {"version":3,"file":"rate_limiting.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rate_limiting.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAiB7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAKrD,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AAKjB;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACvC,4CAA4C;IAC5C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,wDAAwD;IACxD,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CACpB,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,iBAAiB,GAAG,oBAAoB,CAAC,CAC5E,CAAC;IACF;;OAEG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAChC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,4BAA4B,GAAI,SAAS,uBAAuB,KAAG,IA4N/E,CAAC"}
1
+ {"version":3,"file":"rate_limiting.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rate_limiting.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAiB7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAKrD,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AAKjB,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,oBAAoB,CAAC;AAGxD;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACvC,4CAA4C;IAC5C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,wDAAwD;IACxD,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CACpB,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,iBAAiB,GAAG,oBAAoB,CAAC,CAC5E,CAAC;IACF;;OAEG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAChC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,aAAa,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;CACtC;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,4BAA4B,GAAI,SAAS,uBAAuB,KAAG,IAqP/E,CAAC"}
@@ -18,7 +18,9 @@ import { AUTH_MIGRATION_NS } from '../auth/migrations.js';
18
18
  import { create_test_app } from './app_server.js';
19
19
  import { create_pglite_factory, create_describe_db, AUTH_INTEGRATION_TRUNCATE_TABLES, } from './db.js';
20
20
  import { find_auth_route, assert_rate_limit_retry_after_header } from './integration_helpers.js';
21
+ import { rpc_call_non_browser, require_rpc_endpoint_path } from './rpc_helpers.js';
21
22
  import { run_migrations } from '../db/migrate.js';
23
+ import { account_verify_action_spec } from '../auth/account_action_specs.js';
22
24
  /**
23
25
  * Standard rate limiting integration test suite.
24
26
  *
@@ -37,6 +39,9 @@ import { run_migrations } from '../db/migrate.js';
37
39
  */
38
40
  export const describe_rate_limiting_tests = (options) => {
39
41
  const max_attempts = options.max_attempts ?? 2;
42
+ // Hard-fail early so consumers see a clear setup error instead of a
43
+ // confusing test failure when `rpc_endpoints` is missing.
44
+ const rpc_path = require_rpc_endpoint_path(options.rpc_endpoints);
40
45
  const init_schema = async (db) => {
41
46
  await run_migrations(db, [AUTH_MIGRATION_NS]);
42
47
  };
@@ -177,24 +182,50 @@ export const describe_rate_limiting_tests = (options) => {
177
182
  bearer_ip_rate_limiter,
178
183
  },
179
184
  });
180
- const verify_route = find_auth_route(test_app.route_specs, '/verify', 'GET');
181
- assert.ok(verify_route, 'Expected GET /verify route ensure create_route_specs includes account routes');
185
+ // Probe `account_verify` via RPC with an invalid bearer token.
186
+ // The REST `/api/account/verify` shim is status-only (empty body
187
+ // for nginx `auth_request`), so we use the RPC surface to exercise
188
+ // a typed authenticated method. The bearer_auth rate limiter
189
+ // increments per attempt regardless of the route's own auth outcome.
190
+ // Use `rpc_call_non_browser` so the default `origin` header is
191
+ // suppressed — bearer_auth discards the token when Origin or
192
+ // Referer is present (browser context), which would short-circuit
193
+ // before the rate limiter records the attempt.
194
+ //
195
+ // Note: the rate limiter short-circuits before the RPC dispatcher,
196
+ // so the 429 response is a REST-shaped `RateLimitError`, not a
197
+ // JSON-RPC envelope. We use the underlying `app.request` for the
198
+ // blocked probe so `rpc_call_non_browser` doesn't throw on the
199
+ // non-envelope body.
200
+ const bearer_probe_headers = {
201
+ authorization: 'Bearer secret_fuz_token_invalid',
202
+ };
182
203
  // Fire max_attempts invalid bearer requests (sequential — must exhaust the window)
183
204
  for (let i = 0; i < max_attempts; i++) {
184
- const res = await test_app.app.request(verify_route.path, {
185
- headers: {
186
- host: 'localhost',
187
- authorization: 'Bearer secret_fuz_token_invalid',
188
- },
205
+ const res = await rpc_call_non_browser({
206
+ app: test_app.app,
207
+ path: rpc_path,
208
+ method: account_verify_action_spec.method,
209
+ id: 'rl-probe',
210
+ headers: bearer_probe_headers,
189
211
  });
190
212
  assert.notStrictEqual(res.status, 429, `Request ${i + 1}/${max_attempts} should not be rate limited`);
191
213
  }
192
- // The next request should be rate limited
193
- const blocked_res = await test_app.app.request(verify_route.path, {
214
+ // The next request should be rate limited. The 429 body is REST-shape
215
+ // (middleware short-circuits before the RPC dispatcher), so go
216
+ // direct — `rpc_call_non_browser` would throw on the non-envelope body.
217
+ const blocked_res = await test_app.app.request(rpc_path, {
218
+ method: 'POST',
194
219
  headers: {
195
220
  host: 'localhost',
196
- authorization: 'Bearer secret_fuz_token_invalid',
221
+ 'content-type': 'application/json',
222
+ ...bearer_probe_headers,
197
223
  },
224
+ body: JSON.stringify({
225
+ jsonrpc: '2.0',
226
+ method: account_verify_action_spec.method,
227
+ id: 'rl-probe-blocked',
228
+ }),
198
229
  });
199
230
  assert.strictEqual(blocked_res.status, 429);
200
231
  const body = await blocked_res.json();
@@ -1,11 +1,17 @@
1
1
  import './assert_dev_env.js';
2
2
  import { z } from 'zod';
3
3
  import { type JsonrpcErrorCode } from '../http/jsonrpc.js';
4
+ import type { RequestResponseActionSpec } from '../actions/action_spec.js';
5
+ import type { RpcAction } from '../actions/action_rpc.js';
6
+ import type { AppSurfaceRpcEndpoint, AppSurfaceRpcMethod, RpcEndpointSpec } from '../http/surface.js';
4
7
  /**
5
8
  * Create a `RequestInit` for a JSON-RPC POST request.
6
9
  *
7
10
  * @param method - JSON-RPC method name
8
- * @param params - params object (omit for null-input methods)
11
+ * @param params - params object (omit or pass `null` for null-input methods;
12
+ * both are serialized without a `params` field so the envelope
13
+ * schema accepts the request — `"params":null` is not a valid
14
+ * JSON-RPC value)
9
15
  * @param id - request id (default `'test'`)
10
16
  * @returns a `RequestInit` with the JSON-RPC envelope as body
11
17
  */
@@ -41,4 +47,150 @@ export declare const assert_jsonrpc_error_response: (body: unknown, expected_cod
41
47
  * @param output_schema - optional Zod schema to validate the `result` field against
42
48
  */
43
49
  export declare const assert_jsonrpc_success_response: (body: unknown, output_schema?: z.ZodType) => void;
50
+ /**
51
+ * Minimal transport surface — the duck type `Hono.request` already satisfies.
52
+ * Extracted so test setups that want an in-process / WS / mock path can plug
53
+ * a different dispatcher without changing call sites.
54
+ */
55
+ export type RpcTestTransport = (url: string, init: RequestInit) => Promise<Response>;
56
+ /** Adapt a `Hono`-style app into an `RpcTestTransport`. */
57
+ export declare const http_transport: (app: {
58
+ request: (input: string, init: RequestInit) => Promise<Response> | Response;
59
+ }) => RpcTestTransport;
60
+ /** Discriminated return from `rpc_call`. `status` is the HTTP status. */
61
+ export type RpcCallResult = {
62
+ ok: true;
63
+ status: number;
64
+ result: unknown;
65
+ } | {
66
+ ok: false;
67
+ status: number;
68
+ error: {
69
+ code: number;
70
+ message: string;
71
+ data?: unknown;
72
+ };
73
+ };
74
+ /** Arguments for `rpc_call`. */
75
+ export interface RpcCallArgs {
76
+ /** Hono-like app (anything with a `request(url, init)` method). */
77
+ app: {
78
+ request: (input: string, init: RequestInit) => Promise<Response> | Response;
79
+ };
80
+ /** RPC endpoint path, e.g. `'/api/rpc'`. */
81
+ path: string;
82
+ /** JSON-RPC method name. */
83
+ method: string;
84
+ /** Params object. Omit or pass `null` for null-input methods. */
85
+ params?: unknown;
86
+ /** Extra request headers (session cookie, bearer, etc.). Overrides defaults. */
87
+ headers?: Record<string, string>;
88
+ /** Request id. Defaults to `'test'`. */
89
+ id?: string | number;
90
+ /** HTTP verb — `'POST'` (default) or `'GET'` for `side_effects: false` methods. */
91
+ verb?: 'POST' | 'GET';
92
+ /**
93
+ * Suppress the default `origin` header. Required for bearer-auth paths:
94
+ * `bearer_auth` discards the token when Origin or Referer is present
95
+ * (browser context), so probing it via `rpc_call` needs this flag — or
96
+ * use `rpc_call_non_browser`, which sets it for you.
97
+ */
98
+ suppress_default_origin?: boolean;
99
+ }
100
+ /**
101
+ * One-shot JSON-RPC call over a Hono app.
102
+ *
103
+ * Merges sensible defaults (`host`, `origin`, `Content-Type`) under
104
+ * caller-provided headers, fires POST (default) or GET, parses the envelope,
105
+ * and returns a discriminated result.
106
+ *
107
+ * Throws `Error` only on envelope-shape violations (neither
108
+ * `JsonrpcResponse` nor `JsonrpcErrorResponse` parses) — protocol-level
109
+ * failures the caller should never tolerate. All JSON-RPC errors come back
110
+ * via `{ok: false, error}` so assertions can focus on `error.code` /
111
+ * `error.data.reason`.
112
+ */
113
+ export declare const rpc_call: (args: RpcCallArgs) => Promise<RpcCallResult>;
114
+ /**
115
+ * Same as `rpc_call` but without the default `origin` header. Use for
116
+ * bearer-auth probes: `bearer_auth` discards the token when Origin or
117
+ * Referer is present (browser context), so a bearer probe via `rpc_call`
118
+ * would short-circuit to 401 before the token is ever validated.
119
+ *
120
+ * Equivalent to `rpc_call({...args, suppress_default_origin: true})`.
121
+ */
122
+ export declare const rpc_call_non_browser: (args: Omit<RpcCallArgs, "suppress_default_origin">) => Promise<RpcCallResult>;
123
+ /**
124
+ * Typed discriminated result returned by `rpc_call_for_spec`. The success
125
+ * branch's `result` is inferred from `TSpec['output']`. The error branch
126
+ * stays untyped because JSON-RPC `error.data` shapes vary per error and
127
+ * are asserted per call site.
128
+ */
129
+ export type RpcCallResultForSpec<TSpec extends RequestResponseActionSpec> = {
130
+ ok: true;
131
+ status: number;
132
+ result: z.infer<TSpec['output']>;
133
+ } | {
134
+ ok: false;
135
+ status: number;
136
+ error: {
137
+ code: number;
138
+ message: string;
139
+ data?: unknown;
140
+ };
141
+ };
142
+ /** Arguments for `rpc_call_for_spec`. `spec` replaces the loose `method` field. */
143
+ export type RpcCallForSpecArgs<TSpec extends RequestResponseActionSpec> = Omit<RpcCallArgs, 'method' | 'params'> & {
144
+ /** Action spec whose `method` drives the envelope and whose `input`/`output` types pin params + result. */
145
+ spec: TSpec;
146
+ /** Params, typed against `spec.input`. */
147
+ params: z.infer<TSpec['input']>;
148
+ };
149
+ /**
150
+ * Typed wrapper over `rpc_call` — binds `params` to `z.infer<spec.input>`
151
+ * and the success `result` to `z.infer<spec.output>` via the generic.
152
+ *
153
+ * Success results are validated at runtime against `spec.output` (same
154
+ * contract as `rpc_call_typed`); a mismatch throws. Error responses come
155
+ * back on the discriminated `{ok: false, error}` branch — use this for
156
+ * happy-path + denial-path assertions where the error `data.reason` shape
157
+ * is still asserted manually. For adversarial input tests that send
158
+ * malformed params, use the untyped `rpc_call`.
159
+ */
160
+ export declare const rpc_call_for_spec: <TSpec extends RequestResponseActionSpec>(args: RpcCallForSpecArgs<TSpec>) => Promise<RpcCallResultForSpec<TSpec>>;
161
+ /**
162
+ * Same as `rpc_call` but parses the success `result` through the given
163
+ * output schema and returns typed data. Envelope-level failures or error
164
+ * responses throw — use the untyped `rpc_call` for tests that need to
165
+ * assert on specific error shapes.
166
+ */
167
+ export declare const rpc_call_typed: <T>(args: RpcCallArgs, output_schema: z.ZodType<T>) => Promise<T>;
168
+ /**
169
+ * Find the `RpcAction` for a method within a set of RPC endpoint specs.
170
+ * Returns both the endpoint path and the matched action. `undefined` when
171
+ * the method is not registered.
172
+ */
173
+ export declare const find_rpc_action: (rpc_endpoints: ReadonlyArray<RpcEndpointSpec>, method: string) => {
174
+ path: string;
175
+ action: RpcAction;
176
+ } | undefined;
177
+ /**
178
+ * Find the generated surface entry for a method — the shape returned by
179
+ * `generate_app_surface` (JSON-serializable, useful for schema assertions
180
+ * at the boundary of a consumer test).
181
+ */
182
+ export declare const find_rpc_method: (rpc_endpoints: ReadonlyArray<AppSurfaceRpcEndpoint>, method: string) => {
183
+ path: string;
184
+ method_spec: AppSurfaceRpcMethod;
185
+ } | undefined;
186
+ /**
187
+ * Resolve a single RPC endpoint path — the common case where a consumer
188
+ * mounts exactly one `create_rpc_endpoint`. Throws when `rpc_endpoints` is
189
+ * empty (hard-fail; see the suite options docs) or ambiguous (more than one
190
+ * endpoint registered).
191
+ *
192
+ * Callers that need multi-endpoint support should iterate `rpc_endpoints`
193
+ * directly.
194
+ */
195
+ export declare const require_rpc_endpoint_path: (rpc_endpoints: ReadonlyArray<RpcEndpointSpec>) => string;
44
196
  //# sourceMappingURL=rpc_helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rpc_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAW7B,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,EAIN,KAAK,gBAAgB,EACrB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAChC,QAAQ,MAAM,EACd,SAAS,OAAO,EAChB,KAAI,MAAM,GAAG,MAAe,KAC1B,WAID,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAC9B,eAAe,MAAM,EACrB,QAAQ,MAAM,EACd,SAAS,OAAO,EAChB,KAAI,MAAM,GAAG,MAAe,KAC1B,MAMF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,6BAA6B,GACzC,MAAM,OAAO,EACb,gBAAgB,gBAAgB,KAC9B,IAUF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,+BAA+B,GAAI,MAAM,OAAO,EAAE,gBAAgB,CAAC,CAAC,OAAO,KAAG,IAU1F,CAAC"}
1
+ {"version":3,"file":"rpc_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_helpers.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,10 @@
1
1
  import './assert_dev_env.js';
2
2
  /**
3
- * JSON-RPC request construction and response assertion helpers.
3
+ * JSON-RPC test helpers — request construction, response assertion, and
4
+ * one-shot call ergonomics.
4
5
  *
5
- * Shared by `rpc_attack_surface.ts` and `rpc_round_trip.ts`.
6
+ * Shared by `rpc_attack_surface.ts`, `rpc_round_trip.ts`, and
7
+ * consumer-facing admin/audit suites that exercise RPC methods directly.
6
8
  *
7
9
  * @module
8
10
  */
@@ -13,15 +15,23 @@ import { JSONRPC_VERSION, JsonrpcErrorResponse, JsonrpcResponse, } from '../http
13
15
  * Create a `RequestInit` for a JSON-RPC POST request.
14
16
  *
15
17
  * @param method - JSON-RPC method name
16
- * @param params - params object (omit for null-input methods)
18
+ * @param params - params object (omit or pass `null` for null-input methods;
19
+ * both are serialized without a `params` field so the envelope
20
+ * schema accepts the request — `"params":null` is not a valid
21
+ * JSON-RPC value)
17
22
  * @param id - request id (default `'test'`)
18
23
  * @returns a `RequestInit` with the JSON-RPC envelope as body
19
24
  */
20
- export const create_rpc_post_init = (method, params, id = 'test') => ({
21
- method: 'POST',
22
- headers: { 'Content-Type': 'application/json' },
23
- body: JSON.stringify({ jsonrpc: JSONRPC_VERSION, method, params, id }),
24
- });
25
+ export const create_rpc_post_init = (method, params, id = 'test') => {
26
+ const envelope = { jsonrpc: JSONRPC_VERSION, method, id };
27
+ if (params !== undefined && params !== null)
28
+ envelope.params = params;
29
+ return {
30
+ method: 'POST',
31
+ headers: { 'Content-Type': 'application/json' },
32
+ body: JSON.stringify(envelope),
33
+ };
34
+ };
25
35
  /**
26
36
  * Build a GET URL with JSON-RPC query parameters.
27
37
  *
@@ -72,3 +82,169 @@ export const assert_jsonrpc_success_response = (body, output_schema) => {
72
82
  assert.ok(output_result.success, `JSON-RPC result does not match output schema: ${JSON.stringify(output_result.error?.issues)}`);
73
83
  }
74
84
  };
85
+ /** Adapt a `Hono`-style app into an `RpcTestTransport`. */
86
+ export const http_transport = (app) => async (url, init) => app.request(url, init);
87
+ /** Base default headers merged into every `rpc_call` request. */
88
+ const RPC_CALL_DEFAULT_HEADERS_BASE = {
89
+ host: 'localhost',
90
+ 'Content-Type': 'application/json',
91
+ };
92
+ /** Default headers merged into every `rpc_call` request. Includes `origin`. */
93
+ const RPC_CALL_DEFAULT_HEADERS = {
94
+ ...RPC_CALL_DEFAULT_HEADERS_BASE,
95
+ origin: 'http://localhost:5173',
96
+ };
97
+ /**
98
+ * One-shot JSON-RPC call over a Hono app.
99
+ *
100
+ * Merges sensible defaults (`host`, `origin`, `Content-Type`) under
101
+ * caller-provided headers, fires POST (default) or GET, parses the envelope,
102
+ * and returns a discriminated result.
103
+ *
104
+ * Throws `Error` only on envelope-shape violations (neither
105
+ * `JsonrpcResponse` nor `JsonrpcErrorResponse` parses) — protocol-level
106
+ * failures the caller should never tolerate. All JSON-RPC errors come back
107
+ * via `{ok: false, error}` so assertions can focus on `error.code` /
108
+ * `error.data.reason`.
109
+ */
110
+ export const rpc_call = async (args) => {
111
+ const { app, path, method, params, headers, id = 'test', verb = 'POST', suppress_default_origin, } = args;
112
+ const defaults = suppress_default_origin
113
+ ? RPC_CALL_DEFAULT_HEADERS_BASE
114
+ : RPC_CALL_DEFAULT_HEADERS;
115
+ let url;
116
+ let init;
117
+ if (verb === 'GET') {
118
+ url = create_rpc_get_url(path, method, params, id);
119
+ init = { method: 'GET', headers: { ...defaults, ...headers } };
120
+ }
121
+ else {
122
+ url = path;
123
+ const post = create_rpc_post_init(method, params, id);
124
+ init = {
125
+ method: 'POST',
126
+ headers: {
127
+ ...defaults,
128
+ ...post.headers,
129
+ ...headers,
130
+ },
131
+ body: post.body,
132
+ };
133
+ }
134
+ const res = await app.request(url, init);
135
+ const status = res.status;
136
+ const body = await res.json();
137
+ const success = JsonrpcResponse.safeParse(body);
138
+ if (success.success) {
139
+ return { ok: true, status, result: success.data.result };
140
+ }
141
+ const error = JsonrpcErrorResponse.safeParse(body);
142
+ if (error.success) {
143
+ return {
144
+ ok: false,
145
+ status,
146
+ error: {
147
+ code: error.data.error.code,
148
+ message: error.data.error.message,
149
+ data: error.data.error.data,
150
+ },
151
+ };
152
+ }
153
+ throw new Error(`rpc_call: response is not a valid JSON-RPC envelope (method=${method}, status=${status}): ${JSON.stringify(body)}`);
154
+ };
155
+ /**
156
+ * Same as `rpc_call` but without the default `origin` header. Use for
157
+ * bearer-auth probes: `bearer_auth` discards the token when Origin or
158
+ * Referer is present (browser context), so a bearer probe via `rpc_call`
159
+ * would short-circuit to 401 before the token is ever validated.
160
+ *
161
+ * Equivalent to `rpc_call({...args, suppress_default_origin: true})`.
162
+ */
163
+ export const rpc_call_non_browser = (args) => rpc_call({ ...args, suppress_default_origin: true });
164
+ /**
165
+ * Typed wrapper over `rpc_call` — binds `params` to `z.infer<spec.input>`
166
+ * and the success `result` to `z.infer<spec.output>` via the generic.
167
+ *
168
+ * Success results are validated at runtime against `spec.output` (same
169
+ * contract as `rpc_call_typed`); a mismatch throws. Error responses come
170
+ * back on the discriminated `{ok: false, error}` branch — use this for
171
+ * happy-path + denial-path assertions where the error `data.reason` shape
172
+ * is still asserted manually. For adversarial input tests that send
173
+ * malformed params, use the untyped `rpc_call`.
174
+ */
175
+ export const rpc_call_for_spec = async (args) => {
176
+ const { spec, params, ...rest } = args;
177
+ const res = await rpc_call({ ...rest, method: spec.method, params });
178
+ if (!res.ok) {
179
+ return res;
180
+ }
181
+ const parsed = spec.output.safeParse(res.result);
182
+ if (!parsed.success) {
183
+ throw new Error(`rpc_call_for_spec(${spec.method}) result did not match spec.output: ${JSON.stringify(parsed.error.issues)}`);
184
+ }
185
+ return { ok: true, status: res.status, result: parsed.data };
186
+ };
187
+ /**
188
+ * Same as `rpc_call` but parses the success `result` through the given
189
+ * output schema and returns typed data. Envelope-level failures or error
190
+ * responses throw — use the untyped `rpc_call` for tests that need to
191
+ * assert on specific error shapes.
192
+ */
193
+ export const rpc_call_typed = async (args, output_schema) => {
194
+ const res = await rpc_call(args);
195
+ if (!res.ok) {
196
+ throw new Error(`rpc_call_typed(${args.method}) returned error: code=${res.error.code} message=${res.error.message} data=${JSON.stringify(res.error.data)}`);
197
+ }
198
+ const parsed = output_schema.safeParse(res.result);
199
+ if (!parsed.success) {
200
+ throw new Error(`rpc_call_typed(${args.method}) result did not match output schema: ${JSON.stringify(parsed.error.issues)}`);
201
+ }
202
+ return parsed.data;
203
+ };
204
+ // -- registry/surface lookup helpers -----------------------------------------
205
+ /**
206
+ * Find the `RpcAction` for a method within a set of RPC endpoint specs.
207
+ * Returns both the endpoint path and the matched action. `undefined` when
208
+ * the method is not registered.
209
+ */
210
+ export const find_rpc_action = (rpc_endpoints, method) => {
211
+ for (const ep of rpc_endpoints) {
212
+ for (const action of ep.actions) {
213
+ if (action.spec.method === method)
214
+ return { path: ep.path, action };
215
+ }
216
+ }
217
+ return undefined;
218
+ };
219
+ /**
220
+ * Find the generated surface entry for a method — the shape returned by
221
+ * `generate_app_surface` (JSON-serializable, useful for schema assertions
222
+ * at the boundary of a consumer test).
223
+ */
224
+ export const find_rpc_method = (rpc_endpoints, method) => {
225
+ for (const ep of rpc_endpoints) {
226
+ for (const method_spec of ep.methods) {
227
+ if (method_spec.name === method)
228
+ return { path: ep.path, method_spec };
229
+ }
230
+ }
231
+ return undefined;
232
+ };
233
+ /**
234
+ * Resolve a single RPC endpoint path — the common case where a consumer
235
+ * mounts exactly one `create_rpc_endpoint`. Throws when `rpc_endpoints` is
236
+ * empty (hard-fail; see the suite options docs) or ambiguous (more than one
237
+ * endpoint registered).
238
+ *
239
+ * Callers that need multi-endpoint support should iterate `rpc_endpoints`
240
+ * directly.
241
+ */
242
+ export const require_rpc_endpoint_path = (rpc_endpoints) => {
243
+ if (rpc_endpoints.length === 0) {
244
+ throw new Error('rpc_endpoints is empty — the admin/audit integration suites require an RPC endpoint. Pass `rpc_endpoints` on the suite options.');
245
+ }
246
+ if (rpc_endpoints.length > 1) {
247
+ throw new Error(`rpc_endpoints has ${rpc_endpoints.length} entries; this helper expects exactly one. Iterate rpc_endpoints manually for multi-endpoint setups.`);
248
+ }
249
+ return rpc_endpoints[0].path;
250
+ };
@@ -6,6 +6,7 @@ import { type EventSpec } from '../realtime/sse.js';
6
6
  import type { AuditLogEvent } from '../auth/audit_log_schema.js';
7
7
  import { type TestApp, type TestAccount } from './app_server.js';
8
8
  import { type DbFactory } from './db.js';
9
+ import type { RpcEndpointSpec } from '../http/surface.js';
9
10
  /** Config for a single SSE route under test. */
10
11
  export interface SseRouteTestSpec {
11
12
  /** Full HTTP path of the SSE endpoint (e.g., `'/api/tx/subscribe'`). */
@@ -48,6 +49,13 @@ export interface SseRouteTestOptions {
48
49
  * closes the tested streams.
49
50
  */
50
51
  on_audit_event?: (event: AuditLogEvent) => void;
52
+ /**
53
+ * RPC endpoint specs — required so the close-on-revoke assertion can
54
+ * dispatch `account_session_revoke_all` via RPC (the former REST route
55
+ * `POST /api/account/sessions/revoke-all` was removed in the 2026-04-23
56
+ * migration). Hard-fails via `require_rpc_endpoint_path` on setup.
57
+ */
58
+ rpc_endpoints: Array<RpcEndpointSpec>;
51
59
  /** SSE routes to exercise. */
52
60
  routes: Array<SseRouteTestSpec>;
53
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sse_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/sse_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAmB7B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAwB,KAAK,SAAS,EAAuB,MAAM,oBAAoB,CAAC;AAE/F,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAkB,KAAK,OAAO,EAAE,KAAK,WAAW,EAAC,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAAwB,KAAK,SAAS,EAAC,MAAM,SAAS,CAAC;AAM9D,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAChC,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,OAAO,EAAE,CAAC,GAAG,EAAE;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E;;;OAGG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/B;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IACnC,4CAA4C;IAC5C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,qDAAqD;IACrD,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CACpB,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,iBAAiB,GAAG,oBAAoB,CAAC,CAC5E,CAAC;IACF,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAChC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAChD,8BAA8B;IAC9B,MAAM,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC;CAChC;AAyHD;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,mBAAmB,KAAG,IA4GvE,CAAC"}
1
+ {"version":3,"file":"sse_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/sse_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAmB7B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAwB,KAAK,SAAS,EAAuB,MAAM,oBAAoB,CAAC;AAE/F,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAkB,KAAK,OAAO,EAAE,KAAK,WAAW,EAAC,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAAwB,KAAK,SAAS,EAAC,MAAM,SAAS,CAAC;AAM9D,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,oBAAoB,CAAC;AAGxD,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAChC,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,OAAO,EAAE,CAAC,GAAG,EAAE;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E;;;OAGG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/B;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IACnC,4CAA4C;IAC5C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,qDAAqD;IACrD,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CACpB,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,iBAAiB,GAAG,oBAAoB,CAAC,CAC5E,CAAC;IACF,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAChC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAChD;;;;;OAKG;IACH,aAAa,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IACtC,8BAA8B;IAC9B,MAAM,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC;CAChC;AAyHD;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,mBAAmB,KAAG,IAkHvE,CAAC"}