@fuzdev/fuz_app 0.65.0 → 0.67.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 (159) hide show
  1. package/dist/actions/CLAUDE.md +65 -86
  2. package/dist/actions/action_codegen.d.ts +1 -1
  3. package/dist/actions/action_codegen.js +1 -1
  4. package/dist/actions/action_event_data.d.ts +1 -1
  5. package/dist/auth/CLAUDE.md +83 -104
  6. package/dist/auth/audit_log_schema.js +2 -2
  7. package/dist/auth/daemon_token_middleware.d.ts +15 -5
  8. package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
  9. package/dist/auth/daemon_token_middleware.js +24 -15
  10. package/dist/auth/invite_queries.d.ts +17 -7
  11. package/dist/auth/invite_queries.d.ts.map +1 -1
  12. package/dist/auth/invite_queries.js +19 -8
  13. package/dist/auth/signup_routes.d.ts +47 -1
  14. package/dist/auth/signup_routes.d.ts.map +1 -1
  15. package/dist/auth/signup_routes.js +103 -52
  16. package/dist/env/resolve.d.ts +44 -7
  17. package/dist/env/resolve.d.ts.map +1 -1
  18. package/dist/env/resolve.js +94 -27
  19. package/dist/http/CLAUDE.md +47 -52
  20. package/dist/http/jsonrpc.d.ts +23 -7
  21. package/dist/http/jsonrpc.d.ts.map +1 -1
  22. package/dist/http/jsonrpc.js +19 -3
  23. package/dist/http/surface.d.ts +9 -2
  24. package/dist/http/surface.d.ts.map +1 -1
  25. package/dist/runtime/mock.d.ts +1 -1
  26. package/dist/runtime/mock.js +1 -1
  27. package/dist/testing/CLAUDE.md +659 -511
  28. package/dist/testing/admin_integration.d.ts +5 -5
  29. package/dist/testing/admin_integration.d.ts.map +1 -1
  30. package/dist/testing/admin_integration.js +95 -39
  31. package/dist/testing/app_server.d.ts +16 -1
  32. package/dist/testing/app_server.d.ts.map +1 -1
  33. package/dist/testing/app_server.js +18 -3
  34. package/dist/testing/audit_completeness.d.ts +7 -5
  35. package/dist/testing/audit_completeness.d.ts.map +1 -1
  36. package/dist/testing/audit_completeness.js +5 -9
  37. package/dist/testing/bootstrap_success.js +2 -2
  38. package/dist/testing/cross_backend/backend_config.d.ts +113 -0
  39. package/dist/testing/cross_backend/backend_config.d.ts.map +1 -0
  40. package/dist/testing/cross_backend/backend_config.js +1 -0
  41. package/dist/testing/cross_backend/bench/bench_report.d.ts +46 -0
  42. package/dist/testing/cross_backend/bench/bench_report.d.ts.map +1 -0
  43. package/dist/testing/cross_backend/bench/bench_report.js +83 -0
  44. package/dist/testing/cross_backend/bench/run_cross_impl_bench.d.ts +44 -0
  45. package/dist/testing/cross_backend/bench/run_cross_impl_bench.d.ts.map +1 -0
  46. package/dist/testing/cross_backend/bench/run_cross_impl_bench.js +38 -0
  47. package/dist/testing/cross_backend/bench/scenario.d.ts +57 -0
  48. package/dist/testing/cross_backend/bench/scenario.d.ts.map +1 -0
  49. package/dist/testing/cross_backend/bench/scenario.js +28 -0
  50. package/dist/testing/cross_backend/bootstrap_backend.d.ts +41 -0
  51. package/dist/testing/cross_backend/bootstrap_backend.d.ts.map +1 -0
  52. package/dist/testing/cross_backend/bootstrap_backend.js +34 -0
  53. package/dist/testing/cross_backend/build_test_backend_paths.d.ts +24 -0
  54. package/dist/testing/cross_backend/build_test_backend_paths.d.ts.map +1 -0
  55. package/dist/testing/cross_backend/build_test_backend_paths.js +33 -0
  56. package/dist/testing/cross_backend/capabilities.d.ts +3 -2
  57. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  58. package/dist/testing/cross_backend/default_backend_configs.d.ts +122 -0
  59. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -0
  60. package/dist/testing/cross_backend/default_backend_configs.js +111 -0
  61. package/dist/testing/cross_backend/default_secrets.d.ts +40 -0
  62. package/dist/testing/cross_backend/default_secrets.d.ts.map +1 -0
  63. package/dist/testing/cross_backend/default_secrets.js +39 -0
  64. package/dist/testing/cross_backend/default_spine_surface.d.ts +64 -0
  65. package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -0
  66. package/dist/testing/cross_backend/default_spine_surface.js +121 -0
  67. package/dist/testing/cross_backend/setup.d.ts +270 -34
  68. package/dist/testing/cross_backend/setup.d.ts.map +1 -1
  69. package/dist/testing/cross_backend/setup.js +495 -15
  70. package/dist/testing/cross_backend/spawn_backend.d.ts +58 -0
  71. package/dist/testing/cross_backend/spawn_backend.d.ts.map +1 -0
  72. package/dist/testing/cross_backend/spawn_backend.js +229 -0
  73. package/dist/testing/cross_backend/spine_stub_backend_config.d.ts +66 -0
  74. package/dist/testing/cross_backend/spine_stub_backend_config.d.ts.map +1 -0
  75. package/dist/testing/cross_backend/spine_stub_backend_config.js +49 -0
  76. package/dist/testing/cross_backend/sse_round_trip.d.ts +37 -0
  77. package/dist/testing/cross_backend/sse_round_trip.d.ts.map +1 -0
  78. package/dist/testing/cross_backend/sse_round_trip.js +137 -0
  79. package/dist/testing/cross_backend/standard.d.ts +96 -0
  80. package/dist/testing/cross_backend/standard.d.ts.map +1 -0
  81. package/dist/testing/cross_backend/standard.js +49 -0
  82. package/dist/testing/cross_backend/testing_reset_actions.d.ts +171 -0
  83. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -0
  84. package/dist/testing/cross_backend/testing_reset_actions.js +213 -0
  85. package/dist/testing/cross_backend/testing_server_bun.d.ts +5 -0
  86. package/dist/testing/cross_backend/testing_server_bun.d.ts.map +1 -0
  87. package/dist/testing/cross_backend/testing_server_bun.js +59 -0
  88. package/dist/testing/cross_backend/testing_server_core.d.ts +140 -0
  89. package/dist/testing/cross_backend/testing_server_core.d.ts.map +1 -0
  90. package/dist/testing/cross_backend/testing_server_core.js +68 -0
  91. package/dist/testing/cross_backend/testing_server_deno.d.ts +5 -0
  92. package/dist/testing/cross_backend/testing_server_deno.d.ts.map +1 -0
  93. package/dist/testing/cross_backend/testing_server_deno.js +37 -0
  94. package/dist/testing/cross_backend/testing_server_node.d.ts +5 -0
  95. package/dist/testing/cross_backend/testing_server_node.d.ts.map +1 -0
  96. package/dist/testing/cross_backend/testing_server_node.js +50 -0
  97. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +72 -0
  98. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -0
  99. package/dist/testing/cross_backend/ts_spine_backend_config.js +112 -0
  100. package/dist/testing/cross_backend/ws_round_trip.d.ts +35 -0
  101. package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -0
  102. package/dist/testing/cross_backend/ws_round_trip.js +113 -0
  103. package/dist/testing/data_exposure.d.ts +4 -6
  104. package/dist/testing/data_exposure.d.ts.map +1 -1
  105. package/dist/testing/data_exposure.js +1 -5
  106. package/dist/testing/db_entities.d.ts +18 -7
  107. package/dist/testing/db_entities.d.ts.map +1 -1
  108. package/dist/testing/db_entities.js +18 -7
  109. package/dist/testing/integration.d.ts +27 -6
  110. package/dist/testing/integration.d.ts.map +1 -1
  111. package/dist/testing/integration.js +93 -58
  112. package/dist/testing/round_trip.d.ts +4 -5
  113. package/dist/testing/round_trip.d.ts.map +1 -1
  114. package/dist/testing/round_trip.js +1 -5
  115. package/dist/testing/rpc_helpers.d.ts +10 -4
  116. package/dist/testing/rpc_helpers.d.ts.map +1 -1
  117. package/dist/testing/rpc_helpers.js +1 -1
  118. package/dist/testing/rpc_round_trip.d.ts +5 -5
  119. package/dist/testing/rpc_round_trip.d.ts.map +1 -1
  120. package/dist/testing/rpc_round_trip.js +1 -5
  121. package/dist/testing/sse_round_trip.d.ts.map +1 -1
  122. package/dist/testing/sse_round_trip.js +1 -68
  123. package/dist/testing/standard.d.ts +4 -5
  124. package/dist/testing/standard.d.ts.map +1 -1
  125. package/dist/testing/stubs.d.ts +10 -3
  126. package/dist/testing/stubs.d.ts.map +1 -1
  127. package/dist/testing/stubs.js +9 -2
  128. package/dist/testing/testing_rate_limiter.d.ts +59 -0
  129. package/dist/testing/testing_rate_limiter.d.ts.map +1 -0
  130. package/dist/testing/testing_rate_limiter.js +74 -0
  131. package/dist/testing/transports/bootstrap.d.ts +52 -0
  132. package/dist/testing/transports/bootstrap.d.ts.map +1 -0
  133. package/dist/testing/transports/bootstrap.js +70 -0
  134. package/dist/testing/transports/fetch_transport.d.ts +81 -0
  135. package/dist/testing/transports/fetch_transport.d.ts.map +1 -0
  136. package/dist/testing/transports/fetch_transport.js +74 -0
  137. package/dist/testing/transports/sse_frame_reader.d.ts +41 -0
  138. package/dist/testing/transports/sse_frame_reader.d.ts.map +1 -0
  139. package/dist/testing/transports/sse_frame_reader.js +84 -0
  140. package/dist/testing/transports/sse_transport.d.ts +54 -0
  141. package/dist/testing/transports/sse_transport.d.ts.map +1 -0
  142. package/dist/testing/transports/sse_transport.js +51 -0
  143. package/dist/testing/transports/ws_client.d.ts +108 -0
  144. package/dist/testing/transports/ws_client.d.ts.map +1 -0
  145. package/dist/testing/transports/ws_client.js +56 -0
  146. package/dist/testing/transports/ws_transport.d.ts +43 -0
  147. package/dist/testing/transports/ws_transport.d.ts.map +1 -0
  148. package/dist/testing/transports/ws_transport.js +169 -0
  149. package/dist/testing/ws_round_trip.d.ts +21 -103
  150. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  151. package/dist/testing/ws_round_trip.js +42 -40
  152. package/dist/ui/CLAUDE.md +5 -3
  153. package/dist/ui/MenuLink.svelte +16 -16
  154. package/dist/ui/MenuLink.svelte.d.ts +13 -4
  155. package/dist/ui/MenuLink.svelte.d.ts.map +1 -1
  156. package/package.json +20 -3
  157. package/dist/testing/transports/surface_source.d.ts +0 -51
  158. package/dist/testing/transports/surface_source.d.ts.map +0 -1
  159. package/dist/testing/transports/surface_source.js +0 -19
