@fuzdev/fuz_app 0.44.0 → 0.46.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 +65 -39
- 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/action_event_helpers.d.ts.map +1 -1
- package/dist/actions/action_event_helpers.js +14 -4
- 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 +95 -23
- package/dist/actions/frontend_rpc_client.d.ts.map +1 -1
- package/dist/actions/frontend_rpc_client.js +48 -23
- package/dist/actions/rpc_client.d.ts +56 -86
- package/dist/actions/rpc_client.d.ts.map +1 -1
- package/dist/actions/rpc_client.js +51 -106
- 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,11 +581,15 @@ 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
|
|
@@ -605,26 +609,39 @@ attacker-shaped `result.error` payloads cannot overwrite them.
|
|
|
605
609
|
| `create_throwing_rpc_call` | `(method, input?) => Promise<T>` | adapter wiring (e.g. `ui/admin_rpc_adapters.ts`) — method comes from a map |
|
|
606
610
|
| `create_throwing_api` | typed Proxy over `ActionsApi` | direct call sites — `await api.foo(input)` keeps full inference |
|
|
607
611
|
|
|
608
|
-
**
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
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:
|
|
613
627
|
|
|
614
628
|
```ts
|
|
615
|
-
const
|
|
616
|
-
const api = create_throwing_api(
|
|
629
|
+
const api_result = create_rpc_client<MyActionsApi>({peer, environment});
|
|
630
|
+
const api = create_throwing_api(api_result);
|
|
617
631
|
// hot path: await api.foo(input)
|
|
618
|
-
// rare branch: const r = await
|
|
632
|
+
// rare branch: const r = await api_result.foo(input); if (!r.ok) { … }
|
|
619
633
|
```
|
|
620
634
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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
|
|
628
645
|
result shape at runtime and only unwraps when it sees a Result, so
|
|
629
646
|
non-Result returns flow through unchanged.
|
|
630
647
|
|
|
@@ -637,33 +654,42 @@ probed as a thenable by `await`.
|
|
|
637
654
|
|
|
638
655
|
### Frontend factory (`frontend_rpc_client.ts`)
|
|
639
656
|
|
|
640
|
-
`create_frontend_rpc_client<TApi>({specs, path?, transports?})`
|
|
641
|
-
the `ActionRegistry + ActionEventEnvironment + Transports +
|
|
642
|
-
create_rpc_client` boilerplate every
|
|
643
|
-
`lookup_action_handler: () => undefined`
|
|
644
|
-
`request_response` handlers; every
|
|
645
|
-
The `as unknown as TApi` cast happens
|
|
646
|
-
get a typed return without the cast
|
|
647
|
-
|
|
648
|
-
needing extra transports / WS notification handlers / action-history
|
|
649
|
-
wiring) can extend without recreating the bundle.
|
|
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.
|
|
650
665
|
|
|
651
|
-
|
|
652
|
-
`transports` for WS-first or mixed setups — when supplied, the default
|
|
653
|
-
HTTP transport is **not** registered. `local_call` specs in `specs`
|
|
654
|
-
silently no-op because `lookup_action_handler` always returns
|
|
655
|
-
`undefined`; this factory targets wire-dispatched actions.
|
|
666
|
+
Returns both Proxy shapes from one factory call:
|
|
656
667
|
|
|
657
|
-
|
|
658
|
-
`
|
|
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.
|
|
659
671
|
|
|
660
672
|
```ts
|
|
661
|
-
const {api
|
|
673
|
+
const {api, api_result} = create_frontend_rpc_client<MyActionsApi>({
|
|
662
674
|
specs: all_standard_action_specs,
|
|
663
675
|
});
|
|
664
|
-
|
|
676
|
+
// hot path: await api.account_verify()
|
|
677
|
+
// rare branch: const r = await api_result.account_verify(); if (!r.ok) { … }
|
|
665
678
|
```
|
|
666
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
|
+
|
|
667
693
|
`all_standard_action_specs` (in `../auth/standard_action_specs.ts`) is
|
|
668
694
|
the matching aggregate spec list mirroring `create_standard_rpc_actions`
|
|
669
695
|
on the backend — see `../auth/CLAUDE.md` §`standard_rpc_actions.ts`.
|
|
@@ -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;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action_event_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_event_helpers.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"action_event_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_event_helpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -85,12 +85,22 @@ export const create_initial_data = (kind, phase, method, executor, input) => ({
|
|
|
85
85
|
});
|
|
86
86
|
export const extract_action_result = (event) => {
|
|
87
87
|
const { data } = event;
|
|
88
|
+
// `data.error` populated → error path. This covers two cases:
|
|
89
|
+
// 1. `step === 'failed'` — explicit terminal failure.
|
|
90
|
+
// 2. `phase === 'receive_error' | 'send_error'` reached `step === 'handled'`
|
|
91
|
+
// because no handler was registered for the error phase. The dispatcher
|
|
92
|
+
// silently transitions to `handled` in that case but leaves `data.error`
|
|
93
|
+
// populated. Reading `step === 'handled'` first would return
|
|
94
|
+
// `{ok: true, value: null}` and surprise every caller that doesn't
|
|
95
|
+
// register an error-phase handler. Preferring `data.error` lets
|
|
96
|
+
// consumers skip the boilerplate `receive_error` rethrow stub.
|
|
97
|
+
if (data.error) {
|
|
98
|
+
return { ok: false, error: data.error };
|
|
99
|
+
}
|
|
88
100
|
if (data.step === 'handled') {
|
|
89
101
|
return { ok: true, value: data.output };
|
|
90
102
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
// Programming error - event not in terminal state
|
|
103
|
+
// `step === 'failed'` with `data.error === null` is a malformed event;
|
|
104
|
+
// type narrowing accepts it, runtime never produces it.
|
|
95
105
|
throw new Error(`cannot extract result: event in non-terminal state (step: ${data.step})`);
|
|
96
106
|
};
|
|
@@ -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"}
|
|
@@ -2,39 +2,57 @@
|
|
|
2
2
|
* Frontend-only typed RPC client factory.
|
|
3
3
|
*
|
|
4
4
|
* Bundles the `ActionRegistry + ActionEventEnvironment + Transports +
|
|
5
|
-
* ActionPeer + create_rpc_client` boilerplate every
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* ActionPeer + create_rpc_client + create_throwing_api` boilerplate every
|
|
6
|
+
* consumer repeats. `lookup_action_handler` defaults to `() => undefined`
|
|
7
|
+
* (HTTP-only frontends rarely need handlers); pass `options.lookup_action_handler`
|
|
8
|
+
* to wire WS-pushed `remote_notification` dispatch or a `receive_error` /
|
|
9
|
+
* `local_call` hook.
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
11
|
+
* Returns both Proxy shapes from one factory call:
|
|
12
|
+
*
|
|
13
|
+
* - `api` — typed throwing Proxy. `await api.foo(input)` returns the
|
|
14
|
+
* unwrapped value or throws an `Error` carrying `{code, data}` from the
|
|
15
|
+
* JSON-RPC error. Use at hot-path call sites.
|
|
16
|
+
* - `api_result` — typed Result-shaped Proxy. `await api_result.foo(input)`
|
|
17
|
+
* returns `Result<{value}, {error: JsonrpcErrorObject}>`. Use when call
|
|
18
|
+
* sites want to inspect `error.data.reason` without try/catch — and
|
|
19
|
+
* anywhere allocating an `Error` per `{ok: false}` is wasteful (e.g.
|
|
20
|
+
* reconnect-storm `service_unavailable` paths). Result is the protocol
|
|
21
|
+
* primitive; the throwing form is a wrapper over it. Both share the
|
|
22
|
+
* same underlying transport — pick per call site, no construction cost.
|
|
12
23
|
*
|
|
13
|
-
*
|
|
24
|
+
* Generic `TApi` is the consumer's typed Proxy interface. The `as unknown
|
|
25
|
+
* as TApi` double cast happens inside the helper so call sites get a
|
|
26
|
+
* typed return value without the cast hostility. `api`'s type is
|
|
27
|
+
* `ThrowingApi<TApi>` — the mapped type strips the Result wrapper.
|
|
14
28
|
*
|
|
15
29
|
* ```ts
|
|
16
|
-
* const {api
|
|
17
|
-
*
|
|
30
|
+
* const {api, api_result} = create_frontend_rpc_client<MyActionsApi>({
|
|
31
|
+
* specs: all_specs,
|
|
32
|
+
* });
|
|
33
|
+
* // hot path: await api.account_verify()
|
|
34
|
+
* // rare branch: const r = await api_result.account_verify(); if (!r.ok) { … }
|
|
18
35
|
* ```
|
|
19
36
|
*
|
|
20
|
-
* Returns the underlying `peer` and `environment` alongside
|
|
21
|
-
* advanced consumers (zzz-style frontends needing extra
|
|
22
|
-
* notification handlers / action-history wiring) can
|
|
23
|
-
* recreating the bundle.
|
|
37
|
+
* Returns the underlying `peer` and `environment` alongside the two api
|
|
38
|
+
* shapes so advanced consumers (zzz-style frontends needing extra
|
|
39
|
+
* transports / WS notification handlers / action-history wiring) can
|
|
40
|
+
* extend without recreating the bundle.
|
|
24
41
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* factory
|
|
28
|
-
* needs a different wiring shape (custom `environment.lookup_action_handler`).
|
|
42
|
+
* `local_call` specs in `specs` no-op unless `lookup_action_handler`
|
|
43
|
+
* resolves a handler for the `'execute'` phase. Frontend-side `local_call`
|
|
44
|
+
* is uncommon; the factory targets wire-dispatched actions by default.
|
|
29
45
|
*
|
|
30
46
|
* @module
|
|
31
47
|
*/
|
|
32
48
|
import { ActionPeer } from './action_peer.js';
|
|
33
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';
|
|
34
52
|
import type { ActionEventEnvironment } from './action_event_types.js';
|
|
35
53
|
import type { ActionSpecUnion } from './action_spec.js';
|
|
36
54
|
/** Options for `create_frontend_rpc_client`. */
|
|
37
|
-
export interface CreateFrontendRpcClientOptions {
|
|
55
|
+
export interface CreateFrontendRpcClientOptions<TApi extends object = object> {
|
|
38
56
|
/**
|
|
39
57
|
* Action specs the typed Proxy can dispatch. Methods absent from this
|
|
40
58
|
* list silently return `undefined` from the Proxy — the generic `TApi`
|
|
@@ -54,11 +72,62 @@ export interface CreateFrontendRpcClientOptions {
|
|
|
54
72
|
* WS+HTTP mixed setups.
|
|
55
73
|
*/
|
|
56
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
|
+
* Optional handler resolver. Wired onto `environment.lookup_action_handler`
|
|
99
|
+
* — the registry the dispatcher uses to find handlers for inbound
|
|
100
|
+
* messages and lifecycle phases. Defaults to `() => undefined`, which
|
|
101
|
+
* is fine for HTTP-only frontends that never receive a server-pushed
|
|
102
|
+
* notification or register a `receive_error` recovery hook.
|
|
103
|
+
*
|
|
104
|
+
* Common reasons to provide this:
|
|
105
|
+
* - **Server-pushed notifications over WS** — return a handler for
|
|
106
|
+
* `(method, 'receive')` so a `remote_notification` arriving on the
|
|
107
|
+
* socket dispatches to your subscriber bus (tx-style).
|
|
108
|
+
* - **Per-method retry / telemetry on errors** — return a handler for
|
|
109
|
+
* `(method, 'receive_error')`. Note that as of the
|
|
110
|
+
* `extract_action_result` fix, a missing handler already produces
|
|
111
|
+
* `{ok: false, error}` — the stub is no longer required just to
|
|
112
|
+
* surface server errors.
|
|
113
|
+
*/
|
|
114
|
+
lookup_action_handler?: ActionEventEnvironment['lookup_action_handler'];
|
|
57
115
|
}
|
|
58
116
|
/** Bundle returned by `create_frontend_rpc_client`. */
|
|
59
117
|
export interface FrontendRpcClient<TApi> {
|
|
60
|
-
/**
|
|
61
|
-
|
|
118
|
+
/**
|
|
119
|
+
* Typed throwing Proxy. `await api.method(input)` returns the unwrapped
|
|
120
|
+
* value or throws an `Error` with `{code, data}` from the JSON-RPC
|
|
121
|
+
* error. Default for call sites that don't inspect errors.
|
|
122
|
+
*/
|
|
123
|
+
api: ThrowingApi<TApi>;
|
|
124
|
+
/**
|
|
125
|
+
* Typed Result-shaped Proxy. `await api_result.method(input)` returns
|
|
126
|
+
* `Result<{value}, {error: JsonrpcErrorObject}>`. Use when call sites
|
|
127
|
+
* inspect `error.data.reason` without try/catch, or when Error
|
|
128
|
+
* allocation per `{ok: false}` would be wasteful.
|
|
129
|
+
*/
|
|
130
|
+
api_result: TApi;
|
|
62
131
|
/** Underlying peer — exposed for consumers that need to register more transports or send raw messages. */
|
|
63
132
|
peer: ActionPeer;
|
|
64
133
|
/** Action environment — exposed for consumers that need to share it (e.g. attach a notification handler registry). */
|
|
@@ -67,8 +136,11 @@ export interface FrontendRpcClient<TApi> {
|
|
|
67
136
|
/**
|
|
68
137
|
* Build a frontend-only typed RPC client.
|
|
69
138
|
*
|
|
70
|
-
* @param options - `specs` (required), optional `path` / `transports`
|
|
71
|
-
*
|
|
139
|
+
* @param options - `specs` (required), optional `path` / `transports` /
|
|
140
|
+
* `transport_for_method` / `on_action_event`
|
|
141
|
+
* @returns `{api, api_result, peer, environment}` — both Proxy shapes plus
|
|
142
|
+
* the underlying primitives. `api` throws on `{ok: false}`; `api_result`
|
|
143
|
+
* returns the Result.
|
|
72
144
|
*/
|
|
73
|
-
export declare const create_frontend_rpc_client: <TApi>(options: CreateFrontendRpcClientOptions) => FrontendRpcClient<TApi>;
|
|
145
|
+
export declare const create_frontend_rpc_client: <TApi extends object>(options: CreateFrontendRpcClientOptions<TApi>) => FrontendRpcClient<TApi>;
|
|
74
146
|
//# sourceMappingURL=frontend_rpc_client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontend_rpc_client.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/frontend_rpc_client.ts"],"names":[],"mappings":"AAAA
|
|
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;IACpE;;;;;;;;;;;;;;;;OAgBG;IACH,qBAAqB,CAAC,EAAE,sBAAsB,CAAC,uBAAuB,CAAC,CAAC;CACxE;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"}
|
|
@@ -2,30 +2,46 @@
|
|
|
2
2
|
* Frontend-only typed RPC client factory.
|
|
3
3
|
*
|
|
4
4
|
* Bundles the `ActionRegistry + ActionEventEnvironment + Transports +
|
|
5
|
-
* ActionPeer + create_rpc_client` boilerplate every
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* ActionPeer + create_rpc_client + create_throwing_api` boilerplate every
|
|
6
|
+
* consumer repeats. `lookup_action_handler` defaults to `() => undefined`
|
|
7
|
+
* (HTTP-only frontends rarely need handlers); pass `options.lookup_action_handler`
|
|
8
|
+
* to wire WS-pushed `remote_notification` dispatch or a `receive_error` /
|
|
9
|
+
* `local_call` hook.
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
11
|
+
* Returns both Proxy shapes from one factory call:
|
|
12
|
+
*
|
|
13
|
+
* - `api` — typed throwing Proxy. `await api.foo(input)` returns the
|
|
14
|
+
* unwrapped value or throws an `Error` carrying `{code, data}` from the
|
|
15
|
+
* JSON-RPC error. Use at hot-path call sites.
|
|
16
|
+
* - `api_result` — typed Result-shaped Proxy. `await api_result.foo(input)`
|
|
17
|
+
* returns `Result<{value}, {error: JsonrpcErrorObject}>`. Use when call
|
|
18
|
+
* sites want to inspect `error.data.reason` without try/catch — and
|
|
19
|
+
* anywhere allocating an `Error` per `{ok: false}` is wasteful (e.g.
|
|
20
|
+
* reconnect-storm `service_unavailable` paths). Result is the protocol
|
|
21
|
+
* primitive; the throwing form is a wrapper over it. Both share the
|
|
22
|
+
* same underlying transport — pick per call site, no construction cost.
|
|
12
23
|
*
|
|
13
|
-
*
|
|
24
|
+
* Generic `TApi` is the consumer's typed Proxy interface. The `as unknown
|
|
25
|
+
* as TApi` double cast happens inside the helper so call sites get a
|
|
26
|
+
* typed return value without the cast hostility. `api`'s type is
|
|
27
|
+
* `ThrowingApi<TApi>` — the mapped type strips the Result wrapper.
|
|
14
28
|
*
|
|
15
29
|
* ```ts
|
|
16
|
-
* const {api
|
|
17
|
-
*
|
|
30
|
+
* const {api, api_result} = create_frontend_rpc_client<MyActionsApi>({
|
|
31
|
+
* specs: all_specs,
|
|
32
|
+
* });
|
|
33
|
+
* // hot path: await api.account_verify()
|
|
34
|
+
* // rare branch: const r = await api_result.account_verify(); if (!r.ok) { … }
|
|
18
35
|
* ```
|
|
19
36
|
*
|
|
20
|
-
* Returns the underlying `peer` and `environment` alongside
|
|
21
|
-
* advanced consumers (zzz-style frontends needing extra
|
|
22
|
-
* notification handlers / action-history wiring) can
|
|
23
|
-
* recreating the bundle.
|
|
37
|
+
* Returns the underlying `peer` and `environment` alongside the two api
|
|
38
|
+
* shapes so advanced consumers (zzz-style frontends needing extra
|
|
39
|
+
* transports / WS notification handlers / action-history wiring) can
|
|
40
|
+
* extend without recreating the bundle.
|
|
24
41
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* factory
|
|
28
|
-
* needs a different wiring shape (custom `environment.lookup_action_handler`).
|
|
42
|
+
* `local_call` specs in `specs` no-op unless `lookup_action_handler`
|
|
43
|
+
* resolves a handler for the `'execute'` phase. Frontend-side `local_call`
|
|
44
|
+
* is uncommon; the factory targets wire-dispatched actions by default.
|
|
29
45
|
*
|
|
30
46
|
* @module
|
|
31
47
|
*/
|
|
@@ -33,19 +49,22 @@ import { ActionRegistry } from './action_registry.js';
|
|
|
33
49
|
import { ActionPeer } from './action_peer.js';
|
|
34
50
|
import { Transports } from './transports.js';
|
|
35
51
|
import { FrontendHttpTransport } from './transports_http.js';
|
|
36
|
-
import { create_rpc_client } from './rpc_client.js';
|
|
52
|
+
import { create_rpc_client, create_throwing_api, } from './rpc_client.js';
|
|
37
53
|
/**
|
|
38
54
|
* Build a frontend-only typed RPC client.
|
|
39
55
|
*
|
|
40
|
-
* @param options - `specs` (required), optional `path` / `transports`
|
|
41
|
-
*
|
|
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.
|
|
42
61
|
*/
|
|
43
62
|
export const create_frontend_rpc_client = (options) => {
|
|
44
63
|
const registry = new ActionRegistry([...options.specs]);
|
|
45
64
|
const environment = {
|
|
46
65
|
executor: 'frontend',
|
|
47
66
|
lookup_action_spec: (method) => registry.spec_by_method.get(method),
|
|
48
|
-
lookup_action_handler: () => undefined,
|
|
67
|
+
lookup_action_handler: options.lookup_action_handler ?? (() => undefined),
|
|
49
68
|
};
|
|
50
69
|
const transports = new Transports();
|
|
51
70
|
if (options.transports) {
|
|
@@ -56,6 +75,12 @@ export const create_frontend_rpc_client = (options) => {
|
|
|
56
75
|
transports.register_transport(new FrontendHttpTransport(options.path ?? '/api/rpc'));
|
|
57
76
|
}
|
|
58
77
|
const peer = new ActionPeer({ environment, transports });
|
|
59
|
-
const
|
|
60
|
-
|
|
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 };
|
|
61
86
|
};
|