@fuzdev/fuz_app 0.34.0 → 0.36.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.
@@ -81,7 +81,7 @@ not the runtime):
81
81
  - `get_executor_phases(spec, executor)` — phases a given executor (`'frontend' | 'backend'`) participates in for the spec. Deduplicates via `Set` (handles `initiator: 'both'` overlap).
82
82
  - `get_handler_return_type(spec, phase, imports, path_prefix)` — the TS type a phase handler must return; triggers the `ActionOutputs` import as a side effect.
83
83
  - `generate_phase_handlers(spec, executor, imports, {action_event_type?})` — emits the typed handler-map fragment for one action; consumers compose these into `ActionHandlers` types.
84
- - `generate_actions_api_method_signature(spec, {sync_returns_value?})` — single source of truth for the typed `ActionsApi` method shape. Threads `options?: RpcClientCallOptions` (`{signal?, transport_name?, queue?}`) onto every async method. Consumers must regenerate onto this helperolder inline templates using `get_innermost_type_name` directly drop the options arg.
84
+ - `generate_actions_api_method_signature(spec, {sync_returns_value?})` — single source of truth for the typed `ActionsApi` method shape. Threads `options?: RpcClientCallOptions` (`{signal?, transport_name?, queue?}`) onto every async method `request_response`, `remote_notification`, and async `local_call`and wraps the return in `Promise<Result<...>>`. Notifications were previously emitted as `=> void`, mismatching the runtime (`create_remote_notification_method` returns a Promise that resolves to `Result<{value: void}>`); regenerate consumer typed clients to pick up the corrected shape. Older inline templates using `get_innermost_type_name` directly drop the options arg — also regenerate those.
85
85
  - `create_banner(origin_path)` — gen banner comment.
86
86
  - `to_action_spec_identifier(method)` / `to_action_spec_input_identifier` / `to_action_spec_output_identifier` — naming convention helpers (emit `foo_action_spec` / `foo_action_spec.input` / `foo_action_spec.output`).
87
87
  - `get_innermost_type(schema)` / `get_innermost_type_name(schema)` — unwrap Zod `ZodOptional` / `ZodNullable` / `ZodDefault` / transforms / pipes / prefaults to reach the base schema.
@@ -116,10 +116,16 @@ export declare const get_innermost_type_name: (schema: z.ZodType) => string;
116
116
  * surface evolves in one place when fields like `signal` or `transport_name`
117
117
  * are added to per-call options.
118
118
  *
119
- * Async methods (request_response, async local_call) get an optional second
120
- * `options?: RpcClientCallOptions` arg (`{signal?, transport_name?}`). Sync
121
- * local_call methods omit the options arg — `signal` can't cooperatively
122
- * interrupt a synchronous handler and there's no transport to select.
119
+ * Async methods (`request_response`, `remote_notification`, async
120
+ * `local_call`) get an optional second `options?: RpcClientCallOptions` arg
121
+ * (`{signal?, transport_name?, queue?}`) and a `Promise<Result<...>>` return
122
+ * type. Sync `local_call` methods omit the options arg `signal` can't
123
+ * cooperatively interrupt a synchronous handler and there's no transport to
124
+ * select. `remote_notification` is async because
125
+ * `create_remote_notification_method` returns a Promise that resolves to a
126
+ * `Result<{value: void}>` (success) or `Result<{error}>` (transport send
127
+ * failure). Earlier emit shapes declared notifications as `=> void` —
128
+ * regenerate consumer typed clients to pick up the corrected return.
123
129
  *
124
130
  * Consumers must import `ActionInputs`, `ActionOutputs`, `Result`,
125
131
  * `JsonrpcErrorObject`, and (for async) `RpcClientCallOptions` into the