@@ -25,11 +25,9 @@ the pair invariant.
25
25
 
26
26
  Canonical source of truth. Three concrete kinds discriminate on `kind`:
27
27
 
28
- | Kind | `auth` | `side_effects` | `output` | `async` |
29
- | --------------------- | ---------------------- | -------------- | ----------- | ------- |
30
- | `request_response` | `RouteAuth` (non-null) | arbitrary | arbitrary | `true` |
31
- | `remote_notification` | `null` | `true` | `z.ZodVoid` | `true` |
32
- | `local_call` | `null` | arbitrary | arbitrary | boolean |
28
+ - `request_response` `auth: RouteAuth` (non-null), `side_effects` arbitrary, `output` arbitrary, `async: true`.
29
+ - `remote_notification` `auth: null`, `side_effects: true`, `output: z.ZodVoid`, `async: true`.
30
+ - `local_call` `auth: null`, `side_effects` arbitrary, `output` arbitrary, `async` boolean.
33
31
 
34
32
  `RouteAuth` is the flat record `{account, actor, roles?, credential_types?}`
35
33
  from `http/auth_shape.ts` — same shape governs `RouteSpec.auth` so the four
@@ -64,11 +62,9 @@ dropping per-spec `*_METHOD` constants (readers dereference `.method`). See
64
62
 
