@solarisdk/core 0.1.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/LICENSE +17 -0
- package/README.md +21 -0
- package/dist/desktop.d.ts +106 -0
- package/dist/desktop.d.ts.map +1 -0
- package/dist/desktop.js +170 -0
- package/dist/desktop.js.map +1 -0
- package/dist/errors.d.ts +50 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +91 -0
- package/dist/errors.js.map +1 -0
- package/dist/handle.d.ts +169 -0
- package/dist/handle.d.ts.map +1 -0
- package/dist/handle.js +386 -0
- package/dist/handle.js.map +1 -0
- package/dist/http.d.ts +35 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +106 -0
- package/dist/http.js.map +1 -0
- package/dist/image.d.ts +85 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +126 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/sandbox.d.ts +8 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +15 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/template-client.d.ts +35 -0
- package/dist/template-client.d.ts.map +1 -0
- package/dist/template-client.js +95 -0
- package/dist/template-client.js.map +1 -0
- package/dist/transport.d.ts +108 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +337 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +591 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/ws.d.ts +18 -0
- package/dist/ws.d.ts.map +1 -0
- package/dist/ws.js +63 -0
- package/dist/ws.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/** A streamed exec chunk delivered to a v1 per-`id` stream handler. */
|
|
2
|
+
export type RpcStreamHandler = (chunk: {
|
|
3
|
+
stream: "stdout" | "stderr";
|
|
4
|
+
data?: string;
|
|
5
|
+
}) => void;
|
|
6
|
+
/**
|
|
7
|
+
* A v2 async STREAM frame: `{type, ...}` carrying its own stream id
|
|
8
|
+
* (cmdId/ptyId/watchId) and payload. Delivered to a per-`type` frame handler.
|
|
9
|
+
*/
|
|
10
|
+
export interface AsyncFrame {
|
|
11
|
+
type: string;
|
|
12
|
+
cmdId?: string;
|
|
13
|
+
ptyId?: string;
|
|
14
|
+
watchId?: string;
|
|
15
|
+
base64?: string;
|
|
16
|
+
exitCode?: number;
|
|
17
|
+
path?: string;
|
|
18
|
+
[k: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
export type AsyncFrameHandler = (frame: AsyncFrame) => void;
|
|
21
|
+
export interface ControlChannelConfig {
|
|
22
|
+
/** Per-call RPC timeout in milliseconds. Defaults to 30s. */
|
|
23
|
+
callTimeoutMs?: number;
|
|
24
|
+
/** Extra headers for the control WS upgrade (Node only). */
|
|
25
|
+
headers?: Record<string, string>;
|
|
26
|
+
}
|
|
27
|
+
/** The shared control-WS transport. Construct one per live session handle. */
|
|
28
|
+
export declare class ControlChannel {
|
|
29
|
+
#private;
|
|
30
|
+
private controlUrl;
|
|
31
|
+
private ws;
|
|
32
|
+
private connecting;
|
|
33
|
+
private rxBuffer;
|
|
34
|
+
private nextId;
|
|
35
|
+
private readonly pending;
|
|
36
|
+
private readonly streamHandlers;
|
|
37
|
+
/** v2 async frame handlers keyed by `type` then by stream-id. */
|
|
38
|
+
private readonly frameHandlers;
|
|
39
|
+
/**
|
|
40
|
+
* Frames that arrived before their handler was registered, keyed by
|
|
41
|
+
* `type streamId`. The guest can stream `cmd.data`/`cmd.exit` onto the
|
|
42
|
+
* wire BEFORE the `cmd.start` reply (the output pumps start before the reply
|
|
43
|
+
* is sent), so frames routinely beat the caller's onFrame() registration.
|
|
44
|
+
* We stash them here and flush on registration so no early output is lost.
|
|
45
|
+
* Bounded to avoid unbounded growth from frames whose handler never arrives.
|
|
46
|
+
*/
|
|
47
|
+
private readonly orphanFrames;
|
|
48
|
+
private orphanCount;
|
|
49
|
+
private static readonly MAX_ORPHAN_FRAMES;
|
|
50
|
+
/**
|
|
51
|
+
* Per-stream "the channel closed" callbacks, keyed by stream id (cmdId/ptyId).
|
|
52
|
+
* A long-running `commands.run` awaits an exit that arrives as an async frame,
|
|
53
|
+
* NOT a pending RPC reply — so a channel drop must reject it through here, or
|
|
54
|
+
* `wait()` hangs forever.
|
|
55
|
+
*/
|
|
56
|
+
private readonly streamClosers;
|
|
57
|
+
private readonly callTimeoutMs;
|
|
58
|
+
private readonly headers;
|
|
59
|
+
private closed;
|
|
60
|
+
constructor(controlUrl: string, config?: ControlChannelConfig);
|
|
61
|
+
get connected(): boolean;
|
|
62
|
+
/** Point the channel at a (possibly new) control URL — used by reconnect/resume. */
|
|
63
|
+
setControlUrl(url: string): void;
|
|
64
|
+
/**
|
|
65
|
+
* Open the control WebSocket. Idempotent: concurrent callers share one
|
|
66
|
+
* in-flight connect, and an already-open socket resolves immediately.
|
|
67
|
+
*/
|
|
68
|
+
connect(): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Reconnect the control channel after a drop (re-opens the socket; pending
|
|
71
|
+
* calls were already rejected on close). Stream/frame handlers registered by
|
|
72
|
+
* `commands.start` / `pty.create` survive so callers can re-subscribe.
|
|
73
|
+
*/
|
|
74
|
+
reconnect(): Promise<void>;
|
|
75
|
+
/** Register a "channel closed" callback for a stream id (cmdId/ptyId) so a
|
|
76
|
+
* drop rejects its in-flight wait. Cleared on the stream's terminal frame. */
|
|
77
|
+
onStreamClose(id: string, cb: (err: Error) => void): void;
|
|
78
|
+
/** Drop a previously-registered stream-close callback (terminal frame seen). */
|
|
79
|
+
offStreamClose(id: string): void;
|
|
80
|
+
/** Close the channel and reject any in-flight calls. */
|
|
81
|
+
close(): void;
|
|
82
|
+
/** Register a handler for an async-frame `type` keyed on its stream id.
|
|
83
|
+
* Flushes any frames that arrived before this registration (see orphanFrames). */
|
|
84
|
+
onFrame(type: string, id: string, handler: AsyncFrameHandler): void;
|
|
85
|
+
/** Drop a previously-registered async-frame handler. */
|
|
86
|
+
offFrame(type: string, id: string): void;
|
|
87
|
+
private onMessage;
|
|
88
|
+
private dispatch;
|
|
89
|
+
private onClose;
|
|
90
|
+
/** Allocate the next opaque request id (string, per CONTRACTS §4). */
|
|
91
|
+
newId(): string;
|
|
92
|
+
/**
|
|
93
|
+
* Send one JSON-RPC call and await its correlated reply. `onStream` (v1)
|
|
94
|
+
* receives intermediate `{id, stream, data}` frames for this id until the
|
|
95
|
+
* terminal reply resolves the promise. `id` lets callers pre-allocate the
|
|
96
|
+
* request id (so they can subscribe to v2 async frames keyed on the same id).
|
|
97
|
+
*/
|
|
98
|
+
call<T>(method: string, params: unknown, opts?: {
|
|
99
|
+
onStream?: RpcStreamHandler;
|
|
100
|
+
id?: string;
|
|
101
|
+
timeoutMs?: number;
|
|
102
|
+
}): Promise<T>;
|
|
103
|
+
}
|
|
104
|
+
/** Base64 → bytes, working in both Node and the browser. */
|
|
105
|
+
export declare function base64ToBytes(b64: string): Uint8Array;
|
|
106
|
+
/** Bytes → base64, working in both Node and the browser. */
|
|
107
|
+
export declare function bytesToBase64(bytes: Uint8Array): string;
|
|
108
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
// `ControlChannel` — the shared control-WebSocket transport used by both
|
|
2
|
+
// {@link Sandbox} and {@link Desktop}.
|
|
3
|
+
//
|
|
4
|
+
// Wire protocol over the control WS is newline-delimited JSON. THREE kinds of
|
|
5
|
+
// frame travel on the same socket and must be dispatched by SHAPE, not by an
|
|
6
|
+
// assumption of one-reply-per-request:
|
|
7
|
+
//
|
|
8
|
+
// 1. JSON-RPC reply `{id, ok, result?|error?}` — resolves the call `id`.
|
|
9
|
+
// 2. v1 streamed exec `{id, stream:"stdout"|"stderr", data}` — routed to
|
|
10
|
+
// the per-`id` stream handler; the terminal frame is a normal reply (1).
|
|
11
|
+
// 3. v2 async STREAM frame `{type:"cmd.data"|"cmd.exit"|"pty.data"|"fs.event",
|
|
12
|
+
// cmdId|ptyId|watchId, ...}` — these are NOT correlated to a request id;
|
|
13
|
+
// they are dispatched by `type` to a handler keyed on the stream's own id
|
|
14
|
+
// (cmdId/ptyId/watchId). `cmd.exit` is terminal for a started command.
|
|
15
|
+
//
|
|
16
|
+
// The channel owns reconnection-friendly state (pending map, stream handlers,
|
|
17
|
+
// frame handlers) so handles can layer ergonomic methods on top.
|
|
18
|
+
import { ActionError, ConnectionError, PinetreeError, TimeoutError, } from "./errors.js";
|
|
19
|
+
import { openWebSocket } from "./ws.js";
|
|
20
|
+
const DEFAULT_CALL_TIMEOUT_MS = 30_000;
|
|
21
|
+
const CONNECT_TIMEOUT_MS = 15_000;
|
|
22
|
+
/** The shared control-WS transport. Construct one per live session handle. */
|
|
23
|
+
export class ControlChannel {
|
|
24
|
+
controlUrl;
|
|
25
|
+
ws = null;
|
|
26
|
+
connecting = null;
|
|
27
|
+
rxBuffer = "";
|
|
28
|
+
nextId = 1;
|
|
29
|
+
pending = new Map();
|
|
30
|
+
streamHandlers = new Map();
|
|
31
|
+
/** v2 async frame handlers keyed by `type` then by stream-id. */
|
|
32
|
+
frameHandlers = new Map();
|
|
33
|
+
/**
|
|
34
|
+
* Frames that arrived before their handler was registered, keyed by
|
|
35
|
+
* `type streamId`. The guest can stream `cmd.data`/`cmd.exit` onto the
|
|
36
|
+
* wire BEFORE the `cmd.start` reply (the output pumps start before the reply
|
|
37
|
+
* is sent), so frames routinely beat the caller's onFrame() registration.
|
|
38
|
+
* We stash them here and flush on registration so no early output is lost.
|
|
39
|
+
* Bounded to avoid unbounded growth from frames whose handler never arrives.
|
|
40
|
+
*/
|
|
41
|
+
orphanFrames = new Map();
|
|
42
|
+
orphanCount = 0;
|
|
43
|
+
static MAX_ORPHAN_FRAMES = 1024;
|
|
44
|
+
/**
|
|
45
|
+
* Per-stream "the channel closed" callbacks, keyed by stream id (cmdId/ptyId).
|
|
46
|
+
* A long-running `commands.run` awaits an exit that arrives as an async frame,
|
|
47
|
+
* NOT a pending RPC reply — so a channel drop must reject it through here, or
|
|
48
|
+
* `wait()` hangs forever.
|
|
49
|
+
*/
|
|
50
|
+
streamClosers = new Map();
|
|
51
|
+
callTimeoutMs;
|
|
52
|
+
headers;
|
|
53
|
+
closed = false;
|
|
54
|
+
constructor(controlUrl, config = {}) {
|
|
55
|
+
this.controlUrl = controlUrl;
|
|
56
|
+
this.callTimeoutMs = config.callTimeoutMs ?? DEFAULT_CALL_TIMEOUT_MS;
|
|
57
|
+
this.headers = config.headers ?? {};
|
|
58
|
+
}
|
|
59
|
+
get connected() {
|
|
60
|
+
return this.ws?.isOpen ?? false;
|
|
61
|
+
}
|
|
62
|
+
/** Point the channel at a (possibly new) control URL — used by reconnect/resume. */
|
|
63
|
+
setControlUrl(url) {
|
|
64
|
+
this.controlUrl = url;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Open the control WebSocket. Idempotent: concurrent callers share one
|
|
68
|
+
* in-flight connect, and an already-open socket resolves immediately.
|
|
69
|
+
*/
|
|
70
|
+
connect() {
|
|
71
|
+
if (this.connected)
|
|
72
|
+
return Promise.resolve();
|
|
73
|
+
if (this.connecting)
|
|
74
|
+
return this.connecting;
|
|
75
|
+
this.closed = false;
|
|
76
|
+
this.connecting = new Promise((resolve, reject) => {
|
|
77
|
+
let settled = false;
|
|
78
|
+
const timer = setTimeout(() => {
|
|
79
|
+
if (settled)
|
|
80
|
+
return;
|
|
81
|
+
settled = true;
|
|
82
|
+
this.connecting = null;
|
|
83
|
+
reject(new TimeoutError("connect", CONNECT_TIMEOUT_MS));
|
|
84
|
+
}, CONNECT_TIMEOUT_MS);
|
|
85
|
+
openWebSocket(this.controlUrl, this.headers, {
|
|
86
|
+
onOpen: () => {
|
|
87
|
+
if (settled)
|
|
88
|
+
return;
|
|
89
|
+
settled = true;
|
|
90
|
+
clearTimeout(timer);
|
|
91
|
+
this.connecting = null;
|
|
92
|
+
resolve();
|
|
93
|
+
},
|
|
94
|
+
onMessage: (data) => this.onMessage(data),
|
|
95
|
+
onClose: (code, reason) => this.onClose(code, reason),
|
|
96
|
+
onError: (err) => {
|
|
97
|
+
if (!settled) {
|
|
98
|
+
settled = true;
|
|
99
|
+
clearTimeout(timer);
|
|
100
|
+
this.connecting = null;
|
|
101
|
+
reject(err);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
.then((sock) => {
|
|
106
|
+
this.ws = sock;
|
|
107
|
+
})
|
|
108
|
+
.catch((err) => {
|
|
109
|
+
if (settled)
|
|
110
|
+
return;
|
|
111
|
+
settled = true;
|
|
112
|
+
clearTimeout(timer);
|
|
113
|
+
this.connecting = null;
|
|
114
|
+
reject(err instanceof Error ? err : new ConnectionError(String(err)));
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
return this.connecting;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Reconnect the control channel after a drop (re-opens the socket; pending
|
|
121
|
+
* calls were already rejected on close). Stream/frame handlers registered by
|
|
122
|
+
* `commands.start` / `pty.create` survive so callers can re-subscribe.
|
|
123
|
+
*/
|
|
124
|
+
async reconnect() {
|
|
125
|
+
if (this.connected)
|
|
126
|
+
return;
|
|
127
|
+
this.ws?.close();
|
|
128
|
+
this.ws = null;
|
|
129
|
+
this.connecting = null;
|
|
130
|
+
await this.connect();
|
|
131
|
+
}
|
|
132
|
+
/** Register a "channel closed" callback for a stream id (cmdId/ptyId) so a
|
|
133
|
+
* drop rejects its in-flight wait. Cleared on the stream's terminal frame. */
|
|
134
|
+
onStreamClose(id, cb) {
|
|
135
|
+
this.streamClosers.set(id, cb);
|
|
136
|
+
}
|
|
137
|
+
/** Drop a previously-registered stream-close callback (terminal frame seen). */
|
|
138
|
+
offStreamClose(id) {
|
|
139
|
+
this.streamClosers.delete(id);
|
|
140
|
+
}
|
|
141
|
+
/** Reject every in-flight call + stream wait and drop all handler state. */
|
|
142
|
+
#failAll(err) {
|
|
143
|
+
for (const p of this.pending.values()) {
|
|
144
|
+
clearTimeout(p.timer);
|
|
145
|
+
p.reject(err);
|
|
146
|
+
}
|
|
147
|
+
this.pending.clear();
|
|
148
|
+
for (const cb of this.streamClosers.values())
|
|
149
|
+
cb(err);
|
|
150
|
+
this.streamClosers.clear();
|
|
151
|
+
this.streamHandlers.clear();
|
|
152
|
+
this.frameHandlers.clear();
|
|
153
|
+
this.orphanFrames.clear();
|
|
154
|
+
this.orphanCount = 0;
|
|
155
|
+
}
|
|
156
|
+
/** Close the channel and reject any in-flight calls. */
|
|
157
|
+
close() {
|
|
158
|
+
this.closed = true;
|
|
159
|
+
this.#failAll(new ConnectionError("Control channel closed"));
|
|
160
|
+
this.ws?.close();
|
|
161
|
+
this.ws = null;
|
|
162
|
+
}
|
|
163
|
+
// --- async frame subscriptions (v2) --------------------------------------
|
|
164
|
+
/** Register a handler for an async-frame `type` keyed on its stream id.
|
|
165
|
+
* Flushes any frames that arrived before this registration (see orphanFrames). */
|
|
166
|
+
onFrame(type, id, handler) {
|
|
167
|
+
let m = this.frameHandlers.get(type);
|
|
168
|
+
if (!m) {
|
|
169
|
+
m = new Map();
|
|
170
|
+
this.frameHandlers.set(type, m);
|
|
171
|
+
}
|
|
172
|
+
m.set(id, handler);
|
|
173
|
+
const key = type + "\x1f" + id;
|
|
174
|
+
const pending = this.orphanFrames.get(key);
|
|
175
|
+
if (pending) {
|
|
176
|
+
this.orphanFrames.delete(key);
|
|
177
|
+
this.orphanCount -= pending.length;
|
|
178
|
+
for (const f of pending)
|
|
179
|
+
handler(f);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/** Drop a previously-registered async-frame handler. */
|
|
183
|
+
offFrame(type, id) {
|
|
184
|
+
this.frameHandlers.get(type)?.delete(id);
|
|
185
|
+
}
|
|
186
|
+
// --- transport -----------------------------------------------------------
|
|
187
|
+
onMessage(chunk) {
|
|
188
|
+
this.rxBuffer += chunk;
|
|
189
|
+
let nl;
|
|
190
|
+
while ((nl = this.rxBuffer.indexOf("\n")) !== -1) {
|
|
191
|
+
const line = this.rxBuffer.slice(0, nl).trim();
|
|
192
|
+
this.rxBuffer = this.rxBuffer.slice(nl + 1);
|
|
193
|
+
if (line.length > 0)
|
|
194
|
+
this.dispatch(line);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
dispatch(line) {
|
|
198
|
+
let frame;
|
|
199
|
+
try {
|
|
200
|
+
frame = JSON.parse(line);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return; // ignore non-JSON noise
|
|
204
|
+
}
|
|
205
|
+
// (3) v2 async STREAM frame: dispatched by `type` + its own stream id.
|
|
206
|
+
if (typeof frame.type === "string") {
|
|
207
|
+
const streamId = frame.cmdId ?? frame.ptyId ?? frame.watchId ?? "";
|
|
208
|
+
const handler = this.frameHandlers.get(frame.type)?.get(streamId);
|
|
209
|
+
if (handler) {
|
|
210
|
+
handler(frame);
|
|
211
|
+
}
|
|
212
|
+
else if (this.orphanCount < ControlChannel.MAX_ORPHAN_FRAMES) {
|
|
213
|
+
// Handler not registered yet — stash for flush on onFrame() (guest can
|
|
214
|
+
// stream output before the start-reply the caller awaits to subscribe).
|
|
215
|
+
const key = frame.type + "\x1f" + streamId;
|
|
216
|
+
let arr = this.orphanFrames.get(key);
|
|
217
|
+
if (!arr) {
|
|
218
|
+
arr = [];
|
|
219
|
+
this.orphanFrames.set(key, arr);
|
|
220
|
+
}
|
|
221
|
+
arr.push(frame);
|
|
222
|
+
this.orphanCount++;
|
|
223
|
+
}
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (typeof frame.id !== "string")
|
|
227
|
+
return;
|
|
228
|
+
// (2) v1 streamed-exec frame: route to the per-id stream handler; the call
|
|
229
|
+
// stays pending for its terminal {ok,...} reply.
|
|
230
|
+
if (typeof frame.stream === "string") {
|
|
231
|
+
const h = this.streamHandlers.get(frame.id);
|
|
232
|
+
if (h)
|
|
233
|
+
h({ stream: frame.stream, data: frame.data });
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
// (1) JSON-RPC reply.
|
|
237
|
+
const p = this.pending.get(frame.id);
|
|
238
|
+
if (!p)
|
|
239
|
+
return;
|
|
240
|
+
this.pending.delete(frame.id);
|
|
241
|
+
this.streamHandlers.delete(frame.id);
|
|
242
|
+
clearTimeout(p.timer);
|
|
243
|
+
if (frame.ok) {
|
|
244
|
+
p.resolve(frame.result);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
const { message, code } = normalizeRpcError(frame.error);
|
|
248
|
+
p.reject(new ActionError(p.method, message, code));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
onClose(code, reason) {
|
|
252
|
+
this.ws = null;
|
|
253
|
+
// Always tear down handler state + reject in-flight calls AND stream waits.
|
|
254
|
+
// (Previously this early-returned when no RPC was pending, leaving a
|
|
255
|
+
// command's exit-wait — which is frame-based, not a pending RPC — to hang
|
|
256
|
+
// forever, and stale cmd/pty/orphan handlers registered against a dead VM.)
|
|
257
|
+
this.#failAll(new ConnectionError(`Control channel closed (${code}${reason ? `: ${reason}` : ""})`));
|
|
258
|
+
}
|
|
259
|
+
/** Allocate the next opaque request id (string, per CONTRACTS §4). */
|
|
260
|
+
newId() {
|
|
261
|
+
return String(this.nextId++);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Send one JSON-RPC call and await its correlated reply. `onStream` (v1)
|
|
265
|
+
* receives intermediate `{id, stream, data}` frames for this id until the
|
|
266
|
+
* terminal reply resolves the promise. `id` lets callers pre-allocate the
|
|
267
|
+
* request id (so they can subscribe to v2 async frames keyed on the same id).
|
|
268
|
+
*/
|
|
269
|
+
call(method, params, opts = {}) {
|
|
270
|
+
if (this.closed) {
|
|
271
|
+
return Promise.reject(new ConnectionError("Control channel is closed"));
|
|
272
|
+
}
|
|
273
|
+
if (!this.ws || !this.ws.isOpen) {
|
|
274
|
+
return Promise.reject(new ConnectionError("Not connected — call connect() first"));
|
|
275
|
+
}
|
|
276
|
+
const id = opts.id ?? this.newId();
|
|
277
|
+
const frame = JSON.stringify({ id, method, params });
|
|
278
|
+
const timeoutMs = opts.timeoutMs ?? this.callTimeoutMs;
|
|
279
|
+
return new Promise((resolve, reject) => {
|
|
280
|
+
const timer = setTimeout(() => {
|
|
281
|
+
this.pending.delete(id);
|
|
282
|
+
this.streamHandlers.delete(id);
|
|
283
|
+
reject(new TimeoutError(method, timeoutMs));
|
|
284
|
+
}, timeoutMs);
|
|
285
|
+
this.pending.set(id, {
|
|
286
|
+
resolve: resolve,
|
|
287
|
+
reject,
|
|
288
|
+
timer,
|
|
289
|
+
method,
|
|
290
|
+
});
|
|
291
|
+
if (opts.onStream)
|
|
292
|
+
this.streamHandlers.set(id, opts.onStream);
|
|
293
|
+
try {
|
|
294
|
+
this.ws.send(frame + "\n");
|
|
295
|
+
}
|
|
296
|
+
catch (err) {
|
|
297
|
+
this.pending.delete(id);
|
|
298
|
+
this.streamHandlers.delete(id);
|
|
299
|
+
clearTimeout(timer);
|
|
300
|
+
reject(err instanceof Error
|
|
301
|
+
? err
|
|
302
|
+
: new PinetreeError(`Failed to send "${method}"`));
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
function normalizeRpcError(err) {
|
|
308
|
+
if (err == null)
|
|
309
|
+
return { message: "Action failed" };
|
|
310
|
+
if (typeof err === "string")
|
|
311
|
+
return { message: err };
|
|
312
|
+
return { message: err.message ?? err.code ?? "Action failed", code: err.code };
|
|
313
|
+
}
|
|
314
|
+
/** Base64 → bytes, working in both Node and the browser. */
|
|
315
|
+
export function base64ToBytes(b64) {
|
|
316
|
+
const g = globalThis;
|
|
317
|
+
if (typeof g.Buffer !== "undefined") {
|
|
318
|
+
return Uint8Array.from(g.Buffer.from(b64, "base64"));
|
|
319
|
+
}
|
|
320
|
+
const bin = g.atob(b64);
|
|
321
|
+
const out = new Uint8Array(bin.length);
|
|
322
|
+
for (let i = 0; i < bin.length; i++)
|
|
323
|
+
out[i] = bin.charCodeAt(i);
|
|
324
|
+
return out;
|
|
325
|
+
}
|
|
326
|
+
/** Bytes → base64, working in both Node and the browser. */
|
|
327
|
+
export function bytesToBase64(bytes) {
|
|
328
|
+
const g = globalThis;
|
|
329
|
+
if (typeof g.Buffer !== "undefined") {
|
|
330
|
+
return g.Buffer.from(bytes).toString("base64");
|
|
331
|
+
}
|
|
332
|
+
let bin = "";
|
|
333
|
+
for (let i = 0; i < bytes.length; i++)
|
|
334
|
+
bin += String.fromCharCode(bytes[i]);
|
|
335
|
+
return g.btoa(bin);
|
|
336
|
+
}
|
|
337
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|