@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/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
|
package/dist/errors.d.ts
ADDED
|
@@ -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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|