65
63
  ## Kind → binding matrix
66
64
 
67
- | Kind | REST `RouteSpec` | RPC `RouteSpec` (via dispatcher) | WS dispatch | SSE `EventSpec` |
68
- | --------------------- | ---------------- | -------------------------------- | ----------- | --------------- |
69
- | `request_response` | yes (bridge) | yes (`create_rpc_endpoint`) | yes | no |
70
- | `remote_notification` | no | no | server push | yes (bridge) |
71
- | `local_call` | no | no | no | no |
65
+ - `request_response` REST `RouteSpec` via bridge; RPC `RouteSpec` via `create_rpc_endpoint`; WS dispatch yes; no SSE.
66
+ - `remote_notification` no REST/RPC routes; WS server push; SSE `EventSpec` via bridge.
67
+ - `local_call` none (no REST, no RPC, no WS, no SSE).
72
68
 
73
69
  `create_action_route_spec` throws if `spec.auth` is null (notifications and
74
70
  local calls cannot become routes). `create_action_event_spec` throws on any
@@ -93,17 +89,16 @@ registry-only.
93
89
  ## Registry + codegen (`actions/action_registry.ts`, `actions/action_codegen.ts`)
94
90
 
95
91
  **Symmetric design — universal calling abstraction.** SAES is one spec
