@fuzdev/fuz_app 0.31.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.
@@ -65,4 +65,33 @@ export declare const create_rpc_client: (options: CreateRpcClientOptions) => Rec
65
65
  */
66
66
  export interface RpcClientCallOptions extends ActionPeerSendOptions {
67
67
  }
68
+ /**
69
+ * `method, input -> unwrapped output` signature for adapter wiring.
70
+ *
71
+ * The typed `create_rpc_client` Proxy returns `Result<T, JsonrpcErrorObject>`
72
+ * on every call. UI adapters (e.g. `admin_rpc_adapters.ts`) want a
73
+ * throw-on-error shape so form components can match on `error.data.reason`
74
+ * via catch blocks. `create_throwing_rpc_call` bridges the two.
75
+ */
76
+ export type ThrowingRpcCall = <TOutput = unknown>(method: string, input?: unknown) => Promise<TOutput>;
77
+ /**
78
+ * Wrap a typed RPC client so every call returns its unwrapped value or throws.
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.
85
+ *
86
+ * Invariant upheld by `create_rpc_client`: every `{ok: false}` return
87
+ * carries a well-formed `JsonrpcErrorObject` with `code` + `message`.
88
+ * Callers must still use optional chaining on `err.data` because the
89
+ * JSON-RPC `data` field is spec-level optional — a handler that throws
90
+ * `jsonrpc_errors.forbidden()` without a `data` argument produces
91
+ * `err.data === undefined`.
92
+ *
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>>`)
95
+ */
96
+ export declare const create_throwing_rpc_call: (api: Record<string, ((input?: any) => Promise<any>) | undefined>) => ThrowingRpcCall;
68
97
  //# 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"}
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"}
@@ -168,3 +168,34 @@ const create_remote_notification_method = (peer, environment, spec, actions, tra
168
168
  return extract_action_result(event);
169
169
  };
170
170
  };
