@fuzdev/fuz_app 0.43.0 → 0.45.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.
- package/dist/actions/CLAUDE.md +108 -5
- package/dist/actions/action_event.d.ts +10 -1
- package/dist/actions/action_event.d.ts.map +1 -1
- package/dist/actions/action_event.js +7 -0
- package/dist/actions/broadcast_api.d.ts +1 -1
- package/dist/actions/broadcast_api.d.ts.map +1 -1
- package/dist/actions/frontend_rpc_client.d.ts +128 -0
- package/dist/actions/frontend_rpc_client.d.ts.map +1 -0
- package/dist/actions/frontend_rpc_client.js +86 -0
- package/dist/actions/rpc_client.d.ts +86 -60
- package/dist/actions/rpc_client.d.ts.map +1 -1
- package/dist/actions/rpc_client.js +102 -80
- package/dist/auth/CLAUDE.md +24 -13
- package/dist/auth/self_service_role_action_specs.d.ts +20 -48
- package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
- package/dist/auth/self_service_role_action_specs.js +22 -44
- package/dist/auth/self_service_role_actions.d.ts +9 -9
- package/dist/auth/self_service_role_actions.d.ts.map +1 -1
- package/dist/auth/self_service_role_actions.js +48 -53
- package/dist/auth/standard_action_specs.d.ts +31 -0
- package/dist/auth/standard_action_specs.d.ts.map +1 -0
- package/dist/auth/standard_action_specs.js +36 -0
- package/dist/testing/ws_round_trip.d.ts +1 -1
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/ui/CLAUDE.md +10 -11
- package/dist/ui/admin_accounts_state.svelte.d.ts +20 -41
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_invites_state.svelte.d.ts +9 -18
- package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_rpc_adapters.d.ts +41 -29
- package/dist/ui/admin_rpc_adapters.d.ts.map +1 -1
- package/dist/ui/admin_rpc_adapters.js +28 -31
- package/dist/ui/admin_sessions_state.svelte.d.ts +3 -2
- package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/app_settings_state.svelte.d.ts +5 -10
- package/dist/ui/app_settings_state.svelte.d.ts.map +1 -1
- package/dist/ui/audit_log_state.svelte.d.ts +6 -18
- package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/actions/CLAUDE.md
CHANGED
|
@@ -581,16 +581,119 @@ per-method transport selector. Useful when methods are registered on
|
|
|
581
581
|
different backend dispatchers (e.g. streaming action on WS, rest on HTTP).
|
|
582
582
|
Returning `undefined` falls through to the peer's default selection.
|
|
583
583
|
|
|
584
|
-
`
|
|
585
|
-
|
|
586
|
-
`
|
|
587
|
-
`
|
|
588
|
-
history
|
|
584
|
+
`on_action_event: (event: ActionEvent<keyof TApi & string>) => void` —
|
|
585
|
+
optional callback fired once per dispatched action with the live
|
|
586
|
+
`ActionEvent`. Consumers wire reactive state inside the callback — e.g.
|
|
587
|
+
zzz's `Actions` cell calls its own `add_from_json` +
|
|
588
|
+
`listen_to_action_event` here so the history plumbing stays inside zzz
|
|
589
|
+
instead of leaking onto the rpc_client surface. `event.spec.method` and
|
|
590
|
+
`event.data.method` narrow to `keyof TApi & string` so consumers passing
|
|
591
|
+
a generated `ActionsApi` get the literal method-name union without an
|
|
592
|
+
`as ActionMethod` cast at the call site.
|
|
589
593
|
|
|
590
594
|
Cast the return to a generated `ActionsApi` interface for full typing:
|
|
591
595
|
codegen via `generate_actions_api_method_signature` keeps the shape
|
|
592
596
|
consistent. See ../../docs/usage.md §Typed Client Codegen.
|
|
593
597
|
|
|
598
|
+
### Throwing variants — `create_throwing_rpc_call` + `create_throwing_api`
|
|
599
|
+
|
|
600
|
+
Two helpers wrap a typed `create_rpc_client` Proxy so `{ok: false}` results
|
|
601
|
+
throw an `Error` with `{code, message, data?}` (catch blocks read
|
|
602
|
+
`err.data?.reason` — optional chaining required because JSON-RPC `data`
|
|
603
|
+
is spec-level optional). Same hardening on both: only `{code, data}` cross
|
|
604
|
+
onto the Error, leaving `name` / `stack` as the native Error's own so
|
|
605
|
+
attacker-shaped `result.error` payloads cannot overwrite them.
|
|
606
|
+
|
|
607
|
+
| Helper | Shape | Use at |
|
|
608
|
+
| -------------------------- | -------------------------------- | -------------------------------------------------------------------------- |
|
|
609
|
+
| `create_throwing_rpc_call` | `(method, input?) => Promise<T>` | adapter wiring (e.g. `ui/admin_rpc_adapters.ts`) — method comes from a map |
|
|
610
|
+
| `create_throwing_api` | typed Proxy over `ActionsApi` | direct call sites — `await api.foo(input)` keeps full inference |
|
|
611
|
+
|
|
612
|
+
**Layered design.** Result is the protocol primitive — `create_rpc_client`
|
|
613
|
+
returns `Result<{value}, {error}>` per call with no Error allocation. The
|
|
614
|
+
throwing wrappers sit _above_ it as ergonomic adapters; both shapes share
|
|
615
|
+
the same underlying transport and call sites pick per-site. `Result` is
|
|
616
|
+
preferable when the call site inspects `error.data.reason` (no Error
|
|
617
|
+
allocation, no try/catch nesting) or when overhead matters (reconnect
|
|
618
|
+
storms, hot paths). Throwing is preferable when the call site doesn't
|
|
619
|
+
inspect — `await api.foo()` reads cleaner than the `if (!r.ok) throw …`
|
|
620
|
+
ritual.
|
|
621
|
+
|
|
622
|
+
`create_frontend_rpc_client` ships both shapes by default — see
|
|
623
|
+
[Frontend factory](#frontend-factory-frontend_rpc_clientts) below. Direct
|
|
624
|
+
consumers of `create_rpc_client` pass their typed `ActionsApi` as the
|
|
625
|
+
generic to get the typed Result-shaped Proxy without casts, then build
|
|
626
|
+
the throwing form on top:
|
|
627
|
+
|
|
628
|
+
```ts
|
|
629
|
+
const api_result = create_rpc_client<MyActionsApi>({peer, environment});
|
|
630
|
+
const api = create_throwing_api(api_result);
|
|
631
|
+
// hot path: await api.foo(input)
|
|
632
|
+
// rare branch: const r = await api_result.foo(input); if (!r.ok) { … }
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
`create_throwing_rpc_call` is **not** a peer choice for direct call sites —
|
|
636
|
+
it's a niche primitive for method-name-mapping adapter factories
|
|
637
|
+
(`ui/admin_rpc_adapters.ts`) where the method string comes from a domain
|
|
638
|
+
mapping rather than a typed call site. Use it only at adapter boundaries.
|
|
639
|
+
|
|
640
|
+
`ThrowingApi<TApi>` (the mapped type returned by `create_throwing_api`)
|
|
641
|
+
strips `Promise<Result<{value: T}, {error: JsonrpcErrorObject}>>` to
|
|
642
|
+
`Promise<T>` on every method that matches the `request_response` /
|
|
643
|
+
async `local_call` return shape; `remote_notification` (`=> void`) and
|
|
644
|
+
sync `local_call` methods pass through. The Proxy inspects each call's
|
|
645
|
+
result shape at runtime and only unwraps when it sees a Result, so
|
|
646
|
+
non-Result returns flow through unchanged.
|
|
647
|
+
|
|
648
|
+
Both helpers throw `"rpc method not found: <name>"` on invocation of an
|
|
649
|
+
unknown method. For `create_throwing_api` the thrower is returned from
|
|
650
|
+
the Proxy get trap so `api.missing()` errors with the same clear
|
|
651
|
+
message rather than the JS default `"api.missing is not a function"`.
|
|
652
|
+
Symbol props and `then` stay `undefined` so the Proxy doesn't get
|
|
653
|
+
probed as a thenable by `await`.
|
|
654
|
+
|
|
655
|
+
### Frontend factory (`frontend_rpc_client.ts`)
|
|
656
|
+
|
|
657
|
+
`create_frontend_rpc_client<TApi>({specs, path?, transports?, transport_for_method?, on_action_event?})`
|
|
658
|
+
bundles the `ActionRegistry + ActionEventEnvironment + Transports +
|
|
659
|
+
ActionPeer + create_rpc_client + create_throwing_api` boilerplate every
|
|
660
|
+
consumer repeats — plus the `lookup_action_handler: () => undefined`
|
|
661
|
+
stub (frontend never registers `request_response` handlers; every
|
|
662
|
+
method dispatches over the wire). The `as unknown as TApi` cast happens
|
|
663
|
+
inside the helper, so call sites get a typed return without the cast
|
|
664
|
+
hostility.
|
|
665
|
+
|
|
666
|
+
Returns both Proxy shapes from one factory call:
|
|
667
|
+
|
|
668
|
+
- `api: ThrowingApi<TApi>` — typed throwing Proxy. Default for hot-path call sites.
|
|
669
|
+
- `api_result: TApi` — typed Result-shaped Proxy. For sites that inspect `error.data.reason` without try/catch.
|
|
670
|
+
- `peer`, `environment` — exposed for advanced consumers that want to register more transports or share the environment with a separate dispatcher.
|
|
671
|
+
|
|
672
|
+
```ts
|
|
673
|
+
const {api, api_result} = create_frontend_rpc_client<MyActionsApi>({
|
|
674
|
+
specs: all_standard_action_specs,
|
|
675
|
+
});
|
|
676
|
+
// hot path: await api.account_verify()
|
|
677
|
+
// rare branch: const r = await api_result.account_verify(); if (!r.ok) { … }
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
Default transport is `FrontendHttpTransport(path ?? '/api/rpc')`. Pass
|
|
681
|
+
`transports` for WS-first or mixed setups — when supplied, the default
|
|
682
|
+
HTTP transport is **not** registered. `local_call` specs in `specs`
|
|
683
|
+
silently no-op because `lookup_action_handler` always returns
|
|
684
|
+
`undefined`; this factory targets wire-dispatched actions.
|
|
685
|
+
|
|
686
|
+
`transport_for_method` and `on_action_event` are pure pass-throughs to
|
|
687
|
+
`create_rpc_client` — exposed so consumers needing per-method routing
|
|
688
|
+
(tx-style WS-for-actions / HTTP-for-rest split) or per-dispatch event
|
|
689
|
+
wiring (zzz-style reactive Cells observing `ActionEvent` lifecycle)
|
|
690
|
+
don't have to drop down to manual `create_rpc_client` construction
|
|
691
|
+
(which forfeits the bundled `api` / `api_result` pair).
|
|
692
|
+
|
|
693
|
+
`all_standard_action_specs` (in `../auth/standard_action_specs.ts`) is
|
|
694
|
+
the matching aggregate spec list mirroring `create_standard_rpc_actions`
|
|
695
|
+
on the backend — see `../auth/CLAUDE.md` §`standard_rpc_actions.ts`.
|
|
696
|
+
|
|
594
697
|
## Broadcast API (`broadcast_api.ts`)
|
|
595
698
|
|
|
596
699
|
`create_broadcast_api({peer, specs, log?, should_deliver?})` — builds a
|
|
@@ -17,7 +17,16 @@ export type ActionEventChangeObserver<TMethod extends string = string> = (new_da
|
|
|
17
17
|
export declare class ActionEvent<TMethod extends string = string, TPhase extends ActionEventPhase = ActionEventPhase, TStep extends ActionEventStep = ActionEventStep> {
|
|
18
18
|
#private;
|
|
19
19
|
readonly environment: ActionEventEnvironment;
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* `method` narrows to `TMethod` so consumers passing a typed `TApi` to
|
|
22
|
+
* `create_rpc_client` get `event.spec.method` typed as the union of
|
|
23
|
+
* their API's method names rather than plain `string`. The runtime
|
|
24
|
+
* value comes from `lookup_action_spec(method)` keyed off the Proxy
|
|
25
|
+
* get trap, so the narrowing matches the dispatched method.
|
|
26
|
+
*/
|
|
27
|
+
readonly spec: ActionSpecUnion & {
|
|
28
|
+
method: TMethod;
|
|
29
|
+
};
|
|
21
30
|
get data(): ActionEventDataUnion<TMethod> & {
|
|
22
31
|
phase: TPhase;
|
|
23
32
|
step: TStep;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action_event.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_event.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAC,gBAAgB,EAAc,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAWpF,OAAO,KAAK,EACX,cAAc,EACd,sBAAsB,EACtB,mBAAmB,EAEnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAC,sBAAsB,EAAE,eAAe,EAAC,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAkB,KAAK,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAclF,MAAM,MAAM,yBAAyB,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,IAAI,CACxE,QAAQ,EAAE,oBAAoB,CAAC,OAAO,CAAC,EACvC,QAAQ,EAAE,oBAAoB,CAAC,OAAO,CAAC,EACvC,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,KACvB,IAAI,CAAC;AAEV;;GAEG;AACH,qBAAa,WAAW,CACvB,OAAO,SAAS,MAAM,GAAG,MAAM,EAC/B,MAAM,SAAS,gBAAgB,GAAG,gBAAgB,EAClD,KAAK,SAAS,eAAe,GAAG,eAAe;;IAK/C,QAAQ,CAAC,WAAW,EAAE,sBAAsB,CAAC;IAC7C,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"action_event.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_event.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAC,gBAAgB,EAAc,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAWpF,OAAO,KAAK,EACX,cAAc,EACd,sBAAsB,EACtB,mBAAmB,EAEnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAC,sBAAsB,EAAE,eAAe,EAAC,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAkB,KAAK,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAclF,MAAM,MAAM,yBAAyB,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,IAAI,CACxE,QAAQ,EAAE,oBAAoB,CAAC,OAAO,CAAC,EACvC,QAAQ,EAAE,oBAAoB,CAAC,OAAO,CAAC,EACvC,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,KACvB,IAAI,CAAC;AAEV;;GAEG;AACH,qBAAa,WAAW,CACvB,OAAO,SAAS,MAAM,GAAG,MAAM,EAC/B,MAAM,SAAS,gBAAgB,GAAG,gBAAgB,EAClD,KAAK,SAAS,eAAe,GAAG,eAAe;;IAK/C,QAAQ,CAAC,WAAW,EAAE,sBAAsB,CAAC;IAC7C;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG;QAAC,MAAM,EAAE,OAAO,CAAA;KAAC,CAAC;IAEnD,IAAI,IAAI,IAAI,oBAAoB,CAAC,OAAO,CAAC,GAAG;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,KAAK,CAAA;KAAC,CAEvE;gBAGA,WAAW,EAAE,sBAAsB,EACnC,IAAI,EAAE,eAAe,EACrB,IAAI,EAAE,oBAAoB,CAAC,OAAO,CAAC;IAOpC,MAAM,IAAI,oBAAoB,CAAC,OAAO,CAAC;IAMvC,OAAO,CAAC,QAAQ,EAAE,yBAAyB,CAAC,OAAO,CAAC,GAAG,MAAM,IAAI;IAKjE,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC,OAAO,CAAC,GAAG,IAAI;IAUvD;;OAEG;IACH,KAAK,IAAI,IAAI;IA8Cb;;OAEG;IAGG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IA0CnC;;OAEG;IACH,WAAW,IAAI,IAAI;IAkCnB;;OAEG;IACH,UAAU,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAezC,WAAW,IAAI,OAAO;IAItB,eAAe,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI;IAIxC,WAAW,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAQ1C,YAAY,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI;IAUpD,gBAAgB,CAAC,YAAY,EAAE,mBAAmB,GAAG,IAAI;CAyKzD;AAGD;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,SAAS,MAAM,GAAG,MAAM,EAClE,aAAa,sBAAsB,EACnC,MAAM,eAAe,EACrB,OAAO,OAAO,EACd,gBAAgB,gBAAgB,KAC9B,WAAW,CAAC,OAAO,CAiBrB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,6BAA6B,GAAI,OAAO,SAAS,MAAM,GAAG,MAAM,EAC5E,MAAM,oBAAoB,CAAC,OAAO,CAAC,EACnC,aAAa,sBAAsB,KACjC,WAAW,CAAC,OAAO,CAOrB,CAAC;AAIF,eAAO,MAAM,kBAAkB,GAC9B,UAAU,OAAO,EACjB,aAAa,sBAAsB,KACjC,WAGF,CAAC"}
|
|
@@ -19,6 +19,13 @@ export class ActionEvent {
|
|
|
19
19
|
#data;
|
|
20
20
|
#listeners = new Set();
|
|
21
21
|
environment;
|
|
22
|
+
/**
|
|
23
|
+
* `method` narrows to `TMethod` so consumers passing a typed `TApi` to
|
|
24
|
+
* `create_rpc_client` get `event.spec.method` typed as the union of
|
|
25
|
+
* their API's method names rather than plain `string`. The runtime
|
|
26
|
+
* value comes from `lookup_action_spec(method)` keyed off the Proxy
|
|
27
|
+
* get trap, so the narrowing matches the dispatched method.
|
|
28
|
+
*/
|
|
22
29
|
spec;
|
|
23
30
|
get data() {
|
|
24
31
|
return this.#data;
|
|
@@ -94,5 +94,5 @@ export type BroadcastApi = Record<string, (input: never) => Promise<void>>;
|
|
|
94
94
|
* array in sync. Codegen (`action_collections.gen.ts`) is a natural fit
|
|
95
95
|
* if the consumer already generates per-method type maps.
|
|
96
96
|
*/
|
|
97
|
-
export declare const create_broadcast_api: <TApi
|
|
97
|
+
export declare const create_broadcast_api: <TApi extends object>(options: CreateBroadcastApiOptions) => TApi;
|
|
98
98
|
//# sourceMappingURL=broadcast_api.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"broadcast_api.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/broadcast_api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAS,KAAK,MAAM,IAAI,UAAU,EAAC,MAAM,yBAAyB,CAAC;AAG1E,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAEN,KAAK,kBAAkB,EACvB,MAAM,4BAA4B,CAAC;AAEpC;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG,CAC7B,UAAU,EAAE,kBAAkB,EAC9B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,KACV,OAAO,CAAC;AAEb,0CAA0C;AAC1C,MAAM,WAAW,yBAAyB;IACzC,8DAA8D;IAC9D,IAAI,EAAE,UAAU,CAAC;IACjB;;;;;OAKG;IACH,KAAK,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IACtC,gFAAgF;IAChF,GAAG,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IACxB;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,eAAe,CAAC;CACjC;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AAE3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,oBAAoB,GAAI,IAAI,
|
|
1
|
+
{"version":3,"file":"broadcast_api.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/broadcast_api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAS,KAAK,MAAM,IAAI,UAAU,EAAC,MAAM,yBAAyB,CAAC;AAG1E,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAEN,KAAK,kBAAkB,EACvB,MAAM,4BAA4B,CAAC;AAEpC;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG,CAC7B,UAAU,EAAE,kBAAkB,EAC9B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,KACV,OAAO,CAAC;AAEb,0CAA0C;AAC1C,MAAM,WAAW,yBAAyB;IACzC,8DAA8D;IAC9D,IAAI,EAAE,UAAU,CAAC;IACjB;;;;;OAKG;IACH,KAAK,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IACtC,gFAAgF;IAChF,GAAG,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IACxB;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,eAAe,CAAC;CACjC;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AAE3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,oBAAoB,GAAI,IAAI,SAAS,MAAM,EACvD,SAAS,yBAAyB,KAChC,IAoDF,CAAC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frontend-only typed RPC client factory.
|
|
3
|
+
*
|
|
4
|
+
* Bundles the `ActionRegistry + ActionEventEnvironment + Transports +
|
|
5
|
+
* ActionPeer + create_rpc_client + create_throwing_api` boilerplate every
|
|
6
|
+
* consumer repeats — plus the `lookup_action_handler: () => undefined`
|
|
7
|
+
* stub (frontend never registers `request_response` handlers; every method
|
|
8
|
+
* dispatches over the wire).
|
|
9
|
+
*
|
|
10
|
+
* Returns both Proxy shapes from one factory call:
|
|
11
|
+
*
|
|
12
|
+
* - `api` — typed throwing Proxy. `await api.foo(input)` returns the
|
|
13
|
+
* unwrapped value or throws an `Error` carrying `{code, data}` from the
|
|
14
|
+
* JSON-RPC error. Use at hot-path call sites.
|
|
15
|
+
* - `api_result` — typed Result-shaped Proxy. `await api_result.foo(input)`
|
|
16
|
+
* returns `Result<{value}, {error: JsonrpcErrorObject}>`. Use when call
|
|
17
|
+
* sites want to inspect `error.data.reason` without try/catch — and
|
|
18
|
+
* anywhere allocating an `Error` per `{ok: false}` is wasteful (e.g.
|
|
19
|
+
* reconnect-storm `service_unavailable` paths). Result is the protocol
|
|
20
|
+
* primitive; the throwing form is a wrapper over it. Both share the
|
|
21
|
+
* same underlying transport — pick per call site, no construction cost.
|
|
22
|
+
*
|
|
23
|
+
* Generic `TApi` is the consumer's typed Proxy interface. The `as unknown
|
|
24
|
+
* as TApi` double cast happens inside the helper so call sites get a
|
|
25
|
+
* typed return value without the cast hostility. `api`'s type is
|
|
26
|
+
* `ThrowingApi<TApi>` — the mapped type strips the Result wrapper.
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* const {api, api_result} = create_frontend_rpc_client<MyActionsApi>({
|
|
30
|
+
* specs: all_specs,
|
|
31
|
+
* });
|
|
32
|
+
* // hot path: await api.account_verify()
|
|
33
|
+
* // rare branch: const r = await api_result.account_verify(); if (!r.ok) { … }
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* Returns the underlying `peer` and `environment` alongside the two api
|
|
37
|
+
* shapes so advanced consumers (zzz-style frontends needing extra
|
|
38
|
+
* transports / WS notification handlers / action-history wiring) can
|
|
39
|
+
* extend without recreating the bundle.
|
|
40
|
+
*
|
|
41
|
+
* Note: `local_call` specs in `specs` will silently no-op because
|
|
42
|
+
* `lookup_action_handler` always returns `undefined` — the frontend
|
|
43
|
+
* factory is for wire-dispatched actions. Frontend-side `local_call`
|
|
44
|
+
* needs a different wiring shape (custom `environment.lookup_action_handler`).
|
|
45
|
+
*
|
|
46
|
+
* @module
|
|
47
|
+
*/
|
|
48
|
+
import { ActionPeer } from './action_peer.js';
|
|
49
|
+
import { type Transport } from './transports.js';
|
|
50
|
+
import { type ThrowingApi, type TransportForMethod } from './rpc_client.js';
|
|
51
|
+
import type { ActionEvent } from './action_event.js';
|
|
52
|
+
import type { ActionEventEnvironment } from './action_event_types.js';
|
|
53
|
+
import type { ActionSpecUnion } from './action_spec.js';
|
|
54
|
+
/** Options for `create_frontend_rpc_client`. */
|
|
55
|
+
export interface CreateFrontendRpcClientOptions<TApi extends object = object> {
|
|
56
|
+
/**
|
|
57
|
+
* Action specs the typed Proxy can dispatch. Methods absent from this
|
|
58
|
+
* list silently return `undefined` from the Proxy — the generic `TApi`
|
|
59
|
+
* cannot constrain runtime membership, so consumers must keep this list
|
|
60
|
+
* in sync with the typed surface (codegen recommended).
|
|
61
|
+
*/
|
|
62
|
+
specs: ReadonlyArray<ActionSpecUnion>;
|
|
63
|
+
/**
|
|
64
|
+
* HTTP RPC endpoint path for the default `FrontendHttpTransport`.
|
|
65
|
+
* Defaults to `/api/rpc`. Ignored when `transports` is provided.
|
|
66
|
+
*/
|
|
67
|
+
path?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Optional explicit transport list. When provided, the default
|
|
70
|
+
* `FrontendHttpTransport(path)` is **not** registered — the caller is
|
|
71
|
+
* responsible for at least one ready transport. Use for WS-first or
|
|
72
|
+
* WS+HTTP mixed setups.
|
|
73
|
+
*/
|
|
74
|
+
transports?: ReadonlyArray<Transport>;
|
|
75
|
+
/**
|
|
76
|
+
* Optional per-method transport selector — pure pass-through to
|
|
77
|
+
* `create_rpc_client`. Return the transport name to use for a given
|
|
78
|
+
* method, or `undefined` to fall back to the peer's default selection.
|
|
79
|
+
*
|
|
80
|
+
* Useful when methods are registered on different backend dispatchers
|
|
81
|
+
* (e.g. streaming actions on WS, REST RPC on HTTP) — a tx-style mixed
|
|
82
|
+
* setup. Per-call `RpcClientCallOptions.transport_name` overrides this
|
|
83
|
+
* for individual dispatches.
|
|
84
|
+
*/
|
|
85
|
+
transport_for_method?: TransportForMethod;
|
|
86
|
+
/**
|
|
87
|
+
* Optional callback fired once per dispatched action — pure pass-through
|
|
88
|
+
* to `create_rpc_client`. Used by zzz-style consumers that thread the
|
|
89
|
+
* `ActionEvent` into a reactive cell (`add_from_json` + `listen_to_action_event`)
|
|
90
|
+
* for `pending` / `failed` / `value` derivations.
|
|
91
|
+
*
|
|
92
|
+
* `event.spec.method` and `event.data.method` narrow to
|
|
93
|
+
* `keyof TApi & string` — drop the `as ActionMethod` cast at the call
|
|
94
|
+
* site when `TApi` is a generated `ActionsApi` interface.
|
|
95
|
+
*/
|
|
96
|
+
on_action_event?: (event: ActionEvent<keyof TApi & string>) => void;
|
|
97
|
+
}
|
|
98
|
+
/** Bundle returned by `create_frontend_rpc_client`. */
|
|
99
|
+
export interface FrontendRpcClient<TApi> {
|
|
100
|
+
/**
|
|
101
|
+
* Typed throwing Proxy. `await api.method(input)` returns the unwrapped
|
|
102
|
+
* value or throws an `Error` with `{code, data}` from the JSON-RPC
|
|
103
|
+
* error. Default for call sites that don't inspect errors.
|
|
104
|
+
*/
|
|
105
|
+
api: ThrowingApi<TApi>;
|
|
106
|
+
/**
|
|
107
|
+
* Typed Result-shaped Proxy. `await api_result.method(input)` returns
|
|
108
|
+
* `Result<{value}, {error: JsonrpcErrorObject}>`. Use when call sites
|
|
109
|
+
* inspect `error.data.reason` without try/catch, or when Error
|
|
110
|
+
* allocation per `{ok: false}` would be wasteful.
|
|
111
|
+
*/
|
|
112
|
+
api_result: TApi;
|
|
113
|
+
/** Underlying peer — exposed for consumers that need to register more transports or send raw messages. */
|
|
114
|
+
peer: ActionPeer;
|
|
115
|
+
/** Action environment — exposed for consumers that need to share it (e.g. attach a notification handler registry). */
|
|
116
|
+
environment: ActionEventEnvironment;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Build a frontend-only typed RPC client.
|
|
120
|
+
*
|
|
121
|
+
* @param options - `specs` (required), optional `path` / `transports` /
|
|
122
|
+
* `transport_for_method` / `on_action_event`
|
|
123
|
+
* @returns `{api, api_result, peer, environment}` — both Proxy shapes plus
|
|
124
|
+
* the underlying primitives. `api` throws on `{ok: false}`; `api_result`
|
|
125
|
+
* returns the Result.
|
|
126
|
+
*/
|
|
127
|
+
export declare const create_frontend_rpc_client: <TApi extends object>(options: CreateFrontendRpcClientOptions<TApi>) => FrontendRpcClient<TApi>;
|
|
128
|
+
//# sourceMappingURL=frontend_rpc_client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontend_rpc_client.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/frontend_rpc_client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAGH,OAAO,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAa,KAAK,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAE3D,OAAO,EAGN,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,yBAAyB,CAAC;AACpE,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAEtD,gDAAgD;AAChD,MAAM,WAAW,8BAA8B,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IAC3E;;;;;OAKG;IACH,KAAK,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IACtC;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACtC;;;;;;;;;OASG;IACH,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;IAC1C;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;CACpE;AAED,uDAAuD;AACvD,MAAM,WAAW,iBAAiB,CAAC,IAAI;IACtC;;;;OAIG;IACH,GAAG,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;IACvB;;;;;OAKG;IACH,UAAU,EAAE,IAAI,CAAC;IACjB,0GAA0G;IAC1G,IAAI,EAAE,UAAU,CAAC;IACjB,sHAAsH;IACtH,WAAW,EAAE,sBAAsB,CAAC;CACpC;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,GAAI,IAAI,SAAS,MAAM,EAC7D,SAAS,8BAA8B,CAAC,IAAI,CAAC,KAC3C,iBAAiB,CAAC,IAAI,CAsBxB,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frontend-only typed RPC client factory.
|
|
3
|
+
*
|
|
4
|
+
* Bundles the `ActionRegistry + ActionEventEnvironment + Transports +
|
|
5
|
+
* ActionPeer + create_rpc_client + create_throwing_api` boilerplate every
|
|
6
|
+
* consumer repeats — plus the `lookup_action_handler: () => undefined`
|
|
7
|
+
* stub (frontend never registers `request_response` handlers; every method
|
|
8
|
+
* dispatches over the wire).
|
|
9
|
+
*
|
|
10
|
+
* Returns both Proxy shapes from one factory call:
|
|
11
|
+
*
|
|
12
|
+
* - `api` — typed throwing Proxy. `await api.foo(input)` returns the
|
|
13
|
+
* unwrapped value or throws an `Error` carrying `{code, data}` from the
|
|
14
|
+
* JSON-RPC error. Use at hot-path call sites.
|
|
15
|
+
* - `api_result` — typed Result-shaped Proxy. `await api_result.foo(input)`
|
|
16
|
+
* returns `Result<{value}, {error: JsonrpcErrorObject}>`. Use when call
|
|
17
|
+
* sites want to inspect `error.data.reason` without try/catch — and
|
|
18
|
+
* anywhere allocating an `Error` per `{ok: false}` is wasteful (e.g.
|
|
19
|
+
* reconnect-storm `service_unavailable` paths). Result is the protocol
|
|
20
|
+
* primitive; the throwing form is a wrapper over it. Both share the
|
|
21
|
+
* same underlying transport — pick per call site, no construction cost.
|
|
22
|
+
*
|
|
23
|
+
* Generic `TApi` is the consumer's typed Proxy interface. The `as unknown
|
|
24
|
+
* as TApi` double cast happens inside the helper so call sites get a
|
|
25
|
+
* typed return value without the cast hostility. `api`'s type is
|
|
26
|
+
* `ThrowingApi<TApi>` — the mapped type strips the Result wrapper.
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* const {api, api_result} = create_frontend_rpc_client<MyActionsApi>({
|
|
30
|
+
* specs: all_specs,
|
|
31
|
+
* });
|
|
32
|
+
* // hot path: await api.account_verify()
|
|
33
|
+
* // rare branch: const r = await api_result.account_verify(); if (!r.ok) { … }
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* Returns the underlying `peer` and `environment` alongside the two api
|
|
37
|
+
* shapes so advanced consumers (zzz-style frontends needing extra
|
|
38
|
+
* transports / WS notification handlers / action-history wiring) can
|
|
39
|
+
* extend without recreating the bundle.
|
|
40
|
+
*
|
|
41
|
+
* Note: `local_call` specs in `specs` will silently no-op because
|
|
42
|
+
* `lookup_action_handler` always returns `undefined` — the frontend
|
|
43
|
+
* factory is for wire-dispatched actions. Frontend-side `local_call`
|
|
44
|
+
* needs a different wiring shape (custom `environment.lookup_action_handler`).
|
|
45
|
+
*
|
|
46
|
+
* @module
|
|
47
|
+
*/
|
|
48
|
+
import { ActionRegistry } from './action_registry.js';
|
|
49
|
+
import { ActionPeer } from './action_peer.js';
|
|
50
|
+
import { Transports } from './transports.js';
|
|
51
|
+
import { FrontendHttpTransport } from './transports_http.js';
|
|
52
|
+
import { create_rpc_client, create_throwing_api, } from './rpc_client.js';
|
|
53
|
+
/**
|
|
54
|
+
* Build a frontend-only typed RPC client.
|
|
55
|
+
*
|
|
56
|
+
* @param options - `specs` (required), optional `path` / `transports` /
|
|
57
|
+
* `transport_for_method` / `on_action_event`
|
|
58
|
+
* @returns `{api, api_result, peer, environment}` — both Proxy shapes plus
|
|
59
|
+
* the underlying primitives. `api` throws on `{ok: false}`; `api_result`
|
|
60
|
+
* returns the Result.
|
|
61
|
+
*/
|
|
62
|
+
export const create_frontend_rpc_client = (options) => {
|
|
63
|
+
const registry = new ActionRegistry([...options.specs]);
|
|
64
|
+
const environment = {
|
|
65
|
+
executor: 'frontend',
|
|
66
|
+
lookup_action_spec: (method) => registry.spec_by_method.get(method),
|
|
67
|
+
lookup_action_handler: () => undefined,
|
|
68
|
+
};
|
|
69
|
+
const transports = new Transports();
|
|
70
|
+
if (options.transports) {
|
|
71
|
+
for (const t of options.transports)
|
|
72
|
+
transports.register_transport(t);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
transports.register_transport(new FrontendHttpTransport(options.path ?? '/api/rpc'));
|
|
76
|
+
}
|
|
77
|
+
const peer = new ActionPeer({ environment, transports });
|
|
78
|
+
const api_result = create_rpc_client({
|
|
79
|
+
peer,
|
|
80
|
+
environment,
|
|
81
|
+
on_action_event: options.on_action_event,
|
|
82
|
+
transport_for_method: options.transport_for_method,
|
|
83
|
+
});
|
|
84
|
+
const api = create_throwing_api(api_result);
|
|
85
|
+
return { api, api_result, peer, environment };
|
|
86
|
+
};
|
|
@@ -5,14 +5,17 @@
|
|
|
5
5
|
* - **Tier 1** (simple, for tx): transport send/receive, Result return. No `environment`.
|
|
6
6
|
* - **Tier 2** (full, for zzz): ActionEvent lifecycle with `environment`.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
8
|
+
* Pass the consumer's generated `ActionsApi` interface as `<TApi>` to flow
|
|
9
|
+
* full type safety through without an explicit cast at the call site.
|
|
9
10
|
*
|
|
10
11
|
* @module
|
|
11
12
|
*/
|
|
13
|
+
import type { Result } from '@fuzdev/fuz_util/result.js';
|
|
12
14
|
import type { ActionEventEnvironment } from './action_event_types.js';
|
|
15
|
+
import { type ActionEvent } from './action_event.js';
|
|
13
16
|
import type { ActionPeer, ActionPeerSendOptions } from './action_peer.js';
|
|
14
|
-
import type { ActionEventDataUnion } from './action_event_data.js';
|
|
15
17
|
import type { TransportName } from './transports.js';
|
|
18
|
+
import type { JsonrpcErrorObject } from '../http/jsonrpc.js';
|
|
16
19
|
/**
|
|
17
20
|
* Optional per-method transport selector. Return the transport to use for a
|
|
18
21
|
* given method, or `undefined` to let the peer pick via its fallback rules.
|
|
@@ -22,21 +25,21 @@ import type { TransportName } from './transports.js';
|
|
|
22
25
|
* RPC surface lives on HTTP.
|
|
23
26
|
*/
|
|
24
27
|
export type TransportForMethod = (method: string) => TransportName | undefined;
|
|
25
|
-
/** Duck-typed action history — consumers pass their concrete Actions cell. */
|
|
26
|
-
export interface RpcClientActionHistory {
|
|
27
|
-
add_from_json: (json: {
|
|
28
|
-
method: string;
|
|
29
|
-
action_event_data: ActionEventDataUnion;
|
|
30
|
-
}) => {
|
|
31
|
-
listen_to_action_event: (event: any) => void;
|
|
32
|
-
} | undefined;
|
|
33
|
-
}
|
|
34
28
|
/** Options for `create_rpc_client`. */
|
|
35
|
-
export interface CreateRpcClientOptions {
|
|
29
|
+
export interface CreateRpcClientOptions<TApi extends object = object> {
|
|
36
30
|
peer: ActionPeer;
|
|
37
31
|
environment: ActionEventEnvironment;
|
|
38
|
-
/**
|
|
39
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Optional callback fired once per dispatched action with the live
|
|
34
|
+
* `ActionEvent`. Consumers wire reactive state here — e.g. zzz's `Actions`
|
|
35
|
+
* cell calls `add_from_json` + `listen_to_action_event` inside the
|
|
36
|
+
* callback so its history stays decoupled from the rpc_client surface.
|
|
37
|
+
*
|
|
38
|
+
* `event.spec.method` and `event.data.method` narrow to
|
|
39
|
+
* `keyof TApi & string` — drop the `as ActionMethod` cast at the call
|
|
40
|
+
* site when `TApi` is a generated `ActionsApi` interface.
|
|
41
|
+
*/
|
|
42
|
+
on_action_event?: (event: ActionEvent<keyof TApi & string>) => void;
|
|
40
43
|
/**
|
|
41
44
|
* Optional per-method transport selector. When provided, the client calls
|
|
42
45
|
* `peer.send(msg, {transport_name})` with the returned transport for each
|
|
@@ -53,10 +56,22 @@ export interface CreateRpcClientOptions {
|
|
|
53
56
|
* - `remote_notification` → send notification, return Result
|
|
54
57
|
* - `local_call` → execute locally (sync or async), return Result or throw
|
|
55
58
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
59
|
+
* Generic `TApi` is the consumer's typed Proxy interface (typically a
|
|
60
|
+
* codegen-derived `ActionsApi`). Required — no default, so forgetting it
|
|
61
|
+
* is a type error rather than a silent slide into `any`. The `as unknown
|
|
62
|
+
* as TApi` coercion lives inside this function so call sites get a typed
|
|
63
|
+
* return without a cast at the seam. `TApi` is a type-layer promise about
|
|
64
|
+
* what the Proxy responds to; the runtime walks `specs` (kept in sync by
|
|
65
|
+
* the consumer, codegen recommended).
|
|
66
|
+
*
|
|
67
|
+
* ```ts
|
|
68
|
+
* const api_result = create_rpc_client<MyActionsApi>({peer, environment});
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @param options - client options (peer, environment, optional callbacks)
|
|
72
|
+
* @returns a Proxy typed as `TApi` that responds to any method name found in the environment's specs
|
|
58
73
|
*/
|
|
59
|
-
export declare const create_rpc_client: (options: CreateRpcClientOptions
|
|
74
|
+
export declare const create_rpc_client: <TApi extends object>(options: CreateRpcClientOptions<TApi>) => TApi;
|
|
60
75
|
/**
|
|
61
76
|
* Per-call options accepted by every typed Proxy method. Same shape as
|
|
62
77
|
* `ActionPeerSendOptions` — the client threads these through unchanged
|
|
@@ -66,51 +81,62 @@ export declare const create_rpc_client: (options: CreateRpcClientOptions) => Rec
|
|
|
66
81
|
export interface RpcClientCallOptions extends ActionPeerSendOptions {
|
|
67
82
|
}
|
|
68
83
|
/**
|
|
69
|
-
*
|
|
84
|
+
* Maps a typed `ActionsApi` to a throwing variant.
|
|
85
|
+
*
|
|
86
|
+
* For each method whose return type matches the `create_rpc_client` shape
|
|
87
|
+
* (`Promise<Result<{value: T}, {error: JsonrpcErrorObject}>>`), the wrapped
|
|
88
|
+
* method returns `Promise<T>` directly. Other shapes (notifications typed
|
|
89
|
+
* as `=> void`, sync `local_call` methods) pass through unchanged — there
|
|
90
|
+
* is nothing to unwrap.
|
|
70
91
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
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.
|
|
92
|
+
* Input + options parameters are preserved verbatim so generics, branded
|
|
93
|
+
* Uuids, and per-call `RpcClientCallOptions` keep working.
|
|
75
94
|
*/
|
|
76
|
-
export type
|
|
95
|
+
export type ThrowingApi<TApi> = {
|
|
96
|
+
[K in keyof TApi]: TApi[K] extends (input?: infer TInput, options?: infer TOptions) => Promise<Result<{
|
|
97
|
+
value: infer TValue;
|
|
98
|
+
}, {
|
|
99
|
+
error: JsonrpcErrorObject;
|
|
100
|
+
}>> ? (input?: TInput, options?: TOptions) => Promise<TValue> : TApi[K];
|
|
101
|
+
};
|
|
77
102
|
/**
|
|
78
|
-
* Wrap a typed RPC client so every call
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* `
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* methods,
|
|
107
|
-
* `
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
103
|
+
* Wrap a typed RPC client so every call resolves to its unwrapped value or
|
|
104
|
+
* throws an `Error` carrying the JSON-RPC `{code, message, data?}` shape.
|
|
105
|
+
*
|
|
106
|
+
* Implementation is a Proxy because the underlying `create_rpc_client`
|
|
107
|
+
* return is itself a Proxy with no concrete keys — a key-by-key wrap would
|
|
108
|
+
* need to enumerate the typed surface, which only the consumer's generated
|
|
109
|
+
* `ActionsApi` interface knows.
|
|
110
|
+
*
|
|
111
|
+
* Pass-through on non-Result returns is deliberate: sync `local_call`
|
|
112
|
+
* Proxy methods return values directly (see `create_sync_local_call_method`
|
|
113
|
+
* above). The Proxy can't distinguish those at get-time, so the wrapper
|
|
114
|
+
* inspects `result` shape at call-time and only unwraps when it sees a
|
|
115
|
+
* Result. Non-object returns pass through unchanged.
|
|
116
|
+
*
|
|
117
|
+
* Only `{code, data}` cross onto the thrown Error — `name` / `stack` are
|
|
118
|
+
* left as the Error's own properties so attacker-shaped `result.error`
|
|
119
|
+
* payloads cannot overwrite them.
|
|
120
|
+
*
|
|
121
|
+
* Recommended consumer convention: `create_frontend_rpc_client` ships
|
|
122
|
+
* both shapes by default — `api` (throwing) for hot-path call sites and
|
|
123
|
+
* `api_result` (Result) for sites that inspect `error.data.reason`
|
|
124
|
+
* without try/catch. Result is the protocol primitive; this wrapper is
|
|
125
|
+
* the ergonomic layer over it. Picking is per call site — both Proxies
|
|
126
|
+
* share the same underlying transport.
|
|
127
|
+
*
|
|
128
|
+
* Catch blocks read `err.data?.reason` — optional chaining required
|
|
129
|
+
* because JSON-RPC `data` is spec-level optional.
|
|
130
|
+
*
|
|
131
|
+
* On unknown string-keyed methods, the get trap returns a function that
|
|
132
|
+
* throws `"rpc method not found: <prop>"` on invocation — clearer than
|
|
133
|
+
* the JS default `"api.foo is not a function"`. Symbol props and `then`
|
|
134
|
+
* stay undefined so the Proxy isn't accidentally treated as a thenable
|
|
135
|
+
* (`await api` would otherwise probe `then` and trip the thrower).
|
|
136
|
+
*
|
|
137
|
+
* @param api_result - typed Result-returning RPC client from
|
|
138
|
+
* `create_rpc_client<ActionsApi>(...)`. The "_result" suffix names
|
|
139
|
+
* what the underlying calls return (`Result<{value}, {error}>`).
|
|
114
140
|
*/
|
|
115
|
-
export declare const
|
|
141
|
+
export declare const create_throwing_api: <TApi extends object>(api_result: TApi) => ThrowingApi<TApi>;
|
|
116
142
|
//# 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
|
|
1
|
+
{"version":3,"file":"rpc_client.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/rpc_client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,4BAA4B,CAAC;AAQvD,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAsB,KAAK,WAAW,EAAC,MAAM,mBAAmB,CAAC;AAMxE,OAAO,KAAK,EAAC,UAAU,EAAE,qBAAqB,EAAC,MAAM,kBAAkB,CAAC;AACxE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAEnD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,oBAAoB,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,aAAa,GAAG,SAAS,CAAC;AAM/E,uCAAuC;AACvC,MAAM,WAAW,sBAAsB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IACnE,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,sBAAsB,CAAC;IACpC;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;IACpE;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,iBAAiB,GAAI,IAAI,SAAS,MAAM,EACpD,SAAS,sBAAsB,CAAC,IAAI,CAAC,KACnC,IA4BF,CAAC;AA6DF;;;;;GAKG;AACH,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;CAAG;AAoHtE;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,WAAW,CAAC,IAAI,IAAI;KAC9B,CAAC,IAAI,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAClC,KAAK,CAAC,EAAE,MAAM,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM,QAAQ,KACpB,OAAO,CAAC,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,MAAM,CAAA;KAAC,EAAE;QAAC,KAAK,EAAE,kBAAkB,CAAA;KAAC,CAAC,CAAC,GACrE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,KAAK,OAAO,CAAC,MAAM,CAAC,GACvD,IAAI,CAAC,CAAC,CAAC;CACV,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,mBAAmB,GAAI,IAAI,SAAS,MAAM,EAAE,YAAY,IAAI,KAAG,WAAW,CAAC,IAAI,CA8B3F,CAAC"}
|