96
- shape that drives dispatch across (a) network boundaries (frontend ⇄
97
- backend over HTTP / WS) and (b) within the same runtime (`local_call`
98
- actions). `ActionPeer` is symmetric on both sides (`send` + `receive`).
99
- Typed surfaces are paired: `FrontendActionsApi` is "what the frontend can
100
- call" (typed Proxy from `create_rpc_client`); `BackendActionsApi` is "what
101
- the backend can call" (typed object from `create_broadcast_api` today;
102
- broader runtime constructors will join). Remaining asymmetry today:
92
+ shape driving dispatch across (a) network boundaries (frontend ⇄ backend
93
+ over HTTP / WS) and (b) within the same runtime (`local_call` actions).
94
+ `ActionPeer` is symmetric on both sides (`send` + `receive`). Typed
95
+ surfaces are paired: `FrontendActionsApi` is "what the frontend can call"
96
+ (typed Proxy from `create_rpc_client`); `BackendActionsApi` is "what the
97
+ backend can call" (typed object from `create_broadcast_api` today;
98
+ broader runtime constructors will join). Remaining asymmetry:
103
99
  `create_broadcast_api` returns `Promise<void>` while `FrontendActionsApi`
104
100
  methods return `Promise<Result<...>>`. Closing those gaps is on the
