procxy 0.1.0-alpha.1 → 0.1.0-alpha.3
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/CHANGELOG.md +44 -0
- package/README.md +502 -184
- package/dist/child/agent.d.ts +1 -1
- package/dist/child/agent.d.ts.map +1 -1
- package/dist/child/agent.js +44 -6
- package/dist/child/agent.js.map +1 -1
- package/dist/child/child-proxy.d.ts +3 -1
- package/dist/child/child-proxy.d.ts.map +1 -1
- package/dist/child/child-proxy.js +21 -9
- package/dist/child/child-proxy.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/parent/ipc-client.d.ts +1 -0
- package/dist/parent/ipc-client.d.ts.map +1 -1
- package/dist/parent/ipc-client.js +55 -0
- package/dist/parent/ipc-client.js.map +1 -1
- package/dist/parent/parent-proxy.d.ts +1 -1
- package/dist/parent/parent-proxy.d.ts.map +1 -1
- package/dist/parent/parent-proxy.js +3 -0
- package/dist/parent/parent-proxy.js.map +1 -1
- package/dist/parent/procxy.d.ts +2 -2
- package/dist/parent/procxy.d.ts.map +1 -1
- package/dist/parent/procxy.js +45 -12
- package/dist/parent/procxy.js.map +1 -1
- package/dist/shared/protocol.d.ts +15 -2
- package/dist/shared/protocol.d.ts.map +1 -1
- package/dist/shared/serialization.d.ts +6 -0
- package/dist/shared/serialization.d.ts.map +1 -1
- package/dist/shared/serialization.js +101 -1
- package/dist/shared/serialization.js.map +1 -1
- package/dist/types/options.d.ts +9 -2
- package/dist/types/options.d.ts.map +1 -1
- package/dist/types/procxy.d.ts +36 -28
- package/dist/types/procxy.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ Run class instances in isolated child processes while interacting with them as i
|
|
|
17
17
|
## ✨ Features
|
|
18
18
|
|
|
19
19
|
- **🎯 Type-Safe** - Full TypeScript support with IntelliSense autocomplete
|
|
20
|
-
- **🪄
|
|
20
|
+
- **🪄 Automatic Module Resolution** - Zero-config import path detection from your source code
|
|
21
21
|
- **⚡ Fast** - <10ms overhead per method call
|
|
22
22
|
- **🔄 Events & Callbacks** - Transparent EventEmitter forwarding and bidirectional callback support
|
|
23
23
|
- **🏠 Properties** - Read-only properties on parent, full read/write on child
|
|
@@ -66,6 +66,7 @@ const calc2 = await procxy(Calculator, './calculator.js');
|
|
|
66
66
|
### Using Disposables (Recommended)
|
|
67
67
|
|
|
68
68
|
```typescript
|
|
69
|
+
import { procxy } from 'procxy';
|
|
69
70
|
import { Calculator } from './calculator.js';
|
|
70
71
|
|
|
71
72
|
// Automatic cleanup with await using
|
|
@@ -77,22 +78,22 @@ const result = await calc.add(5, 3);
|
|
|
77
78
|
### Constructor Arguments
|
|
78
79
|
|
|
79
80
|
```typescript
|
|
81
|
+
import { procxy } from 'procxy';
|
|
80
82
|
import { Worker } from './worker.js';
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
84
|
+
// Worker class (in worker.ts):
|
|
85
|
+
// class Worker {
|
|
86
|
+
// constructor(public name: string, public threads: number) {}
|
|
87
|
+
//
|
|
88
|
+
// async process(data: string[]): Promise<string[]> {
|
|
89
|
+
// return data.map(s => s.toUpperCase());
|
|
90
|
+
// }
|
|
91
|
+
// }
|
|
90
92
|
|
|
91
93
|
// Pass constructor arguments after options
|
|
92
94
|
const worker = await procxy(
|
|
93
95
|
Worker,
|
|
94
|
-
|
|
95
|
-
undefined, // options (or omit)
|
|
96
|
+
undefined, // options (or omit entirely)
|
|
96
97
|
'MyWorker', // name argument
|
|
97
98
|
4 // threads argument
|
|
98
99
|
);
|
|
@@ -103,9 +104,9 @@ const result = await worker.process(['hello', 'world']);
|
|
|
103
104
|
await worker.$terminate();
|
|
104
105
|
```
|
|
105
106
|
|
|
106
|
-
## 🪄
|
|
107
|
+
## 🪄 Automatic Module Resolution
|
|
107
108
|
|
|
108
|
-
One of procxy's most convenient features is **automatic module path detection**. You don't need to manually specify where your class is located
|
|
109
|
+
One of procxy's most convenient features is **automatic module path detection**. You don't need to manually specify where your class is located - procxy will figure it out by parsing your import statements at runtime.
|
|
109
110
|
|
|
110
111
|
### How It Works
|
|
111
112
|
|
|
@@ -186,8 +187,10 @@ Creates a process-based proxy for a class instance.
|
|
|
186
187
|
|
|
187
188
|
**Parameters:**
|
|
188
189
|
|
|
189
|
-
- `Class: Constructor<T>` - The class constructor to instantiate
|
|
190
|
-
- `modulePath?: string` - **Optional**
|
|
190
|
+
- `Class: Constructor<T>` - The class constructor to instantiate (must be a named class)
|
|
191
|
+
- `modulePath?: string` - **Optional** - Path to the module containing the class
|
|
192
|
+
- **Omit this parameter** when using static imports (automatic resolution)
|
|
193
|
+
- **Provide explicitly** for dynamic imports or complex re-exports
|
|
191
194
|
- `options?: ProcxyOptions` - Optional configuration
|
|
192
195
|
- `...constructorArgs` - Arguments for the class constructor
|
|
193
196
|
|
|
@@ -195,64 +198,68 @@ Creates a process-based proxy for a class instance.
|
|
|
195
198
|
|
|
196
199
|
**Module Path Auto-Detection:**
|
|
197
200
|
|
|
198
|
-
procxy
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
201
|
+
When `modulePath` is omitted, procxy automatically detects the module path by:
|
|
202
|
+
1. Inspecting the call stack to find where procxy was called
|
|
203
|
+
2. Reading and parsing the source file for import/require statements
|
|
204
|
+
3. Matching the class name to find the corresponding import path
|
|
202
205
|
|
|
203
|
-
|
|
204
|
-
const worker = await procxy(Worker);
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
The module path is auto-detected from:
|
|
208
|
-
- ESM named imports: `import { Class } from './path'`
|
|
209
|
-
- ESM default imports: `import Class from './path'`
|
|
210
|
-
- CommonJS imports: `const { Class } = require('./path')`
|
|
211
|
-
- Class definitions in the same file
|
|
212
|
-
|
|
213
|
-
**When to provide explicit modulePath:**
|
|
214
|
-
- Dynamic imports: `const { Worker } = await import('./worker')`
|
|
215
|
-
- Ambiguous import scenarios
|
|
216
|
-
- When importing from multiple locations
|
|
217
|
-
|
|
218
|
-
**Example:**
|
|
219
|
-
|
|
220
|
-
```typescript
|
|
221
|
-
const worker = await procxy(Worker, './worker.js', {
|
|
222
|
-
timeout: 60000, // 60s per method call
|
|
223
|
-
retries: 3, // Retry 3 times on failure
|
|
224
|
-
env: { NODE_ENV: 'production' },
|
|
225
|
-
cwd: '/tmp'
|
|
226
|
-
});
|
|
227
|
-
```
|
|
206
|
+
This works with ESM imports, CommonJS requires, and classes defined in the same file. See [Automatic Module Resolution](#-automatic-module-resolution) for details.
|
|
228
207
|
|
|
229
208
|
### `Procxy<T>` Type
|
|
230
209
|
|
|
231
210
|
The proxy type that wraps your class instance:
|
|
232
211
|
|
|
233
212
|
- **Methods**: All methods become `async` and return `Promise<ReturnType>`
|
|
234
|
-
- **Properties**: Public properties
|
|
235
|
-
- **Callbacks**: Function parameters are automatically proxied across
|
|
236
|
-
- **
|
|
213
|
+
- **Properties**: Public properties are read-only on the parent (synchronized from child after method calls)
|
|
214
|
+
- **Callbacks**: Function parameters are automatically proxied bidirectionally across processes
|
|
215
|
+
- **Events**: EventEmitter events flow from child to parent transparently
|
|
237
216
|
- **Lifecycle Methods**:
|
|
238
|
-
- `$terminate(): Promise<void>` -
|
|
239
|
-
- `$process: ChildProcess` - Access the underlying process
|
|
240
|
-
- **Disposable**: Supports `using` and `await using` for automatic cleanup
|
|
217
|
+
- `$terminate(): Promise<void>` - Gracefully terminate the child process
|
|
218
|
+
- `$process: ChildProcess` - Access the underlying Node.js child process
|
|
219
|
+
- **Disposable Protocol**: Supports `using` and `await using` for automatic cleanup
|
|
220
|
+
- **Type Safety**: Full TypeScript IntelliSense and autocomplete support
|
|
241
221
|
|
|
242
222
|
### `ProcxyOptions`
|
|
243
223
|
|
|
244
|
-
Configuration options:
|
|
224
|
+
Configuration options for customizing child process behavior:
|
|
245
225
|
|
|
246
226
|
```typescript
|
|
247
227
|
interface ProcxyOptions {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
228
|
+
modulePath?: string; // Path to module (optional if using static imports)
|
|
229
|
+
timeout?: number; // Timeout per method call in ms (default: 30000)
|
|
230
|
+
retries?: number; // Number of retry attempts on failure (default: 3)
|
|
231
|
+
env?: Record<string, string>; // Custom environment variables for child process
|
|
232
|
+
cwd?: string; // Working directory for child process
|
|
233
|
+
args?: Jsonifiable[]; // Additional command line arguments
|
|
234
|
+
serialization?: 'json' | 'advanced'; // Serialization mode (default: 'json')
|
|
235
|
+
supportHandles?: boolean; // Enable handle passing for sockets (advanced mode only)
|
|
253
236
|
}
|
|
254
237
|
```
|
|
255
238
|
|
|
239
|
+
**Examples:**
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
import { procxy } from 'procxy';
|
|
243
|
+
import { HeavyWorker } from './heavy-worker.js';
|
|
244
|
+
import { APIClient } from './api-client.js';
|
|
245
|
+
import { FileProcessor } from './file-processor.js';
|
|
246
|
+
import { BinaryProcessor } from './binary-processor.js';
|
|
247
|
+
|
|
248
|
+
// Long-running operations
|
|
249
|
+
await procxy(HeavyWorker, { timeout: 300000 }); // 5 minutes
|
|
250
|
+
|
|
251
|
+
// Custom environment
|
|
252
|
+
await procxy(APIClient, {
|
|
253
|
+
env: { API_KEY: process.env.API_KEY }
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Isolated working directory
|
|
257
|
+
await procxy(FileProcessor, { cwd: '/tmp/workspace' });
|
|
258
|
+
|
|
259
|
+
// Advanced serialization for binary data
|
|
260
|
+
await procxy(BinaryProcessor, { serialization: 'advanced' });
|
|
261
|
+
```
|
|
262
|
+
|
|
256
263
|
## 🎯 Use Cases
|
|
257
264
|
|
|
258
265
|
### CPU-Intensive Tasks
|
|
@@ -260,13 +267,10 @@ interface ProcxyOptions {
|
|
|
260
267
|
Offload heavy computations without blocking the event loop:
|
|
261
268
|
|
|
262
269
|
```typescript
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
// Heavy image processing
|
|
266
|
-
}
|
|
267
|
-
}
|
|
270
|
+
import { procxy } from 'procxy';
|
|
271
|
+
import { ImageProcessor } from './image-processor.js';
|
|
268
272
|
|
|
269
|
-
await using processor = await procxy(ImageProcessor
|
|
273
|
+
await using processor = await procxy(ImageProcessor);
|
|
270
274
|
const resized = await processor.resize(imageData, 800);
|
|
271
275
|
```
|
|
272
276
|
|
|
@@ -275,13 +279,10 @@ const resized = await processor.resize(imageData, 800);
|
|
|
275
279
|
Run untrusted code in isolated processes:
|
|
276
280
|
|
|
277
281
|
```typescript
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
// Execute in isolated environment
|
|
281
|
-
}
|
|
282
|
-
}
|
|
282
|
+
import { procxy } from 'procxy';
|
|
283
|
+
import { SandboxedRunner } from './sandbox.js';
|
|
283
284
|
|
|
284
|
-
const sandbox = await procxy(SandboxedRunner,
|
|
285
|
+
const sandbox = await procxy(SandboxedRunner, {
|
|
285
286
|
timeout: 5000, // Kill after 5s
|
|
286
287
|
env: { NODE_ENV: 'sandbox' }
|
|
287
288
|
});
|
|
@@ -292,16 +293,18 @@ const sandbox = await procxy(SandboxedRunner, './sandbox.js', {
|
|
|
292
293
|
Classes extending EventEmitter work transparently:
|
|
293
294
|
|
|
294
295
|
```typescript
|
|
296
|
+
import { procxy } from 'procxy';
|
|
295
297
|
import { EventEmitter } from 'events';
|
|
298
|
+
import { DataStream } from './stream.js';
|
|
296
299
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
300
|
+
// DataStream class (in stream.ts):
|
|
301
|
+
// class DataStream extends EventEmitter {
|
|
302
|
+
// async start(): Promise<void> {
|
|
303
|
+
// this.emit('data', { chunk: 'example' });
|
|
304
|
+
// }
|
|
305
|
+
// }
|
|
303
306
|
|
|
304
|
-
const stream = await procxy(DataStream
|
|
307
|
+
const stream = await procxy(DataStream);
|
|
305
308
|
|
|
306
309
|
stream.on('data', (chunk) => {
|
|
307
310
|
console.log('Received:', chunk);
|
|
@@ -315,21 +318,25 @@ await stream.start();
|
|
|
315
318
|
Pass callbacks as function parameters and they'll be transparently proxied across the process boundary:
|
|
316
319
|
|
|
317
320
|
```typescript
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
321
|
+
import { procxy } from 'procxy';
|
|
322
|
+
import { AsyncWorker } from './async-worker.js';
|
|
323
|
+
|
|
324
|
+
// AsyncWorker class (in async-worker.ts):
|
|
325
|
+
// class AsyncWorker {
|
|
326
|
+
// async processWithCallback(
|
|
327
|
+
// data: string[],
|
|
328
|
+
// onProgress: (current: number, total: number) => void
|
|
329
|
+
// ): Promise<string[]> {
|
|
330
|
+
// const result = [];
|
|
331
|
+
// for (let i = 0; i < data.length; i++) {
|
|
332
|
+
// result.push(data[i].toUpperCase());
|
|
333
|
+
// onProgress(i + 1, data.length); // Invokes callback in parent
|
|
334
|
+
// }
|
|
335
|
+
// return result;
|
|
336
|
+
// }
|
|
337
|
+
// }
|
|
338
|
+
|
|
339
|
+
const worker = await procxy(AsyncWorker);
|
|
333
340
|
|
|
334
341
|
const result = await worker.processWithCallback(
|
|
335
342
|
['hello', 'world'],
|
|
@@ -352,47 +359,72 @@ const result = await worker.processWithCallback(
|
|
|
352
359
|
|
|
353
360
|
### Properties Support
|
|
354
361
|
|
|
355
|
-
Public properties are accessible
|
|
362
|
+
Public properties are accessible with different capabilities on parent vs. child:
|
|
363
|
+
|
|
364
|
+
**Parent Process (Read-Only):**
|
|
365
|
+
- **Get**: Synchronous property reads from local property store
|
|
366
|
+
- Properties are automatically synced after each method call
|
|
367
|
+
|
|
368
|
+
**Child Process (Read/Write):**
|
|
369
|
+
- **Get**: Direct property access (no IPC overhead)
|
|
370
|
+
- **Set**: Modifies property and syncs to parent
|
|
356
371
|
|
|
357
372
|
```typescript
|
|
373
|
+
// counter.ts
|
|
374
|
+
import { procxy } from 'procxy';
|
|
375
|
+
|
|
358
376
|
class Counter {
|
|
359
377
|
public count: number = 0;
|
|
360
378
|
public name: string = '';
|
|
361
379
|
|
|
362
380
|
increment(): void {
|
|
381
|
+
// Child can SET properties directly
|
|
363
382
|
this.count++;
|
|
364
383
|
}
|
|
365
384
|
|
|
366
385
|
setName(newName: string): void {
|
|
386
|
+
// Child can SET properties
|
|
367
387
|
this.name = newName;
|
|
368
388
|
}
|
|
369
389
|
|
|
370
390
|
getCount(): number {
|
|
391
|
+
// Child can GET properties directly (no IPC)
|
|
371
392
|
return this.count;
|
|
372
393
|
}
|
|
394
|
+
|
|
395
|
+
getName(): string {
|
|
396
|
+
// Child can GET properties directly
|
|
397
|
+
return this.name;
|
|
398
|
+
}
|
|
373
399
|
}
|
|
374
400
|
|
|
375
|
-
|
|
401
|
+
// main.ts
|
|
402
|
+
import { procxy } from 'procxy';
|
|
403
|
+
import { Counter } from './counter.js';
|
|
404
|
+
|
|
405
|
+
const counter = await procxy(Counter);
|
|
376
406
|
|
|
377
|
-
//
|
|
378
|
-
console.log(counter.count); // 0 -
|
|
379
|
-
console.log(counter.name); // '' - synchronous
|
|
407
|
+
// Parent can GET properties (synchronous read from property store)
|
|
408
|
+
console.log(counter.count); // 0 - no IPC, reads from local store
|
|
409
|
+
console.log(counter.name); // '' - synchronous
|
|
380
410
|
|
|
381
|
-
// Modify via child methods
|
|
411
|
+
// Modify via child methods (parent CANNOT set directly)
|
|
382
412
|
await counter.increment();
|
|
383
413
|
await counter.setName('MyCounter');
|
|
384
414
|
|
|
385
|
-
// Parent
|
|
386
|
-
console.log(counter.count); // 1 -
|
|
387
|
-
console.log(counter.name); // 'MyCounter' -
|
|
415
|
+
// Parent GETs updated values (automatically synced from child)
|
|
416
|
+
console.log(counter.count); // 1 - synced after increment()
|
|
417
|
+
console.log(counter.name); // 'MyCounter' - synced after setName()
|
|
418
|
+
|
|
419
|
+
// Parent cannot SET properties (read-only)
|
|
420
|
+
// counter.count = 5; // ❌ Throws error: properties are read-only on parent
|
|
388
421
|
```
|
|
389
422
|
|
|
390
423
|
**Property Synchronization:**
|
|
391
424
|
- Parent maintains a property store synchronized from the child
|
|
392
|
-
- Child can
|
|
393
|
-
-
|
|
394
|
-
-
|
|
395
|
-
- Parent can only read properties (attempting to set throws an error)
|
|
425
|
+
- Child can **get** and **set** properties directly (no IPC needed for reads)
|
|
426
|
+
- Property updates are automatically synced to parent after method calls
|
|
427
|
+
- Parent can only **get** properties (attempting to set throws an error)
|
|
396
428
|
- No race conditions: only the child can modify properties
|
|
397
429
|
|
|
398
430
|
## 🛡️ Error Handling
|
|
@@ -430,7 +462,10 @@ try {
|
|
|
430
462
|
### Timeouts and Retries
|
|
431
463
|
|
|
432
464
|
```typescript
|
|
433
|
-
|
|
465
|
+
import { procxy } from 'procxy';
|
|
466
|
+
import { Worker } from './worker.js';
|
|
467
|
+
|
|
468
|
+
const worker = await procxy(Worker, {
|
|
434
469
|
timeout: 60000, // 60 seconds per call
|
|
435
470
|
retries: 5 // Retry 5 times before failing
|
|
436
471
|
});
|
|
@@ -439,7 +474,10 @@ const worker = await procxy(Worker, './worker.js', {
|
|
|
439
474
|
### Custom Environment
|
|
440
475
|
|
|
441
476
|
```typescript
|
|
442
|
-
|
|
477
|
+
import { procxy } from 'procxy';
|
|
478
|
+
import { Worker } from './worker.js';
|
|
479
|
+
|
|
480
|
+
const worker = await procxy(Worker, {
|
|
443
481
|
env: {
|
|
444
482
|
NODE_ENV: 'production',
|
|
445
483
|
API_KEY: process.env.API_KEY,
|
|
@@ -451,17 +489,253 @@ const worker = await procxy(Worker, './worker.js', {
|
|
|
451
489
|
### Working Directory
|
|
452
490
|
|
|
453
491
|
```typescript
|
|
454
|
-
|
|
492
|
+
import { procxy } from 'procxy';
|
|
493
|
+
import { Worker } from './worker.js';
|
|
494
|
+
|
|
495
|
+
const worker = await procxy(Worker, {
|
|
455
496
|
cwd: '/tmp/workspace'
|
|
456
497
|
});
|
|
457
498
|
```
|
|
458
499
|
|
|
500
|
+
## 🎨 Advanced Serialization (V8 Structured Clone)
|
|
501
|
+
|
|
502
|
+
By default, procxy uses JSON serialization for IPC messages. However, you can enable **advanced serialization mode** to support additional data types using V8's structured clone algorithm.
|
|
503
|
+
|
|
504
|
+
### Supported Types in Advanced Mode
|
|
505
|
+
|
|
506
|
+
When using `serialization: 'advanced'`, you can pass these additional types:
|
|
507
|
+
|
|
508
|
+
- **Binary Data**: `Buffer`, `ArrayBuffer`, `TypedArray` (Uint8Array, Int32Array, Float32Array, etc.)
|
|
509
|
+
- **Collections**: `Map`, `Set` with full fidelity (not converted to arrays)
|
|
510
|
+
- **Large Numbers**: `BigInt` values
|
|
511
|
+
- **Built-in Objects**: `Date`, `RegExp`, `Error` instances with all properties preserved
|
|
512
|
+
|
|
513
|
+
### Usage
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
import { procxy } from 'procxy';
|
|
517
|
+
import { ImageProcessor } from './image-processor.js';
|
|
518
|
+
|
|
519
|
+
// Enable advanced serialization
|
|
520
|
+
await using processor = await procxy(ImageProcessor, {
|
|
521
|
+
serialization: 'advanced' // 👈 Enable V8 structured clone
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
// Now you can pass Buffer, TypedArray, Map, Set, BigInt, etc.
|
|
525
|
+
const imageBuffer = Buffer.from(imageData);
|
|
526
|
+
const processed = await processor.processImage(imageBuffer);
|
|
527
|
+
|
|
528
|
+
// Use Map for caching
|
|
529
|
+
const cache = new Map([
|
|
530
|
+
['key1', Buffer.from('data1')],
|
|
531
|
+
['key2', Buffer.from('data2')]
|
|
532
|
+
]);
|
|
533
|
+
await processor.bulkCache(cache);
|
|
534
|
+
|
|
535
|
+
// Use BigInt for large numbers
|
|
536
|
+
const timestamp = BigInt(Date.now()) * BigInt(1000000);
|
|
537
|
+
await processor.recordTimestamp(timestamp);
|
|
538
|
+
|
|
539
|
+
// TypedArray for binary protocols
|
|
540
|
+
const binaryData = new Uint8Array([0x00, 0x01, 0x02, 0x03]);
|
|
541
|
+
await processor.sendBinary(binaryData);
|
|
542
|
+
|
|
543
|
+
// Set for unique collections
|
|
544
|
+
const uniqueIds = new Set([1, 2, 3, 4, 5]);
|
|
545
|
+
await processor.processIds(uniqueIds);
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### Example: Binary Data Processing
|
|
549
|
+
|
|
550
|
+
```typescript
|
|
551
|
+
// binary-processor.ts
|
|
552
|
+
export class BinaryProcessor {
|
|
553
|
+
// Process Buffer data
|
|
554
|
+
processBuffer(data: Buffer): Buffer {
|
|
555
|
+
const result = Buffer.alloc(data.length);
|
|
556
|
+
for (let i = 0; i < data.length; i++) {
|
|
557
|
+
result[i] = data[i] ^ 0xFF; // XOR transformation
|
|
558
|
+
}
|
|
559
|
+
return result;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Work with TypedArray
|
|
563
|
+
sumFloat32Array(arr: Float32Array): number {
|
|
564
|
+
return arr.reduce((sum, val) => sum + val, 0);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// Use Map for caching
|
|
568
|
+
private cache = new Map<string, Buffer>();
|
|
569
|
+
|
|
570
|
+
cacheData(key: string, data: Buffer): void {
|
|
571
|
+
this.cache.set(key, data);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
getAllCache(): Map<string, Buffer> {
|
|
575
|
+
return new Map(this.cache); // Returns actual Map, not array
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Process BigInt
|
|
579
|
+
multiplyBigInt(a: bigint, b: bigint): bigint {
|
|
580
|
+
return a * b;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// main.ts
|
|
585
|
+
import { procxy } from 'procxy';
|
|
586
|
+
import { BinaryProcessor } from './binary-processor.js';
|
|
587
|
+
|
|
588
|
+
await using processor = await procxy(BinaryProcessor, {
|
|
589
|
+
serialization: 'advanced'
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
// Process binary data
|
|
593
|
+
const input = Buffer.from([0x00, 0x11, 0x22, 0x33]);
|
|
594
|
+
const output = await processor.processBuffer(input);
|
|
595
|
+
// Buffer: [0xFF, 0xEE, 0xDD, 0xCC]
|
|
596
|
+
|
|
597
|
+
// Work with TypedArray
|
|
598
|
+
const floats = new Float32Array([1.1, 2.2, 3.3]);
|
|
599
|
+
const sum = await processor.sumFloat32Array(floats); // 6.6
|
|
600
|
+
|
|
601
|
+
// Use Map
|
|
602
|
+
await processor.cacheData('key1', Buffer.from('data1'));
|
|
603
|
+
const cache = await processor.getAllCache(); // Returns Map, not array
|
|
604
|
+
console.log(cache instanceof Map); // true
|
|
605
|
+
console.log(cache.get('key1')?.toString()); // 'data1'
|
|
606
|
+
|
|
607
|
+
// BigInt support
|
|
608
|
+
const result = await processor.multiplyBigInt(BigInt(123), BigInt(456));
|
|
609
|
+
console.log(result); // 56088n
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### JSON vs Advanced Mode Comparison
|
|
613
|
+
|
|
614
|
+
| Feature | JSON Mode (default) | Advanced Mode |
|
|
615
|
+
|---------|-------------------|---------------|
|
|
616
|
+
| Primitives (string, number, boolean) | ✅ | ✅ |
|
|
617
|
+
| Objects & Arrays | ✅ | ✅ |
|
|
618
|
+
| null & undefined | ✅ | ✅ |
|
|
619
|
+
| Buffer | ❌ | ✅ |
|
|
620
|
+
| TypedArray | ❌ | ✅ |
|
|
621
|
+
| Map | ❌ | ✅ |
|
|
622
|
+
| Set | ❌ | ✅ |
|
|
623
|
+
| BigInt | ❌ | ✅ |
|
|
624
|
+
| Date | ⚠️ (as string) | ✅ (as Date) |
|
|
625
|
+
| RegExp | ⚠️ (as object) | ✅ (as RegExp) |
|
|
626
|
+
| Error | ⚠️ (partial) | ✅ (full props) |
|
|
627
|
+
| Handle Passing (sockets) | ❌ | ✅ (with `supportHandles`) |
|
|
628
|
+
| Performance | Faster for simple objects | Slightly slower |
|
|
629
|
+
|
|
630
|
+
### Handle Passing
|
|
631
|
+
|
|
632
|
+
Advanced serialization mode also enables **handle passing** - the ability to transfer network sockets and server handles between processes.
|
|
633
|
+
|
|
634
|
+
```typescript
|
|
635
|
+
import { procxy } from 'procxy';
|
|
636
|
+
import * as net from 'net';
|
|
637
|
+
|
|
638
|
+
class SocketHandler {
|
|
639
|
+
registerConnection(id: string, socket: net.Socket): void {
|
|
640
|
+
socket.on('data', (data) => {
|
|
641
|
+
console.log(`Received on ${id}:`, data.toString());
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Enable handle passing
|
|
647
|
+
const handler = await procxy(SocketHandler, {
|
|
648
|
+
serialization: 'advanced',
|
|
649
|
+
supportHandles: true // Required for handle passing
|
|
650
|
+
} as const); // Use 'as const' for type inference
|
|
651
|
+
|
|
652
|
+
// Create a server and accept connections
|
|
653
|
+
const server = net.createServer((socket) => {
|
|
654
|
+
// Transfer the socket to the child process
|
|
655
|
+
handler.$sendHandle(socket, 'connection-1'); // TypeScript knows $sendHandle is available!
|
|
656
|
+
|
|
657
|
+
// Register it in the child
|
|
658
|
+
handler.registerConnection('connection-1', socket);
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
server.listen(8080);
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
**Platform Support:**
|
|
665
|
+
- ✅ **Unix/Linux/macOS**: Full support for socket transfer
|
|
666
|
+
- ⚠️ **Windows**: Limited support - some handle types may not work correctly
|
|
667
|
+
|
|
668
|
+
See [examples/advanced-serialization/socket-transfer.ts](./examples/advanced-serialization/socket-transfer.ts) for a complete example.
|
|
669
|
+
|
|
670
|
+
### When to Use Advanced Mode
|
|
671
|
+
|
|
672
|
+
**Use JSON mode (default) when:**
|
|
673
|
+
- Working with simple data structures (objects, arrays, primitives)
|
|
674
|
+
- Maximum performance is critical for simple types
|
|
675
|
+
- No need for binary data or collections
|
|
676
|
+
|
|
677
|
+
**Use Advanced mode when:**
|
|
678
|
+
- Processing binary data (images, files, network protocols)
|
|
679
|
+
- Working with Map/Set collections
|
|
680
|
+
- Handling large numbers with BigInt
|
|
681
|
+
- Need to preserve Date/RegExp/Error objects with full fidelity
|
|
682
|
+
- Transferring TypedArray data between processes
|
|
683
|
+
- **Passing network sockets between processes** (requires `supportHandles: true`)
|
|
684
|
+
|
|
685
|
+
### More Examples
|
|
686
|
+
|
|
687
|
+
See [examples/advanced-serialization/](./examples/advanced-serialization/) for comprehensive examples:
|
|
688
|
+
- [Buffer Processing](./examples/advanced-serialization/buffer-processing.ts) - Image data processing with Buffers
|
|
689
|
+
- [BigInt Calculations](./examples/advanced-serialization/bigint-calculations.ts) - Large number operations
|
|
690
|
+
- [Collection Processing](./examples/advanced-serialization/collection-processing.ts) - Map and Set usage
|
|
691
|
+
- [Socket Transfer](./examples/advanced-serialization/socket-transfer.ts) - Handle passing with network sockets
|
|
692
|
+
- [Error Preservation](./examples/advanced-serialization/error-preservation.ts) - Full Error object preservation
|
|
693
|
+
- [Migration Guide](./examples/advanced-serialization/migration-guide.md) - Step-by-step migration from JSON mode
|
|
694
|
+
|
|
695
|
+
### Performance Considerations
|
|
696
|
+
|
|
697
|
+
Advanced serialization has a small overhead compared to JSON for simple objects, but enables much broader type support. See [Performance Benchmarks](#-performance) for detailed comparisons.
|
|
698
|
+
|
|
699
|
+
```typescript
|
|
700
|
+
// Benchmark results (see benchmark/serialization-comparison.ts)
|
|
701
|
+
// Simple object (100 calls):
|
|
702
|
+
// JSON mode: 0.12ms average
|
|
703
|
+
// Advanced mode: 0.15ms average (~25% slower)
|
|
704
|
+
//
|
|
705
|
+
// Buffer data (100 calls):
|
|
706
|
+
// JSON mode: Not supported
|
|
707
|
+
// Advanced mode: 0.18ms average
|
|
708
|
+
//
|
|
709
|
+
// Map with 100 entries (100 calls):
|
|
710
|
+
// JSON mode: Not supported
|
|
711
|
+
// Advanced mode: 0.25ms average
|
|
712
|
+
```
|
|
713
|
+
|
|
459
714
|
## 🔍 Limitations
|
|
460
715
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
716
|
+
Understanding these constraints will help you use procxy effectively:
|
|
717
|
+
|
|
718
|
+
1. **Serialization Requirements**
|
|
719
|
+
- **JSON mode (default)**: Method arguments and return values must be JSON-serializable
|
|
720
|
+
- **Advanced mode**: Supports V8 structured clone types (Buffer, Map, Set, BigInt, etc.)
|
|
721
|
+
- Functions are automatically proxied as callbacks (no manual serialization needed)
|
|
722
|
+
- **JSON mode**: Circular references and symbols are not supported
|
|
723
|
+
- **Advanced mode**: Circular references are supported via V8 structured clone; symbols are not supported
|
|
724
|
+
|
|
725
|
+
2. **Parent Properties Are Read-Only**
|
|
726
|
+
- Parent process can only **read** properties (via local synchronized store)
|
|
727
|
+
- Child process can **read and write** properties
|
|
728
|
+
- Modifications must be done via child methods, not direct assignment on parent
|
|
729
|
+
|
|
730
|
+
3. **One-Way Event Flow**
|
|
731
|
+
- EventEmitter events flow from child → parent only
|
|
732
|
+
- Parent can listen to events, but cannot emit events to the child
|
|
733
|
+
- The child process owns the EventEmitter instance
|
|
734
|
+
|
|
735
|
+
4. **Callback Context Limitations**
|
|
736
|
+
- Callbacks are invoked with serialized arguments
|
|
737
|
+
- No `this` binding preservation across process boundaries
|
|
738
|
+
- Callback functions cannot access closure variables from the other process
|
|
465
739
|
|
|
466
740
|
## 🧪 Testing
|
|
467
741
|
|
|
@@ -487,27 +761,96 @@ pnpm test tests/integration/basic-invocation.test.ts
|
|
|
487
761
|
|
|
488
762
|
### Module Resolution Errors
|
|
489
763
|
|
|
490
|
-
If you get `ModuleResolutionError`, ensure
|
|
764
|
+
If you get `ModuleResolutionError`, ensure you have a static import or provide an explicit `modulePath`:
|
|
491
765
|
|
|
492
766
|
```typescript
|
|
493
|
-
// ✅
|
|
767
|
+
// ✅ Best - automatic resolution with static import
|
|
768
|
+
import { Worker } from './worker.js';
|
|
769
|
+
await procxy(Worker);
|
|
770
|
+
|
|
771
|
+
// ✅ Also works - explicit path
|
|
494
772
|
await procxy(Worker, './worker.js');
|
|
495
|
-
await procxy(Worker, '/absolute/path/to/worker.js');
|
|
496
773
|
|
|
497
|
-
// ❌
|
|
498
|
-
await
|
|
774
|
+
// ❌ Won't work - dynamic import without explicit path
|
|
775
|
+
const { Worker } = await import('./worker.js');
|
|
776
|
+
await procxy(Worker); // Error: Cannot resolve module path!
|
|
777
|
+
|
|
778
|
+
// ✅ Fix - provide explicit path with dynamic import
|
|
779
|
+
const { Worker } = await import('./worker.js');
|
|
780
|
+
await procxy(Worker, './worker.js');
|
|
499
781
|
```
|
|
500
782
|
|
|
501
783
|
### Serialization Errors
|
|
502
784
|
|
|
503
|
-
Ensure all arguments and return values are
|
|
785
|
+
Ensure all arguments and return values are serializable for your chosen mode:
|
|
504
786
|
|
|
787
|
+
**JSON Mode:**
|
|
505
788
|
```typescript
|
|
506
789
|
// ✅ OK
|
|
507
790
|
await proxy.process({ name: 'test', count: 42 });
|
|
508
791
|
|
|
509
792
|
// ❌ Not OK - contains function
|
|
510
793
|
await proxy.process({ name: 'test', fn: () => {} });
|
|
794
|
+
|
|
795
|
+
// ❌ Not OK - Buffer requires advanced mode
|
|
796
|
+
await proxy.processImage(Buffer.from('data'));
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
**Advanced Mode:**
|
|
800
|
+
```typescript
|
|
801
|
+
// Enable advanced mode
|
|
802
|
+
const proxy = await procxy<Worker, 'advanced'>(
|
|
803
|
+
Worker,
|
|
804
|
+
{ serialization: 'advanced' }
|
|
805
|
+
);
|
|
806
|
+
|
|
807
|
+
// ✅ Now OK - Buffer is supported
|
|
808
|
+
await proxy.processImage(Buffer.from('data'));
|
|
809
|
+
|
|
810
|
+
// ✅ OK - Map and Set supported
|
|
811
|
+
await proxy.processMap(new Map([['key', 'value']]));
|
|
812
|
+
await proxy.processSet(new Set([1, 2, 3]));
|
|
813
|
+
|
|
814
|
+
// ✅ OK - BigInt supported
|
|
815
|
+
await proxy.calculate(123456789n);
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
### Type Inference Issues
|
|
819
|
+
|
|
820
|
+
When using advanced serialization, ensure the type parameter matches the option:
|
|
821
|
+
|
|
822
|
+
```typescript
|
|
823
|
+
// ✅ Correct - type parameter matches serialization option
|
|
824
|
+
const worker = await procxy<Worker, 'advanced'>(
|
|
825
|
+
Worker,
|
|
826
|
+
{ serialization: 'advanced' }
|
|
827
|
+
);
|
|
828
|
+
|
|
829
|
+
// ❌ Wrong - type mismatch will cause TypeScript errors
|
|
830
|
+
const worker = await procxy<Worker, 'json'>(
|
|
831
|
+
Worker,
|
|
832
|
+
{ serialization: 'advanced' } // TypeScript error!
|
|
833
|
+
);
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
### Handle Passing Issues
|
|
837
|
+
|
|
838
|
+
If handle passing doesn't work:
|
|
839
|
+
|
|
840
|
+
```typescript
|
|
841
|
+
// ✅ Ensure both advanced mode AND supportHandles are enabled with 'as const'
|
|
842
|
+
const handler = await procxy(Handler, {
|
|
843
|
+
serialization: 'advanced',
|
|
844
|
+
supportHandles: true // Required!
|
|
845
|
+
} as const); // 'as const' ensures TypeScript infers supportHandles: true
|
|
846
|
+
|
|
847
|
+
// ✅ Now $sendHandle is available in TypeScript autocomplete
|
|
848
|
+
await handler.$sendHandle(socket);
|
|
849
|
+
|
|
850
|
+
// ✅ Check platform - Windows has limited support
|
|
851
|
+
if (process.platform === 'win32') {
|
|
852
|
+
console.warn('Handle passing may not work on Windows');
|
|
853
|
+
}
|
|
511
854
|
```
|
|
512
855
|
|
|
513
856
|
### Timeout Issues
|
|
@@ -515,7 +858,10 @@ await proxy.process({ name: 'test', fn: () => {} });
|
|
|
515
858
|
Increase timeout for long-running methods:
|
|
516
859
|
|
|
517
860
|
```typescript
|
|
518
|
-
|
|
861
|
+
import { procxy } from 'procxy';
|
|
862
|
+
import { Worker } from './worker.js';
|
|
863
|
+
|
|
864
|
+
const worker = await procxy(Worker, {
|
|
519
865
|
timeout: 300000 // 5 minutes
|
|
520
866
|
});
|
|
521
867
|
```
|
|
@@ -589,17 +935,10 @@ class Task extends EventEmitter<Events> {
|
|
|
589
935
|
### Data Processing Pipeline
|
|
590
936
|
|
|
591
937
|
```typescript
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
// Heavy computation in isolated process
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
async compress(data: Buffer, quality: number): Promise<Buffer> {
|
|
598
|
-
// Another heavy operation
|
|
599
|
-
}
|
|
600
|
-
}
|
|
938
|
+
import { procxy } from 'procxy';
|
|
939
|
+
import { ImageProcessor } from './image-processor.js';
|
|
601
940
|
|
|
602
|
-
await using processor = await procxy(ImageProcessor
|
|
941
|
+
await using processor = await procxy(ImageProcessor);
|
|
603
942
|
|
|
604
943
|
let image = await processor.resize(imageData, 800);
|
|
605
944
|
image = await processor.compress(image, 85);
|
|
@@ -610,12 +949,15 @@ console.log('Processed:', image.length, 'bytes');
|
|
|
610
949
|
### Worker Pool Pattern
|
|
611
950
|
|
|
612
951
|
```typescript
|
|
952
|
+
import { procxy } from 'procxy';
|
|
953
|
+
import { Worker } from './worker.js';
|
|
954
|
+
|
|
613
955
|
class WorkerPool {
|
|
614
956
|
private workers: any[] = [];
|
|
615
957
|
|
|
616
958
|
async initialize(poolSize: number): Promise<void> {
|
|
617
959
|
for (let i = 0; i < poolSize; i++) {
|
|
618
|
-
this.workers.push(await procxy(Worker
|
|
960
|
+
this.workers.push(await procxy(Worker));
|
|
619
961
|
}
|
|
620
962
|
}
|
|
621
963
|
|
|
@@ -633,13 +975,10 @@ class WorkerPool {
|
|
|
633
975
|
### Real-Time Data Streaming
|
|
634
976
|
|
|
635
977
|
```typescript
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
// Start streaming data
|
|
639
|
-
}
|
|
640
|
-
}
|
|
978
|
+
import { procxy } from 'procxy';
|
|
979
|
+
import { DataStream } from './stream.js';
|
|
641
980
|
|
|
642
|
-
const stream = await procxy(DataStream
|
|
981
|
+
const stream = await procxy(DataStream);
|
|
643
982
|
|
|
644
983
|
let dataCount = 0;
|
|
645
984
|
stream.on('data', (chunk) => {
|
|
@@ -657,27 +996,10 @@ await stream.startStream(x => x.value > 100);
|
|
|
657
996
|
### Batch Processing with Progress
|
|
658
997
|
|
|
659
998
|
```typescript
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
items: string[],
|
|
663
|
-
onProgress: (processed: number, total: number, item: string) => void
|
|
664
|
-
): Promise<string[]> {
|
|
665
|
-
const results = [];
|
|
666
|
-
for (let i = 0; i < items.length; i++) {
|
|
667
|
-
const result = await this.heavyProcess(items[i]);
|
|
668
|
-
results.push(result);
|
|
669
|
-
onProgress(i + 1, items.length, items[i]);
|
|
670
|
-
}
|
|
671
|
-
return results;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
private async heavyProcess(item: string): Promise<string> {
|
|
675
|
-
// CPU-intensive work
|
|
676
|
-
return item.toUpperCase();
|
|
677
|
-
}
|
|
678
|
-
}
|
|
999
|
+
import { procxy } from 'procxy';
|
|
1000
|
+
import { BatchProcessor } from './batch-processor.js';
|
|
679
1001
|
|
|
680
|
-
const processor = await procxy(BatchProcessor
|
|
1002
|
+
const processor = await procxy(BatchProcessor);
|
|
681
1003
|
|
|
682
1004
|
const results = await processor.processBatch(
|
|
683
1005
|
['item1', 'item2', 'item3'],
|
|
@@ -694,26 +1016,12 @@ console.log('Results:', results);
|
|
|
694
1016
|
Use static factory methods or wrapper functions for cleaner initialization:
|
|
695
1017
|
|
|
696
1018
|
```typescript
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
private connected: boolean = false;
|
|
700
|
-
|
|
701
|
-
async connect(url: string): Promise<void> {
|
|
702
|
-
this.connectionString = url;
|
|
703
|
-
this.connected = true;
|
|
704
|
-
// Simulate connection setup
|
|
705
|
-
await new Promise(r => setTimeout(r, 100));
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
async query(sql: string): Promise<any[]> {
|
|
709
|
-
if (!this.connected) throw new Error('Not connected');
|
|
710
|
-
return [{ result: 'example' }];
|
|
711
|
-
}
|
|
712
|
-
}
|
|
1019
|
+
import { procxy } from 'procxy';
|
|
1020
|
+
import { Database } from './database.js';
|
|
713
1021
|
|
|
714
1022
|
// Factory function for cleaner API
|
|
715
1023
|
async function createDatabase(url: string) {
|
|
716
|
-
const db = await procxy(Database
|
|
1024
|
+
const db = await procxy(Database);
|
|
717
1025
|
await db.connect(url);
|
|
718
1026
|
return db;
|
|
719
1027
|
}
|
|
@@ -729,27 +1037,32 @@ await db.$terminate();
|
|
|
729
1037
|
Build complex instances with fluent API and async setup:
|
|
730
1038
|
|
|
731
1039
|
```typescript
|
|
1040
|
+
import { procxy } from 'procxy';
|
|
1041
|
+
import { Worker } from './worker.js';
|
|
1042
|
+
|
|
732
1043
|
class WorkerBuilder {
|
|
733
1044
|
private name: string = 'Worker';
|
|
734
1045
|
private threads: number = 1;
|
|
735
1046
|
private timeout: number = 30000;
|
|
736
1047
|
|
|
737
|
-
setName(name: string):
|
|
1048
|
+
setName(name: string): this {
|
|
738
1049
|
this.name = name;
|
|
1050
|
+
return this;
|
|
739
1051
|
}
|
|
740
1052
|
|
|
741
|
-
setThreads(threads: number):
|
|
1053
|
+
setThreads(threads: number): this {
|
|
742
1054
|
this.threads = threads;
|
|
1055
|
+
return this;
|
|
743
1056
|
}
|
|
744
1057
|
|
|
745
|
-
setTimeout(ms: number):
|
|
1058
|
+
setTimeout(ms: number): this {
|
|
746
1059
|
this.timeout = ms;
|
|
1060
|
+
return this;
|
|
747
1061
|
}
|
|
748
1062
|
|
|
749
1063
|
async build(): Promise<Procxy<Worker>> {
|
|
750
1064
|
const worker = await procxy(
|
|
751
1065
|
Worker,
|
|
752
|
-
'./worker.js',
|
|
753
1066
|
{ timeout: this.timeout },
|
|
754
1067
|
this.name,
|
|
755
1068
|
this.threads
|
|
@@ -776,6 +1089,9 @@ await worker.processImage(imageData);
|
|
|
776
1089
|
Manage a pool of async-initialized resources:
|
|
777
1090
|
|
|
778
1091
|
```typescript
|
|
1092
|
+
import { procxy } from 'procxy';
|
|
1093
|
+
import { Worker } from './worker.js';
|
|
1094
|
+
|
|
779
1095
|
class AsyncResourcePool<T> {
|
|
780
1096
|
private available: Procxy<T>[] = [];
|
|
781
1097
|
private inUse = new Set<Procxy<T>>();
|
|
@@ -813,7 +1129,7 @@ class AsyncResourcePool<T> {
|
|
|
813
1129
|
|
|
814
1130
|
// Usage
|
|
815
1131
|
async function createWorker() {
|
|
816
|
-
return await procxy(Worker
|
|
1132
|
+
return await procxy(Worker);
|
|
817
1133
|
}
|
|
818
1134
|
|
|
819
1135
|
const pool = new AsyncResourcePool<Worker>();
|
|
@@ -836,13 +1152,16 @@ await pool.shutdown();
|
|
|
836
1152
|
Create and cache instances on first use:
|
|
837
1153
|
|
|
838
1154
|
```typescript
|
|
1155
|
+
import { procxy } from 'procxy';
|
|
1156
|
+
import { Worker } from './worker.js';
|
|
1157
|
+
|
|
839
1158
|
class LazyWorkerSingleton {
|
|
840
1159
|
private static instance: Procxy<Worker> | null = null;
|
|
841
1160
|
|
|
842
1161
|
static async getInstance(): Promise<Procxy<Worker>> {
|
|
843
1162
|
if (!this.instance) {
|
|
844
1163
|
console.log('Initializing worker...');
|
|
845
|
-
this.instance = await procxy(Worker
|
|
1164
|
+
this.instance = await procxy(Worker);
|
|
846
1165
|
|
|
847
1166
|
// Set up cleanup on process exit
|
|
848
1167
|
process.on('exit', async () => {
|
|
@@ -876,6 +1195,10 @@ await LazyWorkerSingleton.reset();
|
|
|
876
1195
|
Resolve dependencies asynchronously before using proxies:
|
|
877
1196
|
|
|
878
1197
|
```typescript
|
|
1198
|
+
import { procxy } from 'procxy';
|
|
1199
|
+
import { Database } from './database.js';
|
|
1200
|
+
import { Cache } from './cache.js';
|
|
1201
|
+
|
|
879
1202
|
class ServiceContainer {
|
|
880
1203
|
private services = new Map<string, any>();
|
|
881
1204
|
|
|
@@ -902,13 +1225,8 @@ class ServiceContainer {
|
|
|
902
1225
|
// Setup
|
|
903
1226
|
const container = new ServiceContainer();
|
|
904
1227
|
|
|
905
|
-
await container.register('database', () =>
|
|
906
|
-
|
|
907
|
-
);
|
|
908
|
-
|
|
909
|
-
await container.register('cache', () =>
|
|
910
|
-
procxy(Cache, './cache.js')
|
|
911
|
-
);
|
|
1228
|
+
await container.register('database', () => procxy(Database));
|
|
1229
|
+
await container.register('cache', () => procxy(Cache));
|
|
912
1230
|
|
|
913
1231
|
// Usage with injected dependencies
|
|
914
1232
|
const db = container.get<Database>('database');
|