@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/README.md +211 -0
- package/dist/errors.d.ts +28 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +45 -0
- package/dist/errors.js.map +10 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +382 -0
- package/dist/index.js.map +12 -0
- package/dist/mock-worker.d.ts +21 -0
- package/dist/mock-worker.d.ts.map +1 -0
- package/dist/mock-worker.js +184 -0
- package/dist/mock-worker.js.map +11 -0
- package/dist/protocol.d.ts +45 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +19 -0
- package/dist/protocol.js.map +10 -0
- package/dist/test-fail.worker.d.ts +5 -0
- package/dist/test-fail.worker.d.ts.map +1 -0
- package/dist/test-fail.worker.js +4 -0
- package/dist/test-fail.worker.js.map +10 -0
- package/dist/test.worker.d.ts +5 -0
- package/dist/test.worker.d.ts.map +1 -0
- package/dist/test.worker.js +198 -0
- package/dist/test.worker.js.map +13 -0
- package/dist/transfer.d.ts +39 -0
- package/dist/transfer.d.ts.map +1 -0
- package/dist/transfer.js +31 -0
- package/dist/transfer.js.map +10 -0
- package/dist/types.d.ts +62 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +9 -0
- package/dist/worker.d.ts +31 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +113 -0
- package/dist/worker.js.map +12 -0
- package/dist/wrap.d.ts +27 -0
- package/dist/wrap.d.ts.map +1 -0
- package/dist/wrap.js +363 -0
- package/dist/wrap.js.map +12 -0
- package/package.json +30 -0
package/dist/worker.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
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/transfer.ts
|
|
17
|
+
var TRANSFER = Symbol("threadx.transfer");
|
|
18
|
+
function t(value, transferables) {
|
|
19
|
+
return {
|
|
20
|
+
[TRANSFER]: true,
|
|
21
|
+
value,
|
|
22
|
+
transferables: transferables ?? [value]
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function isTransferDescriptor(value) {
|
|
26
|
+
return typeof value === "object" && value !== null && TRANSFER in value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/worker.ts
|
|
30
|
+
function isSyncGenerator(value) {
|
|
31
|
+
if (value === null || typeof value !== "object")
|
|
32
|
+
return false;
|
|
33
|
+
return typeof value.next === "function" && typeof value[Symbol.iterator] === "function" && typeof value[Symbol.asyncIterator] !== "function";
|
|
34
|
+
}
|
|
35
|
+
function isAsyncGenerator(value) {
|
|
36
|
+
if (value === null || typeof value !== "object")
|
|
37
|
+
return false;
|
|
38
|
+
return typeof value.next === "function" && typeof value[Symbol.asyncIterator] === "function";
|
|
39
|
+
}
|
|
40
|
+
function isGenerator(value) {
|
|
41
|
+
return isSyncGenerator(value) || isAsyncGenerator(value);
|
|
42
|
+
}
|
|
43
|
+
function postMessage(message, value) {
|
|
44
|
+
if (isTransferDescriptor(value)) {
|
|
45
|
+
self.postMessage(message, value.transferables);
|
|
46
|
+
} else {
|
|
47
|
+
self.postMessage(message);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function expose(methods) {
|
|
51
|
+
const methodNames = Object.keys(methods);
|
|
52
|
+
const activeGenerators = new Map;
|
|
53
|
+
self.postMessage({ $type: "READY", methods: methodNames });
|
|
54
|
+
self.onmessage = async (event) => {
|
|
55
|
+
const msg = event.data;
|
|
56
|
+
if (msg.$type === "CANCEL") {
|
|
57
|
+
const gen = activeGenerators.get(msg.$id);
|
|
58
|
+
if (gen && "return" in gen && typeof gen.return === "function") {
|
|
59
|
+
try {
|
|
60
|
+
gen.return(undefined);
|
|
61
|
+
} catch {}
|
|
62
|
+
}
|
|
63
|
+
activeGenerators.delete(msg.$id);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (msg.$type === "CALL") {
|
|
67
|
+
const { $id, method, args } = msg;
|
|
68
|
+
const fn = methods[method];
|
|
69
|
+
if (!fn) {
|
|
70
|
+
postMessage({
|
|
71
|
+
$type: "REJECT",
|
|
72
|
+
$id,
|
|
73
|
+
error: { name: "Error", message: `Method '${method}' not found` }
|
|
74
|
+
});
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const result = fn(...args);
|
|
79
|
+
if (isGenerator(result) || isAsyncGenerator(result)) {
|
|
80
|
+
activeGenerators.set($id, result);
|
|
81
|
+
try {
|
|
82
|
+
for await (const value of result) {
|
|
83
|
+
if (!activeGenerators.has($id)) {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
postMessage({ $type: "YIELD", $id, value }, value);
|
|
87
|
+
}
|
|
88
|
+
if (activeGenerators.has($id)) {
|
|
89
|
+
postMessage({ $type: "DONE", $id });
|
|
90
|
+
}
|
|
91
|
+
} finally {
|
|
92
|
+
activeGenerators.delete($id);
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
const value = await result;
|
|
96
|
+
postMessage({ $type: "RESOLVE", $id, value }, value);
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
postMessage({
|
|
100
|
+
$type: "REJECT",
|
|
101
|
+
$id,
|
|
102
|
+
error: serializeError(error)
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export {
|
|
109
|
+
t,
|
|
110
|
+
expose
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
//# debugId=4A1BF78538391DC264756E2164756E21
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/protocol.ts", "../src/transfer.ts", "../src/worker.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * Communication protocol types between main thread and worker\n */\n\n// ============ Main Thread → Worker ============\n\nexport interface CallMessage {\n $type: 'CALL';\n $id: number;\n method: string;\n args: unknown[];\n}\n\nexport interface CancelMessage {\n $type: 'CANCEL';\n $id: number;\n}\n\nexport type MainToWorkerMessage = CallMessage | CancelMessage;\n\n// ============ Worker → Main Thread ============\n\nexport interface ReadyMessage {\n $type: 'READY';\n methods: string[];\n}\n\nexport interface ResolveMessage {\n $type: 'RESOLVE';\n $id: number;\n value: unknown;\n}\n\nexport interface RejectMessage {\n $type: 'REJECT';\n $id: number;\n error: SerializedError;\n}\n\nexport interface YieldMessage {\n $type: 'YIELD';\n $id: number;\n value: unknown;\n}\n\nexport interface DoneMessage {\n $type: 'DONE';\n $id: number;\n}\n\nexport type WorkerToMainMessage =\n | ReadyMessage\n | ResolveMessage\n | RejectMessage\n | YieldMessage\n | DoneMessage;\n\n// ============ Error Serialization ============\n\nexport interface SerializedError {\n name: string;\n message: string;\n stack?: string;\n}\n\nexport function serializeError(error: unknown): SerializedError {\n if (error instanceof Error) {\n return {\n name: error.name,\n message: error.message,\n stack: error.stack,\n };\n }\n return {\n name: 'Error',\n message: String(error),\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 * Worker-side API for ThreadX\n */\n\nimport type { MainToWorkerMessage, WorkerToMainMessage } from './protocol.js';\nimport { serializeError } from './protocol.js';\nimport { isTransferDescriptor } from './transfer.js';\n\n// Re-export t() for Worker-side use\nexport { t } from './transfer.js';\n\ntype AnyFunction = (...args: unknown[]) => unknown;\ntype MethodMap = Record<string, AnyFunction>;\n\n/**\n * Check if a value is a Generator (sync)\n * Uses duck typing - checks for next() and Symbol.iterator\n */\nfunction isSyncGenerator(value: unknown): value is Generator {\n if (value === null || typeof value !== 'object') return false;\n return (\n typeof (value as Generator).next === 'function' &&\n typeof (value as Generator)[Symbol.iterator] === 'function' &&\n // Exclude AsyncGenerator (which also has next and Symbol.iterator)\n typeof (value as AsyncGenerator)[Symbol.asyncIterator] !== 'function'\n );\n}\n\n/**\n * Check if a value is an AsyncGenerator\n * Uses duck typing - checks for next() and Symbol.asyncIterator\n */\nfunction isAsyncGenerator(value: unknown): value is AsyncGenerator {\n if (value === null || typeof value !== 'object') return false;\n return (\n typeof (value as AsyncGenerator).next === 'function' &&\n typeof (value as AsyncGenerator)[Symbol.asyncIterator] === 'function'\n );\n}\n\n/**\n * Check if a value is any kind of Generator (sync or async)\n */\nfunction isGenerator(value: unknown): value is Generator | AsyncGenerator {\n return isSyncGenerator(value) || isAsyncGenerator(value);\n}\n\n/**\n * Post a message with optional transferables\n */\nfunction postMessage(message: WorkerToMainMessage, value?: unknown): void {\n if (isTransferDescriptor(value)) {\n self.postMessage(message, value.transferables);\n } else {\n self.postMessage(message);\n }\n}\n\n/**\n * Expose methods to the main thread\n *\n * @example\n * import { expose } from '@shelchin/threadx/worker'\n *\n * expose({\n * add(a: number, b: number) {\n * return a + b\n * },\n *\n * async heavyTask(data: ArrayBuffer) {\n * // Long running computation...\n * return result\n * },\n *\n * *progress(total: number) {\n * for (let i = 0; i <= total; i++) {\n * yield i\n * }\n * }\n * })\n */\nexport function expose<T extends MethodMap>(methods: T): void {\n const methodNames = Object.keys(methods);\n\n // Track active generators for cancellation\n const activeGenerators = new Map<number, Generator | AsyncGenerator>();\n\n // Send ready message\n self.postMessage({ $type: 'READY', methods: methodNames } satisfies WorkerToMainMessage);\n\n // Handle incoming messages\n self.onmessage = async (event: MessageEvent<MainToWorkerMessage>) => {\n const msg = event.data;\n\n // Handle cancellation\n if (msg.$type === 'CANCEL') {\n const gen = activeGenerators.get(msg.$id);\n if (gen && 'return' in gen && typeof gen.return === 'function') {\n try {\n gen.return(undefined);\n } catch {\n // Ignore errors during cancellation\n }\n }\n activeGenerators.delete(msg.$id);\n return;\n }\n\n // Handle method call\n if (msg.$type === 'CALL') {\n const { $id, method, args } = msg;\n const fn = methods[method];\n\n // Method not found\n if (!fn) {\n postMessage({\n $type: 'REJECT',\n $id,\n error: { name: 'Error', message: `Method '${method}' not found` },\n });\n return;\n }\n\n try {\n const result = fn(...args);\n\n // Handle Generator/AsyncGenerator → streaming\n if (isGenerator(result) || isAsyncGenerator(result)) {\n activeGenerators.set($id, result);\n\n try {\n // Use for-await to handle both sync and async generators\n for await (const value of result as AsyncIterable<unknown>) {\n // Check if cancelled\n if (!activeGenerators.has($id)) {\n break;\n }\n postMessage({ $type: 'YIELD', $id, value }, value);\n }\n\n // Only send DONE if not cancelled\n if (activeGenerators.has($id)) {\n postMessage({ $type: 'DONE', $id });\n }\n } finally {\n activeGenerators.delete($id);\n }\n } else {\n // Handle Promise/value → single response\n const value = await result;\n postMessage({ $type: 'RESOLVE', $id, value }, value);\n }\n } catch (error) {\n postMessage({\n $type: 'REJECT',\n $id,\n error: serializeError(error),\n });\n }\n }\n };\n}\n"
|
|
8
|
+
],
|
|
9
|
+
"mappings": "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",
|
|
10
|
+
"debugId": "4A1BF78538391DC264756E2164756E21",
|
|
11
|
+
"names": []
|
|
12
|
+
}
|
package/dist/wrap.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main thread API for ThreadX
|
|
3
|
+
*/
|
|
4
|
+
import type { WrapOptions, WrappedWorker } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Wrap a Worker for seamless RPC communication
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { wrap } from '@shelchin/threadx'
|
|
10
|
+
* import type * as CalcMethods from './calc.worker'
|
|
11
|
+
*
|
|
12
|
+
* const calc = wrap<typeof CalcMethods>(new Worker('./calc.worker.js'))
|
|
13
|
+
*
|
|
14
|
+
* // Call like a regular async function
|
|
15
|
+
* const result = await calc.add(1, 2)
|
|
16
|
+
*
|
|
17
|
+
* // Streaming with for-await
|
|
18
|
+
* for await (const progress of calc.process(data)) {
|
|
19
|
+
* console.log(progress)
|
|
20
|
+
* }
|
|
21
|
+
*/
|
|
22
|
+
export declare function wrap<T>(worker: Worker, options?: WrapOptions): WrappedWorker<T>;
|
|
23
|
+
/**
|
|
24
|
+
* Terminate a wrapped worker
|
|
25
|
+
*/
|
|
26
|
+
export declare function kill<T>(proxy: WrappedWorker<T>): void;
|
|
27
|
+
//# sourceMappingURL=wrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrap.d.ts","sourceRoot":"","sources":["../src/wrap.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAe,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAqK1E;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,aAAa,CAAC,CAAC,CAAC,CA6R/E;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAQrD"}
|
package/dist/wrap.js
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
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
|
+
// src/transfer.ts
|
|
33
|
+
var TRANSFER = Symbol("threadx.transfer");
|
|
34
|
+
function isTransferDescriptor(value) {
|
|
35
|
+
return typeof value === "object" && value !== null && TRANSFER in value;
|
|
36
|
+
}
|
|
37
|
+
function prepareArgs(args) {
|
|
38
|
+
const transfer = [];
|
|
39
|
+
const processedArgs = args.map((arg) => {
|
|
40
|
+
if (isTransferDescriptor(arg)) {
|
|
41
|
+
transfer.push(...arg.transferables);
|
|
42
|
+
return arg.value;
|
|
43
|
+
}
|
|
44
|
+
return arg;
|
|
45
|
+
});
|
|
46
|
+
return { args: processedArgs, transfer };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/wrap.ts
|
|
50
|
+
var DEFAULT_TIMEOUT = 30000;
|
|
51
|
+
var proxyInternals = new WeakMap;
|
|
52
|
+
function createAsyncIterable(worker, id, pending, timeout, method) {
|
|
53
|
+
const buffer = [];
|
|
54
|
+
let waitingResolve = null;
|
|
55
|
+
let waitingReject = null;
|
|
56
|
+
let isDone = false;
|
|
57
|
+
let error = null;
|
|
58
|
+
const callSite = new Error;
|
|
59
|
+
let timeoutId;
|
|
60
|
+
if (timeout > 0) {
|
|
61
|
+
timeoutId = setTimeout(() => {
|
|
62
|
+
const timeoutError = new TimeoutError(method, timeout);
|
|
63
|
+
error = timeoutError;
|
|
64
|
+
if (waitingReject) {
|
|
65
|
+
waitingReject(timeoutError);
|
|
66
|
+
waitingResolve = null;
|
|
67
|
+
waitingReject = null;
|
|
68
|
+
}
|
|
69
|
+
pending.delete(id);
|
|
70
|
+
worker.postMessage({ $type: "CANCEL", $id: id });
|
|
71
|
+
}, timeout);
|
|
72
|
+
}
|
|
73
|
+
pending.set(id, {
|
|
74
|
+
push: (value) => {
|
|
75
|
+
if (timeoutId) {
|
|
76
|
+
clearTimeout(timeoutId);
|
|
77
|
+
timeoutId = setTimeout(() => {
|
|
78
|
+
const timeoutError = new TimeoutError(method, timeout);
|
|
79
|
+
error = timeoutError;
|
|
80
|
+
if (waitingReject) {
|
|
81
|
+
waitingReject(timeoutError);
|
|
82
|
+
waitingResolve = null;
|
|
83
|
+
waitingReject = null;
|
|
84
|
+
}
|
|
85
|
+
pending.delete(id);
|
|
86
|
+
worker.postMessage({ $type: "CANCEL", $id: id });
|
|
87
|
+
}, timeout);
|
|
88
|
+
}
|
|
89
|
+
if (waitingResolve) {
|
|
90
|
+
waitingResolve({ value, done: false });
|
|
91
|
+
waitingResolve = null;
|
|
92
|
+
waitingReject = null;
|
|
93
|
+
} else {
|
|
94
|
+
buffer.push(value);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
done: () => {
|
|
98
|
+
if (timeoutId)
|
|
99
|
+
clearTimeout(timeoutId);
|
|
100
|
+
isDone = true;
|
|
101
|
+
if (waitingResolve) {
|
|
102
|
+
waitingResolve({ value: undefined, done: true });
|
|
103
|
+
waitingResolve = null;
|
|
104
|
+
waitingReject = null;
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
error: (err) => {
|
|
108
|
+
if (timeoutId)
|
|
109
|
+
clearTimeout(timeoutId);
|
|
110
|
+
error = err;
|
|
111
|
+
if (waitingReject) {
|
|
112
|
+
waitingReject(err);
|
|
113
|
+
waitingResolve = null;
|
|
114
|
+
waitingReject = null;
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
method,
|
|
118
|
+
callSite
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
[Symbol.asyncIterator]() {
|
|
122
|
+
return {
|
|
123
|
+
async next() {
|
|
124
|
+
if (buffer.length > 0) {
|
|
125
|
+
return { value: buffer.shift(), done: false };
|
|
126
|
+
}
|
|
127
|
+
if (error) {
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
if (isDone) {
|
|
131
|
+
return { value: undefined, done: true };
|
|
132
|
+
}
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
waitingResolve = resolve;
|
|
135
|
+
waitingReject = reject;
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
async return() {
|
|
139
|
+
if (timeoutId)
|
|
140
|
+
clearTimeout(timeoutId);
|
|
141
|
+
pending.delete(id);
|
|
142
|
+
worker.postMessage({ $type: "CANCEL", $id: id });
|
|
143
|
+
return { value: undefined, done: true };
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function wrap(worker, options) {
|
|
150
|
+
const timeout = options?.timeout ?? DEFAULT_TIMEOUT;
|
|
151
|
+
let state = "init";
|
|
152
|
+
let id = 0;
|
|
153
|
+
const pendingCalls = new Map;
|
|
154
|
+
const pendingStreams = new Map;
|
|
155
|
+
const queue = [];
|
|
156
|
+
let methods = new Set;
|
|
157
|
+
function flushQueue() {
|
|
158
|
+
for (const { message, transfer } of queue) {
|
|
159
|
+
worker.postMessage(message, transfer);
|
|
160
|
+
}
|
|
161
|
+
queue.length = 0;
|
|
162
|
+
}
|
|
163
|
+
function deserializeError(serialized, callSite) {
|
|
164
|
+
return new WorkerError(serialized, callSite);
|
|
165
|
+
}
|
|
166
|
+
worker.onmessage = (event) => {
|
|
167
|
+
const msg = event.data;
|
|
168
|
+
if (msg.$type === "READY") {
|
|
169
|
+
state = "ready";
|
|
170
|
+
methods = new Set(msg.methods);
|
|
171
|
+
flushQueue();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (msg.$type === "RESOLVE") {
|
|
175
|
+
const call = pendingCalls.get(msg.$id);
|
|
176
|
+
if (call) {
|
|
177
|
+
if (call.timeoutId)
|
|
178
|
+
clearTimeout(call.timeoutId);
|
|
179
|
+
call.resolve(msg.value);
|
|
180
|
+
pendingCalls.delete(msg.$id);
|
|
181
|
+
}
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (msg.$type === "REJECT") {
|
|
185
|
+
const call = pendingCalls.get(msg.$id);
|
|
186
|
+
if (call) {
|
|
187
|
+
if (call.timeoutId)
|
|
188
|
+
clearTimeout(call.timeoutId);
|
|
189
|
+
call.reject(deserializeError(msg.error, call.callSite));
|
|
190
|
+
pendingCalls.delete(msg.$id);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const stream = pendingStreams.get(msg.$id);
|
|
194
|
+
if (stream) {
|
|
195
|
+
stream.error(deserializeError(msg.error, stream.callSite));
|
|
196
|
+
pendingStreams.delete(msg.$id);
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (msg.$type === "YIELD") {
|
|
201
|
+
const stream = pendingStreams.get(msg.$id);
|
|
202
|
+
if (stream) {
|
|
203
|
+
stream.push(msg.value);
|
|
204
|
+
}
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (msg.$type === "DONE") {
|
|
208
|
+
const stream = pendingStreams.get(msg.$id);
|
|
209
|
+
if (stream) {
|
|
210
|
+
stream.done();
|
|
211
|
+
pendingStreams.delete(msg.$id);
|
|
212
|
+
}
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
function rejectAllPending(error) {
|
|
217
|
+
for (const call of pendingCalls.values()) {
|
|
218
|
+
if (call.timeoutId)
|
|
219
|
+
clearTimeout(call.timeoutId);
|
|
220
|
+
call.reject(error);
|
|
221
|
+
}
|
|
222
|
+
pendingCalls.clear();
|
|
223
|
+
for (const stream of pendingStreams.values()) {
|
|
224
|
+
if (stream.timeoutId)
|
|
225
|
+
clearTimeout(stream.timeoutId);
|
|
226
|
+
stream.error(error);
|
|
227
|
+
}
|
|
228
|
+
pendingStreams.clear();
|
|
229
|
+
queue.length = 0;
|
|
230
|
+
}
|
|
231
|
+
worker.onerror = (event) => {
|
|
232
|
+
const error = new InitError(event.message || "Worker error");
|
|
233
|
+
rejectAllPending(error);
|
|
234
|
+
state = "dead";
|
|
235
|
+
};
|
|
236
|
+
const proxy = new Proxy({}, {
|
|
237
|
+
get(_, prop) {
|
|
238
|
+
if (prop === "$state")
|
|
239
|
+
return state;
|
|
240
|
+
if (prop === "$pending")
|
|
241
|
+
return pendingCalls.size + pendingStreams.size;
|
|
242
|
+
if (prop === "$worker")
|
|
243
|
+
return worker;
|
|
244
|
+
if (typeof prop === "symbol")
|
|
245
|
+
return;
|
|
246
|
+
return (...args) => {
|
|
247
|
+
const currentId = ++id;
|
|
248
|
+
const { args: processedArgs, transfer } = prepareArgs(args);
|
|
249
|
+
const message = {
|
|
250
|
+
$type: "CALL",
|
|
251
|
+
$id: currentId,
|
|
252
|
+
method: prop,
|
|
253
|
+
args: processedArgs
|
|
254
|
+
};
|
|
255
|
+
const callSite = new Error;
|
|
256
|
+
let isStreaming = false;
|
|
257
|
+
let streamIterable = null;
|
|
258
|
+
let internalPromise = null;
|
|
259
|
+
let promiseResolve = null;
|
|
260
|
+
let promiseReject = null;
|
|
261
|
+
function getInternalPromise() {
|
|
262
|
+
if (!internalPromise) {
|
|
263
|
+
internalPromise = new Promise((resolve, reject) => {
|
|
264
|
+
promiseResolve = resolve;
|
|
265
|
+
promiseReject = reject;
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
return internalPromise;
|
|
269
|
+
}
|
|
270
|
+
let timeoutId;
|
|
271
|
+
if (timeout > 0) {
|
|
272
|
+
timeoutId = setTimeout(() => {
|
|
273
|
+
if (!isStreaming) {
|
|
274
|
+
const timeoutError = new TimeoutError(prop, timeout);
|
|
275
|
+
getInternalPromise();
|
|
276
|
+
if (promiseReject) {
|
|
277
|
+
promiseReject(timeoutError);
|
|
278
|
+
}
|
|
279
|
+
pendingCalls.delete(currentId);
|
|
280
|
+
}
|
|
281
|
+
}, timeout);
|
|
282
|
+
}
|
|
283
|
+
pendingCalls.set(currentId, {
|
|
284
|
+
resolve: (value) => {
|
|
285
|
+
if (timeoutId)
|
|
286
|
+
clearTimeout(timeoutId);
|
|
287
|
+
getInternalPromise();
|
|
288
|
+
if (promiseResolve) {
|
|
289
|
+
promiseResolve(value);
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
reject: (error) => {
|
|
293
|
+
if (timeoutId)
|
|
294
|
+
clearTimeout(timeoutId);
|
|
295
|
+
getInternalPromise();
|
|
296
|
+
if (promiseReject) {
|
|
297
|
+
promiseReject(error);
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
method: prop,
|
|
301
|
+
callSite,
|
|
302
|
+
timeoutId
|
|
303
|
+
});
|
|
304
|
+
if (state === "ready") {
|
|
305
|
+
worker.postMessage(message, transfer);
|
|
306
|
+
} else if (state === "init") {
|
|
307
|
+
queue.push({ message, transfer });
|
|
308
|
+
} else {
|
|
309
|
+
const error = new InitError("Worker is terminated");
|
|
310
|
+
if (timeoutId)
|
|
311
|
+
clearTimeout(timeoutId);
|
|
312
|
+
pendingCalls.delete(currentId);
|
|
313
|
+
return Promise.reject(error);
|
|
314
|
+
}
|
|
315
|
+
const dualResult = {
|
|
316
|
+
then(onfulfilled, onrejected) {
|
|
317
|
+
return getInternalPromise().then(onfulfilled, onrejected);
|
|
318
|
+
},
|
|
319
|
+
catch(onrejected) {
|
|
320
|
+
return getInternalPromise().catch(onrejected);
|
|
321
|
+
},
|
|
322
|
+
finally(onfinally) {
|
|
323
|
+
return getInternalPromise().finally(onfinally);
|
|
324
|
+
},
|
|
325
|
+
[Symbol.asyncIterator]() {
|
|
326
|
+
if (!isStreaming) {
|
|
327
|
+
isStreaming = true;
|
|
328
|
+
const call = pendingCalls.get(currentId);
|
|
329
|
+
if (call?.timeoutId)
|
|
330
|
+
clearTimeout(call.timeoutId);
|
|
331
|
+
pendingCalls.delete(currentId);
|
|
332
|
+
streamIterable = createAsyncIterable(worker, currentId, pendingStreams, timeout, prop);
|
|
333
|
+
}
|
|
334
|
+
return streamIterable[Symbol.asyncIterator]();
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
return dualResult;
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
proxyInternals.set(proxy, {
|
|
342
|
+
setState: (newState) => {
|
|
343
|
+
state = newState;
|
|
344
|
+
},
|
|
345
|
+
rejectAll: rejectAllPending
|
|
346
|
+
});
|
|
347
|
+
return proxy;
|
|
348
|
+
}
|
|
349
|
+
function kill(proxy) {
|
|
350
|
+
const internals = proxyInternals.get(proxy);
|
|
351
|
+
if (internals) {
|
|
352
|
+
const error = new InitError("Worker is terminated");
|
|
353
|
+
internals.rejectAll(error);
|
|
354
|
+
internals.setState("dead");
|
|
355
|
+
}
|
|
356
|
+
proxy.$worker.terminate();
|
|
357
|
+
}
|
|
358
|
+
export {
|
|
359
|
+
wrap,
|
|
360
|
+
kill
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
//# debugId=566FB46BE105655B64756E2164756E21
|
package/dist/wrap.js.map
ADDED
|
@@ -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": "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",
|
|
10
|
+
"debugId": "566FB46BE105655B64756E2164756E21",
|
|
11
|
+
"names": []
|
|
12
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shelchin/threadx",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Seamless Worker communication - call Workers like async functions",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./worker": {
|
|
13
|
+
"types": "./dist/worker.d.ts",
|
|
14
|
+
"default": "./dist/worker.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"examples"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "bun build.ts",
|
|
23
|
+
"dev": "bun build.ts --watch",
|
|
24
|
+
"test": "bun test",
|
|
25
|
+
"test:watch": "bun test --watch"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/bun": "latest"
|
|
29
|
+
}
|
|
30
|
+
}
|