105
- deferred follow-up set in
106
- [SAES RPC closeout](https://github.com/ryanatkn/grimoire/blob/main/quests/HISTORY.md#saes-rpc-direction-2026-04)
101
+ deferred follow-up set from the SAES RPC closeout work
107
102
  — wait for a second backend runtime case.
108
103
 
109
104
  ### `ActionRegistry`
@@ -111,13 +106,11 @@ deferred follow-up set in
111
106
  Query/filter wrapper over `ActionSpecUnion[]`. Codegen-relevant getter
112
107
  groups (each pairs `_specs` with matching `_methods`):
113
108
 
114
- | Getter family | Filter | Drives |
115
- | --------------------- | ------------------------------------------------------------------- | ----------------------------- |
116
- | Kind-narrow | by `kind` | `*ActionMethod` enums |
117
- | `*_handled` | `request_response` + handler-side initiator | `BackendActionHandlers` map |
118
- | `specs_relevant_to_*` | everything the side might encounter | typed-Proxy method enums |
119
- | `broadcast` | `remote_notification`, `initiator !== 'frontend'`, excludes streams | `BackendActionsApi` interface |
120
- | `backend_initiated` | forward-looking kind-agnostic broadcast | same content today |
109
+ - Kind-narrow filter by `kind`; drives `*ActionMethod` enums.
110
+ - `*_handled` `request_response` + handler-side initiator; drives `BackendActionHandlers` map.
111
+ - `specs_relevant_to_*` everything the side might encounter; drives typed-Proxy method enums.
112
+ - `broadcast` `remote_notification`, `initiator !== 'frontend'`, excludes streams; drives `BackendActionsApi` interface.
113
+ - `backend_initiated` forward-looking kind-agnostic broadcast; same content today.
121
114
 
122
115
  Other getters (auth filters, initiator-direction filters) are pre-built API
123
116
  surface unused by codegen today.
@@ -125,28 +118,25 @@ surface unused by codegen today.
125
118
  ### Codegen helpers (`actions/action_codegen.ts`)
126
119
 
127
120
  Used by consumer `*.gen.ts` producers, not the runtime. Detailed signatures
128
-
129
- - options on each function's TSDoc.
130
-
131
- | Helper | Output |
132
- | ---------------------------------------------- | ------------------------------------------------------------------------ |
133
- | `ImportBuilder` | Class managing value/type/namespace imports; auto-tree-shakes type-only |
134
- | `get_executor_phases(spec, executor)` | Phases an executor participates in for the spec |
135
- | `get_handler_return_type` | TS type a phase handler must return; side-effect imports `ActionOutputs` |
136
- | `generate_phase_handlers` | Per-action typed handler-map fragment |
137
- | `generate_actions_api_method_signature` | Single source of truth for the typed `FrontendActionsApi` method shape |
138
- | `generate_action_method_enums` | Up to nine `z.enum` + `z.infer` pairs |
139
- | `generate_action_method_enum_block` | Lower-level escape hatch for cross-product enums |
140
- | `generate_typed_action_event_alias` | Fixed-shape `TypedActionEvent<TMethod, TPhase, TStep>` alias |
141
- | `generate_action_specs_record` | `ActionSpecs` runtime const + interface + `action_specs` array |
142
- | `generate_action_inputs_outputs` | `ActionInputs` + `ActionOutputs` runtime consts + interfaces |
143
- | `generate_action_event_datas` | `ActionEventDatas` interface; per-spec variants |
144
- | `generate_frontend_actions_api` | Typed `FrontendActionsApi` interface |
145
- | `generate_frontend_action_handlers` | `FrontendActionHandlers` interface (Tier 2 only) |
146
- | `generate_backend_actions_api` | `BackendActionsApi` interface + `broadcast_action_specs` array |
147
- | `generate_backend_action_handlers_map` | `BackendActionHandlers` mapped type |
148
- | `compose_gen_file` | Wrapper: banner + `imports.build()` + blocks join |
149
- | `create_namespace_qualifier(sources, imports)` | Multi-source consumer helper; registers `import * as ns` per source |
121
+ and options on each function's TSDoc.
122
+
123
+ - `ImportBuilder` — class managing value/type/namespace imports; auto-tree-shakes type-only.
124
+ - `get_executor_phases(spec, executor)` — phases an executor participates in for the spec.
125
+ - `get_handler_return_type` TS type a phase handler must return; side-effect imports `ActionOutputs`.
126
+ - `generate_phase_handlers` per-action typed handler-map fragment.
127
+ - `generate_actions_api_method_signature` single source of truth for the typed `FrontendActionsApi` method shape.
128
+ - `generate_action_method_enums` up to nine `z.enum` + `z.infer` pairs.
129
+ - `generate_action_method_enum_block` — lower-level escape hatch for cross-product enums.
130
+ - `generate_typed_action_event_alias` fixed-shape `TypedActionEvent<TMethod, TPhase, TStep>` alias.
131
+ - `generate_action_specs_record` `ActionSpecs` runtime const + interface + `action_specs` array.
132
+ - `generate_action_inputs_outputs` `ActionInputs` + `ActionOutputs` runtime consts + interfaces.
133
+ - `generate_action_event_datas` `ActionEventDatas` interface; per-spec variants.
134
+ - `generate_frontend_actions_api` typed `FrontendActionsApi` interface.
135
+ - `generate_frontend_action_handlers` `FrontendActionHandlers` interface (Tier 2 only).
136
+ - `generate_backend_actions_api` `BackendActionsApi` interface + `broadcast_action_specs` array.
137
+ - `generate_backend_action_handlers_map` `BackendActionHandlers` mapped type.
138
+ - `compose_gen_file` — wrapper: banner + `imports.build()` + blocks join.
139
+ - `create_namespace_qualifier(sources, imports)` — multi-source consumer helper; registers `import * as ns` per source.
150
140
 
151
141
  Shared defaults: `DEFAULT_COLLECTIONS_PATH = './action_collections.js'`,
152
142
  `DEFAULT_SPECS_MODULE = './action_specs.js'`,
@@ -158,14 +148,14 @@ multi-source-aware helper uses).
158
148
 
