@jcubic/mitty 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.
@@ -0,0 +1,410 @@
1
+ "use strict";
2
+ var Mitty = (() => {
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ Host: () => Host,
25
+ connect: () => connect
26
+ });
27
+
28
+ // src/protocol.ts
29
+ var HANDLE = /* @__PURE__ */ Symbol.for("@jcubic/mitty/handle");
30
+ function encode_error(error) {
31
+ return {
32
+ type: "error",
33
+ data: [error.name, error.message, error.stack ?? null]
34
+ };
35
+ }
36
+ function decode_error(marker) {
37
+ const [name, message, stack] = marker.data;
38
+ const error = new Error(message);
39
+ error.name = name;
40
+ if (stack !== null) {
41
+ error.stack = stack;
42
+ }
43
+ return error;
44
+ }
45
+ function is_marker(value) {
46
+ return typeof value === "object" && value !== null && typeof value.type === "string" && Array.isArray(value.data);
47
+ }
48
+ function is_function_marker(value) {
49
+ return is_marker(value) && value.type === "function";
50
+ }
51
+ function is_object_marker(value) {
52
+ return is_marker(value) && value.type === "object";
53
+ }
54
+ function is_error_marker(value) {
55
+ return is_marker(value) && value.type === "error";
56
+ }
57
+ function invalid_handle(id) {
58
+ return new Error(`mitty: invalid handle #${id} (released or never created)`);
59
+ }
60
+
61
+ // src/host.ts
62
+ var Host = class {
63
+ constructor(options) {
64
+ // objects that cannot cross the channel, kept alive behind an integer id.
65
+ // there is no GC here - entries live until release() drops them.
66
+ this._objects = /* @__PURE__ */ new Map();
67
+ this._object_id = 0;
68
+ // in-flight invocations of client callbacks, keyed per call (not per
69
+ // function) so the same callback can be running more than once at a time
70
+ this._pending = /* @__PURE__ */ new Map();
71
+ this._call_id = 0;
72
+ this._options = options;
73
+ this._channel = options.channel;
74
+ this._listener = (event) => {
75
+ void this._on_message(event.data);
76
+ };
77
+ this._channel.addEventListener("message", this._listener);
78
+ }
79
+ // -------------------------------------------------------------------------
80
+ // turn a value into a handle the client can call methods on instead of
81
+ // receiving a copy of. Call this from serialize().
82
+ // -------------------------------------------------------------------------
83
+ remote(value) {
84
+ const id = ++this._object_id;
85
+ this._objects.set(id, value);
86
+ return { type: "object", data: [id] };
87
+ }
88
+ // -------------------------------------------------------------------------
89
+ // drop a handle. Returns false if it was already gone.
90
+ // -------------------------------------------------------------------------
91
+ release(handle) {
92
+ const id = typeof handle === "number" ? handle : handle?.data?.[0];
93
+ if (typeof id !== "number") {
94
+ throw new TypeError("mitty: release() expects a handle returned by remote()");
95
+ }
96
+ return this._objects.delete(id);
97
+ }
98
+ // -------------------------------------------------------------------------
99
+ // stop listening and forget every handle. The channel stays open.
100
+ // -------------------------------------------------------------------------
101
+ close() {
102
+ this._channel.removeEventListener("message", this._listener);
103
+ this._objects.clear();
104
+ this._pending.clear();
105
+ }
106
+ // -------------------------------------------------------------------------
107
+ _post(data) {
108
+ this._channel.postMessage(this._serialize(data));
109
+ }
110
+ // -------------------------------------------------------------------------
111
+ _serialize(data) {
112
+ const hook = this._options.serialize?.bind(this);
113
+ return JSON.stringify(data, function(key, value) {
114
+ const raw = this[key];
115
+ if (raw instanceof Error) {
116
+ return encode_error(raw);
117
+ }
118
+ const result = hook ? hook(raw) : raw;
119
+ return result === raw ? value : result;
120
+ });
121
+ }
122
+ // -------------------------------------------------------------------------
123
+ // Returns null for a message that isn't even JSON - there is nothing to
124
+ // reply to in that case. A handle that no longer exists is reported as a
125
+ // deferred error instead of thrown, so the reply can still carry the id
126
+ // the client is waiting on.
127
+ // -------------------------------------------------------------------------
128
+ _unserialize(text) {
129
+ const hook = this._options.unserialize?.bind(this);
130
+ let deferred = null;
131
+ try {
132
+ const data = JSON.parse(text, (_key, value) => {
133
+ if (is_function_marker(value)) {
134
+ const [id, length] = value.data;
135
+ return this._callback(id, length);
136
+ }
137
+ if (is_object_marker(value)) {
138
+ const [id] = value.data;
139
+ if (!this._objects.has(id)) {
140
+ deferred = deferred ?? invalid_handle(id);
141
+ return void 0;
142
+ }
143
+ return this._objects.get(id);
144
+ }
145
+ if (is_error_marker(value)) {
146
+ return decode_error(value);
147
+ }
148
+ return hook ? hook(value) : value;
149
+ });
150
+ return { data, error: deferred };
151
+ } catch {
152
+ return null;
153
+ }
154
+ }
155
+ // -------------------------------------------------------------------------
156
+ // a stub standing in for a function that lives on the client. Calling it
157
+ // sends the arguments over and resolves once the client replies.
158
+ // -------------------------------------------------------------------------
159
+ _callback(id, length) {
160
+ return (...args) => {
161
+ const call = ++this._call_id;
162
+ return new Promise((resolve, reject) => {
163
+ this._pending.set(call, { resolve, reject });
164
+ this._post({ callback: id, call, args: args.slice(0, length) });
165
+ });
166
+ };
167
+ }
168
+ // -------------------------------------------------------------------------
169
+ async _on_message(text) {
170
+ const parsed = this._unserialize(text);
171
+ if (!parsed) {
172
+ return;
173
+ }
174
+ const { data, error } = parsed;
175
+ if (typeof data.call === "number" && data.callback === void 0) {
176
+ const entry = this._pending.get(data.call);
177
+ if (entry) {
178
+ this._pending.delete(data.call);
179
+ if (data.error) {
180
+ entry.reject(data.error);
181
+ } else {
182
+ entry.resolve(data.result);
183
+ }
184
+ }
185
+ return;
186
+ }
187
+ if (typeof data.release === "number") {
188
+ this._objects.delete(data.release);
189
+ return;
190
+ }
191
+ if (typeof data.id !== "number") {
192
+ return;
193
+ }
194
+ try {
195
+ if (error) {
196
+ throw error;
197
+ }
198
+ this._post({ id: data.id, result: await this._invoke(data) });
199
+ } catch (thrown) {
200
+ const failure = thrown instanceof Error ? thrown : new Error(String(thrown));
201
+ this._post({ id: data.id, error: failure });
202
+ }
203
+ }
204
+ // -------------------------------------------------------------------------
205
+ // walk the recorded chain against the root in one go, so a whole
206
+ // expression costs a single message rather than one per step. `object` is
207
+ // the receiver a call binds to (whatever the value was read off of) and
208
+ // `value` is the running result.
209
+ // -------------------------------------------------------------------------
210
+ async _invoke(data) {
211
+ const root = await this._root(data);
212
+ const ops = data.ops ?? [];
213
+ let object = root;
214
+ let value = root;
215
+ let label = typeof data.object === "number" ? `#${data.object}` : data.namespace ?? "";
216
+ for (const op of ops) {
217
+ if (op.type === "get") {
218
+ object = value;
219
+ value = value?.[op.key];
220
+ label += `.${op.key}`;
221
+ } else {
222
+ if (typeof value !== "function") {
223
+ throw new TypeError(`mitty: ${label} is not a function`);
224
+ }
225
+ value = await value.apply(
226
+ object,
227
+ op.args
228
+ );
229
+ object = void 0;
230
+ label += "()";
231
+ }
232
+ }
233
+ return value;
234
+ }
235
+ // -------------------------------------------------------------------------
236
+ async _root(data) {
237
+ if (typeof data.object === "number") {
238
+ if (!this._objects.has(data.object)) {
239
+ throw invalid_handle(data.object);
240
+ }
241
+ return this._objects.get(data.object);
242
+ }
243
+ if (typeof data.namespace !== "string") {
244
+ throw new Error("mitty: request has neither a module name nor a handle");
245
+ }
246
+ const module = await this._options.resolve(data.namespace);
247
+ if (module === null || module === void 0) {
248
+ throw new Error(`mitty: unknown module '${data.namespace}'`);
249
+ }
250
+ return module;
251
+ }
252
+ };
253
+
254
+ // src/client.ts
255
+ function chain_info(value) {
256
+ if (value === null || typeof value !== "object" && typeof value !== "function") {
257
+ return void 0;
258
+ }
259
+ return value[HANDLE];
260
+ }
261
+ function connect(channel) {
262
+ let rpc_id = 0;
263
+ let callback_id = 0;
264
+ const pending = /* @__PURE__ */ new Map();
265
+ const callbacks = /* @__PURE__ */ new Map();
266
+ const callback_ids = /* @__PURE__ */ new Map();
267
+ function serialize(data) {
268
+ return JSON.stringify(data, function(key, value) {
269
+ const raw = this[key];
270
+ if (raw instanceof Error) {
271
+ return encode_error(raw);
272
+ }
273
+ const chain = chain_info(raw);
274
+ if (chain) {
275
+ if (chain.ops.length || typeof chain.root.object !== "number") {
276
+ throw new TypeError(
277
+ "mitty: cannot send an unresolved remote chain - await it first"
278
+ );
279
+ }
280
+ return { type: "object", data: [chain.root.object] };
281
+ }
282
+ if (typeof raw === "function") {
283
+ let id = callback_ids.get(raw);
284
+ if (id === void 0) {
285
+ id = ++callback_id;
286
+ callback_ids.set(raw, id);
287
+ callbacks.set(id, raw);
288
+ }
289
+ return { type: "function", data: [id, raw.length] };
290
+ }
291
+ return value;
292
+ });
293
+ }
294
+ function unserialize(text) {
295
+ return JSON.parse(text, (_key, value) => {
296
+ if (is_object_marker(value)) {
297
+ return make_chain({ object: value.data[0] });
298
+ }
299
+ if (is_error_marker(value)) {
300
+ return decode_error(value);
301
+ }
302
+ return value;
303
+ });
304
+ }
305
+ function post(data) {
306
+ channel.postMessage(serialize(data));
307
+ }
308
+ async function run_callback(data) {
309
+ const fn = callbacks.get(data.callback);
310
+ if (!fn || typeof data.call !== "number") {
311
+ return;
312
+ }
313
+ try {
314
+ post({ call: data.call, result: await fn(...data.args ?? []) });
315
+ } catch (error) {
316
+ post({ call: data.call, error });
317
+ }
318
+ }
319
+ const listener = (event) => {
320
+ let data;
321
+ try {
322
+ data = unserialize(event.data);
323
+ } catch {
324
+ return;
325
+ }
326
+ if (typeof data.callback === "number") {
327
+ void run_callback(data);
328
+ return;
329
+ }
330
+ if (typeof data.id !== "number") {
331
+ return;
332
+ }
333
+ const entry = pending.get(data.id);
334
+ if (!entry) {
335
+ return;
336
+ }
337
+ pending.delete(data.id);
338
+ if (data.error) {
339
+ entry.reject(data.error);
340
+ } else {
341
+ entry.resolve(data.result);
342
+ }
343
+ };
344
+ channel.addEventListener("message", listener);
345
+ function call(root, ops) {
346
+ return new Promise((resolve, reject) => {
347
+ const id = ++rpc_id;
348
+ let payload;
349
+ try {
350
+ payload = serialize({ id, ...root, ops });
351
+ } catch (error) {
352
+ reject(error);
353
+ return;
354
+ }
355
+ pending.set(id, { resolve, reject });
356
+ channel.postMessage(payload);
357
+ });
358
+ }
359
+ function make_chain(root, ops = []) {
360
+ const target = function() {
361
+ };
362
+ return new Proxy(target, {
363
+ apply(_target, _this_arg, args) {
364
+ return make_chain(root, [...ops, { type: "call", args }]);
365
+ },
366
+ get(_target, key) {
367
+ if (key === HANDLE) {
368
+ return { root, ops };
369
+ }
370
+ if (key === "then") {
371
+ if (!ops.length) {
372
+ return void 0;
373
+ }
374
+ return (resolve, reject) => call(root, ops).then(resolve, reject);
375
+ }
376
+ if (typeof key !== "string") {
377
+ return void 0;
378
+ }
379
+ return make_chain(root, [...ops, { type: "get", key }]);
380
+ }
381
+ });
382
+ }
383
+ function handle_id(remote) {
384
+ const chain = chain_info(remote);
385
+ if (!chain || chain.ops.length || typeof chain.root.object !== "number") {
386
+ throw new TypeError("mitty: expected a remote handle");
387
+ }
388
+ return chain.root.object;
389
+ }
390
+ return {
391
+ require(name) {
392
+ return make_chain({ namespace: name });
393
+ },
394
+ handle(remote) {
395
+ return handle_id(remote);
396
+ },
397
+ release(remote) {
398
+ post({ release: typeof remote === "number" ? remote : handle_id(remote) });
399
+ },
400
+ close() {
401
+ channel.removeEventListener("message", listener);
402
+ pending.clear();
403
+ callbacks.clear();
404
+ callback_ids.clear();
405
+ }
406
+ };
407
+ }
408
+ return __toCommonJS(index_exports);
409
+ })();
410
+ //# sourceMappingURL=index.global.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/protocol.ts","../src/host.ts","../src/client.ts"],"sourcesContent":["export { Host } from './host';\nexport type { HostOptions } from './host';\nexport { connect } from './client';\nexport type {\n Channel,\n ChannelEvent,\n ChannelListener,\n Client,\n ErrorMarker,\n FunctionMarker,\n Marker,\n ObjectMarker,\n Op,\n Remote,\n} from './types';\n","import type { ErrorMarker, FunctionMarker, Marker, ObjectMarker } from './types';\n\n// key used to read the root/ops out of a chain proxy without going through the\n// get trap's normal \"record another step\" behaviour. Symbol.for() so two copies\n// of the library (e.g. a bundled one and one from a CDN) still recognise each\n// other's proxies.\nexport const HANDLE = Symbol.for('@jcubic/mitty/handle');\n\nexport function encode_error(error: Error): ErrorMarker {\n return {\n type: 'error',\n data: [error.name, error.message, error.stack ?? null],\n };\n}\n\nexport function decode_error(marker: ErrorMarker): Error {\n const [name, message, stack] = marker.data;\n const error = new Error(message);\n error.name = name;\n if (stack !== null) {\n // keep the host's stack - it points at where the call actually failed,\n // which is far more useful than a stack inside this library\n error.stack = stack;\n }\n return error;\n}\n\nfunction is_marker(value: unknown): value is Marker {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as Marker).type === 'string' &&\n Array.isArray((value as Marker).data)\n );\n}\n\nexport function is_function_marker(value: unknown): value is FunctionMarker {\n return is_marker(value) && value.type === 'function';\n}\n\nexport function is_object_marker(value: unknown): value is ObjectMarker {\n return is_marker(value) && value.type === 'object';\n}\n\nexport function is_error_marker(value: unknown): value is ErrorMarker {\n return is_marker(value) && value.type === 'error';\n}\n\nexport function invalid_handle(id: number): Error {\n return new Error(`mitty: invalid handle #${id} (released or never created)`);\n}\n","import {\n decode_error,\n encode_error,\n invalid_handle,\n is_error_marker,\n is_function_marker,\n is_object_marker,\n} from './protocol';\nimport type { Channel, ChannelListener, ObjectMarker, Op } from './types';\n\nexport interface HostOptions {\n // the transport this host listens on; the host never closes it\n channel: Channel;\n // turn a module name from require() into the value the chain runs against.\n // Return null/undefined for an unknown name. May be async.\n resolve(name: string): unknown;\n // called for every value on its way out. Return this.remote(value) for\n // anything that cannot survive JSON (DOM nodes, jQuery objects, class\n // instances with methods), otherwise return the value untouched.\n serialize?(this: Host, value: unknown): unknown;\n // called for every value coming in from the client\n unserialize?(this: Host, value: unknown): unknown;\n}\n\ninterface Message {\n id?: number;\n namespace?: string;\n object?: number;\n ops?: Op[];\n callback?: number;\n call?: number;\n args?: unknown[];\n result?: unknown;\n error?: unknown;\n release?: number;\n}\n\ntype Resolver = { resolve: (value: unknown) => void; reject: (reason: unknown) => void };\n\nexport class Host {\n private _options: HostOptions;\n private _channel: Channel;\n private _listener: ChannelListener;\n // objects that cannot cross the channel, kept alive behind an integer id.\n // there is no GC here - entries live until release() drops them.\n private _objects = new Map<number, unknown>();\n private _object_id = 0;\n // in-flight invocations of client callbacks, keyed per call (not per\n // function) so the same callback can be running more than once at a time\n private _pending = new Map<number, Resolver>();\n private _call_id = 0;\n\n constructor(options: HostOptions) {\n this._options = options;\n this._channel = options.channel;\n this._listener = event => {\n void this._on_message(event.data);\n };\n this._channel.addEventListener('message', this._listener);\n }\n\n // -------------------------------------------------------------------------\n // turn a value into a handle the client can call methods on instead of\n // receiving a copy of. Call this from serialize().\n // -------------------------------------------------------------------------\n public remote(value: unknown): ObjectMarker {\n const id = ++this._object_id;\n this._objects.set(id, value);\n return { type: 'object', data: [id] };\n }\n\n // -------------------------------------------------------------------------\n // drop a handle. Returns false if it was already gone.\n // -------------------------------------------------------------------------\n public release(handle: ObjectMarker | number): boolean {\n const id = typeof handle === 'number' ? handle : handle?.data?.[0];\n if (typeof id !== 'number') {\n throw new TypeError('mitty: release() expects a handle returned by remote()');\n }\n return this._objects.delete(id);\n }\n\n // -------------------------------------------------------------------------\n // stop listening and forget every handle. The channel stays open.\n // -------------------------------------------------------------------------\n public close(): void {\n this._channel.removeEventListener('message', this._listener);\n this._objects.clear();\n this._pending.clear();\n }\n\n // -------------------------------------------------------------------------\n private _post(data: Message): void {\n this._channel.postMessage(this._serialize(data));\n }\n\n // -------------------------------------------------------------------------\n private _serialize(data: Message): string {\n // bound here because the replacer has to be a plain function - its\n // `this` is the holder object, not the Host\n const hook = this._options.serialize?.bind(this);\n return JSON.stringify(data, function (this: Record<string, unknown>, key, value) {\n // read the untouched value off the holder: by the time a replacer\n // runs, JSON.stringify has already swapped in the result of any\n // toJSON(), which hides what we need to recognise\n const raw = this[key];\n if (raw instanceof Error) {\n return encode_error(raw);\n }\n const result = hook ? hook(raw) : raw;\n // when the hook passes a value through, fall back to `value` so\n // ordinary JSON conventions (Date#toJSON and friends) still apply\n return result === raw ? value : result;\n });\n }\n\n // -------------------------------------------------------------------------\n // Returns null for a message that isn't even JSON - there is nothing to\n // reply to in that case. A handle that no longer exists is reported as a\n // deferred error instead of thrown, so the reply can still carry the id\n // the client is waiting on.\n // -------------------------------------------------------------------------\n private _unserialize(text: string): { data: Message; error: Error | null } | null {\n const hook = this._options.unserialize?.bind(this);\n let deferred: Error | null = null;\n try {\n const data = JSON.parse(text, (_key, value) => {\n if (is_function_marker(value)) {\n const [id, length] = value.data;\n return this._callback(id, length);\n }\n if (is_object_marker(value)) {\n const [id] = value.data;\n if (!this._objects.has(id)) {\n deferred = deferred ?? invalid_handle(id);\n return undefined;\n }\n return this._objects.get(id);\n }\n if (is_error_marker(value)) {\n return decode_error(value);\n }\n return hook ? hook(value) : value;\n }) as Message;\n return { data, error: deferred };\n } catch {\n return null;\n }\n }\n\n // -------------------------------------------------------------------------\n // a stub standing in for a function that lives on the client. Calling it\n // sends the arguments over and resolves once the client replies.\n // -------------------------------------------------------------------------\n private _callback(id: number, length: number) {\n return (...args: unknown[]): Promise<unknown> => {\n const call = ++this._call_id;\n return new Promise((resolve, reject) => {\n this._pending.set(call, { resolve, reject });\n // trim to the declared arity: callers like jQuery pass extras\n // (event objects, indexes) that usually cannot be serialized\n this._post({ callback: id, call, args: args.slice(0, length) });\n });\n };\n }\n\n // -------------------------------------------------------------------------\n private async _on_message(text: string): Promise<void> {\n const parsed = this._unserialize(text);\n if (!parsed) {\n return;\n }\n const { data, error } = parsed;\n\n // a client callback finished - `callback` is absent, which is what\n // distinguishes a result coming back from an invocation going out\n if (typeof data.call === 'number' && data.callback === undefined) {\n const entry = this._pending.get(data.call);\n if (entry) {\n this._pending.delete(data.call);\n if (data.error) {\n entry.reject(data.error);\n } else {\n entry.resolve(data.result);\n }\n }\n return;\n }\n\n if (typeof data.release === 'number') {\n this._objects.delete(data.release);\n return;\n }\n\n if (typeof data.id !== 'number') {\n return;\n }\n\n try {\n if (error) {\n throw error;\n }\n this._post({ id: data.id, result: await this._invoke(data) });\n } catch (thrown) {\n const failure = thrown instanceof Error ? thrown : new Error(String(thrown));\n this._post({ id: data.id, error: failure });\n }\n }\n\n // -------------------------------------------------------------------------\n // walk the recorded chain against the root in one go, so a whole\n // expression costs a single message rather than one per step. `object` is\n // the receiver a call binds to (whatever the value was read off of) and\n // `value` is the running result.\n // -------------------------------------------------------------------------\n private async _invoke(data: Message): Promise<unknown> {\n const root = await this._root(data);\n const ops = data.ops ?? [];\n let object: unknown = root;\n let value: unknown = root;\n let label =\n typeof data.object === 'number' ? `#${data.object}` : (data.namespace ?? '');\n for (const op of ops) {\n if (op.type === 'get') {\n object = value;\n value = (value as Record<string, unknown> | null | undefined)?.[op.key];\n label += `.${op.key}`;\n } else {\n if (typeof value !== 'function') {\n throw new TypeError(`mitty: ${label} is not a function`);\n }\n value = await (value as (...args: unknown[]) => unknown).apply(\n object,\n op.args,\n );\n // a result is not bound to anything until it is read off\n // something else, so the next call has no receiver\n object = undefined;\n label += '()';\n }\n }\n return value;\n }\n\n // -------------------------------------------------------------------------\n private async _root(data: Message): Promise<unknown> {\n if (typeof data.object === 'number') {\n if (!this._objects.has(data.object)) {\n throw invalid_handle(data.object);\n }\n return this._objects.get(data.object);\n }\n if (typeof data.namespace !== 'string') {\n throw new Error('mitty: request has neither a module name nor a handle');\n }\n const module = await this._options.resolve(data.namespace);\n if (module === null || module === undefined) {\n throw new Error(`mitty: unknown module '${data.namespace}'`);\n }\n return module;\n }\n}\n","import {\n HANDLE,\n decode_error,\n encode_error,\n is_error_marker,\n is_object_marker,\n} from './protocol';\nimport type { Channel, ChannelListener, Client, Op, Remote } from './types';\n\n// what a chain is rooted at: a module looked up by name, or an object that\n// stayed behind on the host\ninterface Root {\n namespace?: string;\n object?: number;\n}\n\ninterface ChainInfo {\n root: Root;\n ops: Op[];\n}\n\ninterface Message {\n id?: number;\n callback?: number;\n call?: number;\n args?: unknown[];\n result?: unknown;\n error?: unknown;\n release?: number;\n}\n\ntype Callback = (...args: unknown[]) => unknown;\n\nfunction chain_info(value: unknown): ChainInfo | undefined {\n if (value === null || (typeof value !== 'object' && typeof value !== 'function')) {\n return undefined;\n }\n return (value as Record<symbol, ChainInfo | undefined>)[HANDLE];\n}\n\n// -----------------------------------------------------------------------------\n// Connect to a Host listening on the other end of `channel`. Safe to call in a\n// worker loaded with either `import` or `importScripts`.\n// -----------------------------------------------------------------------------\nexport function connect(channel: Channel): Client {\n let rpc_id = 0;\n let callback_id = 0;\n const pending = new Map<\n number,\n { resolve: (value: unknown) => void; reject: (reason: unknown) => void }\n >();\n // functions handed to the host, so it can call back into this worker.\n // keyed both ways to reuse an id when the same function is passed twice.\n const callbacks = new Map<number, Callback>();\n const callback_ids = new Map<Callback, number>();\n\n function serialize(data: unknown): string {\n return JSON.stringify(data, function (this: Record<string, unknown>, key, value) {\n // the raw holder value, before JSON.stringify applied toJSON() -\n // a chain proxy answers every property access, toJSON included\n const raw = this[key];\n if (raw instanceof Error) {\n return encode_error(raw);\n }\n const chain = chain_info(raw);\n if (chain) {\n if (chain.ops.length || typeof chain.root.object !== 'number') {\n throw new TypeError(\n 'mitty: cannot send an unresolved remote chain - await it first',\n );\n }\n // a handle can go back to the host, which swaps it for the\n // object it stands for\n return { type: 'object', data: [chain.root.object] };\n }\n if (typeof raw === 'function') {\n let id = callback_ids.get(raw as Callback);\n if (id === undefined) {\n id = ++callback_id;\n callback_ids.set(raw as Callback, id);\n callbacks.set(id, raw as Callback);\n }\n return { type: 'function', data: [id, raw.length] };\n }\n return value;\n });\n }\n\n function unserialize(text: string): Message {\n return JSON.parse(text, (_key, value) => {\n if (is_object_marker(value)) {\n // a handle to something on the host - make it a chain rooted\n // there rather than handing back the marker itself\n return make_chain({ object: value.data[0] });\n }\n if (is_error_marker(value)) {\n return decode_error(value);\n }\n return value;\n }) as Message;\n }\n\n function post(data: Message): void {\n channel.postMessage(serialize(data));\n }\n\n async function run_callback(data: Message): Promise<void> {\n const fn = callbacks.get(data.callback as number);\n if (!fn || typeof data.call !== 'number') {\n return;\n }\n try {\n post({ call: data.call, result: await fn(...(data.args ?? [])) });\n } catch (error) {\n post({ call: data.call, error });\n }\n }\n\n const listener: ChannelListener = event => {\n let data: Message;\n try {\n data = unserialize(event.data);\n } catch {\n return;\n }\n if (typeof data.callback === 'number') {\n void run_callback(data);\n return;\n }\n if (typeof data.id !== 'number') {\n return;\n }\n const entry = pending.get(data.id);\n if (!entry) {\n return;\n }\n pending.delete(data.id);\n if (data.error) {\n entry.reject(data.error);\n } else {\n entry.resolve(data.result);\n }\n };\n\n channel.addEventListener('message', listener);\n\n function call(root: Root, ops: Op[]): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const id = ++rpc_id;\n let payload: string;\n try {\n payload = serialize({ id, ...root, ops });\n } catch (error) {\n // an argument that cannot be sent (an unresolved chain, a\n // circular structure) fails the call rather than the channel\n reject(error);\n return;\n }\n pending.set(id, { resolve, reject });\n channel.postMessage(payload);\n });\n }\n\n // -------------------------------------------------------------------------\n // Records property accesses and calls without touching the channel. The\n // whole chain is sent once, when something awaits it.\n // -------------------------------------------------------------------------\n function make_chain(root: Root, ops: Op[] = []): Remote {\n const target = function () {} as unknown as Remote;\n return new Proxy(target, {\n apply(_target, _this_arg, args: unknown[]) {\n return make_chain(root, [...ops, { type: 'call', args }]);\n },\n get(_target, key) {\n if (key === HANDLE) {\n return { root, ops };\n }\n if (key === 'then') {\n // becoming a real thenable is what triggers execution.\n // Only do that when there is something to run: a bare\n // handle (no ops yet) has to stay non-thenable, because it\n // resolves to another handle and promise resolution would\n // otherwise keep adopting it forever.\n if (!ops.length) {\n return undefined;\n }\n return (\n resolve: (value: unknown) => void,\n reject: (reason: unknown) => void,\n ) => call(root, ops).then(resolve, reject);\n }\n if (typeof key !== 'string') {\n return undefined;\n }\n return make_chain(root, [...ops, { type: 'get', key }]);\n },\n });\n }\n\n // the host-side id a bare handle stands for. A chain that still has ops, or\n // one rooted at a module, does not name a single object.\n function handle_id(remote: unknown): number {\n const chain = chain_info(remote);\n if (!chain || chain.ops.length || typeof chain.root.object !== 'number') {\n throw new TypeError('mitty: expected a remote handle');\n }\n return chain.root.object;\n }\n\n return {\n require(name: string): Remote {\n return make_chain({ namespace: name });\n },\n handle(remote: unknown): number {\n return handle_id(remote);\n },\n release(remote: unknown): void {\n post({ release: typeof remote === 'number' ? remote : handle_id(remote) });\n },\n close(): void {\n channel.removeEventListener('message', listener);\n pending.clear();\n callbacks.clear();\n callback_ids.clear();\n },\n };\n}\n"],"mappings":"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","names":[]}