171
+ /**
172
+ * Wrap a typed RPC client so every call returns its unwrapped value or throws.
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.
179
+ *
180
+ * Invariant upheld by `create_rpc_client`: every `{ok: false}` return
181
+ * carries a well-formed `JsonrpcErrorObject` with `code` + `message`.
182
+ * Callers must still use optional chaining on `err.data` because the
183
+ * JSON-RPC `data` field is spec-level optional — a handler that throws
184
+ * `jsonrpc_errors.forbidden()` without a `data` argument produces
185
+ * `err.data === undefined`.
186
+ *
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>>`)
189
+ */
190
+ export const create_throwing_rpc_call = (api) => {
191
+ return async (method, input) => {
192
+ const fn = api[method];
193
+ if (!fn)
194
+ throw new Error(`rpc method not found: ${method}`);
195
+ const result = await fn(input);
196
+ if (!result.ok) {
197
+ throw Object.assign(new Error(result.error?.message ?? 'rpc error'), result.error);
198
+ }
199
+ return result.value;
200
+ };
201
+ };
@@ -836,6 +836,28 @@ 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`.
853
+
854
+ Pair this with `create_app_server`'s `rpc_endpoints` factory form
855
+ (`(ctx) => Array<RpcEndpointSpec>`) so the combined action list gets
856
+ `ctx.deps` + `ctx.app_settings` — `create_app_server` auto-mounts the
857
+ endpoint via `create_rpc_endpoint`, so consumers don't need to mount it
858
+ again in `create_route_specs`. See `../../../docs/usage.md` §Server
859
+ Assembly.
860
+
839
861
  ### `account_action_specs.ts` + `account_actions.ts` — seven self-service RPC actions
840
862
 
841
863
  Counterpart to `account_routes.ts`. Cookie-lifecycle flows (`login`,
@@ -0,0 +1,49 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,32 @@
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
+ ];
@@ -129,8 +129,19 @@ export interface AppServerOptions {
129
129
  };
130
130
  /** SSE event specs for surface generation. Defaults to `[]` (no SSE events). */
131
131
  event_specs?: Array<EventSpec>;
132
- /** RPC endpoint specs for surface generation. */
133
- rpc_endpoints?: Array<RpcEndpointSpec>;
132
+ /**
133
+ * RPC endpoint specs — single source of truth for both surface generation
134
+ * *and* live dispatch. Each entry is mounted via `create_rpc_endpoint`
135
+ * against the assembled Hono app, so consumers no longer call
136
+ * `create_rpc_endpoint` themselves inside `create_route_specs`.
137
+ *
138
+ * Accepts either an array (evaluated eagerly) or a factory
139
+ * `(ctx: AppServerContext) => Array<RpcEndpointSpec>` (evaluated after the
140
+ * server context is assembled). Use the factory form when action lists
141
+ * depend on `ctx.deps` / `ctx.app_settings` — e.g.
142
+ * `create_admin_rpc_actions(ctx.deps, {app_settings: ctx.app_settings})`.
143
+ */
144
+ rpc_endpoints?: Array<RpcEndpointSpec> | ((context: AppServerContext) => Array<RpcEndpointSpec>);
134
145
  /** Env schema for surface generation. Pass `z.object({})` when there are no env vars beyond `BaseServerEnv`. */
135
146
  env_schema: z.ZodObject;
136
147
  /** Middleware applied after routes, before static serving. Included in surface. */
@@ -1 +1 @@
1
- {"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"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"}
@@ -31,6 +31,7 @@ import { create_surface_route_spec } from '../http/common_routes.js';
31
31
  import { create_auth_middleware_specs } from '../auth/middleware.js';
32
32
  import { fuz_auth_guard_resolver } from '../auth/route_guards.js';
33
33
  import { ERROR_PAYLOAD_TOO_LARGE } from '../http/error_schemas.js';
34
+ import { create_rpc_endpoint } from '../actions/action_rpc.js';
34
35
  /** Default maximum request body size: 1 MiB. */
35
36
  export const DEFAULT_MAX_BODY_SIZE = 1024 * 1024;
36
37
  /**
@@ -133,6 +134,16 @@ export const create_app_server = async (options) => {
133
134
  const prefix = options.bootstrap.route_prefix ?? '/api/account';
134
135
  factory_routes.push(...prefix_route_specs(prefix, bootstrap_routes));
135
136
  }
137
+ // RPC endpoint auto-mount — resolve specs then append their routes so
138
+ // surface generation and live dispatch share one source of truth.
139
+ const resolved_rpc_endpoints = typeof options.rpc_endpoints === 'function'
140
+ ? options.rpc_endpoints(context)
141
+ : options.rpc_endpoints;
142
+ if (resolved_rpc_endpoints) {
143
+ for (const endpoint of resolved_rpc_endpoints) {
144
+ factory_routes.push(...create_rpc_endpoint({ path: endpoint.path, actions: endpoint.actions, log }));
145
+ }
146
+ }
136
147
  // Surface route (default: enabled)
137
148
  if (options.surface_route !== false) {
138
149
  factory_routes.push(create_surface_route_spec(surface_ref));
@@ -151,7 +162,7 @@ export const create_app_server = async (options) => {
151
162
  route_specs,
152
163
  env_schema: options.env_schema,
153
164
  event_specs: all_event_specs,
154
- rpc_endpoints: options.rpc_endpoints,
165
+ rpc_endpoints: resolved_rpc_endpoints,
155
166
  });
156
167
  // Config-level diagnostics (concatenated after spec-level from generate_app_surface)
157
168
  const config_diagnostics = [];
@@ -15,8 +15,13 @@ export interface AuditCompletenessTestOptions {
15
15
  /**
16
16
  * RPC endpoint specs — the source `RpcAction` arrays. Required; the
17
17
  * admin permit flow is RPC-only and the suite hard-fails without it.
18
+ *
19
+ * Accepts either an array (eager) or a factory
20
+ * `(ctx: AppServerContext) => Array<RpcEndpointSpec>` — the factory form
21
+ * is required when action handlers must close over the per-test
22
+ * `ctx.app_settings` / `ctx.deps` (e.g. exercising `app_settings_update`).
18
23
  */
19
- rpc_endpoints: Array<RpcEndpointSpec>;
24
+ rpc_endpoints: Array<RpcEndpointSpec> | ((ctx: AppServerContext) => Array<RpcEndpointSpec>);
20
25
  /** Optional overrides for `AppServerOptions`. */
21
26
  app_options?: Partial<Omit<AppServerOptions, 'backend' | 'session_options' | 'create_route_specs'>>;
22
27
  /** Database factories to run tests against. Default: pglite only. */
@@ -1 +1 @@
1
- {"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAkB7B,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;AAMrD,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AA0BjB,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,oBAAoB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C,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;;;OAGG;IACH,aAAa,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IACtC,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;CAChC;AAoDD;;;;;;;;GAQG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAgezF,CAAC"}
1
+ {"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAkB7B,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;AAMrD,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AAOjB,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,oBAAoB,CAAC;AAsBxD;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C,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;;;;;;;;OAQG;IACH,aAAa,EAAE,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;IAC5F,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;CAChC;AAoDD;;;;;;;;GAQG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IA2ezF,CAAC"}
@@ -22,6 +22,7 @@ import { find_auth_route } from './integration_helpers.js';
22
22
  import { run_migrations } from '../db/migrate.js';
23
23
  import { query_accept_offer } from '../auth/permit_offer_queries.js';
24
24
  import { rpc_call, require_rpc_endpoint_path } from './rpc_helpers.js';
25
+ import { create_stub_app_server_context } from './stubs.js';
25
26
  import { permit_offer_create_action_spec, permit_revoke_action_spec, } from '../auth/permit_offer_action_specs.js';
26
27
  import { admin_session_revoke_all_action_spec, admin_token_revoke_all_action_spec, app_settings_update_action_spec, invite_create_action_spec, invite_delete_action_spec, } from '../auth/admin_action_specs.js';
27
28
  import { account_session_list_action_spec, account_session_revoke_action_spec, account_session_revoke_all_action_spec, account_token_create_action_spec, account_token_list_action_spec, account_token_revoke_action_spec, } from '../auth/account_action_specs.js';
@@ -67,8 +68,18 @@ const json_session_headers = (test_app, extra) => test_app.create_session_header
67
68
  */
68
69
  export const describe_audit_completeness_tests = (options) => {
69
70
  // Hard-fail early so consumers see a clear setup error instead of a
70
- // confusing test failure when `rpc_endpoints` is missing.
71
- const rpc_path = require_rpc_endpoint_path(options.rpc_endpoints);
71
+ // confusing test failure when `rpc_endpoints` is missing. For the
72
+ // factory form we invoke the factory once here with a stub ctx purely
73
+ // to extract the endpoint path (a stable string like `/api/rpc`); the
74
+ // resulting actions array is discarded. `create_app_server` invokes
75
+ // the factory a second time inside each test with its real ctx, and
76
+ // those are the handlers that actually serve requests. Safe as long
77
+ // as the factory is pure — the stock helpers (e.g.
78
+ // `create_admin_rpc_actions`) are.
79
+ const rpc_endpoints_for_setup = typeof options.rpc_endpoints === 'function'
80
+ ? options.rpc_endpoints(create_stub_app_server_context(options.session_options))
81
+ : options.rpc_endpoints;
82
+ const rpc_path = require_rpc_endpoint_path(rpc_endpoints_for_setup);
72
83
  const init_schema = async (db) => {
73
84
  await run_migrations(db, [AUTH_MIGRATION_NS]);
74
85
  };
@@ -76,8 +76,16 @@ export interface CreateTestAppSurfaceSpecOptions {
76
76
  env_schema?: z.ZodObject;
77
77
  /** SSE event specs for surface generation. */
78
78
  event_specs?: Array<EventSpec>;
79
- /** RPC endpoint specs for surface generation. */
80
- rpc_endpoints?: Array<RpcEndpointSpec>;
79
+ /**
80
+ * RPC endpoint specs for surface generation.
81
+ *
82
+ * Accepts either an array (eager) or a factory
83
+ * `(ctx: AppServerContext) => Array<RpcEndpointSpec>` — symmetric with
84
+ * `create_app_server`'s `rpc_endpoints` option, so consumers can pass
85
+ * the same factory to both entry points. The factory runs once against
86
+ * the stub `AppServerContext` this helper already builds.
87
+ */
88
+ rpc_endpoints?: Array<RpcEndpointSpec> | ((ctx: AppServerContext) => Array<RpcEndpointSpec>);
81
89
  /** Transform middleware array (e.g., tx's `extend_middleware_for_tx_binary`). */
82
90
  transform_middleware?: (specs: Array<MiddlewareSpec>) => Array<MiddlewareSpec>;
83
91
  /** Bootstrap route prefix (default: `'/api/account'`). */
@@ -1 +1 @@
1
- {"version":3,"file":"stubs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/stubs.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAa7B,OAAO,KAAK,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAE3B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,4BAA4B,CAAC;AAE/D,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAC/B,OAAO,EAAqB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAEzE,OAAO,EAEN,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAKlD;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,GAAG,GAAG,EAAE,OAAO,MAAM,KAAG,CAqBtD,CAAC;AAET;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,GAAG,GAAG,EAAE,QAAQ,MAAM,EAAE,YAAY,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,CAOxF,CAAC;AAET,iEAAiE;AACjE,eAAO,MAAM,IAAI,EAAE,GAAkC,CAAC;AAEtD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,QAAO,EAI/B,CAAC;AAEJ,gDAAgD;AAChD,eAAO,MAAM,YAAY,QAAO,QAAgC,CAAC;AAEjE,2CAA2C;AAC3C,eAAO,MAAM,OAAO,GAAU,IAAI,GAAG,EAAE,MAAM,GAAG,KAAG,OAAO,CAAC,IAAI,CAAW,CAAC;AAI3E,2EAA2E;AAC3E,eAAO,MAAM,aAAa,EAAE,OAS3B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,QAAO,OAStC,CAAC;AAEH,2FAA2F;AAC3F,eAAO,MAAM,0BAA0B,GAAI,UAAU;IACpD,iDAAiD;IACjD,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAC/B,KAAG,KAAK,CAAC,cAAc,CAqBvB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B,GAC1C,iBAAiB,cAAc,CAAC,MAAM,CAAC,KACrC,gBAmBF,CAAC;AAEF,kDAAkD;AAClD,MAAM,WAAW,+BAA+B;IAC/C,6DAA6D;IAC7D,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,qFAAqF;IACrF,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE,oEAAoE;IACpE,UAAU,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACzB,8CAA8C;IAC9C,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/B,iDAAiD;IACjD,aAAa,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IACvC,iFAAiF;IACjF,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,KAAK,CAAC,cAAc,CAAC,CAAC;IAC/E,0DAA0D;IAC1D,sBAAsB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GACxC,SAAS,+BAA+B,KACtC,cAyBF,CAAC"}
1
+ {"version":3,"file":"stubs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/stubs.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAa7B,OAAO,KAAK,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAE3B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,4BAA4B,CAAC;AAE/D,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAC/B,OAAO,EAAqB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAGzE,OAAO,EAEN,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAKlD;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,GAAG,GAAG,EAAE,OAAO,MAAM,KAAG,CAqBtD,CAAC;AAET;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,GAAG,GAAG,EAAE,QAAQ,MAAM,EAAE,YAAY,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,CAOxF,CAAC;AAET,iEAAiE;AACjE,eAAO,MAAM,IAAI,EAAE,GAAkC,CAAC;AAEtD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,QAAO,EAI/B,CAAC;AAEJ,gDAAgD;AAChD,eAAO,MAAM,YAAY,QAAO,QAAgC,CAAC;AAEjE,2CAA2C;AAC3C,eAAO,MAAM,OAAO,GAAU,IAAI,GAAG,EAAE,MAAM,GAAG,KAAG,OAAO,CAAC,IAAI,CAAW,CAAC;AAI3E,2EAA2E;AAC3E,eAAO,MAAM,aAAa,EAAE,OAS3B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,QAAO,OAStC,CAAC;AAEH,2FAA2F;AAC3F,eAAO,MAAM,0BAA0B,GAAI,UAAU;IACpD,iDAAiD;IACjD,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAC/B,KAAG,KAAK,CAAC,cAAc,CAqBvB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B,GAC1C,iBAAiB,cAAc,CAAC,MAAM,CAAC,KACrC,gBAmBF,CAAC;AAEF,kDAAkD;AAClD,MAAM,WAAW,+BAA+B;IAC/C,6DAA6D;IAC7D,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,qFAAqF;IACrF,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE,oEAAoE;IACpE,UAAU,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACzB,8CAA8C;IAC9C,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/B;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;IAC7F,iFAAiF;IACjF,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,KAAK,CAAC,cAAc,CAAC,CAAC;IAC/E,0DAA0D;IAC1D,sBAAsB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GACxC,SAAS,+BAA+B,KACtC,cA2CF,CAAC"}
@@ -12,6 +12,7 @@ import { ApiError, RateLimitError } from '../http/error_schemas.js';
12
12
  import { Db } from '../db/db.js';
13
13
  import { prefix_route_specs } from '../http/route_spec.js';
14
14
  import { create_bootstrap_route_specs } from '../auth/bootstrap_routes.js';
15
+ import { create_rpc_endpoint } from '../actions/action_rpc.js';
15
16
  import { create_app_surface_spec, } from '../http/surface.js';
16
17
  import { BaseServerEnv } from '../server/env.js';
17
18
  /* eslint-disable @typescript-eslint/require-await */
@@ -178,7 +179,21 @@ export const create_test_app_surface_spec = (options) => {
178
179
  ip_rate_limiter: null,
179
180
  });
180
181
  const prefix = options.bootstrap_route_prefix ?? '/api/account';
181
- const route_specs = [...consumer_routes, ...prefix_route_specs(prefix, bootstrap_routes)];
182
+ // Auto-mount rpc endpoints (mirrors create_app_server) so consumer
183
+ // `create_route_specs` does not need to call `create_rpc_endpoint`.
184
+ const resolved_rpc_endpoints = typeof options.rpc_endpoints === 'function'
185
+ ? options.rpc_endpoints(ctx)
186
+ : options.rpc_endpoints;
187
+ const rpc_route_specs = resolved_rpc_endpoints?.flatMap((endpoint) => create_rpc_endpoint({
188
+ path: endpoint.path,
189
+ actions: endpoint.actions,
190
+ log: ctx.deps.log,
191
+ })) ?? [];
192
+ const route_specs = [
193
+ ...consumer_routes,
194
+ ...rpc_route_specs,
195
+ ...prefix_route_specs(prefix, bootstrap_routes),
196
+ ];
182
197
  let middleware_specs = create_stub_api_middleware();
183
198
  if (options.transform_middleware) {
184
199
  middleware_specs = options.transform_middleware(middleware_specs);
@@ -188,6 +203,6 @@ export const create_test_app_surface_spec = (options) => {
188
203
  route_specs,
189
204
  env_schema: options.env_schema ?? BaseServerEnv,
190
205
  event_specs: options.event_specs,
191
- rpc_endpoints: options.rpc_endpoints,
206
+ rpc_endpoints: resolved_rpc_endpoints,
192
207
  });
193
208
  };
package/dist/ui/CLAUDE.md CHANGED
@@ -266,6 +266,18 @@ destructive actions.
266
266
  `Loadable` + `app_settings_rpc_context` + narrow `AppSettingsRpc`
267
267
  (`get`, `update`). Fields: `settings`, `updating`. Single mutation
268
268
  `update_open_signup(boolean)`.
269
+ - `admin_rpc_adapters.ts` (plain `.ts`, no reactive state) — bundled
270
+ wiring for the four admin RPC contexts. `create_admin_rpc_adapters(rpc_call)`
271
+ takes a single `AdminRpcCall` closure (alias of `ThrowingRpcCall` from
272
+ `../actions/rpc_client.ts`) and returns `{admin_accounts, admin_invites,
273
+ audit_log, app_settings}` adapter objects. `provide_admin_rpc_contexts(adapters)`
274
+ calls `set` on all four contexts in one shot. Pair with
275
+ `create_throwing_rpc_call(api)` from `../actions/rpc_client.ts` to turn a
276
+ typed `create_rpc_client` Proxy into the throw-on-error `rpc_call`
277
+ signature — two lines at the admin shell layout. Method-name mapping is
278
+ in the module TSDoc (`grant_permit` → `permit_offer_create`,
279
+ `retract_offer` → `permit_offer_retract`, etc.) and the
280
+ `admin_rpc_adapters.test.ts` fixtures.
269
281
 
270
282
  ## RPC adapter contexts
271
283
 
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Admin RPC adapter helpers for consumer UIs.
3
+ *
4
+ * Bridges a typed `rpc_call`-shaped function to the four narrow admin RPC
5
+ * interfaces the state classes consume — `AdminAccountsRpc`,
6
+ * `AdminInvitesRpc`, `AuditLogRpc`, `AppSettingsRpc`. Two calls at the
7
+ * admin shell layout wire everything:
8
+ *
9
+ * ```ts
10
+ * import {create_throwing_rpc_call} from '@fuzdev/fuz_app/actions/rpc_client.js';
11
+ * const rpc_call = create_throwing_rpc_call(api);
12
+ * provide_admin_rpc_contexts(create_admin_rpc_adapters(rpc_call));
13
+ * ```
14
+ *
15
+ * `create_throwing_rpc_call` unwraps every `Result` to throw on error, spreading
16
+ * the JSON-RPC `{code, message, data?}` onto the thrown `Error` so form
17
+ * components (e.g. `PermitOfferForm.svelte`) can match on
18
+ * `error.data?.reason` via `ERROR_OFFER_*` constants — optional chaining is
19
+ * required because JSON-RPC `data` is spec-level optional. Consumers that
20
+ * need a custom unwrap strategy can supply any function matching
21
+ * `AdminRpcCall` directly instead.
22
+ *
23
+ * No `.svelte.ts` suffix — this module holds no reactive state, only
24
+ * method-name mappings.
25
+ *
26
+ * @module
27
+ */
28
+ import type { ThrowingRpcCall } from '../actions/rpc_client.js';
29
+ import { type AdminAccountsRpc } from './admin_accounts_state.svelte.js';
30
+ import { type AdminInvitesRpc } from './admin_invites_state.svelte.js';
31
+ import { type AuditLogRpc } from './audit_log_state.svelte.js';
32
+ import { type AppSettingsRpc } from './app_settings_state.svelte.js';
33
+ /**
34
+ * Function-shaped contract for dispatching an RPC call by method name.
35
+ *
36
+ * Alias of `ThrowingRpcCall` — kept as a domain-specific name so reads of
37
+ * the admin UI code stay self-contained. Receives the method string and
38
+ * input, returns a Promise of the output — or throws on error carrying the
39
+ * JSON-RPC `{code, message, data?}` shape.
40
+ *
41
+ * The generic is load-bearing: contextual typing lets the narrow
42
+ * `Admin*Rpc` return types flow into `TOutput` so adapter methods typecheck
43
+ * without explicit casts.
44
+ */
45
+ export type AdminRpcCall = ThrowingRpcCall;
46
+ /** The four admin RPC adapters assembled from a shared `rpc_call`. */
47
+ export interface AdminRpcAdapters {
48
+ admin_accounts: AdminAccountsRpc;
49
+ admin_invites: AdminInvitesRpc;
50
+ audit_log: AuditLogRpc;
51
+ app_settings: AppSettingsRpc;
52
+ }
53
+ /**
54
+ * Build the four admin RPC adapters from a single typed `rpc_call`.
55
+ *
56
+ * Method-name mapping:
57
+ *
58
+ * | Narrow RPC method | Action spec method |
59
+ * | ----------------------------------- | ---------------------------- |
60
+ * | `admin_accounts.list_accounts` | `admin_account_list` |
61
+ * | `admin_accounts.list_sessions` | `admin_session_list` |
62
+ * | `admin_accounts.grant_permit` | `permit_offer_create` |
63
+ * | `admin_accounts.revoke_permit` | `permit_revoke` |
64
+ * | `admin_accounts.retract_offer` | `permit_offer_retract` |
65
+ * | `admin_accounts.session_revoke_all` | `admin_session_revoke_all` |
66
+ * | `admin_accounts.token_revoke_all` | `admin_token_revoke_all` |
67
+ * | `admin_invites.list` | `invite_list` |
68
+ * | `admin_invites.create` | `invite_create` |
69
+ * | `admin_invites.delete` | `invite_delete` |
70
+ * | `audit_log.list` | `audit_log_list` |
71
+ * | `audit_log.permit_history` | `audit_log_permit_history` |
72
+ * | `app_settings.get` | `app_settings_get` |
73
+ * | `app_settings.update` | `app_settings_update` |
74
+ *
75
+ * All four adapter factories call through the same `rpc_call` — consumers
76
+ * only construct one adapter closure (typically wrapping
77
+ * `create_rpc_client`'s Proxy + Result-unwrap) regardless of how many
78
+ * admin surfaces they mount.
79
+ */
80
+ export declare const create_admin_rpc_adapters: (rpc_call: AdminRpcCall) => AdminRpcAdapters;
81
+ /**
82
+ * Wire all four admin RPC contexts in a single call.
83
+ *
84
+ * Call once at the admin shell layout (e.g. `src/routes/admin/+layout.svelte`)
85
+ * with adapters built from `create_admin_rpc_adapters`. Every `Admin*.svelte`
86
+ * component that reads a context below this point sees the adapters.
87
+ *
88
+ * Each context accessor reads `adapters.{domain}` on every invocation, so
89
+ * mutating an adapter field on the same object propagates. Replacing the
90
+ * whole adapter set requires calling `provide_admin_rpc_contexts` again
91
+ * during init — in practice this is one-shot at layout mount.
92
+ */
93
+ export declare const provide_admin_rpc_contexts: (adapters: AdminRpcAdapters) => void;
94
+ //# sourceMappingURL=admin_rpc_adapters.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Admin RPC adapter helpers for consumer UIs.
3
+ *
4
+ * Bridges a typed `rpc_call`-shaped function to the four narrow admin RPC
5
+ * interfaces the state classes consume — `AdminAccountsRpc`,
6
+ * `AdminInvitesRpc`, `AuditLogRpc`, `AppSettingsRpc`. Two calls at the
7
+ * admin shell layout wire everything:
8
+ *
9
+ * ```ts
10
+ * import {create_throwing_rpc_call} from '@fuzdev/fuz_app/actions/rpc_client.js';
11
+ * const rpc_call = create_throwing_rpc_call(api);
12
+ * provide_admin_rpc_contexts(create_admin_rpc_adapters(rpc_call));
13
+ * ```
14
+ *
15
+ * `create_throwing_rpc_call` unwraps every `Result` to throw on error, spreading
16
+ * the JSON-RPC `{code, message, data?}` onto the thrown `Error` so form
17
+ * components (e.g. `PermitOfferForm.svelte`) can match on
18
+ * `error.data?.reason` via `ERROR_OFFER_*` constants — optional chaining is
19
+ * required because JSON-RPC `data` is spec-level optional. Consumers that
20
+ * need a custom unwrap strategy can supply any function matching
21
+ * `AdminRpcCall` directly instead.
22
+ *
23
+ * No `.svelte.ts` suffix — this module holds no reactive state, only
24
+ * method-name mappings.
25
+ *
26
+ * @module
27
+ */
28
+ import { admin_accounts_rpc_context } from './admin_accounts_state.svelte.js';
29
+ import { admin_invites_rpc_context } from './admin_invites_state.svelte.js';
30
+ import { audit_log_rpc_context } from './audit_log_state.svelte.js';
31
+ import { app_settings_rpc_context } from './app_settings_state.svelte.js';
32
+ /**
33
+ * Build the four admin RPC adapters from a single typed `rpc_call`.
34
+ *
35
+ * Method-name mapping:
36
+ *
37
+ * | Narrow RPC method | Action spec method |
38
+ * | ----------------------------------- | ---------------------------- |
39
+ * | `admin_accounts.list_accounts` | `admin_account_list` |
40
+ * | `admin_accounts.list_sessions` | `admin_session_list` |
41
+ * | `admin_accounts.grant_permit` | `permit_offer_create` |
42
+ * | `admin_accounts.revoke_permit` | `permit_revoke` |
43
+ * | `admin_accounts.retract_offer` | `permit_offer_retract` |
44
+ * | `admin_accounts.session_revoke_all` | `admin_session_revoke_all` |
45
+ * | `admin_accounts.token_revoke_all` | `admin_token_revoke_all` |
46
+ * | `admin_invites.list` | `invite_list` |
47
+ * | `admin_invites.create` | `invite_create` |
48
+ * | `admin_invites.delete` | `invite_delete` |
49
+ * | `audit_log.list` | `audit_log_list` |
50
+ * | `audit_log.permit_history` | `audit_log_permit_history` |
51
+ * | `app_settings.get` | `app_settings_get` |
52
+ * | `app_settings.update` | `app_settings_update` |
53
+ *
54
+ * All four adapter factories call through the same `rpc_call` — consumers
55
+ * only construct one adapter closure (typically wrapping
56
+ * `create_rpc_client`'s Proxy + Result-unwrap) regardless of how many
57
+ * admin surfaces they mount.
58
+ */
59
+ export const create_admin_rpc_adapters = (rpc_call) => ({
60
+ admin_accounts: {
61
+ list_accounts: () => rpc_call('admin_account_list', null),
62
+ list_sessions: () => rpc_call('admin_session_list', null),
63
+ grant_permit: (params) => rpc_call('permit_offer_create', params),
64
+ revoke_permit: (params) => rpc_call('permit_revoke', params),
65
+ retract_offer: (offer_id) => rpc_call('permit_offer_retract', { offer_id }),
66
+ session_revoke_all: (params) => rpc_call('admin_session_revoke_all', params),
67
+ token_revoke_all: (params) => rpc_call('admin_token_revoke_all', params),
68
+ },
69
+ admin_invites: {
70
+ list: () => rpc_call('invite_list', null),
71
+ create: (params) => rpc_call('invite_create', params),
72
+ delete: (params) => rpc_call('invite_delete', params),
73
+ },
74
+ audit_log: {
75
+ list: (options) => rpc_call('audit_log_list', options ?? {}),
76
+ permit_history: (params) => rpc_call('audit_log_permit_history', params ?? {}),
77
+ },
78
+ app_settings: {
79
+ get: () => rpc_call('app_settings_get', null),
80
+ update: (params) => rpc_call('app_settings_update', params),
81
+ },
82
+ });
83
+ /**
84
+ * Wire all four admin RPC contexts in a single call.
85
+ *
86
+ * Call once at the admin shell layout (e.g. `src/routes/admin/+layout.svelte`)
87
+ * with adapters built from `create_admin_rpc_adapters`. Every `Admin*.svelte`
88
+ * component that reads a context below this point sees the adapters.
89
+ *
90
+ * Each context accessor reads `adapters.{domain}` on every invocation, so
91
+ * mutating an adapter field on the same object propagates. Replacing the
92
+ * whole adapter set requires calling `provide_admin_rpc_contexts` again
93
+ * during init — in practice this is one-shot at layout mount.
94
+ */
95
+ export const provide_admin_rpc_contexts = (adapters) => {
96
+ admin_accounts_rpc_context.set(() => adapters.admin_accounts);
97
+ admin_invites_rpc_context.set(() => adapters.admin_invites);
98
+ audit_log_rpc_context.set(() => adapters.audit_log);
99
+ app_settings_rpc_context.set(() => adapters.app_settings);
100
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzdev/fuz_app",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "description": "fullstack app library",
5
5
  "glyph": "🗝",
6
6
  "logo": "logo.svg",