159
149
  **Protocol actions filtered by default.** Every spec-iterating helper
160
150
  accepts `{include_protocol_actions?: boolean}` (default `false`) and drops
161
- `heartbeat` / `cancel`. Protocol actions ship from fuz_app and are spread
162
- into each consumer's `actions` array at registration time (via
151
+ `heartbeat` / `cancel`. Protocol actions ship from fuz_app and spread into
152
+ each consumer's `actions` array at registration time (via
163
153
  `protocol_actions` from `actions/protocol.ts`); they should not appear in
164
154
  consumer-owned typed surfaces. Pass `include_protocol_actions: true` only
165
155
  if a consumer genuinely owns protocol actions in their typed API.
166
156
 
167
- **Consumer tiers.** Single-source consumers (zzz) drop into the
168
- helpers and accept the default `* as specs` namespace import. Multi-source
157
+ **Consumer tiers.** Single-source consumers (zzz) drop into the helpers
158
+ and accept the default `* as specs` namespace import. Multi-source
169
159
  consumers (zap, visiones — stitching local specs with
170
160
  `all_admin_action_specs` / `all_role_grant_offer_action_specs` /
171
161
  `all_account_action_specs` / `all_self_service_role_action_specs` from
@@ -201,8 +191,8 @@ parsing, GET vs POST split, `c.json` binding); the
201
191
  auth/validation/dispatch pipeline is shared with the WebSocket dispatcher.
202
192
 
203
193
  **Phase order: 401 → 400 → 403 → handler.** Validate first, authorize
204
- after. The trade-off is that an unauthorized caller sees the validation
205
- step; the alternative ordering (403-before-400) was rejected because
194
+ after. Trade-off: an unauthorized caller sees the validation step. The
195
+ alternative ordering (403-before-400) was rejected because
206
196
  defense-in-depth via attack-surface obscurity is illusory when the surface
207
197
  is published in `library.json` codegen anyway.
208
198
 
@@ -253,18 +243,15 @@ the handler's input / output types to `z.infer<TSpec['input']>` /
253
243
  `z.infer<TSpec['output']>` and tightens `ctx.auth` per the conditional
254
244
  `HandlerForSpec<TSpec>`:
255
245
 
256
- | Spec auth axes | `ctx.auth` |
257
- | ------------------------------------------------------ | ------------------------ |
258
- | `auth.actor === 'required'` | `RequestActorContext` |
259
- | `auth.account === 'required' && auth.actor === 'none'` | `RequestContext` |
260
- | else (public, optional axes) | `RequestContext \| null` |
246
+ - `auth.actor === 'required'` → `ctx.auth: RequestActorContext`.
247
+ - `auth.account === 'required' && auth.actor === 'none'` → `ctx.auth: RequestContext`.
248
+ - else (public, optional axes) → `ctx.auth: RequestContext | null`.
261
249
 
262
- Use at every spec → handler binding site so handler-type errors surface at
263
- the factory call instead of at runtime. The bracketed form
250
+ Use at every spec → handler binding site so handler-type errors surface
251
+ at the factory call instead of at runtime. The bracketed form
264
252
  `[T] extends ['required']` defeats distributive conditionals so a degraded
265
253
  `AuthAxisState` union (when the spec was typed without preserving its
266
- literal) falls through to the loosest tier instead of collapsing to the
267
- narrowest.
254
+ literal) falls through to the loosest tier instead of the narrowest.
268
255
 
269
256
  zzz uses a codegen-driven `Record<Method, Handler>` map for the same
270
257
  narrowing — ideal when handlers are stateless free functions. fuz_app's
