@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 ADDED
@@ -0,0 +1,211 @@
1
+ # @shelchin/threadx
2
+
3
+ Seamless Worker communication - call Workers like async functions.
4
+
5
+ ## Features
6
+
7
+ - **Transparent RPC** - Call Worker methods like regular async functions
8
+ - **Streaming Support** - Use generators for streaming results with `for await`
9
+ - **Zero-Copy Transfer** - Transfer ArrayBuffers without copying
10
+ - **Type Safe** - Full TypeScript support with automatic type inference
11
+ - **Cancellation** - Break out of streams to cancel Worker operations
12
+ - **Cross-Platform** - Works in Browser, Bun, and Deno
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ bun add @shelchin/threadx
18
+ # or
19
+ npm install @shelchin/threadx
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ### Worker Side
25
+
26
+ ```typescript
27
+ // calc.worker.ts
28
+ import { expose } from '@shelchin/threadx/worker'
29
+
30
+ expose({
31
+ add(a: number, b: number) {
32
+ return a + b
33
+ },
34
+
35
+ async fetchData(url: string) {
36
+ const res = await fetch(url)
37
+ return res.json()
38
+ },
39
+
40
+ *progress(total: number) {
41
+ for (let i = 0; i <= total; i++) {
42
+ yield Math.round((i / total) * 100)
43
+ }
44
+ }
45
+ })
46
+ ```
47
+
48
+ ### Main Thread
49
+
50
+ ```typescript
51
+ // main.ts
52
+ import { wrap, t, kill } from '@shelchin/threadx'
53
+ import type * as CalcMethods from './calc.worker'
54
+
55
+ const calc = wrap<typeof CalcMethods>(new Worker('./calc.worker.js'))
56
+
57
+ // RPC call - just like a regular function
58
+ const sum = await calc.add(1, 2) // 3
59
+
60
+ // Streaming with for-await
61
+ for await (const percent of calc.progress(100)) {
62
+ console.log(`${percent}%`)
63
+ }
64
+
65
+ // Zero-copy transfer
66
+ const buffer = new ArrayBuffer(1024 * 1024)
67
+ await calc.processBuffer(t(buffer))
68
+ // buffer.byteLength is now 0 (transferred)
69
+
70
+ // Terminate worker
71
+ kill(calc)
72
+ ```
73
+
74
+ ## API
75
+
76
+ ### Main Thread
77
+
78
+ #### `wrap<T>(worker, options?)`
79
+
80
+ Wrap a Worker for seamless RPC.
81
+
82
+ ```typescript
83
+ const api = wrap<typeof WorkerMethods>(new Worker('./worker.js'), {
84
+ timeout: 30000, // Default timeout in ms
85
+ name: 'calc' // Debug name
86
+ })
87
+ ```
88
+
89
+ #### `t(value, transferables?)`
90
+
91
+ Mark value for zero-copy transfer.
92
+
93
+ ```typescript
94
+ // Single transferable
95
+ await api.process(t(buffer))
96
+
97
+ // Object with specified transferables
98
+ await api.process(t({ image: buffer, meta: info }, [buffer]))
99
+ ```
100
+
101
+ #### `kill(proxy)`
102
+
103
+ Terminate the Worker.
104
+
105
+ ```typescript
106
+ kill(api)
107
+ ```
108
+
109
+ #### State Hooks
110
+
111
+ ```typescript
112
+ api.$state // 'init' | 'ready' | 'dead'
113
+ api.$pending // Number of pending calls
114
+ api.$worker // Original Worker instance
115
+ ```
116
+
117
+ ### Worker Side
118
+
119
+ #### `expose(methods)`
120
+
121
+ Expose methods to main thread.
122
+
123
+ ```typescript
124
+ import { expose } from '@shelchin/threadx/worker'
125
+
126
+ expose({
127
+ // Sync function → Promise
128
+ add: (a, b) => a + b,
129
+
130
+ // Async function → Promise
131
+ async fetch(url) {
132
+ return await fetch(url).then(r => r.json())
133
+ },
134
+
135
+ // Generator → AsyncIterable
136
+ *range(start, end) {
137
+ for (let i = start; i <= end; i++) yield i
138
+ },
139
+
140
+ // Async Generator → AsyncIterable
141
+ async *stream(urls) {
142
+ for (const url of urls) {
143
+ yield await fetch(url).then(r => r.json())
144
+ }
145
+ }
146
+ })
147
+ ```
148
+
149
+ ### Error Types
150
+
151
+ ```typescript
152
+ import { WorkerError, TimeoutError, InitError } from '@shelchin/threadx'
153
+
154
+ try {
155
+ await api.riskyMethod()
156
+ } catch (e) {
157
+ if (e instanceof WorkerError) {
158
+ console.log(e.message) // Error message
159
+ console.log(e.workerStack) // Stack from Worker
160
+ }
161
+ if (e instanceof TimeoutError) {
162
+ console.log('Call timed out')
163
+ }
164
+ if (e instanceof InitError) {
165
+ console.log('Worker failed to initialize')
166
+ }
167
+ }
168
+ ```
169
+
170
+ ## Streaming & Cancellation
171
+
172
+ Generators automatically stream values. Use `break` to cancel:
173
+
174
+ ```typescript
175
+ for await (const chunk of api.longProcess()) {
176
+ console.log(chunk)
177
+ if (shouldStop) break // Sends CANCEL to Worker
178
+ }
179
+ ```
180
+
181
+ ## Type Safety
182
+
183
+ Full type inference from Worker methods:
184
+
185
+ ```typescript
186
+ // worker.ts
187
+ export function add(a: number, b: number): number { ... }
188
+ export function* count(n: number): Generator<number> { ... }
189
+
190
+ // main.ts
191
+ import type * as Methods from './worker'
192
+
193
+ const api = wrap<typeof Methods>(worker)
194
+
195
+ await api.add(1, 2) // Promise<number>
196
+ api.count(10) // AsyncIterable<number>
197
+ ```
198
+
199
+ ## Platform Support
200
+
201
+ | Platform | Support |
202
+ |----------|---------|
203
+ | Browser | Native Worker |
204
+ | Bun | Native Worker |
205
+ | Deno | Native Worker |
206
+ | Node.js | Not supported |
207
+ | Cloudflare Workers | Not supported |
208
+
209
+ ## License
210
+
211
+ MIT
@@ -0,0 +1,28 @@
1
+ import type { SerializedError } from './protocol.js';
2
+ /**
3
+ * Error thrown when Worker execution fails
4
+ */
5
+ export declare class WorkerError extends Error {
6
+ /** Stack trace from the Worker side */
7
+ workerStack?: string;
8
+ constructor(serialized: SerializedError, callSite?: Error);
9
+ }
10
+ /**
11
+ * Error thrown when a call times out
12
+ */
13
+ export declare class TimeoutError extends Error {
14
+ constructor(method: string, timeout: number);
15
+ }
16
+ /**
17
+ * Error thrown when Worker initialization fails
18
+ */
19
+ export declare class InitError extends Error {
20
+ constructor(message: string);
21
+ }
22
+ /**
23
+ * Error thrown on unsupported runtime
24
+ */
25
+ export declare class UnsupportedRuntimeError extends Error {
26
+ constructor(runtime: string);
27
+ }
28
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD;;GAEG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;gBAET,UAAU,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,KAAK;CAY1D;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;gBACzB,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAI5C;AAED;;GAEG;AACH,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI5B"}
package/dist/errors.js ADDED
@@ -0,0 +1,45 @@
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
+ export {
39
+ WorkerError,
40
+ UnsupportedRuntimeError,
41
+ TimeoutError,
42
+ InitError
43
+ };
44
+
45
+ //# debugId=254D6696BA51CA2D64756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/errors.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
+ ],
7
+ "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;",
8
+ "debugId": "254D6696BA51CA2D64756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * ThreadX - Seamless Worker Communication
3
+ *
4
+ * @example
5
+ * // Main thread
6
+ * import { wrap, t, kill } from '@shelchin/threadx'
7
+ * import type * as CalcMethods from './calc.worker'
8
+ *
9
+ * const calc = wrap<typeof CalcMethods>(new Worker('./calc.worker.js'))
10
+ *
11
+ * // RPC call
12
+ * const sum = await calc.add(1, 2)
13
+ *
14
+ * // Streaming call
15
+ * for await (const progress of calc.process(data)) {
16
+ * console.log(progress)
17
+ * }
18
+ *
19
+ * // Zero-copy transfer
20
+ * await calc.processBuffer(t(arrayBuffer))
21
+ *
22
+ * // Terminate
23
+ * kill(calc)
24
+ *
25
+ * @example
26
+ * // Worker side (calc.worker.ts)
27
+ * import { expose } from '@shelchin/threadx/worker'
28
+ *
29
+ * expose({
30
+ * add: (a, b) => a + b,
31
+ * *process(data) {
32
+ * for (let i = 0; i <= 100; i++) {
33
+ * yield i
34
+ * }
35
+ * }
36
+ * })
37
+ */
38
+ export { wrap, kill } from './wrap.js';
39
+ export { t } from './transfer.js';
40
+ export { WorkerError, TimeoutError, InitError, UnsupportedRuntimeError } from './errors.js';
41
+ export type { WrapOptions, WrappedWorker, WorkerState } from './types.js';
42
+ export type { TransferDescriptor } from './transfer.js';
43
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAGH,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAGlC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAG5F,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1E,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC"}