@@ -1 +1 @@
1
- {"version":3,"file":"action_codegen.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_codegen.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB,OAAO,KAAK,EAAC,eAAe,EAAE,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAOxE;;GAEG;AACH,UAAU,UAAU;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,WAAW,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,aAAa;;IACzB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAa;IAE1D;;;;OAIG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAQrC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAI1C;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI;IAOrD;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI;IAgCtD;;;OAGG;IACH,KAAK,IAAI,MAAM;IAIf;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;;OAGG;IACH,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC;IAIxB;;OAEG;IACH,KAAK,IAAI,IAAI;CAqDb;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM,eAAe,EACrB,UAAU,UAAU,GAAG,SAAS,KAC9B,KAAK,CAAC,gBAAgB,CA4DxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,eAAe,EACrB,OAAO,gBAAgB,EACvB,SAAS,aAAa,EACtB,aAAa,MAAM,KACjB,MAkBF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,eAAe,EACrB,UAAU,UAAU,GAAG,SAAS,EAChC,SAAS,aAAa,EACtB,UAAU;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAC,KACpC,MA4BF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,GAAI,aAAa,MAAM,KAAG,MACU,CAAC;AAG/D,eAAO,MAAM,yBAAyB,GAAI,QAAQ,MAAM,KAAG,MAAiC,CAAC;AAC7F,eAAO,MAAM,+BAA+B,GAAI,QAAQ,MAAM,KAAG,MACpB,CAAC;AAC9C,eAAO,MAAM,gCAAgC,GAAI,QAAQ,MAAM,KAAG,MACpB,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,CAAC,CAAC,OAwBxD,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,MAI3D,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,qCAAqC,GACjD,MAAM,eAAe,EACrB,UAAU;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAC,KACtC,MAmBF,CAAC"}
1
+ {"version":3,"file":"action_codegen.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_codegen.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB,OAAO,KAAK,EAAC,eAAe,EAAE,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAOxE;;GAEG;AACH,UAAU,UAAU;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,WAAW,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,aAAa;;IACzB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAa;IAE1D;;;;OAIG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAQrC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAI1C;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI;IAOrD;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI;IAgCtD;;;OAGG;IACH,KAAK,IAAI,MAAM;IAIf;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;;OAGG;IACH,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC;IAIxB;;OAEG;IACH,KAAK,IAAI,IAAI;CAqDb;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM,eAAe,EACrB,UAAU,UAAU,GAAG,SAAS,KAC9B,KAAK,CAAC,gBAAgB,CA4DxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,eAAe,EACrB,OAAO,gBAAgB,EACvB,SAAS,aAAa,EACtB,aAAa,MAAM,KACjB,MAkBF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,eAAe,EACrB,UAAU,UAAU,GAAG,SAAS,EAChC,SAAS,aAAa,EACtB,UAAU;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAC,KACpC,MA4BF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,GAAI,aAAa,MAAM,KAAG,MACU,CAAC;AAG/D,eAAO,MAAM,yBAAyB,GAAI,QAAQ,MAAM,KAAG,MAAiC,CAAC;AAC7F,eAAO,MAAM,+BAA+B,GAAI,QAAQ,MAAM,KAAG,MACpB,CAAC;AAC9C,eAAO,MAAM,gCAAgC,GAAI,QAAQ,MAAM,KAAG,MACpB,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,CAAC,CAAC,OAwBxD,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,MAI3D,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,qCAAqC,GACjD,MAAM,eAAe,EACrB,UAAU;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAC,KACtC,MAoBF,CAAC"}
@@ -322,10 +322,16 @@ export const get_innermost_type_name = (schema) => {
322
322
  * surface evolves in one place when fields like `signal` or `transport_name`
323
323
  * are added to per-call options.
324
324
  *
325
- * Async methods (request_response, async local_call) get an optional second
326
- * `options?: RpcClientCallOptions` arg (`{signal?, transport_name?}`). Sync
327
- * local_call methods omit the options arg — `signal` can't cooperatively
328
- * interrupt a synchronous handler and there's no transport to select.
325
+ * Async methods (`request_response`, `remote_notification`, async
326
+ * `local_call`) get an optional second `options?: RpcClientCallOptions` arg
327
+ * (`{signal?, transport_name?, queue?}`) and a `Promise<Result<...>>` return
328
+ * type. Sync `local_call` methods omit the options arg `signal` can't
329
+ * cooperatively interrupt a synchronous handler and there's no transport to
330
+ * select. `remote_notification` is async because
331
+ * `create_remote_notification_method` returns a Promise that resolves to a
332
+ * `Result<{value: void}>` (success) or `Result<{error}>` (transport send
333
+ * failure). Earlier emit shapes declared notifications as `=> void` —
334
+ * regenerate consumer typed clients to pick up the corrected return.
329
335
  *
330
336
  * Consumers must import `ActionInputs`, `ActionOutputs`, `Result`,
331
337
  * `JsonrpcErrorObject`, and (for async) `RpcClientCallOptions` into the
@@ -345,7 +351,7 @@ export const generate_actions_api_method_signature = (spec, options) => {
345
351
  const input_param = has_input
346
352
  ? `input${spec.input.safeParse(undefined).success ? '?' : ''}: ActionInputs['${spec.method}']`
347
353
  : 'input?: void';
348
- const is_async = spec.kind === 'request_response' || (spec.kind === 'local_call' && spec.async);
354
+ const is_async = spec.kind === 'request_response' || spec.kind === 'remote_notification' || spec.async;
349
355
  const options_param = is_async ? ', options?: RpcClientCallOptions' : '';
350
356
  const result_return = `Result<{value: ActionOutputs['${spec.method}']}, {error: JsonrpcErrorObject}>`;
351
357
  const return_type = is_async
@@ -77,11 +77,12 @@ export type ThrowingRpcCall = <TOutput = unknown>(method: string, input?: unknow
77
77
  /**
78
78
  * Wrap a typed RPC client so every call returns its unwrapped value or throws.
79
79
  *
80
- * On `{ok: false}`, throws an `Error` with the JSON-RPC error object's
81
- * `{code, message, data}` spread onto it — so catch blocks that inspect
82
- * `err.data?.reason` continue to work. On unknown method, throws a clear
83
- * "rpc method not found" error instead of the cryptic `undefined is not a
84
- * function` that would otherwise surface.
80
+ * On `{ok: false}`, throws an `Error` whose `message` comes from the
81
+ * JSON-RPC error object, plus `{code, data}` as own properties — so
82
+ * catch blocks reading `err.message` / `err.code` / `err.data?.reason`
83
+ * all work. On unknown method, throws a clear "rpc method not found"
84
+ * error instead of the cryptic `undefined is not a function` that
85
+ * would otherwise surface.
85
86
  *
86
87
  * Invariant upheld by `create_rpc_client`: every `{ok: false}` return
87
88
  * carries a well-formed `JsonrpcErrorObject` with `code` + `message`.
@@ -90,8 +91,26 @@ export type ThrowingRpcCall = <TOutput = unknown>(method: string, input?: unknow
90
91
  * `jsonrpc_errors.forbidden()` without a `data` argument produces
91
92
  * `err.data === undefined`.
92
93
  *
93
- * @param api - typed RPC client from `create_rpc_client` (or any Proxy-like
94
- * object mapping method names to `(input) => Promise<Result<T, error>>`)
94
+ * Only `{code, data}` cross onto the thrown Error — `message` flows
95
+ * through the `Error` constructor argument, and `name` / `stack` are
96
+ * left as the Error's own so attacker-shaped `result.error` payloads
97
+ * cannot overwrite them.
98
+ *
99
+ * The mapped-type generic constraint accepts both shapes without a cast:
100
+ * a codegen-derived typed `ActionsApi` (named-method interface, e.g.
101
+ * `{account_verify: (input) => Promise<Result<...>>, ...}`) and a loose
102
+ * `Record<string, (input?: any) => Promise<any> | void>`. Using `keyof TApi`
103
+ * in the constraint avoids the index-signature requirement that would
104
+ * otherwise force consumers to `as unknown as Record<string, …>` their
105
+ * generated client. The `| void` arm tolerates `remote_notification`
106
+ * methods, whose `ActionsApi` signature is `(input) => void` even though
107
+ * `create_remote_notification_method` returns a Promise at runtime — the
108
+ * throwing wrapper is intended for `request_response` calls but must
109
+ * accept mixed `ActionsApi` shapes without forcing a cast at the seam.
110
+ *
111
+ * @param api - typed RPC client from `create_rpc_client` (or any object
112
+ * whose values are all `(input?) => Promise<...> | void` functions —
113
+ * notably the consumer's generated `ActionsApi` interface)
95
114
  */
96
- export declare const create_throwing_rpc_call: (api: Record<string, ((input?: any) => Promise<any>) | undefined>) => ThrowingRpcCall;
115
+ export declare const create_throwing_rpc_call: <TApi extends Record<keyof TApi, (input?: any) => Promise<any> | void>>(api: TApi) => ThrowingRpcCall;
97
116
  //# sourceMappingURL=rpc_client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rpc_client.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/rpc_client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,yBAAyB,CAAC;AAOpE,OAAO,KAAK,EAAC,UAAU,EAAE,qBAAqB,EAAC,MAAM,kBAAkB,CAAC;AACxE,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAGnD;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,aAAa,GAAG,SAAS,CAAC;AAM/E,8EAA8E;AAC9E,MAAM,WAAW,sBAAsB;IACtC,aAAa,EAAE,CAAC,IAAI,EAAE;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,oBAAoB,CAAA;KAAC,KAC5E;QACA,sBAAsB,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;KAC5C,GACD,SAAS,CAAC;CACb;AAED,uCAAuC;AACvC,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,sBAAsB,CAAC;IACpC,kEAAkE;IAClE,OAAO,CAAC,EAAE,sBAAsB,CAAC;IACjC;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;CAC1C;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,GAC7B,SAAS,sBAAsB,KAC7B,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,GAAG,CAgB7C,CAAC;AA2DF;;;;;GAKG;AACH,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;CAAG;AAgItE;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,GAAG,OAAO,EAC/C,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,OAAO,KACX,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,wBAAwB,GACpC,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,KAC9D,eAUF,CAAC"}
1
+ {"version":3,"file":"rpc_client.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/rpc_client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,yBAAyB,CAAC;AAOpE,OAAO,KAAK,EAAC,UAAU,EAAE,qBAAqB,EAAC,MAAM,kBAAkB,CAAC;AACxE,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAGnD;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,aAAa,GAAG,SAAS,CAAC;AAM/E,8EAA8E;AAC9E,MAAM,WAAW,sBAAsB;IACtC,aAAa,EAAE,CAAC,IAAI,EAAE;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,oBAAoB,CAAA;KAAC,KAC5E;QACA,sBAAsB,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;KAC5C,GACD,SAAS,CAAC;CACb;AAED,uCAAuC;AACvC,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,sBAAsB,CAAC;IACpC,kEAAkE;IAClE,OAAO,CAAC,EAAE,sBAAsB,CAAC;IACjC;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;CAC1C;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,GAC7B,SAAS,sBAAsB,KAC7B,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,GAAG,CAgB7C,CAAC;AA2DF;;;;;GAKG;AACH,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;CAAG;AAgItE;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,GAAG,OAAO,EAC/C,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,OAAO,KACX,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,wBAAwB,GACpC,IAAI,SAAS,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,EAErE,KAAK,IAAI,KACP,eAcF,CAAC"}
@@ -171,11 +171,12 @@ const create_remote_notification_method = (peer, environment, spec, actions, tra
171
171
  /**
172
172
  * Wrap a typed RPC client so every call returns its unwrapped value or throws.
173
173
  *
174
- * On `{ok: false}`, throws an `Error` with the JSON-RPC error object's
175
- * `{code, message, data}` spread onto it — so catch blocks that inspect
176
- * `err.data?.reason` continue to work. On unknown method, throws a clear
177
- * "rpc method not found" error instead of the cryptic `undefined is not a
178
- * function` that would otherwise surface.
174
+ * On `{ok: false}`, throws an `Error` whose `message` comes from the
175
+ * JSON-RPC error object, plus `{code, data}` as own properties — so
176
+ * catch blocks reading `err.message` / `err.code` / `err.data?.reason`
177
+ * all work. On unknown method, throws a clear "rpc method not found"
178
+ * error instead of the cryptic `undefined is not a function` that
179
+ * would otherwise surface.
179
180
  *
180
181
  * Invariant upheld by `create_rpc_client`: every `{ok: false}` return
181
182
  * carries a well-formed `JsonrpcErrorObject` with `code` + `message`.
@@ -184,17 +185,39 @@ const create_remote_notification_method = (peer, environment, spec, actions, tra
184
185
  * `jsonrpc_errors.forbidden()` without a `data` argument produces
185
186
  * `err.data === undefined`.
186
187
  *
187
- * @param api - typed RPC client from `create_rpc_client` (or any Proxy-like
188
- * object mapping method names to `(input) => Promise<Result<T, error>>`)
188
+ * Only `{code, data}` cross onto the thrown Error — `message` flows
189
+ * through the `Error` constructor argument, and `name` / `stack` are
190
+ * left as the Error's own so attacker-shaped `result.error` payloads
191
+ * cannot overwrite them.
192
+ *
193
+ * The mapped-type generic constraint accepts both shapes without a cast:
194
+ * a codegen-derived typed `ActionsApi` (named-method interface, e.g.
195
+ * `{account_verify: (input) => Promise<Result<...>>, ...}`) and a loose
196
+ * `Record<string, (input?: any) => Promise<any> | void>`. Using `keyof TApi`
197
+ * in the constraint avoids the index-signature requirement that would
198
+ * otherwise force consumers to `as unknown as Record<string, …>` their
199
+ * generated client. The `| void` arm tolerates `remote_notification`
200
+ * methods, whose `ActionsApi` signature is `(input) => void` even though
201
+ * `create_remote_notification_method` returns a Promise at runtime — the
202
+ * throwing wrapper is intended for `request_response` calls but must
203
+ * accept mixed `ActionsApi` shapes without forcing a cast at the seam.
204
+ *
205
+ * @param api - typed RPC client from `create_rpc_client` (or any object
206
+ * whose values are all `(input?) => Promise<...> | void` functions —
207
+ * notably the consumer's generated `ActionsApi` interface)
189
208
  */
190
209
  export const create_throwing_rpc_call = (api) => {
210
+ const rec = api;
191
211
  return async (method, input) => {
192
- const fn = api[method];
212
+ const fn = rec[method];
193
213
  if (!fn)
194
214
  throw new Error(`rpc method not found: ${method}`);
195
215
  const result = await fn(input);
196
216
  if (!result.ok) {
197
- throw Object.assign(new Error(result.error?.message ?? 'rpc error'), result.error);
217
+ throw Object.assign(new Error(result.error?.message ?? 'rpc error'), {
218
+ code: result.error?.code,
219
+ data: result.error?.data,
220
+ });
198
221
  }
199
222
  return result.value;
200
223
  };
@@ -836,20 +836,25 @@ Options:
836
836
  `all_permit_offer_action_specs: Array<RequestResponseActionSpec>` —
837
837
  codegen-ready registry.
838
838
 
839
- ### `admin_rpc_actions.ts` — combined admin + permit-offer factory
840
-
841
- `create_admin_rpc_actions(deps, options)` spreads
842
- `create_admin_actions` and `create_permit_offer_actions` into a single
843
- `Array<RpcAction>`, so consumers that mount the stock fuz_app admin
844
- surface don't hand-wire the two factories. `roles` is shared between
845
- both factories; `app_settings` flows to admin only; `default_ttl_ms`
846
- and `authorize` flow to permit-offer only; `notification_sender` is
847
- wired through to permit-offer (admin ignores it).
848
-
849
- `AdminRpcActionsOptions` composes `AdminActionOptions` +
850
- `PermitOfferActionOptions`. `AdminRpcActionsDeps` is the same shape as
851
- `PermitOfferActionDeps` `log`, `on_audit_event`, optional
852
- `notification_sender`.
839
+ ### `standard_rpc_actions.ts` — combined admin + permit-offer + account factory
840
+
841
+ `create_standard_rpc_actions(deps, options)` spreads
842
+ `create_admin_actions`, `create_permit_offer_actions`, and
843
+ `create_account_actions` into a single `Array<RpcAction>` the
844
+ canonical fuz_app "standard" RPC surface (25 actions with
845
+ `app_settings` wired, 23 without). Consumers that want a narrower
846
+ surface drop down to the per-domain factories directly.
847
+
848
+ Option routing: `roles` is shared between admin and permit-offer;
849
+ `app_settings` flows to admin only; `default_ttl_ms` and `authorize`
850
+ flow to permit-offer only; `max_tokens` flows to account only;
851
+ `notification_sender` is wired through to permit-offer (admin +
852
+ account ignore it).
853
+
854
+ `StandardRpcActionsOptions` composes `AdminActionOptions` +
855
+ `PermitOfferActionOptions` + `AccountActionOptions`.
856
+ `StandardRpcActionsDeps` is the same shape as `PermitOfferActionDeps`
857
+ — `log`, `on_audit_event`, optional `notification_sender`.
853
858
 
854
859
  Pair this with `create_app_server`'s `rpc_endpoints` factory form
855
860
  (`(ctx) => Array<RpcEndpointSpec>`) so the combined action list gets
@@ -858,6 +863,15 @@ endpoint via `create_rpc_endpoint`, so consumers don't need to mount it
858
863
  again in `create_route_specs`. See `../../../docs/usage.md` §Server
859
864
  Assembly.
860
865
 
866
+ Pre-bundle consumers spread `create_admin_actions` and
867
+ `create_permit_offer_actions` separately, then also
868
+ `create_account_actions`. The bundled helper replaces all three —
869
+ bundling account actions into the "standard" surface is deliberate:
870
+ the admin integration suite exercises `account_token_create` /
871
+ `account_token_revoke` (cross-account isolation scenarios), so a
872
+ consumer wiring the admin surface without account actions will hit
873
+ `method not found` on first admin-suite run.
874
+
861
875
  ### `account_action_specs.ts` + `account_actions.ts` — seven self-service RPC actions
862
876
 
863
877
  Counterpart to `account_routes.ts`. Cookie-lifecycle flows (`login`,
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Combined admin + permit-offer + account RPC actions for fuz_app consumers.
3
+ *
4
+ * The canonical "standard" RPC surface: every stock fuz_app RPC action a
5
+ * typical web consumer wants on one endpoint. Consumers that want a
6
+ * narrower surface drop down to the per-domain factories directly
7
+ * (`create_admin_actions` / `create_permit_offer_actions` /
8
+ * `create_account_actions`).
9
+ *
10
+ * Option routing: shared `roles` flows to both admin and permit-offer;
11
+ * `app_settings` goes to admin only; `default_ttl_ms` and `authorize` go
12
+ * to permit-offer only; `max_tokens` goes to account only;
13
+ * `notification_sender` reaches permit-offer transparently (admin + account
14
+ * ignore it).
15
+ *
16
+ * Paired with `create_admin_rpc_adapters` on the UI side.
17
+ *
18
+ * @module
19
+ */
20
+ import { type AdminActionOptions } from './admin_actions.js';
21
+ import { type PermitOfferActionDeps, type PermitOfferActionOptions } from './permit_offer_actions.js';
22
+ import { type AccountActionOptions } from './account_actions.js';
23
+ import type { RpcAction } from '../actions/action_rpc.js';
24
+ /**
25
+ * Options for `create_standard_rpc_actions`.
26
+ *
27
+ * Composes `AdminActionOptions` (`roles`, `app_settings`),
28
+ * `PermitOfferActionOptions` (`roles`, `default_ttl_ms`, `authorize`), and
29
+ * `AccountActionOptions` (`max_tokens`). `roles` is shared between admin
30
+ * and permit-offer — the caller supplies it once and the helper threads
31
+ * the same reference to both.
32
+ */
33
+ export interface StandardRpcActionsOptions extends AdminActionOptions, PermitOfferActionOptions, AccountActionOptions {
34
+ }
35
+ /**
36
+ * Dependencies for `create_standard_rpc_actions`.
37
+ *
38
+ * Same shape as `PermitOfferActionDeps` — `log`, `on_audit_event`, and an
39
+ * optional `notification_sender` for permit-offer WS fan-out. Admin and
40
+ * account factories only read `log` + `on_audit_event`; the extra field
41
+ * is harmless.
42
+ */
43
+ export type StandardRpcActionsDeps = PermitOfferActionDeps;
44
+ /**
45
+ * Build the combined admin + permit-offer + account RPC action set.
46
+ *
47
+ * Spreads `create_admin_actions(deps, {roles, app_settings})`,
48
+ * `create_permit_offer_actions(deps, {roles, default_ttl_ms, authorize})`,
49
+ * and `create_account_actions(deps, {max_tokens})`. The shared `roles`
50
+ * option flows to admin + permit-offer.
51
+ *
52
+ * @param deps - stateless capabilities (log, on_audit_event, optional notification_sender)
53
+ * @param options - role schema, optional app-settings ref, permit-offer config, account config
54
+ * @returns RPC actions to pass as `rpc_endpoints` or spread into `create_rpc_endpoint`
55
+ */
56
+ export declare const create_standard_rpc_actions: (deps: StandardRpcActionsDeps, options?: StandardRpcActionsOptions) => Array<RpcAction>;
57
+ //# sourceMappingURL=standard_rpc_actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standard_rpc_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/standard_rpc_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAuB,KAAK,kBAAkB,EAAC,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAEN,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAyB,KAAK,oBAAoB,EAAC,MAAM,sBAAsB,CAAC;AACvF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAExD;;;;;;;;GAQG;AACH,MAAM,WAAW,yBAChB,SAAQ,kBAAkB,EAAE,wBAAwB,EAAE,oBAAoB;CAAG;AAE9E;;;;;;;GAOG;AACH,MAAM,MAAM,sBAAsB,GAAG,qBAAqB,CAAC;AAE3D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,2BAA2B,GACvC,MAAM,sBAAsB,EAC5B,UAAS,yBAA8B,KACrC,KAAK,CAAC,SAAS,CAIjB,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Combined admin + permit-offer + account RPC actions for fuz_app consumers.
3
+ *
4
+ * The canonical "standard" RPC surface: every stock fuz_app RPC action a
5
+ * typical web consumer wants on one endpoint. Consumers that want a
6
+ * narrower surface drop down to the per-domain factories directly
7
+ * (`create_admin_actions` / `create_permit_offer_actions` /
8
+ * `create_account_actions`).
9
+ *
10
+ * Option routing: shared `roles` flows to both admin and permit-offer;
11
+ * `app_settings` goes to admin only; `default_ttl_ms` and `authorize` go
12
+ * to permit-offer only; `max_tokens` goes to account only;
13
+ * `notification_sender` reaches permit-offer transparently (admin + account
14
+ * ignore it).
15
+ *
16
+ * Paired with `create_admin_rpc_adapters` on the UI side.
17
+ *
18
+ * @module
19
+ */
20
+ import { create_admin_actions } from './admin_actions.js';
21
+ import { create_permit_offer_actions, } from './permit_offer_actions.js';
22
+ import { create_account_actions } from './account_actions.js';
23
+ /**
24
+ * Build the combined admin + permit-offer + account RPC action set.
25
+ *
26
+ * Spreads `create_admin_actions(deps, {roles, app_settings})`,
27
+ * `create_permit_offer_actions(deps, {roles, default_ttl_ms, authorize})`,
28
+ * and `create_account_actions(deps, {max_tokens})`. The shared `roles`
29
+ * option flows to admin + permit-offer.
30
+ *
31
+ * @param deps - stateless capabilities (log, on_audit_event, optional notification_sender)
32
+ * @param options - role schema, optional app-settings ref, permit-offer config, account config
33
+ * @returns RPC actions to pass as `rpc_endpoints` or spread into `create_rpc_endpoint`
34
+ */
35
+ export const create_standard_rpc_actions = (deps, options = {}) => [
36
+ ...create_admin_actions(deps, options),
37
+ ...create_permit_offer_actions(deps, options),
38
+ ...create_account_actions(deps, options),
39
+ ];
@@ -139,7 +139,7 @@ export interface AppServerOptions {
139
139
  * `(ctx: AppServerContext) => Array<RpcEndpointSpec>` (evaluated after the
140
140
  * server context is assembled). Use the factory form when action lists
141
141
  * depend on `ctx.deps` / `ctx.app_settings` — e.g.
142
- * `create_admin_rpc_actions(ctx.deps, {app_settings: ctx.app_settings})`.
142
+ * `create_standard_rpc_actions(ctx.deps, {app_settings: ctx.app_settings})`.
143
143
  */
144
144
  rpc_endpoints?: Array<RpcEndpointSpec> | ((context: AppServerContext) => Array<RpcEndpointSpec>);
145
145
  /** Env schema for surface generation. Pass `z.object({})` when there are no env vars beyond `BaseServerEnv`. */
@@ -267,8 +267,13 @@ Walks Zod schemas to generate valid values for adversarial/round-trip tests.
267
267
  - `generate_valid_value(field, field_schema)` — base-type switch
268
268
  producing a valid sample (UUIDs → nil UUID, strings → `'xxxxxxxxxx'`,
269
269
  numbers → `1`, objects → recurse, enums → first entry, etc.).
270
- Falls back through `/` + URL prefixes if a branded-string refinement
271
- rejects the plain base.
270
+ For branded-string refinements, walks a fallback chain synthesized
271
+ from the `pattern` string the JSON Schema representation exposes:
272
+ fixed-length hex (`^[0-9a-f]{N}$` — blake3 / sha256 / md5 digests;
273
+ `0`.repeat(N)), prefix-lengthed slug (`^<prefix>_[A-Za-z0-9_-]{N}$`
274
+ — `ApiTokenId`-style ids; `<prefix>_` + `x`.repeat(N)), absolute
275
+ path prefix, URL prefix. First candidate that `safeParse` accepts
276
+ is used.
272
277
  - `resolve_valid_path(path, params_schema?)` — swaps `:param` for
273
278
  valid-format values (nil UUID for UUID params, `test_param` otherwise).
274
279
  - `generate_valid_body(input_schema) => Record<string, unknown> | undefined` —
@@ -546,7 +551,7 @@ the same `RpcEndpointsSuiteOption` union every DB-backed suite accepts
546
551
  `rpc_round_trip`, `sse_round_trip`). Prefer the factory form: it forwards
547
552
  raw to `app_options.rpc_endpoints` so `create_app_server` resolves it per-test
548
553
  with the real ctx — the only way action handlers can close over
549
- `ctx.deps` / `ctx.app_settings` (e.g. `create_admin_rpc_actions(ctx.deps,
554
+ `ctx.deps` / `ctx.app_settings` (e.g. `create_standard_rpc_actions(ctx.deps,
550
555
  {app_settings: ctx.app_settings})`). Factory must return the same endpoint
551
556
  `path` regardless of ctx — `resolve_rpc_endpoints_for_setup` invokes it
552
557
  once with a stub ctx for path lookup and `create_app_server` invokes it
@@ -558,9 +563,23 @@ revoke-all plus audit-log list/history are all RPC-only since the
558
563
  2026-04-22 migration. A confusing test failure mid-suite is worse than a
559
564
  clear setup error.
560
565
 
566
+ The suite also exercises `account_token_create` (and
567
+ `account_token_revoke`) for the cross-admin isolation + audit-trail
568
+ scenarios. Wire the account actions alongside admin / permit-offer —
569
+ the easiest path is `create_standard_rpc_actions`, which bundles all
570
+ three. Consumers that only wire admin will hit `method not found:
571
+ account_token_create` on first run.
572
+
561
573
  Error-coverage scope is narrowed to the REST suffixes still on the
562
- admin surface (`/sessions`, `/audit-log/stream`); the RPC surface is
563
- covered by `describe_rpc_round_trip_tests`.
574
+ admin surface (`/audit-log/stream`); the RPC surface is covered by
575
+ `describe_rpc_round_trip_tests`. Post-RPC-migration that surface is
576
+ 0–1 routes — when the scoped count is ≤1, the `afterAll` hook logs
577
+ `[error coverage] skipped admin REST coverage assertion — …` and
578
+ does not fail. The 20% `DEFAULT_INTEGRATION_ERROR_COVERAGE` baseline
579
+ is a REST-era threshold; the RPC surface has its own coverage via
580
+ `describe_rpc_round_trip_tests`. TODO: move this error-coverage
581
+ collector to the RPC round-trip suite entirely and delete this skip
582
+ branch.
564
583
 
565
584
  ### `audit_completeness.ts` — `describe_audit_completeness_tests`
566
585
 
@@ -24,7 +24,7 @@ export interface StandardAdminIntegrationTestOptions {
24
24
  * `(ctx: AppServerContext) => Array<RpcEndpointSpec>` — the factory form
25
25
  * is required when action handlers must close over the per-test
26
26
  * `ctx.app_settings` / `ctx.deps` (e.g. the canonical
27
- * `create_admin_rpc_actions(ctx.deps, {app_settings: ctx.app_settings})`
27
+ * `create_standard_rpc_actions(ctx.deps, {app_settings: ctx.app_settings})`
28
28
  * pattern). The factory must return the same endpoint `path` regardless
29
29
  * of ctx — it is invoked once at setup with a stub ctx for path lookup
30
30
  * and again per-test by `create_app_server` for live dispatch.
@@ -1 +1 @@
1
- {"version":3,"file":"admin_integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/admin_integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA+B7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAErD,OAAO,EAA0B,KAAK,gBAAgB,EAAC,MAAM,wBAAwB,CAAC;AAEtF,OAAO,EAA6C,KAAK,eAAe,EAAC,MAAM,iBAAiB,CAAC;AACjG,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AASjB,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAqB1B;;GAEG;AACH,MAAM,WAAW,mCAAmC;IACnD,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,4GAA4G;IAC5G,KAAK,EAAE,gBAAgB,CAAC;IACxB;;;;;;;;;;;;OAYG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B;;;OAGG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAChC;AAgCD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,mCAAmC,KAC1C,IAu0BF,CAAC"}
1
+ {"version":3,"file":"admin_integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/admin_integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA+B7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAErD,OAAO,EAA0B,KAAK,gBAAgB,EAAC,MAAM,wBAAwB,CAAC;AAEtF,OAAO,EAA6C,KAAK,eAAe,EAAC,MAAM,iBAAiB,CAAC;AACjG,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AASjB,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAqB1B;;GAEG;AACH,MAAM,WAAW,mCAAmC;IACnD,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,4GAA4G;IAC5G,KAAK,EAAE,gBAAgB,CAAC;IACxB;;;;;;;;;;;;OAYG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B;;;OAGG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAChC;AAgCD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,mCAAmC,KAC1C,IAy1BF,CAAC"}
@@ -101,10 +101,30 @@ export const describe_standard_admin_integration_tests = (options) => {
101
101
  // admin REST route remaining is the optional
102
102
  // `GET /audit-log/stream` SSE, plus the shared RPC endpoint
103
103
  // path itself (admin methods live behind spec-level role auth).
104
+ // The `/audit-log/stream` suffix tracks the hardcoded path in
105
+ // `auth/audit_log_routes.ts` — if consumers ever need to mount
106
+ // the audit SSE at a different suffix, promote this to an
107
+ // `audit_log_path_suffix` option on
108
+ // `StandardAdminIntegrationTestOptions`.
104
109
  const admin_routes = captured_route_specs.filter((s) => s.path.endsWith('/audit-log/stream') &&
105
110
  s.auth.type === 'role' &&
106
111
  s.auth.role === 'admin');
107
- assert_error_coverage(error_collector, admin_routes.length > 0 ? admin_routes : captured_route_specs, { min_coverage: DEFAULT_INTEGRATION_ERROR_COVERAGE });
112
+ // Adaptive threshold: when the scoped admin REST surface is
113
+ // effectively empty (0–1 routes, typical post-RPC-migration),
114
+ // the 20% baseline is meaningless — a single SSE route that
115
+ // can't be exercised against an error schema drops the ratio
116
+ // to 0.0%. Log an informational skip instead of asserting.
117
+ // The admin RPC surface is covered by
118
+ // `describe_rpc_round_trip_tests`, not this collector.
119
+ if (admin_routes.length <= 1) {
120
+ console.log(`[error coverage] skipped admin REST coverage assertion — ` +
121
+ `scoped surface has ${admin_routes.length} route(s); ` +
122
+ `admin RPC surface is covered by describe_rpc_round_trip_tests`);
123
+ return;
124
+ }
125
+ assert_error_coverage(error_collector, admin_routes, {
126
+ min_coverage: DEFAULT_INTEGRATION_ERROR_COVERAGE,
127
+ });
108
128
  }
109
129
  });
110
130
  /** Make request headers for a given session cookie. */
@@ -32,7 +32,7 @@ export interface StandardIntegrationTestOptions {
32
32
  * `(ctx: AppServerContext) => Array<RpcEndpointSpec>` — the factory form
33
33
  * is required when action handlers must close over the per-test
34
34
  * `ctx.app_settings` / `ctx.deps` (e.g. the canonical
35
- * `create_admin_rpc_actions(ctx.deps, {app_settings: ctx.app_settings})`
35
+ * `create_standard_rpc_actions(ctx.deps, {app_settings: ctx.app_settings})`
36
36
  * pattern). The factory must return the same endpoint `path` regardless
37
37
  * of ctx — it is invoked once at setup with a stub ctx for path lookup
38
38
  * and again per-test by `create_app_server` for live dispatch.
@@ -11,7 +11,7 @@ import type { SessionOptions } from '../auth/session_cookie.js';
11
11
  * a factory that takes an `AppServerContext` and returns endpoint specs. The
12
12
  * factory form is required when action handlers must close over the
13
13
  * per-test `ctx.app_settings` / `ctx.deps` (e.g. the canonical
14
- * `create_admin_rpc_actions(ctx.deps, {app_settings: ctx.app_settings})`
14
+ * `create_standard_rpc_actions(ctx.deps, {app_settings: ctx.app_settings})`
15
15
  * pattern). `create_app_server` resolves either shape natively; test helpers
16
16
  * forward the raw value to `app_options.rpc_endpoints` for live dispatch.
17
17
  */
@@ -28,9 +28,9 @@ export type RpcEndpointsSuiteOption = Array<RpcEndpointSpec> | ((ctx: AppServerC
28
28
  *
29
29
  * Safe as long as the factory is pure with respect to the endpoint `path`
30
30
  * and the action `spec.method` list — the canonical helpers
31
- * (`create_admin_rpc_actions`, `create_account_actions`, etc.) are. Factories
32
- * that return a different `path` based on `ctx` will produce a setup/runtime
33
- * mismatch; don't do that.
31
+ * (`create_standard_rpc_actions`, `create_admin_actions`, `create_account_actions`,
32
+ * etc.) are. Factories that return a different `path` based on `ctx` will
33
+ * produce a setup/runtime mismatch; don't do that.
34
34
  */
35
35
  export declare const resolve_rpc_endpoints_for_setup: (rpc_endpoints: RpcEndpointsSuiteOption, session_options: SessionOptions<string>) => Array<RpcEndpointSpec>;
36
36
  /**
@@ -24,9 +24,9 @@ import { create_stub_app_server_context } from './stubs.js';
24
24
  *
25
25
  * Safe as long as the factory is pure with respect to the endpoint `path`
26
26
  * and the action `spec.method` list — the canonical helpers
27
- * (`create_admin_rpc_actions`, `create_account_actions`, etc.) are. Factories
28
- * that return a different `path` based on `ctx` will produce a setup/runtime
29
- * mismatch; don't do that.
27
+ * (`create_standard_rpc_actions`, `create_admin_actions`, `create_account_actions`,
28
+ * etc.) are. Factories that return a different `path` based on `ctx` will
29
+ * produce a setup/runtime mismatch; don't do that.
30
30
  */
31
31
  export const resolve_rpc_endpoints_for_setup = (rpc_endpoints, session_options) => typeof rpc_endpoints === 'function'
32
32
  ? rpc_endpoints(create_stub_app_server_context(session_options))
@@ -1 +1 @@
1
- {"version":3,"file":"schema_generators.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/schema_generators.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,EAKN,KAAK,YAAY,EACjB,MAAM,yBAAyB,CAAC;AAIjC;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAI,cAAc,CAAC,CAAC,OAAO,KAAG,MAAM,GAAG,IAShE,CAAC;AAqDF,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,GAAI,OAAO,YAAY,EAAE,cAAc,CAAC,CAAC,OAAO,KAAG,OAkDnF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,gBAAgB,CAAC,CAAC,SAAS,KAAG,MAa9E,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAC/B,cAAc,CAAC,CAAC,OAAO,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAkB5B,CAAC"}
1
+ {"version":3,"file":"schema_generators.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/schema_generators.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,EAKN,KAAK,YAAY,EACjB,MAAM,yBAAyB,CAAC;AAIjC;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAI,cAAc,CAAC,CAAC,OAAO,KAAG,MAAM,GAAG,IAShE,CAAC;AA+FF,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,GAAI,OAAO,YAAY,EAAE,cAAc,CAAC,CAAC,OAAO,KAAG,OAkDnF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,gBAAgB,CAAC,CAAC,SAAS,KAAG,MAa9E,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAC/B,cAAc,CAAC,CAAC,OAAO,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAkB5B,CAAC"}
@@ -43,6 +43,43 @@ const generate_hex_pattern_value = (pattern) => {
43
43
  return null;
44
44
  return '0'.repeat(n);
45
45
  };
46
+ /**
47
+ * Extract a candidate value from a JSON Schema `pattern` when the shape is a
48
+ * prefix-lengthed slug — a fixed literal prefix followed by `_` and a
49
+ * base64url-style character class of fixed length. Covers `ApiTokenId`
50
+ * (`tok_[A-Za-z0-9_-]{12}`) and any similarly-shaped branded id.
51
+ *
52
+ * Returns the prefix followed by the right number of `x` characters (which
53
+ * satisfy every base64url-style character class). Returns `null` when the
54
+ * pattern doesn't match the expected shape.
55
+ *
56
+ * Coverage today:
57
+ * - Prefix must start with a letter and be alphanumeric (e.g. `tok_`, `ses_`).
58
+ * - Character class must be exactly `[A-Za-z0-9_-]` (Zod passes the regex
59
+ * source through verbatim — no character-class reordering).
60
+ * - Fixed-length quantifier `{N}`.
61
+ *
62
+ * Known gaps (will fall through to the absolute-path / URL candidates or
63
+ * the base `xxxxxxxxxx` string — may or may not satisfy the refinement):
64
+ * - Digit-only classes (e.g. `^ord_\d{8}$`) — `x`-fill fails.
65
+ * - Base64 with `+/=` (e.g. `^b64_[A-Za-z0-9+/=]{N}$`) — character class
66
+ * doesn't match the detection regex. `x`-fill would still satisfy the
67
+ * refinement if the detection were widened.
68
+ * - No-prefix fixed-length slugs (e.g. `^[A-Za-z0-9_-]{43}$` — daemon
69
+ * token shape) are not matched here; see `generate_hex_pattern_value`
70
+ * for the hex variant.
71
+ * Widen the detection regex when a new branded shape surfaces.
72
+ */
73
+ const generate_prefix_slug_pattern_value = (pattern) => {
74
+ const match = /^\^([A-Za-z][A-Za-z0-9]*)_\[A-Za-z0-9_-\]\{(\d+)\}\$$/.exec(pattern);
75
+ if (!match)
76
+ return null;
77
+ const prefix = match[1];
78
+ const n = Number(match[2]);
79
+ if (!Number.isInteger(n) || n <= 0)
80
+ return null;
81
+ return `${prefix}_${'x'.repeat(n)}`;
82
+ };
46
83
  /** Generate a string that satisfies minLength/maxLength/pattern constraints via JSON Schema. */
47
84
  const generate_valid_string = (field_schema) => {
48
85
  let min_length = 0;
@@ -72,6 +109,12 @@ const generate_valid_string = (field_schema) => {
72
109
  if (hex !== null && field_schema.safeParse(hex).success)
73
110
  return hex;
74
111
  }
112
+ // Prefix-lengthed slug refinement (e.g. ApiTokenId: tok_[A-Za-z0-9_-]{12})
113
+ if (pattern) {
114
+ const slug = generate_prefix_slug_pattern_value(pattern);
115
+ if (slug !== null && field_schema.safeParse(slug).success)
116
+ return slug;
117
+ }
75
118
  // Absolute path refinement (e.g. DiskfilePath)
76
119
  const with_slash = '/' + base;
77
120
  if (field_schema.safeParse(with_slash).success)
@@ -4,7 +4,16 @@
4
4
  * Bridges a typed `rpc_call`-shaped function to the four narrow admin RPC
5
5
  * interfaces the state classes consume — `AdminAccountsRpc`,
6
6
  * `AdminInvitesRpc`, `AuditLogRpc`, `AppSettingsRpc`. Two calls at the
7
- * admin shell layout wire everything:
7
+ * admin shell layout wire everything.
8
+ *
9
+ * Intentionally admin-only despite the backend-side
10
+ * `create_standard_rpc_actions` rename (admin + permit-offer + account).
11
+ * Account-surface methods flow through `account_sessions_rpc_context`
12
+ * (wired at the self-service layout), and permit-offer methods that
13
+ * surface in the admin UI (`permit_offer_create`, `permit_revoke`,
14
+ * `permit_offer_retract`) live inside the `AdminAccountsRpc` interface —
15
+ * they belong to the admin UX, not a separate wire pairing. The UI side
16
+ * and backend factory names diverge by design.
8
17
  *
9
18
  * ```ts
10
19
  * import {create_throwing_rpc_call} from '@fuzdev/fuz_app/actions/rpc_client.js';
@@ -1 +1 @@
1
- {"version":3,"file":"admin_rpc_adapters.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/admin_rpc_adapters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAA6B,KAAK,gBAAgB,EAAC,MAAM,kCAAkC,CAAC;AACnG,OAAO,EAA4B,KAAK,eAAe,EAAC,MAAM,iCAAiC,CAAC;AAChG,OAAO,EAAwB,KAAK,WAAW,EAAC,MAAM,6BAA6B,CAAC;AACpF,OAAO,EAA2B,KAAK,cAAc,EAAC,MAAM,gCAAgC,CAAC;AAE7F;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GAAG,eAAe,CAAC;AAE3C,sEAAsE;AACtE,MAAM,WAAW,gBAAgB;IAChC,cAAc,EAAE,gBAAgB,CAAC;IACjC,aAAa,EAAE,eAAe,CAAC;IAC/B,SAAS,EAAE,WAAW,CAAC;IACvB,YAAY,EAAE,cAAc,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,yBAAyB,GAAI,UAAU,YAAY,KAAG,gBAuBjE,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GAAI,UAAU,gBAAgB,KAAG,IAKvE,CAAC"}
1
+ {"version":3,"file":"admin_rpc_adapters.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/admin_rpc_adapters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAA6B,KAAK,gBAAgB,EAAC,MAAM,kCAAkC,CAAC;AACnG,OAAO,EAA4B,KAAK,eAAe,EAAC,MAAM,iCAAiC,CAAC;AAChG,OAAO,EAAwB,KAAK,WAAW,EAAC,MAAM,6BAA6B,CAAC;AACpF,OAAO,EAA2B,KAAK,cAAc,EAAC,MAAM,gCAAgC,CAAC;AAE7F;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GAAG,eAAe,CAAC;AAE3C,sEAAsE;AACtE,MAAM,WAAW,gBAAgB;IAChC,cAAc,EAAE,gBAAgB,CAAC;IACjC,aAAa,EAAE,eAAe,CAAC;IAC/B,SAAS,EAAE,WAAW,CAAC;IACvB,YAAY,EAAE,cAAc,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,yBAAyB,GAAI,UAAU,YAAY,KAAG,gBAuBjE,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GAAI,UAAU,gBAAgB,KAAG,IAKvE,CAAC"}
@@ -4,7 +4,16 @@
4
4
  * Bridges a typed `rpc_call`-shaped function to the four narrow admin RPC
5
5
  * interfaces the state classes consume — `AdminAccountsRpc`,
6
6
  * `AdminInvitesRpc`, `AuditLogRpc`, `AppSettingsRpc`. Two calls at the
7
- * admin shell layout wire everything:
7
+ * admin shell layout wire everything.
8
+ *
9
+ * Intentionally admin-only despite the backend-side
10
+ * `create_standard_rpc_actions` rename (admin + permit-offer + account).
11
+ * Account-surface methods flow through `account_sessions_rpc_context`
12
+ * (wired at the self-service layout), and permit-offer methods that
13
+ * surface in the admin UI (`permit_offer_create`, `permit_revoke`,
14
+ * `permit_offer_retract`) live inside the `AdminAccountsRpc` interface —
15
+ * they belong to the admin UX, not a separate wire pairing. The UI side
16
+ * and backend factory names diverge by design.
8
17
  *
9
18
  * ```ts
10
19
  * import {create_throwing_rpc_call} from '@fuzdev/fuz_app/actions/rpc_client.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzdev/fuz_app",
3
- "version": "0.34.0",
3
+ "version": "0.36.0",
4
4
  "description": "fullstack app library",
5
5
  "glyph": "🗝",
6
6
  "logo": "logo.svg",
@@ -1,49 +0,0 @@
1
- /**
2
- * Combined admin + permit-offer RPC actions for fuz_app consumers.
3
- *
4
- * Consumers that want the stock fuz_app admin surface spread into their
5
- * JSON-RPC endpoint import this helper instead of hand-wiring
6
- * `create_admin_actions` + `create_permit_offer_actions`. The shared `roles`
7
- * schema flows to both factories; `app_settings` goes to admin only;
8
- * `default_ttl_ms` and `authorize` go to permit-offer only;
9
- * `notification_sender` reaches permit-offer transparently (admin ignores it).
10
- *
11
- * Paired with `create_admin_rpc_adapters` on the UI side — same "admin RPC
12
- * surface" concept expressed on each wire endpoint.
13
- *
14
- * @module
15
- */
16
- import { type AdminActionOptions } from './admin_actions.js';
17
- import { type PermitOfferActionDeps, type PermitOfferActionOptions } from './permit_offer_actions.js';
18
- import type { RpcAction } from '../actions/action_rpc.js';
19
- /**
20
- * Options for `create_admin_rpc_actions`.
21
- *
22
- * Composes `AdminActionOptions` (`roles`, `app_settings`) with
23
- * `PermitOfferActionOptions` (`roles`, `default_ttl_ms`, `authorize`). `roles`
24
- * is shared between both factories — the caller supplies it once and the
25
- * helper threads the same reference to both.
26
- */
27
- export interface AdminRpcActionsOptions extends AdminActionOptions, PermitOfferActionOptions {
28
- }
29
- /**
30
- * Dependencies for `create_admin_rpc_actions`.
31
- *
32
- * Same shape as `PermitOfferActionDeps` — `log`, `on_audit_event`, and an
33
- * optional `notification_sender` for permit-offer WS fan-out. The admin
34
- * factory only reads `log` + `on_audit_event`; the extra field is harmless.
35
- */
36
- export type AdminRpcActionsDeps = PermitOfferActionDeps;
37
- /**
38
- * Build the combined admin + permit-offer RPC action set.
39
- *
40
- * Spreads `create_admin_actions(deps, {roles, app_settings})` and
41
- * `create_permit_offer_actions(deps, {roles, default_ttl_ms, authorize})`.
42
- * The shared `roles` option flows to both.
43
- *
44
- * @param deps - stateless capabilities (log, on_audit_event, optional notification_sender)
45
- * @param options - role schema, optional app-settings ref, permit-offer TTL and authorize
46
- * @returns RPC actions to pass as `rpc_endpoints` or spread into `create_rpc_endpoint`
47
- */
48
- export declare const create_admin_rpc_actions: (deps: AdminRpcActionsDeps, options?: AdminRpcActionsOptions) => Array<RpcAction>;
49
- //# sourceMappingURL=admin_rpc_actions.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"admin_rpc_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/admin_rpc_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAuB,KAAK,kBAAkB,EAAC,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAEN,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAExD;;;;;;;GAOG;AACH,MAAM,WAAW,sBAAuB,SAAQ,kBAAkB,EAAE,wBAAwB;CAAG;AAE/F;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,CAAC;AAExD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GACpC,MAAM,mBAAmB,EACzB,UAAS,sBAA2B,KAClC,KAAK,CAAC,SAAS,CAGjB,CAAC"}
@@ -1,32 +0,0 @@
1
- /**
2
- * Combined admin + permit-offer RPC actions for fuz_app consumers.
3
- *
4
- * Consumers that want the stock fuz_app admin surface spread into their
5
- * JSON-RPC endpoint import this helper instead of hand-wiring
6
- * `create_admin_actions` + `create_permit_offer_actions`. The shared `roles`
7
- * schema flows to both factories; `app_settings` goes to admin only;
8
- * `default_ttl_ms` and `authorize` go to permit-offer only;
9
- * `notification_sender` reaches permit-offer transparently (admin ignores it).
10
- *
11
- * Paired with `create_admin_rpc_adapters` on the UI side — same "admin RPC
12
- * surface" concept expressed on each wire endpoint.
13
- *
14
- * @module
15
- */
16
- import { create_admin_actions } from './admin_actions.js';
17
- import { create_permit_offer_actions, } from './permit_offer_actions.js';
18
- /**
19
- * Build the combined admin + permit-offer RPC action set.
20
- *
21
- * Spreads `create_admin_actions(deps, {roles, app_settings})` and
22
- * `create_permit_offer_actions(deps, {roles, default_ttl_ms, authorize})`.
23
- * The shared `roles` option flows to both.
24
- *
25
- * @param deps - stateless capabilities (log, on_audit_event, optional notification_sender)
26
- * @param options - role schema, optional app-settings ref, permit-offer TTL and authorize
27
- * @returns RPC actions to pass as `rpc_endpoints` or spread into `create_rpc_endpoint`
28
- */
29
- export const create_admin_rpc_actions = (deps, options = {}) => [
30
- ...create_admin_actions(deps, options),
31
- ...create_permit_offer_actions(deps, options),
32
- ];