@@ -308,10 +295,8 @@ Critical invariant: every action-handler surface applies DEV-only output
308
295
  validation and produces the **same failure mode** — log an error, return
309
296
  the response unchanged, do not throw, do not mutate status.
310
297
 
311
- | Surface | Code location | Hot path under production |
312
- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
313
- | REST bridge | `http/route_spec.ts` — `wrap_output_validation` (applied via `apply_route_specs`; inherited by `create_action_route_spec`) | short-circuit (no parse) |
314
- | HTTP RPC + WebSocket dispatch | `actions/perform_action.ts` — `if (DEV) spec.output.safeParse(output)` inside the shared dispatch core | short-circuit (no parse) |
298
+ - REST bridge `http/route_spec.ts` `wrap_output_validation` (applied via `apply_route_specs`; inherited by `create_action_route_spec`). Production hot path short-circuits (no parse).
299
+ - HTTP RPC + WebSocket dispatch — `actions/perform_action.ts` `if (DEV) spec.output.safeParse(output)` inside the shared dispatch core. Production hot path short-circuits (no parse).
315
300
 
316
301
  Caller-facing `input` schemas are validated **always** (DEV + production)
317
302
  — they're the contract with external callers. Server-authored `output`
@@ -345,11 +330,9 @@ and `allow_fallback: boolean` (default `true`). Explicit
345
330
 
346
331
  ### Transport modules
347
332
 
348
- | Module | Name | Role |
349
- | ---------------------------------- | ------------------------ | ------------------------------------------------------------------------------------ |
350
- | `actions/transports_http.ts` | `frontend_http_rpc` | Thin `fetch` adapter; POST default, GET on `has_side_effects(method) === false` |
351
- | `actions/transports_ws.ts` | `frontend_websocket_rpc` | Thin adapter over `WebsocketRpcConnection` (default impl: `FrontendWebsocketClient`) |
352
- | `actions/transports_ws_backend.ts` | `backend_websocket_rpc` | Server-side WS with session tracking; satisfies `FilterableBroadcastTransport` |
333
+ - `actions/transports_http.ts` `frontend_http_rpc`; thin `fetch` adapter, POST default, GET on `has_side_effects(method) === false`.
334
+ - `actions/transports_ws.ts` `frontend_websocket_rpc`; thin adapter over `WebsocketRpcConnection` (default impl: `FrontendWebsocketClient`).
335
+ - `actions/transports_ws_backend.ts` `backend_websocket_rpc`; server-side WS with session tracking; satisfies `FilterableBroadcastTransport`.
353
336
 
354
337
  `FrontendHttpTransport` synthesizes a JSON-RPC error envelope via
355
338
  `http_status_to_jsonrpc_error_code` on non-OK HTTP; DEV warns on drift
@@ -547,13 +530,12 @@ Two abort signals composed via `AbortSignal.any`:
547
530
  ## Protocol actions (`actions/protocol.ts`)
548
531
 
549
532
  Two shared `{spec, handler}` tuples that every consumer spreads into both
550
- sides' `actions` arrays — disconnect detection and per-request cancel work
551
- identically across every repo without per-consumer ping plumbing.
533
+ sides' `actions` arrays — disconnect detection and per-request cancel
534
+ work identically across every repo without per-consumer ping plumbing.
552
535
 
553
536
  The category is wire-protocol concerns shipped by fuz_app, not consumer
554
- domain logic. Contrast that matters: protocol vs domain. A future
555
- clock-skew probe or reconnect-resume token belongs here; a `payment_charge`
556
- action does not.
537
+ domain logic. Protocol vs domain: a future clock-skew probe or
538
+ reconnect-resume token belongs here; a `payment_charge` action does not.
557
539
 
558
540
  Two const arrays:
559
541
 
@@ -566,8 +548,8 @@ stub), frontend registry only stores specs. Both bundles plus the codegen
566
548
 
567
549
  **Not auto-spread by `create_frontend_rpc_client` or `register_ws_endpoint`** —
568
550
  bundled helpers stay pure factories so the dispatch surface stays
569
- grep-traceable at every consumer registration site and consumers can
570
- override individual protocol actions without an opt-out flag.
551
+ grep-traceable at every registration site and consumers can override
552
+ individual protocol actions without an opt-out flag.
571
553
 
