@celestia-island/plana-rpc-client 0.1.5 → 0.1.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celestia-island/plana-rpc-client",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/celestia-island/plana"
package/src/client.ts CHANGED
@@ -565,7 +565,14 @@ export class RpcClient {
565
565
  clearTimeout(entry.timer);
566
566
  if (data.error) {
567
567
  const msg: string = data.error.message || "unknown rpc error";
568
- const kind: RpcErrorKind = data.error.code === -32003 ? "forbidden" : "rpc";
568
+ // Authorization denials only: -32002 is chest's dedicated
569
+ // admin/workspace-role gate code, -32005 its platform-proxy
570
+ // grant gate. -32003 is chest's GENERIC internal/upstream
571
+ // error code — classifying it as "forbidden" (the old
572
+ // mapping) made upstream faults render as permission
573
+ // problems in every consumer webui.
574
+ const kind: RpcErrorKind =
575
+ data.error.code === -32002 || data.error.code === -32005 ? "forbidden" : "rpc";
569
576
  entry.reject(new RpcError(kind, entry.method, msg));
570
577
  } else {
571
578
  entry.resolve(data.result);
@@ -832,7 +839,10 @@ export class RpcClient {
832
839
  const body = await resp.json() as any;
833
840
  if (body.error) {
834
841
  const msg: string = body.error.message || "unknown rpc error";
835
- const kind: RpcErrorKind = body.error.code === -32003 ? "forbidden" : "rpc";
842
+ // Same classification as the WS path: -32002/-32005 are chest's
843
+ // authorization-denial codes; -32003 is generic upstream/internal.
844
+ const kind: RpcErrorKind =
845
+ body.error.code === -32002 || body.error.code === -32005 ? "forbidden" : "rpc";
836
846
  throw new RpcError(kind, method, msg);
837
847
  }
838
848
  return body.result as T;
package/src/index.ts CHANGED
@@ -8,3 +8,7 @@ export type {
8
8
  RpcErrorKind,
9
9
  RpcNotification,
10
10
  } from "./client.js";
11
+
12
+ // Relay extension (edge side) — see docs/en/rpc/relay-profile.md.
13
+ export { RELAY, relayMethods, RelayClient, createTauriTransport, createMemoryTransport } from "./relay.js";
14
+ export type { RelayContext, EdgeFrame, EdgeTransport } from "./relay.js";
package/src/relay.ts ADDED
@@ -0,0 +1,153 @@
1
+ // Relay extension — the edge (webview/UI) side.
2
+ //
3
+ // Mirrors `plana-rpc-client`'s Rust `relay` module and `plana-tauri`'s
4
+ // bridge: one request/response transport function plus one notification
5
+ // lane, canonical JSON-RPC 2.0 frames, the reserved `relay.*`
6
+ // system-control method names, and the hop context for chains. A webview
7
+ // picks a transport (Tauri v2 global bridge or in-memory for tests) and
8
+ // speaks the same surface in every app.
9
+
10
+ /** Reserved system-control namespace (never forwarded). */
11
+ export const RELAY = "relay";
12
+
13
+ /** Pre-standardized `relay.*` method names (see docs/en/rpc/relay-profile.md). */
14
+ export const relayMethods = {
15
+ netSetProxy: "relay.net.set_proxy",
16
+ netConfigureEntrypoint: "relay.net.configure_entrypoint",
17
+ connOpen: "relay.conn.open",
18
+ fwdMap: "relay.fwd.map",
19
+ fwdList: "relay.fwd.list",
20
+ windowMinimize: "relay.window.minimize",
21
+ windowMaximizeToggle: "relay.window.maximize_toggle",
22
+ windowClose: "relay.window.close",
23
+ windowState: "relay.window.state",
24
+ } as const;
25
+
26
+ /** Per-frame relay context: hop count for the loop guard + trace. */
27
+ export interface RelayContext {
28
+ hops: number;
29
+ via?: string[];
30
+ }
31
+
32
+ /** One edge frame: canonical JSON-RPC request members + relay extension. */
33
+ export interface EdgeFrame {
34
+ method: string;
35
+ params?: unknown;
36
+ relay?: RelayContext;
37
+ }
38
+
39
+ /** The transport contract any framework adapter implements. */
40
+ export interface EdgeTransport {
41
+ call(frame: EdgeFrame): Promise<unknown>;
42
+ /** Subscribe to the notification lane; returns an unsubscribe. */
43
+ listen(handler: (method: string, params?: unknown) => void): () => void;
44
+ }
45
+
46
+ /**
47
+ * Tauri v2 transport: `bridge_call` invoke + the `bridge_event` lane.
48
+ * Reads the strict v2 global shape (`__TAURI__.core.invoke`).
49
+ */
50
+ export function createTauriTransport(): EdgeTransport {
51
+ const raw = (window as unknown as {
52
+ __TAURI__?: {
53
+ core?: { invoke?: (cmd: string, args?: Record<string, unknown>) => Promise<unknown> };
54
+ event?: {
55
+ listen?: (
56
+ event: string,
57
+ handler: (e: { payload: unknown }) => void,
58
+ ) => Promise<() => void>;
59
+ };
60
+ };
61
+ }).__TAURI__;
62
+ const invoke = raw?.core?.invoke;
63
+ const listen = raw?.event?.listen;
64
+ if (!invoke || !listen) {
65
+ throw new Error("tauri v2 global API unavailable (no withGlobalTauri shell)");
66
+ }
67
+ const subscribers = new Set<(method: string, params?: unknown) => void>();
68
+ let wired = false;
69
+ return {
70
+ async call(frame) {
71
+ return invoke("bridge_call", {
72
+ method: frame.method,
73
+ params: frame.params ?? null,
74
+ relay: frame.relay ?? null,
75
+ });
76
+ },
77
+ listen(handler) {
78
+ if (!wired) {
79
+ wired = true;
80
+ void listen("bridge_event", (e) => {
81
+ const payload = e.payload as { method?: string; params?: unknown };
82
+ if (payload?.method) {
83
+ for (const fn of subscribers) fn(payload.method, payload.params);
84
+ }
85
+ });
86
+ }
87
+ subscribers.add(handler);
88
+ return () => subscribers.delete(handler);
89
+ },
90
+ };
91
+ }
92
+
93
+ /** In-memory transport for tests and non-framework embedding. */
94
+ export function createMemoryTransport(
95
+ far: (frame: EdgeFrame) => Promise<unknown>,
96
+ ): EdgeTransport {
97
+ return {
98
+ call: (frame) => far(frame),
99
+ listen: () => () => {},
100
+ };
101
+ }
102
+
103
+ /** The edge client every UI surface shares. */
104
+ export class RelayClient {
105
+ constructor(private transport: EdgeTransport) {}
106
+
107
+ /** One request/response call; the transport owns correlation. */
108
+ call(method: string, params?: unknown, relay?: RelayContext): Promise<unknown> {
109
+ return this.transport.call({ method, params, relay });
110
+ }
111
+
112
+ /** Typed helpers over the standard system-control battery. */
113
+ setProxy(config: {
114
+ scheme: "http" | "https" | "socks5";
115
+ host: string;
116
+ username?: string;
117
+ password?: string;
118
+ }): Promise<unknown> {
119
+ return this.call(relayMethods.netSetProxy, config);
120
+ }
121
+
122
+ configureEntrypoint(url: string): Promise<unknown> {
123
+ return this.call(relayMethods.netConfigureEntrypoint, { url });
124
+ }
125
+
126
+ fwdMap(namespacePrefix: string, endpoint: string): Promise<unknown> {
127
+ return this.call(relayMethods.fwdMap, { namespacePrefix, endpoint });
128
+ }
129
+
130
+ fwdList(): Promise<unknown> {
131
+ return this.call(relayMethods.fwdList);
132
+ }
133
+
134
+ windowMinimize(): Promise<unknown> {
135
+ return this.call(relayMethods.windowMinimize);
136
+ }
137
+
138
+ windowMaximizeToggle(): Promise<unknown> {
139
+ return this.call(relayMethods.windowMaximizeToggle);
140
+ }
141
+
142
+ windowClose(): Promise<unknown> {
143
+ return this.call(relayMethods.windowClose);
144
+ }
145
+
146
+ windowState(): Promise<unknown> {
147
+ return this.call(relayMethods.windowState);
148
+ }
149
+
150
+ onNotification(handler: (method: string, params?: unknown) => void): () => void {
151
+ return this.transport.listen(handler);
152
+ }
153
+ }