@bigmistqke/rpc 0.1.2 → 0.1.4
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/.claude/settings.local.json +7 -0
- package/dist/fetch-node.js +139 -0
- package/dist/fetch-node.js.map +1 -1
- package/dist/fetch.d.ts +1 -1
- package/dist/fetch.js +148 -7
- package/dist/fetch.js.map +1 -1
- package/dist/handle-18d6fe9b.d.ts +24 -0
- package/dist/messenger.d.ts +80 -9
- package/dist/messenger.js +186 -40
- package/dist/messenger.js.map +1 -1
- package/dist/stream.d.ts +1 -1
- package/dist/stream.js +69 -28
- package/dist/stream.js.map +1 -1
- package/dist/{types-4d4495dd.d.ts → types-9f54da43.d.ts} +5 -1
- package/dist/websocket.d.ts +50 -0
- package/dist/websocket.js +617 -0
- package/dist/websocket.js.map +1 -0
- package/package.json +13 -7
- package/src/core.ts +100 -0
- package/src/fetch/index.ts +2 -1
- package/src/fetch/node.ts +1 -1
- package/src/handle.ts +48 -0
- package/src/{messenger.ts → messenger/index.ts} +163 -21
- package/src/{message-protocol.ts → protocol.ts} +11 -1
- package/src/server-send-events/index.ts +3 -2
- package/src/stream/encoding.ts +2 -2
- package/src/stream/index.ts +3 -14
- package/src/types.ts +6 -1
- package/src/utils.ts +0 -32
- package/src/websocket/index.ts +142 -0
- package/test/encoding.test.ts +4 -4
- package/test/fetch.test.ts +3 -3
- package/test/messenger.test.ts +255 -41
- package/test/{message-protocol.test.ts → protocol.test.ts} +7 -7
- package/test/sse.test.ts +3 -3
- package/test/stream.test.ts +4 -8
- package/test/utils.test.ts +1 -2
- package/test/websocket.test.ts +514 -0
- package/tsup.config.ts +2 -1
- package/LICENSE +0 -21
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
type Fn = (...arg: Array<any>) => any;
|
|
2
|
+
/** Check if T is a Handled type and extract the inner type */
|
|
3
|
+
type UnwrapHandled<T> = T extends {
|
|
4
|
+
readonly ['__rpc_handled__']: infer U extends object;
|
|
5
|
+
} ? RPC<U> : T;
|
|
2
6
|
type isObject<T> = T extends object ? true : false;
|
|
3
7
|
type HasMethod<T> = T extends object ? {
|
|
4
8
|
[K in keyof T]: T[K] extends Fn ? true : HasMethod<T[K]>;
|
|
@@ -19,7 +23,7 @@ type FilterNoResponseMethod<T> = {
|
|
|
19
23
|
/**********************************************************************************/
|
|
20
24
|
/**********************************************************************************/
|
|
21
25
|
interface ResponseMethod<T extends Fn> {
|
|
22
|
-
(...args: Parameters<T>): Promise<ReturnType<T
|
|
26
|
+
(...args: Parameters<T>): Promise<UnwrapHandled<Awaited<ReturnType<T>>>>;
|
|
23
27
|
}
|
|
24
28
|
type ResponseRPCNode<T> = T extends Fn ? ResponseMethod<T> : T extends readonly [any, ...any[]] ? {
|
|
25
29
|
[K in keyof T]: ResponseRPCNode<T[K]>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { R as RPC$1 } from './types-9f54da43.js';
|
|
2
|
+
export { H as Handled, h as handle } from './handle-18d6fe9b.js';
|
|
3
|
+
|
|
4
|
+
declare const $WEBSOCKET: unique symbol;
|
|
5
|
+
interface WebSocketLike {
|
|
6
|
+
send(data: string): void;
|
|
7
|
+
close(): void;
|
|
8
|
+
addEventListener(type: string, listener: (event: unknown) => void): void;
|
|
9
|
+
}
|
|
10
|
+
type RPC<T extends object, WS extends WebSocketLike = WebSocketLike> = RPC$1<T> & {
|
|
11
|
+
[$WEBSOCKET]: WS;
|
|
12
|
+
};
|
|
13
|
+
/**********************************************************************************/
|
|
14
|
+
/**********************************************************************************/
|
|
15
|
+
/**
|
|
16
|
+
* Exposes methods as an RPC endpoint over the given WebSocket.
|
|
17
|
+
*
|
|
18
|
+
* @param methods - Object containing methods to expose
|
|
19
|
+
* @param options - Target WebSocket and optional abort signal
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* expose({
|
|
24
|
+
* add: (a, b) => a + b,
|
|
25
|
+
* multiply: (a, b) => a * b,
|
|
26
|
+
* }, { to: ws })
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
declare function expose<TMethods extends object>(methods: TMethods, { to }: {
|
|
30
|
+
to: WebSocketLike;
|
|
31
|
+
}): void;
|
|
32
|
+
/**
|
|
33
|
+
* Creates an RPC proxy for calling remote methods on the given WebSocket.
|
|
34
|
+
*
|
|
35
|
+
* @param ws - The WebSocket to communicate with
|
|
36
|
+
* @param options - Optional abort signal
|
|
37
|
+
* @returns A proxy object for calling remote methods
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* const server = rpc<ServerMethods>(ws)
|
|
42
|
+
* const result = await server.add(2, 3)
|
|
43
|
+
*
|
|
44
|
+
* // Access underlying WebSocket
|
|
45
|
+
* server[$WEBSOCKET].close()
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
declare function rpc<T extends object, WS extends WebSocketLike = WebSocketLike>(ws: WS): RPC<T, WS>;
|
|
49
|
+
|
|
50
|
+
export { $WEBSOCKET, RPC, WebSocketLike, expose, rpc };
|
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
// ../../node_modules/.pnpm/valibot@1.0.0_typescript@5.7.2/node_modules/valibot/dist/index.js
|
|
2
|
+
var store;
|
|
3
|
+
function getGlobalConfig(config2) {
|
|
4
|
+
return {
|
|
5
|
+
lang: config2?.lang ?? store?.lang,
|
|
6
|
+
message: config2?.message,
|
|
7
|
+
abortEarly: config2?.abortEarly ?? store?.abortEarly,
|
|
8
|
+
abortPipeEarly: config2?.abortPipeEarly ?? store?.abortPipeEarly
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
var store2;
|
|
12
|
+
function getGlobalMessage(lang) {
|
|
13
|
+
return store2?.get(lang);
|
|
14
|
+
}
|
|
15
|
+
var store3;
|
|
16
|
+
function getSchemaMessage(lang) {
|
|
17
|
+
return store3?.get(lang);
|
|
18
|
+
}
|
|
19
|
+
var store4;
|
|
20
|
+
function getSpecificMessage(reference, lang) {
|
|
21
|
+
return store4?.get(reference)?.get(lang);
|
|
22
|
+
}
|
|
23
|
+
function _stringify(input) {
|
|
24
|
+
const type = typeof input;
|
|
25
|
+
if (type === "string") {
|
|
26
|
+
return `"${input}"`;
|
|
27
|
+
}
|
|
28
|
+
if (type === "number" || type === "bigint" || type === "boolean") {
|
|
29
|
+
return `${input}`;
|
|
30
|
+
}
|
|
31
|
+
if (type === "object" || type === "function") {
|
|
32
|
+
return (input && Object.getPrototypeOf(input)?.constructor?.name) ?? "null";
|
|
33
|
+
}
|
|
34
|
+
return type;
|
|
35
|
+
}
|
|
36
|
+
function _addIssue(context, label, dataset, config2, other) {
|
|
37
|
+
const input = other && "input" in other ? other.input : dataset.value;
|
|
38
|
+
const expected = other?.expected ?? context.expects ?? null;
|
|
39
|
+
const received = other?.received ?? _stringify(input);
|
|
40
|
+
const issue = {
|
|
41
|
+
kind: context.kind,
|
|
42
|
+
type: context.type,
|
|
43
|
+
input,
|
|
44
|
+
expected,
|
|
45
|
+
received,
|
|
46
|
+
message: `Invalid ${label}: ${expected ? `Expected ${expected} but r` : "R"}eceived ${received}`,
|
|
47
|
+
requirement: context.requirement,
|
|
48
|
+
path: other?.path,
|
|
49
|
+
issues: other?.issues,
|
|
50
|
+
lang: config2.lang,
|
|
51
|
+
abortEarly: config2.abortEarly,
|
|
52
|
+
abortPipeEarly: config2.abortPipeEarly
|
|
53
|
+
};
|
|
54
|
+
const isSchema = context.kind === "schema";
|
|
55
|
+
const message = other?.message ?? context.message ?? getSpecificMessage(context.reference, issue.lang) ?? (isSchema ? getSchemaMessage(issue.lang) : null) ?? config2.message ?? getGlobalMessage(issue.lang);
|
|
56
|
+
if (message !== void 0) {
|
|
57
|
+
issue.message = typeof message === "function" ? (
|
|
58
|
+
// @ts-expect-error
|
|
59
|
+
message(issue)
|
|
60
|
+
) : message;
|
|
61
|
+
}
|
|
62
|
+
if (isSchema) {
|
|
63
|
+
dataset.typed = false;
|
|
64
|
+
}
|
|
65
|
+
if (dataset.issues) {
|
|
66
|
+
dataset.issues.push(issue);
|
|
67
|
+
} else {
|
|
68
|
+
dataset.issues = [issue];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function _getStandardProps(context) {
|
|
72
|
+
return {
|
|
73
|
+
version: 1,
|
|
74
|
+
vendor: "valibot",
|
|
75
|
+
validate(value2) {
|
|
76
|
+
return context["~run"]({ value: value2 }, getGlobalConfig());
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function getFallback(schema, dataset, config2) {
|
|
81
|
+
return typeof schema.fallback === "function" ? (
|
|
82
|
+
// @ts-expect-error
|
|
83
|
+
schema.fallback(dataset, config2)
|
|
84
|
+
) : (
|
|
85
|
+
// @ts-expect-error
|
|
86
|
+
schema.fallback
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
function getDefault(schema, dataset, config2) {
|
|
90
|
+
return typeof schema.default === "function" ? (
|
|
91
|
+
// @ts-expect-error
|
|
92
|
+
schema.default(dataset, config2)
|
|
93
|
+
) : (
|
|
94
|
+
// @ts-expect-error
|
|
95
|
+
schema.default
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
function any() {
|
|
99
|
+
return {
|
|
100
|
+
kind: "schema",
|
|
101
|
+
type: "any",
|
|
102
|
+
reference: any,
|
|
103
|
+
expects: "any",
|
|
104
|
+
async: false,
|
|
105
|
+
get "~standard"() {
|
|
106
|
+
return _getStandardProps(this);
|
|
107
|
+
},
|
|
108
|
+
"~run"(dataset) {
|
|
109
|
+
dataset.typed = true;
|
|
110
|
+
return dataset;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function array(item, message) {
|
|
115
|
+
return {
|
|
116
|
+
kind: "schema",
|
|
117
|
+
type: "array",
|
|
118
|
+
reference: array,
|
|
119
|
+
expects: "Array",
|
|
120
|
+
async: false,
|
|
121
|
+
item,
|
|
122
|
+
message,
|
|
123
|
+
get "~standard"() {
|
|
124
|
+
return _getStandardProps(this);
|
|
125
|
+
},
|
|
126
|
+
"~run"(dataset, config2) {
|
|
127
|
+
const input = dataset.value;
|
|
128
|
+
if (Array.isArray(input)) {
|
|
129
|
+
dataset.typed = true;
|
|
130
|
+
dataset.value = [];
|
|
131
|
+
for (let key = 0; key < input.length; key++) {
|
|
132
|
+
const value2 = input[key];
|
|
133
|
+
const itemDataset = this.item["~run"]({ value: value2 }, config2);
|
|
134
|
+
if (itemDataset.issues) {
|
|
135
|
+
const pathItem = {
|
|
136
|
+
type: "array",
|
|
137
|
+
origin: "value",
|
|
138
|
+
input,
|
|
139
|
+
key,
|
|
140
|
+
value: value2
|
|
141
|
+
};
|
|
142
|
+
for (const issue of itemDataset.issues) {
|
|
143
|
+
if (issue.path) {
|
|
144
|
+
issue.path.unshift(pathItem);
|
|
145
|
+
} else {
|
|
146
|
+
issue.path = [pathItem];
|
|
147
|
+
}
|
|
148
|
+
dataset.issues?.push(issue);
|
|
149
|
+
}
|
|
150
|
+
if (!dataset.issues) {
|
|
151
|
+
dataset.issues = itemDataset.issues;
|
|
152
|
+
}
|
|
153
|
+
if (config2.abortEarly) {
|
|
154
|
+
dataset.typed = false;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (!itemDataset.typed) {
|
|
159
|
+
dataset.typed = false;
|
|
160
|
+
}
|
|
161
|
+
dataset.value.push(itemDataset.value);
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
_addIssue(this, "type", dataset, config2);
|
|
165
|
+
}
|
|
166
|
+
return dataset;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function boolean(message) {
|
|
171
|
+
return {
|
|
172
|
+
kind: "schema",
|
|
173
|
+
type: "boolean",
|
|
174
|
+
reference: boolean,
|
|
175
|
+
expects: "boolean",
|
|
176
|
+
async: false,
|
|
177
|
+
message,
|
|
178
|
+
get "~standard"() {
|
|
179
|
+
return _getStandardProps(this);
|
|
180
|
+
},
|
|
181
|
+
"~run"(dataset, config2) {
|
|
182
|
+
if (typeof dataset.value === "boolean") {
|
|
183
|
+
dataset.typed = true;
|
|
184
|
+
} else {
|
|
185
|
+
_addIssue(this, "type", dataset, config2);
|
|
186
|
+
}
|
|
187
|
+
return dataset;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function number(message) {
|
|
192
|
+
return {
|
|
193
|
+
kind: "schema",
|
|
194
|
+
type: "number",
|
|
195
|
+
reference: number,
|
|
196
|
+
expects: "number",
|
|
197
|
+
async: false,
|
|
198
|
+
message,
|
|
199
|
+
get "~standard"() {
|
|
200
|
+
return _getStandardProps(this);
|
|
201
|
+
},
|
|
202
|
+
"~run"(dataset, config2) {
|
|
203
|
+
if (typeof dataset.value === "number" && !isNaN(dataset.value)) {
|
|
204
|
+
dataset.typed = true;
|
|
205
|
+
} else {
|
|
206
|
+
_addIssue(this, "type", dataset, config2);
|
|
207
|
+
}
|
|
208
|
+
return dataset;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function object(entries, message) {
|
|
213
|
+
return {
|
|
214
|
+
kind: "schema",
|
|
215
|
+
type: "object",
|
|
216
|
+
reference: object,
|
|
217
|
+
expects: "Object",
|
|
218
|
+
async: false,
|
|
219
|
+
entries,
|
|
220
|
+
message,
|
|
221
|
+
get "~standard"() {
|
|
222
|
+
return _getStandardProps(this);
|
|
223
|
+
},
|
|
224
|
+
"~run"(dataset, config2) {
|
|
225
|
+
const input = dataset.value;
|
|
226
|
+
if (input && typeof input === "object") {
|
|
227
|
+
dataset.typed = true;
|
|
228
|
+
dataset.value = {};
|
|
229
|
+
for (const key in this.entries) {
|
|
230
|
+
const valueSchema = this.entries[key];
|
|
231
|
+
if (key in input || (valueSchema.type === "exact_optional" || valueSchema.type === "optional" || valueSchema.type === "nullish") && // @ts-expect-error
|
|
232
|
+
valueSchema.default !== void 0) {
|
|
233
|
+
const value2 = key in input ? (
|
|
234
|
+
// @ts-expect-error
|
|
235
|
+
input[key]
|
|
236
|
+
) : getDefault(valueSchema);
|
|
237
|
+
const valueDataset = valueSchema["~run"]({ value: value2 }, config2);
|
|
238
|
+
if (valueDataset.issues) {
|
|
239
|
+
const pathItem = {
|
|
240
|
+
type: "object",
|
|
241
|
+
origin: "value",
|
|
242
|
+
input,
|
|
243
|
+
key,
|
|
244
|
+
value: value2
|
|
245
|
+
};
|
|
246
|
+
for (const issue of valueDataset.issues) {
|
|
247
|
+
if (issue.path) {
|
|
248
|
+
issue.path.unshift(pathItem);
|
|
249
|
+
} else {
|
|
250
|
+
issue.path = [pathItem];
|
|
251
|
+
}
|
|
252
|
+
dataset.issues?.push(issue);
|
|
253
|
+
}
|
|
254
|
+
if (!dataset.issues) {
|
|
255
|
+
dataset.issues = valueDataset.issues;
|
|
256
|
+
}
|
|
257
|
+
if (config2.abortEarly) {
|
|
258
|
+
dataset.typed = false;
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (!valueDataset.typed) {
|
|
263
|
+
dataset.typed = false;
|
|
264
|
+
}
|
|
265
|
+
dataset.value[key] = valueDataset.value;
|
|
266
|
+
} else if (valueSchema.fallback !== void 0) {
|
|
267
|
+
dataset.value[key] = getFallback(valueSchema);
|
|
268
|
+
} else if (valueSchema.type !== "exact_optional" && valueSchema.type !== "optional" && valueSchema.type !== "nullish") {
|
|
269
|
+
_addIssue(this, "key", dataset, config2, {
|
|
270
|
+
input: void 0,
|
|
271
|
+
expected: `"${key}"`,
|
|
272
|
+
path: [
|
|
273
|
+
{
|
|
274
|
+
type: "object",
|
|
275
|
+
origin: "key",
|
|
276
|
+
input,
|
|
277
|
+
key,
|
|
278
|
+
// @ts-expect-error
|
|
279
|
+
value: input[key]
|
|
280
|
+
}
|
|
281
|
+
]
|
|
282
|
+
});
|
|
283
|
+
if (config2.abortEarly) {
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
_addIssue(this, "type", dataset, config2);
|
|
290
|
+
}
|
|
291
|
+
return dataset;
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
function optional(wrapped, default_) {
|
|
296
|
+
return {
|
|
297
|
+
kind: "schema",
|
|
298
|
+
type: "optional",
|
|
299
|
+
reference: optional,
|
|
300
|
+
expects: `(${wrapped.expects} | undefined)`,
|
|
301
|
+
async: false,
|
|
302
|
+
wrapped,
|
|
303
|
+
default: default_,
|
|
304
|
+
get "~standard"() {
|
|
305
|
+
return _getStandardProps(this);
|
|
306
|
+
},
|
|
307
|
+
"~run"(dataset, config2) {
|
|
308
|
+
if (dataset.value === void 0) {
|
|
309
|
+
if (this.default !== void 0) {
|
|
310
|
+
dataset.value = getDefault(this, dataset, config2);
|
|
311
|
+
}
|
|
312
|
+
if (dataset.value === void 0) {
|
|
313
|
+
dataset.typed = true;
|
|
314
|
+
return dataset;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return this.wrapped["~run"](dataset, config2);
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
function string(message) {
|
|
322
|
+
return {
|
|
323
|
+
kind: "schema",
|
|
324
|
+
type: "string",
|
|
325
|
+
reference: string,
|
|
326
|
+
expects: "string",
|
|
327
|
+
async: false,
|
|
328
|
+
message,
|
|
329
|
+
get "~standard"() {
|
|
330
|
+
return _getStandardProps(this);
|
|
331
|
+
},
|
|
332
|
+
"~run"(dataset, config2) {
|
|
333
|
+
if (typeof dataset.value === "string") {
|
|
334
|
+
dataset.typed = true;
|
|
335
|
+
} else {
|
|
336
|
+
_addIssue(this, "type", dataset, config2);
|
|
337
|
+
}
|
|
338
|
+
return dataset;
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function unknown() {
|
|
343
|
+
return {
|
|
344
|
+
kind: "schema",
|
|
345
|
+
type: "unknown",
|
|
346
|
+
reference: unknown,
|
|
347
|
+
expects: "unknown",
|
|
348
|
+
async: false,
|
|
349
|
+
get "~standard"() {
|
|
350
|
+
return _getStandardProps(this);
|
|
351
|
+
},
|
|
352
|
+
"~run"(dataset) {
|
|
353
|
+
dataset.typed = true;
|
|
354
|
+
return dataset;
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function safeParse(schema, input, config2) {
|
|
359
|
+
const dataset = schema["~run"]({ value: input }, getGlobalConfig(config2));
|
|
360
|
+
return {
|
|
361
|
+
typed: dataset.typed,
|
|
362
|
+
success: !dataset.issues,
|
|
363
|
+
output: dataset.value,
|
|
364
|
+
issues: dataset.issues
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/handle.ts
|
|
369
|
+
var $HANDLE_MARKER = Symbol("RPC-HANDLE-MARKER");
|
|
370
|
+
function handle(methods) {
|
|
371
|
+
return { [$HANDLE_MARKER]: true, methods };
|
|
372
|
+
}
|
|
373
|
+
function isHandleMarker(value) {
|
|
374
|
+
return !!value && typeof value === "object" && $HANDLE_MARKER in value;
|
|
375
|
+
}
|
|
376
|
+
var HANDLE_NAMESPACE_PREFIX = "__rpc_handle_";
|
|
377
|
+
var handleNamespaceCounter = 0;
|
|
378
|
+
function nextHandleNamespaceId() {
|
|
379
|
+
return `${HANDLE_NAMESPACE_PREFIX}${handleNamespaceCounter++}`;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// src/utils.ts
|
|
383
|
+
function createIdAllocator() {
|
|
384
|
+
const freeIds = new Array();
|
|
385
|
+
let id = 0;
|
|
386
|
+
return {
|
|
387
|
+
create() {
|
|
388
|
+
if (freeIds.length) {
|
|
389
|
+
return freeIds.pop();
|
|
390
|
+
}
|
|
391
|
+
return id++;
|
|
392
|
+
},
|
|
393
|
+
free(id2) {
|
|
394
|
+
freeIds.push(id2);
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function createIdRegistry() {
|
|
399
|
+
const map = /* @__PURE__ */ new Map();
|
|
400
|
+
const idFactory = createIdAllocator();
|
|
401
|
+
return {
|
|
402
|
+
register(value) {
|
|
403
|
+
const id = idFactory.create();
|
|
404
|
+
map.set(id, value);
|
|
405
|
+
return id;
|
|
406
|
+
},
|
|
407
|
+
free(id) {
|
|
408
|
+
idFactory.free(id);
|
|
409
|
+
return map.get(id);
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
function defer() {
|
|
414
|
+
let resolve = null;
|
|
415
|
+
let reject = null;
|
|
416
|
+
return {
|
|
417
|
+
promise: new Promise((_resolve, _reject) => (resolve = _resolve, reject = _reject)),
|
|
418
|
+
resolve,
|
|
419
|
+
reject
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function createShape(schema, create) {
|
|
423
|
+
return {
|
|
424
|
+
validate: (value) => safeParse(schema, value).success,
|
|
425
|
+
create
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// src/protocol.ts
|
|
430
|
+
var $MESSENGER_REQUEST = "RPC_PROXY_REQUEST";
|
|
431
|
+
var requestSchema = object({
|
|
432
|
+
[$MESSENGER_REQUEST]: number(),
|
|
433
|
+
payload: unknown()
|
|
434
|
+
});
|
|
435
|
+
var RequestShape = createShape(requestSchema, (id, payload) => ({
|
|
436
|
+
[$MESSENGER_REQUEST]: id,
|
|
437
|
+
payload
|
|
438
|
+
}));
|
|
439
|
+
var $MESSENGER_RESPONSE = "RPC_PROXY_RESPONSE";
|
|
440
|
+
var ResponseShape = createShape(
|
|
441
|
+
object({
|
|
442
|
+
[$MESSENGER_RESPONSE]: number(),
|
|
443
|
+
payload: optional(unknown())
|
|
444
|
+
}),
|
|
445
|
+
(request, payload) => ({
|
|
446
|
+
[$MESSENGER_RESPONSE]: request[$MESSENGER_REQUEST],
|
|
447
|
+
payload
|
|
448
|
+
})
|
|
449
|
+
);
|
|
450
|
+
var $MESSENGER_ERROR = "RPC_PROXY_ERROR";
|
|
451
|
+
var ErrorShape = createShape(
|
|
452
|
+
object({
|
|
453
|
+
[$MESSENGER_ERROR]: number(),
|
|
454
|
+
error: unknown()
|
|
455
|
+
}),
|
|
456
|
+
(data, error) => ({
|
|
457
|
+
[$MESSENGER_ERROR]: data[$MESSENGER_REQUEST],
|
|
458
|
+
error
|
|
459
|
+
})
|
|
460
|
+
);
|
|
461
|
+
var $MESSENGER_RPC_REQUEST = "RPC_PROXY_RPC_REQUEST";
|
|
462
|
+
var RPCPayloadShape = createShape(
|
|
463
|
+
object({
|
|
464
|
+
[$MESSENGER_RPC_REQUEST]: boolean(),
|
|
465
|
+
topics: array(string()),
|
|
466
|
+
args: array(any())
|
|
467
|
+
}),
|
|
468
|
+
(topics, args) => ({ [$MESSENGER_RPC_REQUEST]: true, topics, args })
|
|
469
|
+
);
|
|
470
|
+
var $MESSENGER_HANDLE = "RPC_PROXY_HANDLE";
|
|
471
|
+
var HandleResponseShape = createShape(
|
|
472
|
+
object({
|
|
473
|
+
[$MESSENGER_HANDLE]: string()
|
|
474
|
+
// namespace ID
|
|
475
|
+
}),
|
|
476
|
+
(namespaceId) => ({ [$MESSENGER_HANDLE]: namespaceId })
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
// src/core.ts
|
|
480
|
+
function createCommander(apply) {
|
|
481
|
+
function _createCommander(topics, apply2) {
|
|
482
|
+
return new Proxy(function() {
|
|
483
|
+
}, {
|
|
484
|
+
get(target, topic) {
|
|
485
|
+
if (typeof topic === "symbol")
|
|
486
|
+
return target[topic];
|
|
487
|
+
if (topic === "then")
|
|
488
|
+
return void 0;
|
|
489
|
+
return _createCommander([...topics, topic], apply2);
|
|
490
|
+
},
|
|
491
|
+
apply(_, __, args) {
|
|
492
|
+
return apply2(topics, args);
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
return _createCommander([], apply);
|
|
497
|
+
}
|
|
498
|
+
function callMethod(methods, topics, args) {
|
|
499
|
+
const method = topics.reduce((acc, topic) => {
|
|
500
|
+
const result = acc?.[topic];
|
|
501
|
+
return result;
|
|
502
|
+
}, methods);
|
|
503
|
+
if (typeof method !== "function") {
|
|
504
|
+
throw new Error(`Topics did not resolve to a function: [${topics.join(",")}]`);
|
|
505
|
+
}
|
|
506
|
+
return method(...args);
|
|
507
|
+
}
|
|
508
|
+
function isHandleResponse(value) {
|
|
509
|
+
return !!value && typeof value === "object" && $MESSENGER_HANDLE in value && typeof value[$MESSENGER_HANDLE] === "string";
|
|
510
|
+
}
|
|
511
|
+
function createExposeRequestHandler(methods) {
|
|
512
|
+
const namespaceHandlers = /* @__PURE__ */ new Map();
|
|
513
|
+
const processResult = (result) => {
|
|
514
|
+
if (isHandleMarker(result)) {
|
|
515
|
+
const namespaceId = nextHandleNamespaceId();
|
|
516
|
+
namespaceHandlers.set(namespaceId, result.methods);
|
|
517
|
+
return HandleResponseShape.create(namespaceId);
|
|
518
|
+
}
|
|
519
|
+
return result;
|
|
520
|
+
};
|
|
521
|
+
return async (topics, args) => {
|
|
522
|
+
const firstTopic = topics[0];
|
|
523
|
+
if (firstTopic && firstTopic.startsWith(HANDLE_NAMESPACE_PREFIX)) {
|
|
524
|
+
const handler = namespaceHandlers.get(firstTopic);
|
|
525
|
+
if (!handler) {
|
|
526
|
+
throw new Error(`Unknown namespace: ${firstTopic}`);
|
|
527
|
+
}
|
|
528
|
+
const result2 = await callMethod(handler, topics.slice(1), args);
|
|
529
|
+
return processResult(result2);
|
|
530
|
+
}
|
|
531
|
+
const result = await callMethod(methods, topics, args);
|
|
532
|
+
return processResult(result);
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
function createRpcCommander(request, topicPrefix = []) {
|
|
536
|
+
return createCommander((topics, methodArgs) => {
|
|
537
|
+
const fullTopics = [...topicPrefix, ...topics];
|
|
538
|
+
return request(fullTopics, methodArgs).then((result) => {
|
|
539
|
+
if (isHandleResponse(result)) {
|
|
540
|
+
return createRpcCommander(
|
|
541
|
+
request,
|
|
542
|
+
result[$MESSENGER_HANDLE] ? [result[$MESSENGER_HANDLE]] : []
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
return result;
|
|
546
|
+
});
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// src/websocket/index.ts
|
|
551
|
+
var $WEBSOCKET = Symbol.for("RPC-WEBSOCKET");
|
|
552
|
+
var MessageEventShape = createShape(object({ data: unknown() }), (data) => ({
|
|
553
|
+
data
|
|
554
|
+
}));
|
|
555
|
+
function parseData(raw) {
|
|
556
|
+
return JSON.parse(typeof raw === "string" ? raw : String(raw));
|
|
557
|
+
}
|
|
558
|
+
function createRequester(ws) {
|
|
559
|
+
const promiseRegistry = createIdRegistry();
|
|
560
|
+
ws.addEventListener("message", (event) => {
|
|
561
|
+
if (!MessageEventShape.validate(event))
|
|
562
|
+
return;
|
|
563
|
+
try {
|
|
564
|
+
const data = parseData(event.data);
|
|
565
|
+
if (ErrorShape.validate(data)) {
|
|
566
|
+
promiseRegistry.free(data[$MESSENGER_ERROR])?.reject(data.error);
|
|
567
|
+
} else if (ResponseShape.validate(data)) {
|
|
568
|
+
promiseRegistry.free(data[$MESSENGER_RESPONSE])?.resolve(data.payload);
|
|
569
|
+
}
|
|
570
|
+
} catch (error) {
|
|
571
|
+
console.error(error);
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
return (payload) => {
|
|
575
|
+
const { promise, resolve, reject } = defer();
|
|
576
|
+
const id = promiseRegistry.register({ resolve, reject });
|
|
577
|
+
ws.send(JSON.stringify(RequestShape.create(id, payload)));
|
|
578
|
+
return promise;
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
function expose(methods, { to }) {
|
|
582
|
+
const handleRequest = createExposeRequestHandler(methods);
|
|
583
|
+
to.addEventListener("message", async (event) => {
|
|
584
|
+
if (!MessageEventShape.validate(event))
|
|
585
|
+
return;
|
|
586
|
+
try {
|
|
587
|
+
const data = parseData(event.data);
|
|
588
|
+
if (RequestShape.validate(data)) {
|
|
589
|
+
try {
|
|
590
|
+
if (RPCPayloadShape.validate(data.payload)) {
|
|
591
|
+
const result = await handleRequest(data.payload.topics, data.payload.args);
|
|
592
|
+
to.send(JSON.stringify(ResponseShape.create(data, result)));
|
|
593
|
+
}
|
|
594
|
+
} catch (error) {
|
|
595
|
+
console.error("Error while processing rpc request:", error, data.payload);
|
|
596
|
+
to.send(JSON.stringify(ErrorShape.create(data, error)));
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
} catch (error) {
|
|
600
|
+
console.error(error);
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
function rpc(ws) {
|
|
605
|
+
const request = createRequester(ws);
|
|
606
|
+
const proxy = createRpcCommander((topics, args) => {
|
|
607
|
+
return request(RPCPayloadShape.create(topics, args));
|
|
608
|
+
});
|
|
609
|
+
return Object.assign(proxy, { [$WEBSOCKET]: ws });
|
|
610
|
+
}
|
|
611
|
+
export {
|
|
612
|
+
$WEBSOCKET,
|
|
613
|
+
expose,
|
|
614
|
+
handle,
|
|
615
|
+
rpc
|
|
616
|
+
};
|
|
617
|
+
//# sourceMappingURL=websocket.js.map
|