572
554
  ### Individual actions
573
555
 
@@ -645,10 +627,8 @@ the live `ActionEvent` (zzz wires reactive history here).
645
627
 
646
628
  ### Throwing variants
647
629
 
648
- | Helper | Shape | Use at |
649
- | -------------------------- | ------------------------------------- | -------------------------------------------------------------------------- |
650
- | `create_throwing_rpc_call` | `(method, input?) => Promise<T>` | Adapter wiring (e.g. `ui/admin_rpc_adapters.ts`) — method comes from a map |
651
- | `create_throwing_api` | Typed Proxy over `FrontendActionsApi` | Direct call sites — `await api.foo(input)` keeps full inference |
630
+ - `create_throwing_rpc_call` shape `(method, input?) => Promise<T>`. Use at adapter wiring (e.g. `ui/admin_rpc_adapters.ts`) — method comes from a map.
631
+ - `create_throwing_api` typed Proxy over `FrontendActionsApi`. Use at direct call sites — `await api.foo(input)` keeps full inference.
652
632
 
653
633
  **Layered design.** `Result` is the protocol primitive —
654
634
  `create_rpc_client` returns `Result<{value}, {error}>` with no Error
@@ -657,8 +637,7 @@ both shapes share the same underlying transport and call sites pick
657
637
  per-site. `Result` is preferable when the call site inspects
658
638
  `error.data.reason` (no allocation, no try/catch) or when overhead matters
659
639
  (reconnect storms, hot paths). Throwing is preferable when the call site
660
- doesn't inspect — `await api.foo()` reads cleaner than the `if (!r.ok) throw …`
661
- ritual.
640
+ doesn't inspect — `await api.foo()` reads cleaner than `if (!r.ok) throw …`.
662
641
 
663
642
  Hardening on both: only `{code, data}` cross onto the Error, leaving
664
643
  `name` / `stack` as the native Error's own so attacker-shaped
@@ -716,7 +695,7 @@ failure), wraps in `JsonrpcNotification`, sends via the peer's resolved
716
695
  transport. `transport_name` on `peer.default_send_options` pins the target
717
696
  deterministically — no fallback, because broadcast is 1→N over a specific
718
697
  primary transport and "any ready transport" could reach an unexpected
719
- audience. Silently skips when no ready transport.
698
+ audience. Silently skips when none ready.
720
699
 
721
700
  `should_deliver: (identity, method, input) => boolean` — optional
722
701
  per-connection ACL predicate. When set, fans out via
@@ -457,7 +457,7 @@ export interface SpecSource {
457
457
  * @example
458
458
  * ```ts
459
459
  * const sources = [
460
- * {ns: 'tx_specs', module: './action_specs.js', specs: all_tx_action_specs},
460
+ * {ns: 'tx_specs', module: './action_specs.js', specs: all_zap_action_specs},
461
461
  * {ns: 'admin_specs', module: '@fuzdev/fuz_app/auth/admin_action_specs.js', specs: all_admin_action_specs},
462
462
  * ];
463
463
  *
@@ -943,7 +943,7 @@ export type ${type_name} = {
943
943
  * @example
944
944
  * ```ts
945
945
  * const sources = [
946
- * {ns: 'tx_specs', module: './action_specs.js', specs: all_tx_action_specs},
946
+ * {ns: 'tx_specs', module: './action_specs.js', specs: all_zap_action_specs},
947
947
  * {ns: 'admin_specs', module: '@fuzdev/fuz_app/auth/admin_action_specs.js', specs: all_admin_action_specs},
948
948
  * ];
949
949
  *
@@ -56,7 +56,7 @@ export declare const ActionEventData: z.ZodObject<{
56
56
  response: z.ZodNullable<z.ZodUnion<readonly [z.ZodObject<{
57
57
  jsonrpc: z.ZodLiteral<"2.0">;
58
58
  id: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
59
- result: z.ZodObject<{}, z.core.$loose>;
59
+ result: z.ZodJSONSchema;
60
60
  }, z.core.$loose>, z.ZodObject<{
61
61
  jsonrpc: z.ZodLiteral<"2.0">;
62
62
  id: z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;