@shelchin/threadx 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/dist/index.js ADDED
@@ -0,0 +1,382 @@
1
+ // src/errors.ts
2
+ class WorkerError extends Error {
3
+ workerStack;
4
+ constructor(serialized, callSite) {
5
+ super(serialized.message);
6
+ this.name = serialized.name || "WorkerError";
7
+ this.workerStack = serialized.stack;
8
+ if (callSite?.stack) {
9
+ this.stack = `${this.name}: ${this.message}
10
+ ` + ` at Worker
11
+ ` + callSite.stack.split(`
12
+ `).slice(1).join(`
13
+ `);
14
+ }
15
+ }
16
+ }
17
+
18
+ class TimeoutError extends Error {
19
+ constructor(method, timeout) {
20
+ super(`Call to '${method}' timed out after ${timeout}ms`);
21
+ this.name = "TimeoutError";
22
+ }
23
+ }
24
+
25
+ class InitError extends Error {
26
+ constructor(message) {
27
+ super(message);
28
+ this.name = "InitError";
29
+ }
30
+ }
31
+
32
+ class UnsupportedRuntimeError extends Error {
33
+ constructor(runtime) {
34
+ super(`ThreadX is not supported on ${runtime}. Supported runtimes: Browser, Bun, Deno`);
35
+ this.name = "UnsupportedRuntimeError";
36
+ }
37
+ }
38
+
39
+ // src/transfer.ts
40
+ var TRANSFER = Symbol("threadx.transfer");
41
+ function t(value, transferables) {
42
+ return {
43
+ [TRANSFER]: true,
44
+ value,
45
+ transferables: transferables ?? [value]
46
+ };
47
+ }
48
+ function isTransferDescriptor(value) {
49
+ return typeof value === "object" && value !== null && TRANSFER in value;
50
+ }
51
+ function prepareArgs(args) {
52
+ const transfer = [];
53
+ const processedArgs = args.map((arg) => {
54
+ if (isTransferDescriptor(arg)) {
55
+ transfer.push(...arg.transferables);
56
+ return arg.value;
57
+ }
58
+ return arg;
59
+ });
60
+ return { args: processedArgs, transfer };
61
+ }
62
+
63
+ // src/wrap.ts
64
+ var DEFAULT_TIMEOUT = 30000;
65
+ var proxyInternals = new WeakMap;
66
+ function createAsyncIterable(worker, id, pending, timeout, method) {
67
+ const buffer = [];
68
+ let waitingResolve = null;
69
+ let waitingReject = null;
70
+ let isDone = false;
71
+ let error = null;
72
+ const callSite = new Error;
73
+ let timeoutId;
74
+ if (timeout > 0) {
75
+ timeoutId = setTimeout(() => {
76
+ const timeoutError = new TimeoutError(method, timeout);
77
+ error = timeoutError;
78
+ if (waitingReject) {
79
+ waitingReject(timeoutError);
80
+ waitingResolve = null;
81
+ waitingReject = null;
82
+ }
83
+ pending.delete(id);
84
+ worker.postMessage({ $type: "CANCEL", $id: id });
85
+ }, timeout);
86
+ }
87
+ pending.set(id, {
88
+ push: (value) => {
89
+ if (timeoutId) {
90
+ clearTimeout(timeoutId);
91
+ timeoutId = setTimeout(() => {
92
+ const timeoutError = new TimeoutError(method, timeout);
93
+ error = timeoutError;
94
+ if (waitingReject) {
95
+ waitingReject(timeoutError);
96
+ waitingResolve = null;
97
+ waitingReject = null;
98
+ }
99
+ pending.delete(id);
100
+ worker.postMessage({ $type: "CANCEL", $id: id });
101
+ }, timeout);
102
+ }
103
+ if (waitingResolve) {
104
+ waitingResolve({ value, done: false });
105
+ waitingResolve = null;
106
+ waitingReject = null;
107
+ } else {
108
+ buffer.push(value);
109
+ }
110
+ },
111
+ done: () => {
112
+ if (timeoutId)
113
+ clearTimeout(timeoutId);
114
+ isDone = true;
115
+ if (waitingResolve) {
116
+ waitingResolve({ value: undefined, done: true });
117
+ waitingResolve = null;
118
+ waitingReject = null;
119
+ }
120
+ },
121
+ error: (err) => {
122
+ if (timeoutId)
123
+ clearTimeout(timeoutId);
124
+ error = err;
125
+ if (waitingReject) {
126
+ waitingReject(err);
127
+ waitingResolve = null;
128
+ waitingReject = null;
129
+ }
130
+ },
131
+ method,
132
+ callSite
133
+ });
134
+ return {
135
+ [Symbol.asyncIterator]() {
136
+ return {
137
+ async next() {
138
+ if (buffer.length > 0) {
139
+ return { value: buffer.shift(), done: false };
140
+ }
141
+ if (error) {
142
+ throw error;
143
+ }
144
+ if (isDone) {
145
+ return { value: undefined, done: true };
146
+ }
147
+ return new Promise((resolve, reject) => {
148
+ waitingResolve = resolve;
149
+ waitingReject = reject;
150
+ });
151
+ },
152
+ async return() {
153
+ if (timeoutId)
154
+ clearTimeout(timeoutId);
155
+ pending.delete(id);
156
+ worker.postMessage({ $type: "CANCEL", $id: id });
157
+ return { value: undefined, done: true };
158
+ }
159
+ };
160
+ }
161
+ };
162
+ }
163
+ function wrap(worker, options) {
164
+ const timeout = options?.timeout ?? DEFAULT_TIMEOUT;
165
+ let state = "init";
166
+ let id = 0;
167
+ const pendingCalls = new Map;
168
+ const pendingStreams = new Map;
169
+ const queue = [];
170
+ let methods = new Set;
171
+ function flushQueue() {
172
+ for (const { message, transfer } of queue) {
173
+ worker.postMessage(message, transfer);
174
+ }
175
+ queue.length = 0;
176
+ }
177
+ function deserializeError(serialized, callSite) {
178
+ return new WorkerError(serialized, callSite);
179
+ }
180
+ worker.onmessage = (event) => {
181
+ const msg = event.data;
182
+ if (msg.$type === "READY") {
183
+ state = "ready";
184
+ methods = new Set(msg.methods);
185
+ flushQueue();
186
+ return;
187
+ }
188
+ if (msg.$type === "RESOLVE") {
189
+ const call = pendingCalls.get(msg.$id);
190
+ if (call) {
191
+ if (call.timeoutId)
192
+ clearTimeout(call.timeoutId);
193
+ call.resolve(msg.value);
194
+ pendingCalls.delete(msg.$id);
195
+ }
196
+ return;
197
+ }
198
+ if (msg.$type === "REJECT") {
199
+ const call = pendingCalls.get(msg.$id);
200
+ if (call) {
201
+ if (call.timeoutId)
202
+ clearTimeout(call.timeoutId);
203
+ call.reject(deserializeError(msg.error, call.callSite));
204
+ pendingCalls.delete(msg.$id);
205
+ return;
206
+ }
207
+ const stream = pendingStreams.get(msg.$id);
208
+ if (stream) {
209
+ stream.error(deserializeError(msg.error, stream.callSite));
210
+ pendingStreams.delete(msg.$id);
211
+ }
212
+ return;
213
+ }
214
+ if (msg.$type === "YIELD") {
215
+ const stream = pendingStreams.get(msg.$id);
216
+ if (stream) {
217
+ stream.push(msg.value);
218
+ }
219
+ return;
220
+ }
221
+ if (msg.$type === "DONE") {
222
+ const stream = pendingStreams.get(msg.$id);
223
+ if (stream) {
224
+ stream.done();
225
+ pendingStreams.delete(msg.$id);
226
+ }
227
+ return;
228
+ }
229
+ };
230
+ function rejectAllPending(error) {
231
+ for (const call of pendingCalls.values()) {
232
+ if (call.timeoutId)
233
+ clearTimeout(call.timeoutId);
234
+ call.reject(error);
235
+ }
236
+ pendingCalls.clear();
237
+ for (const stream of pendingStreams.values()) {
238
+ if (stream.timeoutId)
239
+ clearTimeout(stream.timeoutId);
240
+ stream.error(error);
241
+ }
242
+ pendingStreams.clear();
243
+ queue.length = 0;
244
+ }
245
+ worker.onerror = (event) => {
246
+ const error = new InitError(event.message || "Worker error");
247
+ rejectAllPending(error);
248
+ state = "dead";
249
+ };
250
+ const proxy = new Proxy({}, {
251
+ get(_, prop) {
252
+ if (prop === "$state")
253
+ return state;
254
+ if (prop === "$pending")
255
+ return pendingCalls.size + pendingStreams.size;
256
+ if (prop === "$worker")
257
+ return worker;
258
+ if (typeof prop === "symbol")
259
+ return;
260
+ return (...args) => {
261
+ const currentId = ++id;
262
+ const { args: processedArgs, transfer } = prepareArgs(args);
263
+ const message = {
264
+ $type: "CALL",
265
+ $id: currentId,
266
+ method: prop,
267
+ args: processedArgs
268
+ };
269
+ const callSite = new Error;
270
+ let isStreaming = false;
271
+ let streamIterable = null;
272
+ let internalPromise = null;
273
+ let promiseResolve = null;
274
+ let promiseReject = null;
275
+ function getInternalPromise() {
276
+ if (!internalPromise) {
277
+ internalPromise = new Promise((resolve, reject) => {
278
+ promiseResolve = resolve;
279
+ promiseReject = reject;
280
+ });
281
+ }
282
+ return internalPromise;
283
+ }
284
+ let timeoutId;
285
+ if (timeout > 0) {
286
+ timeoutId = setTimeout(() => {
287
+ if (!isStreaming) {
288
+ const timeoutError = new TimeoutError(prop, timeout);
289
+ getInternalPromise();
290
+ if (promiseReject) {
291
+ promiseReject(timeoutError);
292
+ }
293
+ pendingCalls.delete(currentId);
294
+ }
295
+ }, timeout);
296
+ }
297
+ pendingCalls.set(currentId, {
298
+ resolve: (value) => {
299
+ if (timeoutId)
300
+ clearTimeout(timeoutId);
301
+ getInternalPromise();
302
+ if (promiseResolve) {
303
+ promiseResolve(value);
304
+ }
305
+ },
306
+ reject: (error) => {
307
+ if (timeoutId)
308
+ clearTimeout(timeoutId);
309
+ getInternalPromise();
310
+ if (promiseReject) {
311
+ promiseReject(error);
312
+ }
313
+ },
314
+ method: prop,
315
+ callSite,
316
+ timeoutId
317
+ });
318
+ if (state === "ready") {
319
+ worker.postMessage(message, transfer);
320
+ } else if (state === "init") {
321
+ queue.push({ message, transfer });
322
+ } else {
323
+ const error = new InitError("Worker is terminated");
324
+ if (timeoutId)
325
+ clearTimeout(timeoutId);
326
+ pendingCalls.delete(currentId);
327
+ return Promise.reject(error);
328
+ }
329
+ const dualResult = {
330
+ then(onfulfilled, onrejected) {
331
+ return getInternalPromise().then(onfulfilled, onrejected);
332
+ },
333
+ catch(onrejected) {
334
+ return getInternalPromise().catch(onrejected);
335
+ },
336
+ finally(onfinally) {
337
+ return getInternalPromise().finally(onfinally);
338
+ },
339
+ [Symbol.asyncIterator]() {
340
+ if (!isStreaming) {
341
+ isStreaming = true;
342
+ const call = pendingCalls.get(currentId);
343
+ if (call?.timeoutId)
344
+ clearTimeout(call.timeoutId);
345
+ pendingCalls.delete(currentId);
346
+ streamIterable = createAsyncIterable(worker, currentId, pendingStreams, timeout, prop);
347
+ }
348
+ return streamIterable[Symbol.asyncIterator]();
349
+ }
350
+ };
351
+ return dualResult;
352
+ };
353
+ }
354
+ });
355
+ proxyInternals.set(proxy, {
356
+ setState: (newState) => {
357
+ state = newState;
358
+ },
359
+ rejectAll: rejectAllPending
360
+ });
361
+ return proxy;
362
+ }
363
+ function kill(proxy) {
364
+ const internals = proxyInternals.get(proxy);
365
+ if (internals) {
366
+ const error = new InitError("Worker is terminated");
367
+ internals.rejectAll(error);
368
+ internals.setState("dead");
369
+ }
370
+ proxy.$worker.terminate();
371
+ }
372
+ export {
373
+ wrap,
374
+ t,
375
+ kill,
376
+ WorkerError,
377
+ UnsupportedRuntimeError,
378
+ TimeoutError,
379
+ InitError
380
+ };
381
+
382
+ //# debugId=0606BCFD2676783C64756E2164756E21
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/errors.ts", "../src/transfer.ts", "../src/wrap.ts"],
4
+ "sourcesContent": [
5
+ "import type { SerializedError } from './protocol.js';\n\n/**\n * Error thrown when Worker execution fails\n */\nexport class WorkerError extends Error {\n /** Stack trace from the Worker side */\n workerStack?: string;\n\n constructor(serialized: SerializedError, callSite?: Error) {\n super(serialized.message);\n this.name = serialized.name || 'WorkerError';\n this.workerStack = serialized.stack;\n\n // Combine stacks: Worker error + call site\n if (callSite?.stack) {\n this.stack = `${this.name}: ${this.message}\\n` +\n ` at Worker\\n` +\n callSite.stack.split('\\n').slice(1).join('\\n');\n }\n }\n}\n\n/**\n * Error thrown when a call times out\n */\nexport class TimeoutError extends Error {\n constructor(method: string, timeout: number) {\n super(`Call to '${method}' timed out after ${timeout}ms`);\n this.name = 'TimeoutError';\n }\n}\n\n/**\n * Error thrown when Worker initialization fails\n */\nexport class InitError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'InitError';\n }\n}\n\n/**\n * Error thrown on unsupported runtime\n */\nexport class UnsupportedRuntimeError extends Error {\n constructor(runtime: string) {\n super(`ThreadX is not supported on ${runtime}. Supported runtimes: Browser, Bun, Deno`);\n this.name = 'UnsupportedRuntimeError';\n }\n}\n",
6
+ "/**\n * Transferable marking utility\n */\n\n/** Symbol to identify transfer descriptors */\nexport const TRANSFER = Symbol('threadx.transfer');\n\n/** Descriptor for transferable values */\nexport interface TransferDescriptor<T = unknown> {\n [TRANSFER]: true;\n value: T;\n transferables: Transferable[];\n}\n\n/**\n * Mark a value as Transferable for zero-copy transfer to Worker\n *\n * @example\n * // Single transferable\n * const buffer = new ArrayBuffer(1024)\n * await worker.process(t(buffer))\n * // buffer.byteLength === 0 (transferred)\n *\n * @example\n * // Object with specified transferables\n * const data = { image: buffer, meta: info }\n * await worker.process(t(data, [buffer]))\n */\nexport function t<T extends Transferable>(value: T): TransferDescriptor<T>;\nexport function t<T>(value: T, transferables: Transferable[]): TransferDescriptor<T>;\nexport function t<T>(value: T, transferables?: Transferable[]): TransferDescriptor<T> {\n return {\n [TRANSFER]: true,\n value,\n transferables: transferables ?? [value as unknown as Transferable],\n };\n}\n\n/**\n * Check if a value is a transfer descriptor\n */\nexport function isTransferDescriptor(value: unknown): value is TransferDescriptor {\n return typeof value === 'object' && value !== null && TRANSFER in value;\n}\n\n/**\n * Process arguments to extract transferables\n */\nexport function prepareArgs(args: unknown[]): { args: unknown[]; transfer: Transferable[] } {\n const transfer: Transferable[] = [];\n const processedArgs = args.map((arg) => {\n if (isTransferDescriptor(arg)) {\n transfer.push(...arg.transferables);\n return arg.value;\n }\n return arg;\n });\n return { args: processedArgs, transfer };\n}\n",
7
+ "/**\n * Main thread API for ThreadX\n */\n\nimport type { WorkerToMainMessage, CallMessage, CancelMessage, SerializedError } from './protocol.js';\nimport { WorkerError, TimeoutError, InitError } from './errors.js';\nimport { prepareArgs } from './transfer.js';\nimport type { WorkerState, WrapOptions, WrappedWorker } from './types.js';\n\n/** Default timeout in milliseconds */\nconst DEFAULT_TIMEOUT = 30000;\n\n/** Internal state for kill() to access */\ninterface InternalState {\n setState: (state: WorkerState) => void;\n rejectAll: (error: Error) => void;\n}\n\n/** WeakMap to store internal state for each proxy */\nconst proxyInternals = new WeakMap<object, InternalState>();\n\n/** Pending RPC call */\ninterface PendingCall {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n method: string;\n callSite: Error;\n timeoutId?: ReturnType<typeof setTimeout>;\n}\n\n/** Pending streaming call */\ninterface PendingStream {\n push: (value: unknown) => void;\n done: () => void;\n error: (error: Error) => void;\n method: string;\n callSite: Error;\n timeoutId?: ReturnType<typeof setTimeout>;\n}\n\n/** Queued call before ready */\ninterface QueuedCall {\n message: CallMessage;\n transfer: Transferable[];\n}\n\n/**\n * Create an AsyncIterable for streaming results\n */\nfunction createAsyncIterable<T>(\n worker: Worker,\n id: number,\n pending: Map<number, PendingStream>,\n timeout: number,\n method: string\n): AsyncIterable<T> {\n // Buffer for values received before consumed\n const buffer: T[] = [];\n // Resolvers waiting for values\n let waitingResolve: ((result: IteratorResult<T>) => void) | null = null;\n let waitingReject: ((error: Error) => void) | null = null;\n let isDone = false;\n let error: Error | null = null;\n\n // Capture call site for better stack traces\n const callSite = new Error();\n\n // Set up timeout\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n if (timeout > 0) {\n timeoutId = setTimeout(() => {\n const timeoutError = new TimeoutError(method, timeout);\n error = timeoutError;\n if (waitingReject) {\n waitingReject(timeoutError);\n waitingResolve = null;\n waitingReject = null;\n }\n pending.delete(id);\n // Send cancel to worker\n worker.postMessage({ $type: 'CANCEL', $id: id } satisfies CancelMessage);\n }, timeout);\n }\n\n // Register pending stream handler\n pending.set(id, {\n push: (value: unknown) => {\n // Reset timeout on each yield\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = setTimeout(() => {\n const timeoutError = new TimeoutError(method, timeout);\n error = timeoutError;\n if (waitingReject) {\n waitingReject(timeoutError);\n waitingResolve = null;\n waitingReject = null;\n }\n pending.delete(id);\n worker.postMessage({ $type: 'CANCEL', $id: id } satisfies CancelMessage);\n }, timeout);\n }\n\n if (waitingResolve) {\n waitingResolve({ value: value as T, done: false });\n waitingResolve = null;\n waitingReject = null;\n } else {\n buffer.push(value as T);\n }\n },\n done: () => {\n if (timeoutId) clearTimeout(timeoutId);\n isDone = true;\n if (waitingResolve) {\n waitingResolve({ value: undefined as T, done: true });\n waitingResolve = null;\n waitingReject = null;\n }\n },\n error: (err: Error) => {\n if (timeoutId) clearTimeout(timeoutId);\n error = err;\n if (waitingReject) {\n waitingReject(err);\n waitingResolve = null;\n waitingReject = null;\n }\n },\n method,\n callSite,\n });\n\n return {\n [Symbol.asyncIterator]() {\n return {\n async next(): Promise<IteratorResult<T>> {\n // Return buffered value if available\n if (buffer.length > 0) {\n return { value: buffer.shift()!, done: false };\n }\n\n // Return error if set\n if (error) {\n throw error;\n }\n\n // Return done if finished\n if (isDone) {\n return { value: undefined as T, done: true };\n }\n\n // Wait for next value\n return new Promise((resolve, reject) => {\n waitingResolve = resolve;\n waitingReject = reject;\n });\n },\n\n async return(): Promise<IteratorResult<T>> {\n // Called when for-await is broken\n if (timeoutId) clearTimeout(timeoutId);\n pending.delete(id);\n // Send cancel to worker\n worker.postMessage({ $type: 'CANCEL', $id: id } satisfies CancelMessage);\n return { value: undefined as T, done: true };\n },\n };\n },\n };\n}\n\n/**\n * Wrap a Worker for seamless RPC communication\n *\n * @example\n * import { wrap } from '@shelchin/threadx'\n * import type * as CalcMethods from './calc.worker'\n *\n * const calc = wrap<typeof CalcMethods>(new Worker('./calc.worker.js'))\n *\n * // Call like a regular async function\n * const result = await calc.add(1, 2)\n *\n * // Streaming with for-await\n * for await (const progress of calc.process(data)) {\n * console.log(progress)\n * }\n */\nexport function wrap<T>(worker: Worker, options?: WrapOptions): WrappedWorker<T> {\n const timeout = options?.timeout ?? DEFAULT_TIMEOUT;\n\n // State\n let state: WorkerState = 'init';\n let id = 0;\n const pendingCalls = new Map<number, PendingCall>();\n const pendingStreams = new Map<number, PendingStream>();\n const queue: QueuedCall[] = [];\n let methods: Set<string> = new Set();\n\n // Flush queued calls after ready\n function flushQueue(): void {\n for (const { message, transfer } of queue) {\n worker.postMessage(message, transfer);\n }\n queue.length = 0;\n }\n\n // Deserialize error from worker\n function deserializeError(serialized: SerializedError, callSite: Error): Error {\n return new WorkerError(serialized, callSite);\n }\n\n // Handle messages from worker\n worker.onmessage = (event: MessageEvent<WorkerToMainMessage>) => {\n const msg = event.data;\n\n // Handle ready message\n if (msg.$type === 'READY') {\n state = 'ready';\n methods = new Set(msg.methods);\n flushQueue();\n return;\n }\n\n // Handle RPC responses\n if (msg.$type === 'RESOLVE') {\n const call = pendingCalls.get(msg.$id);\n if (call) {\n if (call.timeoutId) clearTimeout(call.timeoutId);\n call.resolve(msg.value);\n pendingCalls.delete(msg.$id);\n }\n return;\n }\n\n if (msg.$type === 'REJECT') {\n const call = pendingCalls.get(msg.$id);\n if (call) {\n if (call.timeoutId) clearTimeout(call.timeoutId);\n call.reject(deserializeError(msg.error, call.callSite));\n pendingCalls.delete(msg.$id);\n return;\n }\n\n // Also check streaming calls\n const stream = pendingStreams.get(msg.$id);\n if (stream) {\n stream.error(deserializeError(msg.error, stream.callSite));\n pendingStreams.delete(msg.$id);\n }\n return;\n }\n\n // Handle streaming responses\n if (msg.$type === 'YIELD') {\n const stream = pendingStreams.get(msg.$id);\n if (stream) {\n stream.push(msg.value);\n }\n return;\n }\n\n if (msg.$type === 'DONE') {\n const stream = pendingStreams.get(msg.$id);\n if (stream) {\n stream.done();\n pendingStreams.delete(msg.$id);\n }\n return;\n }\n };\n\n // Function to reject all pending calls (including queued)\n function rejectAllPending(error: Error): void {\n // Reject pending RPC calls\n for (const call of pendingCalls.values()) {\n if (call.timeoutId) clearTimeout(call.timeoutId);\n call.reject(error);\n }\n pendingCalls.clear();\n\n // Reject pending streams (with timeout cleanup)\n for (const stream of pendingStreams.values()) {\n if (stream.timeoutId) clearTimeout(stream.timeoutId);\n stream.error(error);\n }\n pendingStreams.clear();\n\n // Clear queued calls - they'll never be sent\n queue.length = 0;\n }\n\n // Handle worker errors\n worker.onerror = (event) => {\n const error = new InitError(event.message || 'Worker error');\n rejectAllPending(error);\n state = 'dead';\n };\n\n // Create proxy\n const proxy = new Proxy({} as WrappedWorker<T>, {\n get(_, prop: string | symbol) {\n // State hooks\n if (prop === '$state') return state;\n if (prop === '$pending') return pendingCalls.size + pendingStreams.size;\n if (prop === '$worker') return worker;\n\n // Symbol properties\n if (typeof prop === 'symbol') return undefined;\n\n // Method call\n return (...args: unknown[]) => {\n const currentId = ++id;\n const { args: processedArgs, transfer } = prepareArgs(args);\n\n const message: CallMessage = {\n $type: 'CALL',\n $id: currentId,\n method: prop,\n args: processedArgs,\n };\n\n // Check if we know it's a streaming method (we don't at compile time)\n // So we need a heuristic or always return a promise that can be iterated\n // For simplicity, we'll use a convention: methods starting with $ or\n // containing 'stream', 'progress', 'iterate' are streaming\n // Actually, let's just return a special object that can be both awaited and iterated\n\n // Better approach: Return a \"dual\" object that works as both Promise and AsyncIterable\n // The user's code pattern determines which is used\n\n // Simpler approach for now: all methods return Promise\n // Streaming methods must be called with forAwait pattern explicitly\n // We detect streaming by checking if YIELD messages come back\n\n // Actually, the cleanest approach: return an object that:\n // - Has .then() for promise behavior\n // - Has [Symbol.asyncIterator]() for streaming behavior\n // The first YIELD or RESOLVE determines which path\n\n const callSite = new Error();\n\n // Create a dual-mode response object\n let isStreaming = false;\n let streamIterable: AsyncIterable<unknown> | null = null;\n\n // Create internal promise once (to support multiple .then() calls)\n let internalPromise: Promise<unknown> | null = null;\n let promiseResolve: ((value: unknown) => void) | null = null;\n let promiseReject: ((error: Error) => void) | null = null;\n\n // Lazily create the internal promise\n function getInternalPromise(): Promise<unknown> {\n if (!internalPromise) {\n internalPromise = new Promise<unknown>((resolve, reject) => {\n promiseResolve = resolve;\n promiseReject = reject;\n });\n }\n return internalPromise;\n }\n\n // Set up timeout for RPC mode\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n if (timeout > 0) {\n timeoutId = setTimeout(() => {\n if (!isStreaming) {\n const timeoutError = new TimeoutError(prop, timeout);\n // Ensure promise exists before rejecting\n getInternalPromise();\n if (promiseReject) {\n promiseReject(timeoutError);\n }\n pendingCalls.delete(currentId);\n }\n }, timeout);\n }\n\n // Register pending call\n pendingCalls.set(currentId, {\n resolve: (value) => {\n if (timeoutId) clearTimeout(timeoutId);\n // Ensure promise exists before resolving\n getInternalPromise();\n if (promiseResolve) {\n promiseResolve(value);\n }\n },\n reject: (error) => {\n if (timeoutId) clearTimeout(timeoutId);\n // Ensure promise exists before rejecting\n getInternalPromise();\n if (promiseReject) {\n promiseReject(error);\n }\n },\n method: prop,\n callSite,\n timeoutId,\n });\n\n // Send or queue the message\n if (state === 'ready') {\n worker.postMessage(message, transfer);\n } else if (state === 'init') {\n queue.push({ message, transfer });\n } else {\n // Worker is dead\n const error = new InitError('Worker is terminated');\n if (timeoutId) clearTimeout(timeoutId);\n pendingCalls.delete(currentId);\n return Promise.reject(error);\n }\n\n // Return dual-mode object\n const dualResult = {\n // Promise interface - use shared internal promise\n then<TResult1 = unknown, TResult2 = never>(\n onfulfilled?: ((value: unknown) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: Error) => TResult2 | PromiseLike<TResult2>) | null\n ): Promise<TResult1 | TResult2> {\n return getInternalPromise().then(onfulfilled, onrejected) as Promise<TResult1 | TResult2>;\n },\n\n catch<TResult = never>(\n onrejected?: ((reason: Error) => TResult | PromiseLike<TResult>) | null\n ): Promise<unknown | TResult> {\n return getInternalPromise().catch(onrejected);\n },\n\n finally(onfinally?: (() => void) | null): Promise<unknown> {\n return getInternalPromise().finally(onfinally);\n },\n\n // AsyncIterable interface\n [Symbol.asyncIterator](): AsyncIterator<unknown> {\n // Switch to streaming mode\n if (!isStreaming) {\n isStreaming = true;\n\n // Remove from pendingCalls, add to pendingStreams\n const call = pendingCalls.get(currentId);\n if (call?.timeoutId) clearTimeout(call.timeoutId);\n pendingCalls.delete(currentId);\n\n // Create streaming iterable\n streamIterable = createAsyncIterable(\n worker,\n currentId,\n pendingStreams,\n timeout,\n prop\n );\n }\n\n return streamIterable![Symbol.asyncIterator]();\n },\n };\n\n return dualResult;\n };\n },\n });\n\n // Register internal state for kill()\n proxyInternals.set(proxy, {\n setState: (newState: WorkerState) => {\n state = newState;\n },\n rejectAll: rejectAllPending,\n });\n\n return proxy;\n}\n\n/**\n * Terminate a wrapped worker\n */\nexport function kill<T>(proxy: WrappedWorker<T>): void {\n const internals = proxyInternals.get(proxy);\n if (internals) {\n const error = new InitError('Worker is terminated');\n internals.rejectAll(error);\n internals.setState('dead');\n }\n proxy.$worker.terminate();\n}\n"
8
+ ],
9
+ "mappings": ";AAKO,MAAM,oBAAoB,MAAM;AAAA,EAErC;AAAA,EAEA,WAAW,CAAC,YAA6B,UAAkB;AAAA,IACzD,MAAM,WAAW,OAAO;AAAA,IACxB,KAAK,OAAO,WAAW,QAAQ;AAAA,IAC/B,KAAK,cAAc,WAAW;AAAA,IAG9B,IAAI,UAAU,OAAO;AAAA,MACnB,KAAK,QAAQ,GAAG,KAAK,SAAS,KAAK;AAAA,IACjC;AAAA,IACA,SAAS,MAAM,MAAM;AAAA,CAAI,EAAE,MAAM,CAAC,EAAE,KAAK;AAAA,CAAI;AAAA,IACjD;AAAA;AAEJ;AAAA;AAKO,MAAM,qBAAqB,MAAM;AAAA,EACtC,WAAW,CAAC,QAAgB,SAAiB;AAAA,IAC3C,MAAM,YAAY,2BAA2B,WAAW;AAAA,IACxD,KAAK,OAAO;AAAA;AAEhB;AAAA;AAKO,MAAM,kBAAkB,MAAM;AAAA,EACnC,WAAW,CAAC,SAAiB;AAAA,IAC3B,MAAM,OAAO;AAAA,IACb,KAAK,OAAO;AAAA;AAEhB;AAAA;AAKO,MAAM,gCAAgC,MAAM;AAAA,EACjD,WAAW,CAAC,SAAiB;AAAA,IAC3B,MAAM,+BAA+B,iDAAiD;AAAA,IACtF,KAAK,OAAO;AAAA;AAEhB;;;AC9CO,IAAM,WAAW,OAAO,kBAAkB;AAyB1C,SAAS,CAAI,CAAC,OAAU,eAAuD;AAAA,EACpF,OAAO;AAAA,KACJ,WAAW;AAAA,IACZ;AAAA,IACA,eAAe,iBAAiB,CAAC,KAAgC;AAAA,EACnE;AAAA;AAMK,SAAS,oBAAoB,CAAC,OAA6C;AAAA,EAChF,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,YAAY;AAAA;AAM7D,SAAS,WAAW,CAAC,MAAgE;AAAA,EAC1F,MAAM,WAA2B,CAAC;AAAA,EAClC,MAAM,gBAAgB,KAAK,IAAI,CAAC,QAAQ;AAAA,IACtC,IAAI,qBAAqB,GAAG,GAAG;AAAA,MAC7B,SAAS,KAAK,GAAG,IAAI,aAAa;AAAA,MAClC,OAAO,IAAI;AAAA,IACb;AAAA,IACA,OAAO;AAAA,GACR;AAAA,EACD,OAAO,EAAE,MAAM,eAAe,SAAS;AAAA;;;AC/CzC,IAAM,kBAAkB;AASxB,IAAM,iBAAiB,IAAI;AA8B3B,SAAS,mBAAsB,CAC7B,QACA,IACA,SACA,SACA,QACkB;AAAA,EAElB,MAAM,SAAc,CAAC;AAAA,EAErB,IAAI,iBAA+D;AAAA,EACnE,IAAI,gBAAiD;AAAA,EACrD,IAAI,SAAS;AAAA,EACb,IAAI,QAAsB;AAAA,EAG1B,MAAM,WAAW,IAAI;AAAA,EAGrB,IAAI;AAAA,EACJ,IAAI,UAAU,GAAG;AAAA,IACf,YAAY,WAAW,MAAM;AAAA,MAC3B,MAAM,eAAe,IAAI,aAAa,QAAQ,OAAO;AAAA,MACrD,QAAQ;AAAA,MACR,IAAI,eAAe;AAAA,QACjB,cAAc,YAAY;AAAA,QAC1B,iBAAiB;AAAA,QACjB,gBAAgB;AAAA,MAClB;AAAA,MACA,QAAQ,OAAO,EAAE;AAAA,MAEjB,OAAO,YAAY,EAAE,OAAO,UAAU,KAAK,GAAG,CAAyB;AAAA,OACtE,OAAO;AAAA,EACZ;AAAA,EAGA,QAAQ,IAAI,IAAI;AAAA,IACd,MAAM,CAAC,UAAmB;AAAA,MAExB,IAAI,WAAW;AAAA,QACb,aAAa,SAAS;AAAA,QACtB,YAAY,WAAW,MAAM;AAAA,UAC3B,MAAM,eAAe,IAAI,aAAa,QAAQ,OAAO;AAAA,UACrD,QAAQ;AAAA,UACR,IAAI,eAAe;AAAA,YACjB,cAAc,YAAY;AAAA,YAC1B,iBAAiB;AAAA,YACjB,gBAAgB;AAAA,UAClB;AAAA,UACA,QAAQ,OAAO,EAAE;AAAA,UACjB,OAAO,YAAY,EAAE,OAAO,UAAU,KAAK,GAAG,CAAyB;AAAA,WACtE,OAAO;AAAA,MACZ;AAAA,MAEA,IAAI,gBAAgB;AAAA,QAClB,eAAe,EAAE,OAAmB,MAAM,MAAM,CAAC;AAAA,QACjD,iBAAiB;AAAA,QACjB,gBAAgB;AAAA,MAClB,EAAO;AAAA,QACL,OAAO,KAAK,KAAU;AAAA;AAAA;AAAA,IAG1B,MAAM,MAAM;AAAA,MACV,IAAI;AAAA,QAAW,aAAa,SAAS;AAAA,MACrC,SAAS;AAAA,MACT,IAAI,gBAAgB;AAAA,QAClB,eAAe,EAAE,OAAO,WAAgB,MAAM,KAAK,CAAC;AAAA,QACpD,iBAAiB;AAAA,QACjB,gBAAgB;AAAA,MAClB;AAAA;AAAA,IAEF,OAAO,CAAC,QAAe;AAAA,MACrB,IAAI;AAAA,QAAW,aAAa,SAAS;AAAA,MACrC,QAAQ;AAAA,MACR,IAAI,eAAe;AAAA,QACjB,cAAc,GAAG;AAAA,QACjB,iBAAiB;AAAA,QACjB,gBAAgB;AAAA,MAClB;AAAA;AAAA,IAEF;AAAA,IACA;AAAA,EACF,CAAC;AAAA,EAED,OAAO;AAAA,KACJ,OAAO,cAAc,GAAG;AAAA,MACvB,OAAO;AAAA,aACC,KAAI,GAA+B;AAAA,UAEvC,IAAI,OAAO,SAAS,GAAG;AAAA,YACrB,OAAO,EAAE,OAAO,OAAO,MAAM,GAAI,MAAM,MAAM;AAAA,UAC/C;AAAA,UAGA,IAAI,OAAO;AAAA,YACT,MAAM;AAAA,UACR;AAAA,UAGA,IAAI,QAAQ;AAAA,YACV,OAAO,EAAE,OAAO,WAAgB,MAAM,KAAK;AAAA,UAC7C;AAAA,UAGA,OAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AAAA,YACtC,iBAAiB;AAAA,YACjB,gBAAgB;AAAA,WACjB;AAAA;AAAA,aAGG,OAAM,GAA+B;AAAA,UAEzC,IAAI;AAAA,YAAW,aAAa,SAAS;AAAA,UACrC,QAAQ,OAAO,EAAE;AAAA,UAEjB,OAAO,YAAY,EAAE,OAAO,UAAU,KAAK,GAAG,CAAyB;AAAA,UACvE,OAAO,EAAE,OAAO,WAAgB,MAAM,KAAK;AAAA;AAAA,MAE/C;AAAA;AAAA,EAEJ;AAAA;AAoBK,SAAS,IAAO,CAAC,QAAgB,SAAyC;AAAA,EAC/E,MAAM,UAAU,SAAS,WAAW;AAAA,EAGpC,IAAI,QAAqB;AAAA,EACzB,IAAI,KAAK;AAAA,EACT,MAAM,eAAe,IAAI;AAAA,EACzB,MAAM,iBAAiB,IAAI;AAAA,EAC3B,MAAM,QAAsB,CAAC;AAAA,EAC7B,IAAI,UAAuB,IAAI;AAAA,EAG/B,SAAS,UAAU,GAAS;AAAA,IAC1B,aAAa,SAAS,cAAc,OAAO;AAAA,MACzC,OAAO,YAAY,SAAS,QAAQ;AAAA,IACtC;AAAA,IACA,MAAM,SAAS;AAAA;AAAA,EAIjB,SAAS,gBAAgB,CAAC,YAA6B,UAAwB;AAAA,IAC7E,OAAO,IAAI,YAAY,YAAY,QAAQ;AAAA;AAAA,EAI7C,OAAO,YAAY,CAAC,UAA6C;AAAA,IAC/D,MAAM,MAAM,MAAM;AAAA,IAGlB,IAAI,IAAI,UAAU,SAAS;AAAA,MACzB,QAAQ;AAAA,MACR,UAAU,IAAI,IAAI,IAAI,OAAO;AAAA,MAC7B,WAAW;AAAA,MACX;AAAA,IACF;AAAA,IAGA,IAAI,IAAI,UAAU,WAAW;AAAA,MAC3B,MAAM,OAAO,aAAa,IAAI,IAAI,GAAG;AAAA,MACrC,IAAI,MAAM;AAAA,QACR,IAAI,KAAK;AAAA,UAAW,aAAa,KAAK,SAAS;AAAA,QAC/C,KAAK,QAAQ,IAAI,KAAK;AAAA,QACtB,aAAa,OAAO,IAAI,GAAG;AAAA,MAC7B;AAAA,MACA;AAAA,IACF;AAAA,IAEA,IAAI,IAAI,UAAU,UAAU;AAAA,MAC1B,MAAM,OAAO,aAAa,IAAI,IAAI,GAAG;AAAA,MACrC,IAAI,MAAM;AAAA,QACR,IAAI,KAAK;AAAA,UAAW,aAAa,KAAK,SAAS;AAAA,QAC/C,KAAK,OAAO,iBAAiB,IAAI,OAAO,KAAK,QAAQ,CAAC;AAAA,QACtD,aAAa,OAAO,IAAI,GAAG;AAAA,QAC3B;AAAA,MACF;AAAA,MAGA,MAAM,SAAS,eAAe,IAAI,IAAI,GAAG;AAAA,MACzC,IAAI,QAAQ;AAAA,QACV,OAAO,MAAM,iBAAiB,IAAI,OAAO,OAAO,QAAQ,CAAC;AAAA,QACzD,eAAe,OAAO,IAAI,GAAG;AAAA,MAC/B;AAAA,MACA;AAAA,IACF;AAAA,IAGA,IAAI,IAAI,UAAU,SAAS;AAAA,MACzB,MAAM,SAAS,eAAe,IAAI,IAAI,GAAG;AAAA,MACzC,IAAI,QAAQ;AAAA,QACV,OAAO,KAAK,IAAI,KAAK;AAAA,MACvB;AAAA,MACA;AAAA,IACF;AAAA,IAEA,IAAI,IAAI,UAAU,QAAQ;AAAA,MACxB,MAAM,SAAS,eAAe,IAAI,IAAI,GAAG;AAAA,MACzC,IAAI,QAAQ;AAAA,QACV,OAAO,KAAK;AAAA,QACZ,eAAe,OAAO,IAAI,GAAG;AAAA,MAC/B;AAAA,MACA;AAAA,IACF;AAAA;AAAA,EAIF,SAAS,gBAAgB,CAAC,OAAoB;AAAA,IAE5C,WAAW,QAAQ,aAAa,OAAO,GAAG;AAAA,MACxC,IAAI,KAAK;AAAA,QAAW,aAAa,KAAK,SAAS;AAAA,MAC/C,KAAK,OAAO,KAAK;AAAA,IACnB;AAAA,IACA,aAAa,MAAM;AAAA,IAGnB,WAAW,UAAU,eAAe,OAAO,GAAG;AAAA,MAC5C,IAAI,OAAO;AAAA,QAAW,aAAa,OAAO,SAAS;AAAA,MACnD,OAAO,MAAM,KAAK;AAAA,IACpB;AAAA,IACA,eAAe,MAAM;AAAA,IAGrB,MAAM,SAAS;AAAA;AAAA,EAIjB,OAAO,UAAU,CAAC,UAAU;AAAA,IAC1B,MAAM,QAAQ,IAAI,UAAU,MAAM,WAAW,cAAc;AAAA,IAC3D,iBAAiB,KAAK;AAAA,IACtB,QAAQ;AAAA;AAAA,EAIV,MAAM,QAAQ,IAAI,MAAM,CAAC,GAAuB;AAAA,IAC9C,GAAG,CAAC,GAAG,MAAuB;AAAA,MAE5B,IAAI,SAAS;AAAA,QAAU,OAAO;AAAA,MAC9B,IAAI,SAAS;AAAA,QAAY,OAAO,aAAa,OAAO,eAAe;AAAA,MACnE,IAAI,SAAS;AAAA,QAAW,OAAO;AAAA,MAG/B,IAAI,OAAO,SAAS;AAAA,QAAU;AAAA,MAG9B,OAAO,IAAI,SAAoB;AAAA,QAC7B,MAAM,YAAY,EAAE;AAAA,QACpB,QAAQ,MAAM,eAAe,aAAa,YAAY,IAAI;AAAA,QAE1D,MAAM,UAAuB;AAAA,UAC3B,OAAO;AAAA,UACP,KAAK;AAAA,UACL,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,QAoBA,MAAM,WAAW,IAAI;AAAA,QAGrB,IAAI,cAAc;AAAA,QAClB,IAAI,iBAAgD;AAAA,QAGpD,IAAI,kBAA2C;AAAA,QAC/C,IAAI,iBAAoD;AAAA,QACxD,IAAI,gBAAiD;AAAA,QAGrD,SAAS,kBAAkB,GAAqB;AAAA,UAC9C,IAAI,CAAC,iBAAiB;AAAA,YACpB,kBAAkB,IAAI,QAAiB,CAAC,SAAS,WAAW;AAAA,cAC1D,iBAAiB;AAAA,cACjB,gBAAgB;AAAA,aACjB;AAAA,UACH;AAAA,UACA,OAAO;AAAA;AAAA,QAIT,IAAI;AAAA,QACJ,IAAI,UAAU,GAAG;AAAA,UACf,YAAY,WAAW,MAAM;AAAA,YAC3B,IAAI,CAAC,aAAa;AAAA,cAChB,MAAM,eAAe,IAAI,aAAa,MAAM,OAAO;AAAA,cAEnD,mBAAmB;AAAA,cACnB,IAAI,eAAe;AAAA,gBACjB,cAAc,YAAY;AAAA,cAC5B;AAAA,cACA,aAAa,OAAO,SAAS;AAAA,YAC/B;AAAA,aACC,OAAO;AAAA,QACZ;AAAA,QAGA,aAAa,IAAI,WAAW;AAAA,UAC1B,SAAS,CAAC,UAAU;AAAA,YAClB,IAAI;AAAA,cAAW,aAAa,SAAS;AAAA,YAErC,mBAAmB;AAAA,YACnB,IAAI,gBAAgB;AAAA,cAClB,eAAe,KAAK;AAAA,YACtB;AAAA;AAAA,UAEF,QAAQ,CAAC,UAAU;AAAA,YACjB,IAAI;AAAA,cAAW,aAAa,SAAS;AAAA,YAErC,mBAAmB;AAAA,YACnB,IAAI,eAAe;AAAA,cACjB,cAAc,KAAK;AAAA,YACrB;AAAA;AAAA,UAEF,QAAQ;AAAA,UACR;AAAA,UACA;AAAA,QACF,CAAC;AAAA,QAGD,IAAI,UAAU,SAAS;AAAA,UACrB,OAAO,YAAY,SAAS,QAAQ;AAAA,QACtC,EAAO,SAAI,UAAU,QAAQ;AAAA,UAC3B,MAAM,KAAK,EAAE,SAAS,SAAS,CAAC;AAAA,QAClC,EAAO;AAAA,UAEL,MAAM,QAAQ,IAAI,UAAU,sBAAsB;AAAA,UAClD,IAAI;AAAA,YAAW,aAAa,SAAS;AAAA,UACrC,aAAa,OAAO,SAAS;AAAA,UAC7B,OAAO,QAAQ,OAAO,KAAK;AAAA;AAAA,QAI7B,MAAM,aAAa;AAAA,UAEjB,IAA0C,CACxC,aACA,YAC8B;AAAA,YAC9B,OAAO,mBAAmB,EAAE,KAAK,aAAa,UAAU;AAAA;AAAA,UAG1D,KAAsB,CACpB,YAC4B;AAAA,YAC5B,OAAO,mBAAmB,EAAE,MAAM,UAAU;AAAA;AAAA,UAG9C,OAAO,CAAC,WAAmD;AAAA,YACzD,OAAO,mBAAmB,EAAE,QAAQ,SAAS;AAAA;AAAA,WAI9C,OAAO,cAAc,GAA2B;AAAA,YAE/C,IAAI,CAAC,aAAa;AAAA,cAChB,cAAc;AAAA,cAGd,MAAM,OAAO,aAAa,IAAI,SAAS;AAAA,cACvC,IAAI,MAAM;AAAA,gBAAW,aAAa,KAAK,SAAS;AAAA,cAChD,aAAa,OAAO,SAAS;AAAA,cAG7B,iBAAiB,oBACf,QACA,WACA,gBACA,SACA,IACF;AAAA,YACF;AAAA,YAEA,OAAO,eAAgB,OAAO,eAAe;AAAA;AAAA,QAEjD;AAAA,QAEA,OAAO;AAAA;AAAA;AAAA,EAGb,CAAC;AAAA,EAGD,eAAe,IAAI,OAAO;AAAA,IACxB,UAAU,CAAC,aAA0B;AAAA,MACnC,QAAQ;AAAA;AAAA,IAEV,WAAW;AAAA,EACb,CAAC;AAAA,EAED,OAAO;AAAA;AAMF,SAAS,IAAO,CAAC,OAA+B;AAAA,EACrD,MAAM,YAAY,eAAe,IAAI,KAAK;AAAA,EAC1C,IAAI,WAAW;AAAA,IACb,MAAM,QAAQ,IAAI,UAAU,sBAAsB;AAAA,IAClD,UAAU,UAAU,KAAK;AAAA,IACzB,UAAU,SAAS,MAAM;AAAA,EAC3B;AAAA,EACA,MAAM,QAAQ,UAAU;AAAA;",
10
+ "debugId": "0606BCFD2676783C64756E2164756E21",
11
+ "names": []
12
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Mock Worker for testing
3
+ * Simulates Worker message passing in the same thread
4
+ */
5
+ export interface MockWorkerMethods {
6
+ [key: string]: (...args: unknown[]) => unknown;
7
+ }
8
+ /**
9
+ * Create a mock Worker that runs in the same thread
10
+ * Useful for testing without actual Worker threads
11
+ */
12
+ export declare function createMockWorker(methods: MockWorkerMethods): Worker;
13
+ /**
14
+ * Create a mock Worker that delays READY message
15
+ */
16
+ export declare function createSlowStartWorker(methods: MockWorkerMethods, delay: number): Worker;
17
+ /**
18
+ * Create a mock Worker that fails on initialization
19
+ */
20
+ export declare function createFailingWorker(errorMessage: string): Worker;
21
+ //# sourceMappingURL=mock-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-worker.d.ts","sourceRoot":"","sources":["../src/mock-worker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,WAAW,iBAAiB;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;CAChD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CA+HnE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAkCvF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CA8BhE"}
@@ -0,0 +1,184 @@
1
+ // src/protocol.ts
2
+ function serializeError(error) {
3
+ if (error instanceof Error) {
4
+ return {
5
+ name: error.name,
6
+ message: error.message,
7
+ stack: error.stack
8
+ };
9
+ }
10
+ return {
11
+ name: "Error",
12
+ message: String(error)
13
+ };
14
+ }
15
+
16
+ // src/mock-worker.ts
17
+ function createMockWorker(methods) {
18
+ let onmessageHandler = null;
19
+ let onerrorHandler = null;
20
+ let isTerminated = false;
21
+ const activeGenerators = new Map;
22
+ const mockWorker = {
23
+ postMessage(data, _transfer) {
24
+ if (isTerminated)
25
+ return;
26
+ queueMicrotask(async () => {
27
+ if (isTerminated)
28
+ return;
29
+ if (data.$type === "CANCEL") {
30
+ const gen = activeGenerators.get(data.$id);
31
+ if (gen && "return" in gen && typeof gen.return === "function") {
32
+ try {
33
+ gen.return(undefined);
34
+ } catch {}
35
+ }
36
+ activeGenerators.delete(data.$id);
37
+ return;
38
+ }
39
+ if (data.$type === "CALL") {
40
+ const { $id, method, args } = data;
41
+ const fn = methods[method];
42
+ if (!fn) {
43
+ sendMessage({
44
+ $type: "REJECT",
45
+ $id,
46
+ error: { name: "Error", message: `Method '${method}' not found` }
47
+ });
48
+ return;
49
+ }
50
+ try {
51
+ const result = fn(...args);
52
+ if (isGenerator(result)) {
53
+ activeGenerators.set($id, result);
54
+ try {
55
+ for await (const value of result) {
56
+ if (!activeGenerators.has($id) || isTerminated)
57
+ break;
58
+ sendMessage({ $type: "YIELD", $id, value });
59
+ }
60
+ if (activeGenerators.has($id) && !isTerminated) {
61
+ sendMessage({ $type: "DONE", $id });
62
+ }
63
+ } finally {
64
+ activeGenerators.delete($id);
65
+ }
66
+ } else {
67
+ const value = await result;
68
+ if (!isTerminated) {
69
+ sendMessage({ $type: "RESOLVE", $id, value });
70
+ }
71
+ }
72
+ } catch (error) {
73
+ if (!isTerminated) {
74
+ sendMessage({ $type: "REJECT", $id, error: serializeError(error) });
75
+ }
76
+ }
77
+ }
78
+ });
79
+ },
80
+ set onmessage(handler) {
81
+ onmessageHandler = handler;
82
+ },
83
+ get onmessage() {
84
+ return onmessageHandler;
85
+ },
86
+ set onerror(handler) {
87
+ onerrorHandler = handler;
88
+ },
89
+ get onerror() {
90
+ return onerrorHandler;
91
+ },
92
+ terminate() {
93
+ isTerminated = true;
94
+ activeGenerators.clear();
95
+ },
96
+ addEventListener() {},
97
+ removeEventListener() {},
98
+ dispatchEvent() {
99
+ return true;
100
+ }
101
+ };
102
+ function sendMessage(message) {
103
+ if (onmessageHandler && !isTerminated) {
104
+ onmessageHandler({ data: message });
105
+ }
106
+ }
107
+ function isGenerator(value) {
108
+ if (value === null || typeof value !== "object")
109
+ return false;
110
+ return typeof value.next === "function" && (typeof value[Symbol.iterator] === "function" || typeof value[Symbol.asyncIterator] === "function");
111
+ }
112
+ queueMicrotask(() => {
113
+ if (!isTerminated) {
114
+ sendMessage({ $type: "READY", methods: Object.keys(methods) });
115
+ }
116
+ });
117
+ return mockWorker;
118
+ }
119
+ function createSlowStartWorker(methods, delay) {
120
+ const worker = createMockWorker(methods);
121
+ const originalOnmessage = Object.getOwnPropertyDescriptor(worker, "onmessage");
122
+ let readyDelayed = false;
123
+ let pendingReady = null;
124
+ let actualHandler = null;
125
+ Object.defineProperty(worker, "onmessage", {
126
+ set(handler) {
127
+ actualHandler = handler;
128
+ if (originalOnmessage?.set) {
129
+ originalOnmessage.set.call(worker, (event) => {
130
+ if (!readyDelayed && event.data.$type === "READY") {
131
+ pendingReady = event.data;
132
+ readyDelayed = true;
133
+ setTimeout(() => {
134
+ if (actualHandler && pendingReady) {
135
+ actualHandler({ data: pendingReady });
136
+ }
137
+ }, delay);
138
+ } else if (actualHandler) {
139
+ actualHandler(event);
140
+ }
141
+ });
142
+ }
143
+ },
144
+ get() {
145
+ return actualHandler;
146
+ }
147
+ });
148
+ return worker;
149
+ }
150
+ function createFailingWorker(errorMessage) {
151
+ let onerrorHandler = null;
152
+ const mockWorker = {
153
+ postMessage() {},
154
+ set onmessage(_handler) {},
155
+ get onmessage() {
156
+ return null;
157
+ },
158
+ set onerror(handler) {
159
+ onerrorHandler = handler;
160
+ queueMicrotask(() => {
161
+ if (onerrorHandler) {
162
+ onerrorHandler({ message: errorMessage });
163
+ }
164
+ });
165
+ },
166
+ get onerror() {
167
+ return onerrorHandler;
168
+ },
169
+ terminate() {},
170
+ addEventListener() {},
171
+ removeEventListener() {},
172
+ dispatchEvent() {
173
+ return true;
174
+ }
175
+ };
176
+ return mockWorker;
177
+ }
178
+ export {
179
+ createSlowStartWorker,
180
+ createMockWorker,
181
+ createFailingWorker
182
+ };
183
+
184
+ //# debugId=A291C1DCC73E48BC64756E2164756E21