alepha 0.13.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-jobs/index.d.ts +26 -26
- package/dist/api-users/index.d.ts +1 -1
- package/dist/cli/{dist-Sz2EXvQX.cjs → dist-Dl9Vl7Ur.js} +17 -13
- package/dist/cli/{dist-BBPjuQ56.js.map → dist-Dl9Vl7Ur.js.map} +1 -1
- package/dist/cli/index.d.ts +3 -11
- package/dist/cli/index.js +106 -74
- package/dist/cli/index.js.map +1 -1
- package/dist/email/index.js +71 -73
- package/dist/email/index.js.map +1 -1
- package/dist/orm/index.d.ts +1 -1
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/index.d.ts +4 -4
- package/dist/retry/index.d.ts +1 -1
- package/dist/retry/index.js +2 -2
- package/dist/retry/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +6 -6
- package/dist/security/index.d.ts +28 -28
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server-health/index.d.ts +17 -17
- package/dist/server-metrics/index.js +170 -174
- package/dist/server-metrics/index.js.map +1 -1
- package/dist/server-security/index.d.ts +9 -9
- package/dist/vite/index.js +4 -5
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.d.ts +7 -7
- package/package.json +52 -103
- package/src/cli/apps/AlephaPackageBuilderCli.ts +7 -2
- package/src/cli/assets/appRouterTs.ts +9 -0
- package/src/cli/assets/indexHtml.ts +2 -1
- package/src/cli/assets/mainBrowserTs.ts +10 -0
- package/src/cli/commands/CoreCommands.ts +6 -5
- package/src/cli/commands/DrizzleCommands.ts +65 -57
- package/src/cli/commands/VerifyCommands.ts +1 -1
- package/src/cli/services/ProjectUtils.ts +44 -38
- package/src/orm/providers/DrizzleKitProvider.ts +1 -1
- package/src/retry/descriptors/$retry.ts +5 -3
- package/src/server/providers/NodeHttpServerProvider.ts +1 -1
- package/src/vite/helpers/boot.ts +3 -3
- package/dist/api-files/index.cjs +0 -1293
- package/dist/api-files/index.cjs.map +0 -1
- package/dist/api-files/index.d.cts +0 -829
- package/dist/api-jobs/index.cjs +0 -274
- package/dist/api-jobs/index.cjs.map +0 -1
- package/dist/api-jobs/index.d.cts +0 -654
- package/dist/api-notifications/index.cjs +0 -380
- package/dist/api-notifications/index.cjs.map +0 -1
- package/dist/api-notifications/index.d.cts +0 -289
- package/dist/api-parameters/index.cjs +0 -66
- package/dist/api-parameters/index.cjs.map +0 -1
- package/dist/api-parameters/index.d.cts +0 -84
- package/dist/api-users/index.cjs +0 -6009
- package/dist/api-users/index.cjs.map +0 -1
- package/dist/api-users/index.d.cts +0 -4740
- package/dist/api-verifications/index.cjs +0 -407
- package/dist/api-verifications/index.cjs.map +0 -1
- package/dist/api-verifications/index.d.cts +0 -207
- package/dist/batch/index.cjs +0 -408
- package/dist/batch/index.cjs.map +0 -1
- package/dist/batch/index.d.cts +0 -330
- package/dist/bin/index.cjs +0 -17
- package/dist/bin/index.cjs.map +0 -1
- package/dist/bin/index.d.cts +0 -1
- package/dist/bucket/index.cjs +0 -303
- package/dist/bucket/index.cjs.map +0 -1
- package/dist/bucket/index.d.cts +0 -355
- package/dist/cache/index.cjs +0 -241
- package/dist/cache/index.cjs.map +0 -1
- package/dist/cache/index.d.cts +0 -202
- package/dist/cache-redis/index.cjs +0 -84
- package/dist/cache-redis/index.cjs.map +0 -1
- package/dist/cache-redis/index.d.cts +0 -40
- package/dist/cli/chunk-DSlc6foC.cjs +0 -43
- package/dist/cli/dist-BBPjuQ56.js +0 -2778
- package/dist/cli/dist-Sz2EXvQX.cjs.map +0 -1
- package/dist/cli/index.cjs +0 -1241
- package/dist/cli/index.cjs.map +0 -1
- package/dist/cli/index.d.cts +0 -422
- package/dist/command/index.cjs +0 -693
- package/dist/command/index.cjs.map +0 -1
- package/dist/command/index.d.cts +0 -340
- package/dist/core/index.cjs +0 -2264
- package/dist/core/index.cjs.map +0 -1
- package/dist/core/index.d.cts +0 -1927
- package/dist/datetime/index.cjs +0 -318
- package/dist/datetime/index.cjs.map +0 -1
- package/dist/datetime/index.d.cts +0 -145
- package/dist/email/index.cjs +0 -10874
- package/dist/email/index.cjs.map +0 -1
- package/dist/email/index.d.cts +0 -186
- package/dist/fake/index.cjs +0 -34641
- package/dist/fake/index.cjs.map +0 -1
- package/dist/fake/index.d.cts +0 -74
- package/dist/file/index.cjs +0 -1212
- package/dist/file/index.cjs.map +0 -1
- package/dist/file/index.d.cts +0 -698
- package/dist/lock/index.cjs +0 -226
- package/dist/lock/index.cjs.map +0 -1
- package/dist/lock/index.d.cts +0 -361
- package/dist/lock-redis/index.cjs +0 -113
- package/dist/lock-redis/index.cjs.map +0 -1
- package/dist/lock-redis/index.d.cts +0 -24
- package/dist/logger/index.cjs +0 -521
- package/dist/logger/index.cjs.map +0 -1
- package/dist/logger/index.d.cts +0 -281
- package/dist/orm/index.cjs +0 -2986
- package/dist/orm/index.cjs.map +0 -1
- package/dist/orm/index.d.cts +0 -2213
- package/dist/queue/index.cjs +0 -1044
- package/dist/queue/index.cjs.map +0 -1
- package/dist/queue/index.d.cts +0 -1265
- package/dist/queue-redis/index.cjs +0 -873
- package/dist/queue-redis/index.cjs.map +0 -1
- package/dist/queue-redis/index.d.cts +0 -82
- package/dist/redis/index.cjs +0 -153
- package/dist/redis/index.cjs.map +0 -1
- package/dist/redis/index.d.cts +0 -82
- package/dist/retry/index.cjs +0 -146
- package/dist/retry/index.cjs.map +0 -1
- package/dist/retry/index.d.cts +0 -172
- package/dist/router/index.cjs +0 -111
- package/dist/router/index.cjs.map +0 -1
- package/dist/router/index.d.cts +0 -46
- package/dist/scheduler/index.cjs +0 -576
- package/dist/scheduler/index.cjs.map +0 -1
- package/dist/scheduler/index.d.cts +0 -145
- package/dist/security/index.cjs +0 -2402
- package/dist/security/index.cjs.map +0 -1
- package/dist/security/index.d.cts +0 -598
- package/dist/server/index.cjs +0 -1680
- package/dist/server/index.cjs.map +0 -1
- package/dist/server/index.d.cts +0 -810
- package/dist/server-auth/index.cjs +0 -3146
- package/dist/server-auth/index.cjs.map +0 -1
- package/dist/server-auth/index.d.cts +0 -1164
- package/dist/server-cache/index.cjs +0 -252
- package/dist/server-cache/index.cjs.map +0 -1
- package/dist/server-cache/index.d.cts +0 -164
- package/dist/server-compress/index.cjs +0 -141
- package/dist/server-compress/index.cjs.map +0 -1
- package/dist/server-compress/index.d.cts +0 -38
- package/dist/server-cookies/index.cjs +0 -234
- package/dist/server-cookies/index.cjs.map +0 -1
- package/dist/server-cookies/index.d.cts +0 -144
- package/dist/server-cors/index.cjs +0 -201
- package/dist/server-cors/index.cjs.map +0 -1
- package/dist/server-cors/index.d.cts +0 -140
- package/dist/server-health/index.cjs +0 -62
- package/dist/server-health/index.cjs.map +0 -1
- package/dist/server-health/index.d.cts +0 -58
- package/dist/server-helmet/index.cjs +0 -131
- package/dist/server-helmet/index.cjs.map +0 -1
- package/dist/server-helmet/index.d.cts +0 -97
- package/dist/server-links/index.cjs +0 -992
- package/dist/server-links/index.cjs.map +0 -1
- package/dist/server-links/index.d.cts +0 -513
- package/dist/server-metrics/index.cjs +0 -4535
- package/dist/server-metrics/index.cjs.map +0 -1
- package/dist/server-metrics/index.d.cts +0 -35
- package/dist/server-multipart/index.cjs +0 -237
- package/dist/server-multipart/index.cjs.map +0 -1
- package/dist/server-multipart/index.d.cts +0 -50
- package/dist/server-proxy/index.cjs +0 -186
- package/dist/server-proxy/index.cjs.map +0 -1
- package/dist/server-proxy/index.d.cts +0 -234
- package/dist/server-rate-limit/index.cjs +0 -241
- package/dist/server-rate-limit/index.cjs.map +0 -1
- package/dist/server-rate-limit/index.d.cts +0 -183
- package/dist/server-security/index.cjs +0 -316
- package/dist/server-security/index.cjs.map +0 -1
- package/dist/server-security/index.d.cts +0 -173
- package/dist/server-static/index.cjs +0 -170
- package/dist/server-static/index.cjs.map +0 -1
- package/dist/server-static/index.d.cts +0 -121
- package/dist/server-swagger/index.cjs +0 -1021
- package/dist/server-swagger/index.cjs.map +0 -1
- package/dist/server-swagger/index.d.cts +0 -382
- package/dist/sms/index.cjs +0 -221
- package/dist/sms/index.cjs.map +0 -1
- package/dist/sms/index.d.cts +0 -130
- package/dist/thread/index.cjs +0 -350
- package/dist/thread/index.cjs.map +0 -1
- package/dist/thread/index.d.cts +0 -260
- package/dist/topic/index.cjs +0 -282
- package/dist/topic/index.cjs.map +0 -1
- package/dist/topic/index.d.cts +0 -523
- package/dist/topic-redis/index.cjs +0 -71
- package/dist/topic-redis/index.cjs.map +0 -1
- package/dist/topic-redis/index.d.cts +0 -42
- package/dist/vite/index.cjs +0 -1077
- package/dist/vite/index.cjs.map +0 -1
- package/dist/vite/index.d.cts +0 -542
- package/dist/websocket/index.cjs +0 -1117
- package/dist/websocket/index.cjs.map +0 -1
- package/dist/websocket/index.d.cts +0 -861
package/dist/thread/index.cjs
DELETED
|
@@ -1,350 +0,0 @@
|
|
|
1
|
-
let alepha = require("alepha");
|
|
2
|
-
let node_os = require("node:os");
|
|
3
|
-
let node_worker_threads = require("node:worker_threads");
|
|
4
|
-
let alepha_logger = require("alepha/logger");
|
|
5
|
-
|
|
6
|
-
//#region src/thread/descriptors/$thread.ts
|
|
7
|
-
/**
|
|
8
|
-
* Creates a worker thread descriptor for offloading CPU-intensive tasks to separate threads.
|
|
9
|
-
*
|
|
10
|
-
* This descriptor enables you to run JavaScript code in Node.js worker threads, allowing you to
|
|
11
|
-
* leverage multiple CPU cores and avoid blocking the main event loop. It provides a pool-based
|
|
12
|
-
* approach with intelligent thread reuse and automatic lifecycle management.
|
|
13
|
-
*
|
|
14
|
-
* **Key Features**
|
|
15
|
-
*
|
|
16
|
-
* - **Thread Pool Management**: Automatically manages a pool of worker threads with configurable limits
|
|
17
|
-
* - **Thread Reuse**: Reuses existing threads to avoid expensive initialization overhead
|
|
18
|
-
* - **Idle Cleanup**: Automatically terminates unused threads after a configurable timeout
|
|
19
|
-
* - **Type-Safe Communication**: Optional TypeBox schema validation for data passed to threads
|
|
20
|
-
* - **CPU-Aware Defaults**: Pool size defaults to CPU count × 2 for optimal performance
|
|
21
|
-
* - **Error Handling**: Proper error propagation and thread cleanup on failures
|
|
22
|
-
*
|
|
23
|
-
* **Use Cases**
|
|
24
|
-
*
|
|
25
|
-
* Perfect for CPU-intensive tasks that would otherwise block the main thread:
|
|
26
|
-
* - Image/video processing
|
|
27
|
-
* - Data transformation and analysis
|
|
28
|
-
* - Cryptographic operations
|
|
29
|
-
* - Heavy computations and algorithms
|
|
30
|
-
* - Background data processing
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* **Basic thread usage:**
|
|
34
|
-
* ```ts
|
|
35
|
-
* import { $thread } from "alepha/thread";
|
|
36
|
-
*
|
|
37
|
-
* class DataProcessor {
|
|
38
|
-
* heavyComputation = $thread({
|
|
39
|
-
* name: "compute",
|
|
40
|
-
* handler: async () => {
|
|
41
|
-
* // This runs in a separate worker thread
|
|
42
|
-
* let result = 0;
|
|
43
|
-
* for (let i = 0; i < 1000000; i++) {
|
|
44
|
-
* result += Math.sqrt(i);
|
|
45
|
-
* }
|
|
46
|
-
* return { result, timestamp: Date.now() };
|
|
47
|
-
* }
|
|
48
|
-
* });
|
|
49
|
-
*
|
|
50
|
-
* async processData() {
|
|
51
|
-
* // Execute in worker thread without blocking main thread
|
|
52
|
-
* const result = await this.heavyComputation.execute();
|
|
53
|
-
* console.log(`Computation result: ${result.result}`);
|
|
54
|
-
* }
|
|
55
|
-
* }
|
|
56
|
-
* ```
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* **Configured thread pool with custom settings:**
|
|
60
|
-
* ```ts
|
|
61
|
-
* class ImageProcessor {
|
|
62
|
-
* imageProcessor = $thread({
|
|
63
|
-
* name: "image-processing",
|
|
64
|
-
* maxPoolSize: 4, // Limit to 4 concurrent threads
|
|
65
|
-
* idleTimeout: 30000, // Clean up idle threads after 30 seconds
|
|
66
|
-
* handler: async () => {
|
|
67
|
-
* // CPU-intensive image processing logic
|
|
68
|
-
* return await processImageData();
|
|
69
|
-
* }
|
|
70
|
-
* });
|
|
71
|
-
* }
|
|
72
|
-
* ```
|
|
73
|
-
*
|
|
74
|
-
* @example
|
|
75
|
-
* **Thread with data validation:**
|
|
76
|
-
* ```ts
|
|
77
|
-
* import { t } from "alepha";
|
|
78
|
-
*
|
|
79
|
-
* class CryptoService {
|
|
80
|
-
* encrypt = $thread({
|
|
81
|
-
* name: "encryption",
|
|
82
|
-
* handler: async () => {
|
|
83
|
-
* // Perform encryption operations
|
|
84
|
-
* return await encryptData();
|
|
85
|
-
* }
|
|
86
|
-
* });
|
|
87
|
-
*
|
|
88
|
-
* async encryptSensitiveData(data: { text: string; key: string }) {
|
|
89
|
-
* // Validate input data before sending to thread
|
|
90
|
-
* const schema = t.object({
|
|
91
|
-
* text: t.text(),
|
|
92
|
-
* key: t.text()
|
|
93
|
-
* });
|
|
94
|
-
*
|
|
95
|
-
* return await this.encrypt.execute(data, schema);
|
|
96
|
-
* }
|
|
97
|
-
* }
|
|
98
|
-
* ```
|
|
99
|
-
*
|
|
100
|
-
* @example
|
|
101
|
-
* **Parallel processing with multiple threads:**
|
|
102
|
-
* ```ts
|
|
103
|
-
* class BatchProcessor {
|
|
104
|
-
* processor = $thread({
|
|
105
|
-
* name: "batch-worker",
|
|
106
|
-
* maxPoolSize: 8, // Allow up to 8 concurrent workers
|
|
107
|
-
* handler: async () => {
|
|
108
|
-
* return await processBatchItem();
|
|
109
|
-
* }
|
|
110
|
-
* });
|
|
111
|
-
*
|
|
112
|
-
* async processBatch(items: any[]) {
|
|
113
|
-
* // Process multiple items in parallel across different threads
|
|
114
|
-
* const promises = items.map(() => this.processor.execute());
|
|
115
|
-
* const results = await Promise.all(promises);
|
|
116
|
-
* return results;
|
|
117
|
-
* }
|
|
118
|
-
* }
|
|
119
|
-
* ```
|
|
120
|
-
*/
|
|
121
|
-
const $thread = (options) => {
|
|
122
|
-
return (0, alepha.createDescriptor)(ThreadDescriptor, options);
|
|
123
|
-
};
|
|
124
|
-
var ThreadDescriptor = class ThreadDescriptor extends alepha.Descriptor {
|
|
125
|
-
script = process.argv[1];
|
|
126
|
-
static globalPool = /* @__PURE__ */ new Map();
|
|
127
|
-
get name() {
|
|
128
|
-
return this.options.name || this.config.propertyKey;
|
|
129
|
-
}
|
|
130
|
-
get maxPoolSize() {
|
|
131
|
-
return this.options.maxPoolSize || (0, node_os.cpus)().length * 2;
|
|
132
|
-
}
|
|
133
|
-
get idleTimeout() {
|
|
134
|
-
return this.options.idleTimeout || 6e4;
|
|
135
|
-
}
|
|
136
|
-
getPool() {
|
|
137
|
-
if (!ThreadDescriptor.globalPool.has(this.name)) ThreadDescriptor.globalPool.set(this.name, new ThreadPool(this.name, this.maxPoolSize, this.idleTimeout, this.script));
|
|
138
|
-
return ThreadDescriptor.globalPool.get(this.name);
|
|
139
|
-
}
|
|
140
|
-
async execute(data, schema) {
|
|
141
|
-
if (schema && data) try {
|
|
142
|
-
alepha.TypeBoxValue.Decode(schema, data);
|
|
143
|
-
} catch (error) {
|
|
144
|
-
throw new Error(`Invalid data: ${error instanceof Error ? error.message : error}`);
|
|
145
|
-
}
|
|
146
|
-
return await this.getPool().execute(data);
|
|
147
|
-
}
|
|
148
|
-
async create() {
|
|
149
|
-
await this.getPool().warmUp();
|
|
150
|
-
}
|
|
151
|
-
async terminate() {
|
|
152
|
-
await this.getPool().terminate();
|
|
153
|
-
ThreadDescriptor.globalPool.delete(this.name);
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
$thread[alepha.KIND] = ThreadDescriptor;
|
|
157
|
-
var ThreadPool = class {
|
|
158
|
-
instances = [];
|
|
159
|
-
queue = [];
|
|
160
|
-
idleTimer;
|
|
161
|
-
constructor(name, maxPoolSize, idleTimeout, script) {
|
|
162
|
-
this.name = name;
|
|
163
|
-
this.maxPoolSize = maxPoolSize;
|
|
164
|
-
this.idleTimeout = idleTimeout;
|
|
165
|
-
this.script = script;
|
|
166
|
-
}
|
|
167
|
-
async warmUp() {
|
|
168
|
-
if (this.instances.length === 0) await this.createInstance();
|
|
169
|
-
}
|
|
170
|
-
async createInstance() {
|
|
171
|
-
const { port1, port2 } = new node_worker_threads.MessageChannel();
|
|
172
|
-
const worker = new node_worker_threads.Worker(this.script, {
|
|
173
|
-
env: {
|
|
174
|
-
...process.env,
|
|
175
|
-
ALEPHA_WORKER: this.name,
|
|
176
|
-
APP_NAME: "WORKER"
|
|
177
|
-
},
|
|
178
|
-
workerData: { port: port2 },
|
|
179
|
-
transferList: [port2]
|
|
180
|
-
});
|
|
181
|
-
const instance = {
|
|
182
|
-
worker,
|
|
183
|
-
port: port1,
|
|
184
|
-
busy: false,
|
|
185
|
-
lastUsed: Date.now(),
|
|
186
|
-
pendingMessages: /* @__PURE__ */ new Map()
|
|
187
|
-
};
|
|
188
|
-
instance.port.on("message", (message) => {
|
|
189
|
-
if (message.type === "response" || message.type === "error") {
|
|
190
|
-
const pending = instance.pendingMessages.get(message.id);
|
|
191
|
-
if (pending) {
|
|
192
|
-
instance.pendingMessages.delete(message.id);
|
|
193
|
-
instance.busy = false;
|
|
194
|
-
instance.lastUsed = Date.now();
|
|
195
|
-
if (message.type === "error") pending.reject(new Error(message.error));
|
|
196
|
-
else pending.resolve(message.data);
|
|
197
|
-
this.processQueue();
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
await new Promise((resolve, reject) => {
|
|
202
|
-
const timeout = setTimeout(() => reject(/* @__PURE__ */ new Error("Thread initialization timeout")), 5e3);
|
|
203
|
-
worker.once("online", () => {
|
|
204
|
-
clearTimeout(timeout);
|
|
205
|
-
resolve();
|
|
206
|
-
});
|
|
207
|
-
worker.once("error", (error) => {
|
|
208
|
-
clearTimeout(timeout);
|
|
209
|
-
reject(error);
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
this.instances.push(instance);
|
|
213
|
-
this.resetIdleTimer();
|
|
214
|
-
return instance;
|
|
215
|
-
}
|
|
216
|
-
async execute(data) {
|
|
217
|
-
return new Promise((resolve, reject) => {
|
|
218
|
-
this.queue.push({
|
|
219
|
-
data,
|
|
220
|
-
resolve,
|
|
221
|
-
reject
|
|
222
|
-
});
|
|
223
|
-
this.processQueue();
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
async processQueue() {
|
|
227
|
-
if (this.queue.length === 0) return;
|
|
228
|
-
let instance = this.instances.find((i) => !i.busy);
|
|
229
|
-
if (!instance && this.instances.length < this.maxPoolSize) try {
|
|
230
|
-
instance = await this.createInstance();
|
|
231
|
-
} catch (error) {
|
|
232
|
-
const { reject: reject$1 } = this.queue.shift();
|
|
233
|
-
reject$1(error instanceof Error ? error : /* @__PURE__ */ new Error("Failed to create thread instance"));
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
if (!instance) return;
|
|
237
|
-
const { data, resolve, reject } = this.queue.shift();
|
|
238
|
-
const messageId = `${Date.now()}-${Math.random()}`;
|
|
239
|
-
instance.busy = true;
|
|
240
|
-
instance.pendingMessages.set(messageId, {
|
|
241
|
-
resolve,
|
|
242
|
-
reject
|
|
243
|
-
});
|
|
244
|
-
const message = {
|
|
245
|
-
id: messageId,
|
|
246
|
-
type: "execute",
|
|
247
|
-
data
|
|
248
|
-
};
|
|
249
|
-
instance.port.postMessage(message);
|
|
250
|
-
}
|
|
251
|
-
resetIdleTimer() {
|
|
252
|
-
if (this.idleTimer) clearTimeout(this.idleTimer);
|
|
253
|
-
this.idleTimer = setTimeout(() => {
|
|
254
|
-
this.cleanupIdleInstances();
|
|
255
|
-
}, this.idleTimeout);
|
|
256
|
-
}
|
|
257
|
-
cleanupIdleInstances() {
|
|
258
|
-
const now = Date.now();
|
|
259
|
-
const instancesToRemove = this.instances.filter((instance) => !instance.busy && now - instance.lastUsed > this.idleTimeout);
|
|
260
|
-
for (const instance of instancesToRemove) {
|
|
261
|
-
const index = this.instances.indexOf(instance);
|
|
262
|
-
if (index > -1) {
|
|
263
|
-
this.instances.splice(index, 1);
|
|
264
|
-
instance.port.close();
|
|
265
|
-
instance.worker.terminate();
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
if (this.instances.length > 0) this.resetIdleTimer();
|
|
269
|
-
}
|
|
270
|
-
async terminate() {
|
|
271
|
-
if (this.idleTimer) clearTimeout(this.idleTimer);
|
|
272
|
-
await Promise.all(this.instances.map(async (instance) => {
|
|
273
|
-
instance.port.close();
|
|
274
|
-
await instance.worker.terminate();
|
|
275
|
-
}));
|
|
276
|
-
this.instances = [];
|
|
277
|
-
this.queue = [];
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
//#endregion
|
|
282
|
-
//#region src/thread/providers/ThreadProvider.ts
|
|
283
|
-
var ThreadProvider = class {
|
|
284
|
-
log = (0, alepha_logger.$logger)();
|
|
285
|
-
env = (0, alepha.$env)(alepha.t.object({ ALEPHA_WORKER: alepha.t.optional(alepha.t.text()) }));
|
|
286
|
-
ready = (0, alepha.$hook)({
|
|
287
|
-
on: "ready",
|
|
288
|
-
handler: async (alepha$1) => {
|
|
289
|
-
const worker = this.env.ALEPHA_WORKER;
|
|
290
|
-
if (!worker) return;
|
|
291
|
-
const threadDescriptor = alepha$1.descriptors($thread).find((thread) => thread.name === worker);
|
|
292
|
-
if (!threadDescriptor) {
|
|
293
|
-
this.log.error(`Thread not found: ${worker}`);
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
this.log.info(`Thread ready: ${threadDescriptor.name}`);
|
|
297
|
-
const communicationPort = node_worker_threads.workerData?.port || node_worker_threads.parentPort;
|
|
298
|
-
if (!communicationPort) {
|
|
299
|
-
this.log.error("No communication port available");
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
communicationPort.on("message", async (message) => {
|
|
303
|
-
if (message.type === "execute") try {
|
|
304
|
-
this.log.debug(`Executing thread handler: ${threadDescriptor.name}`);
|
|
305
|
-
const result = await threadDescriptor.options.handler();
|
|
306
|
-
communicationPort.postMessage({
|
|
307
|
-
id: message.id,
|
|
308
|
-
type: "response",
|
|
309
|
-
data: result
|
|
310
|
-
});
|
|
311
|
-
} catch (error) {
|
|
312
|
-
this.log.error(`Thread execution error: ${error}`);
|
|
313
|
-
communicationPort.postMessage({
|
|
314
|
-
id: message.id,
|
|
315
|
-
type: "error",
|
|
316
|
-
error: error instanceof Error ? error.message : String(error)
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
communicationPort.postMessage({ type: "ready" });
|
|
321
|
-
}
|
|
322
|
-
});
|
|
323
|
-
static async cleanup() {
|
|
324
|
-
if (node_worker_threads.parentPort) node_worker_threads.parentPort.removeAllListeners();
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
//#endregion
|
|
329
|
-
//#region src/thread/index.ts
|
|
330
|
-
alepha.Alepha.prototype.isWorkerThread = function() {
|
|
331
|
-
return !!this.env.ALEPHA_WORKER;
|
|
332
|
-
};
|
|
333
|
-
/**
|
|
334
|
-
* Simple interface for managing worker threads in Alepha.
|
|
335
|
-
*
|
|
336
|
-
* @see {@link $thread}
|
|
337
|
-
* @module alepha.thread
|
|
338
|
-
*/
|
|
339
|
-
const AlephaThread = (0, alepha.$module)({
|
|
340
|
-
name: "alepha.thread",
|
|
341
|
-
descriptors: [$thread],
|
|
342
|
-
services: [ThreadProvider]
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
//#endregion
|
|
346
|
-
exports.$thread = $thread;
|
|
347
|
-
exports.AlephaThread = AlephaThread;
|
|
348
|
-
exports.ThreadDescriptor = ThreadDescriptor;
|
|
349
|
-
exports.ThreadProvider = ThreadProvider;
|
|
350
|
-
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["Descriptor","KIND","name: string","maxPoolSize: number","idleTimeout: number","script: string","MessageChannel","Worker","instance: ThreadInstance","message: ThreadMessage","t","alepha","workerData","parentPort"],"sources":["../../src/thread/descriptors/$thread.ts","../../src/thread/providers/ThreadProvider.ts","../../src/thread/index.ts"],"sourcesContent":["import { cpus } from \"node:os\";\nimport { MessageChannel, type MessagePort, Worker } from \"node:worker_threads\";\nimport type { TSchema } from \"alepha\";\nimport { createDescriptor, Descriptor, KIND, TypeBoxValue } from \"alepha\";\n\n/**\n * Creates a worker thread descriptor for offloading CPU-intensive tasks to separate threads.\n *\n * This descriptor enables you to run JavaScript code in Node.js worker threads, allowing you to\n * leverage multiple CPU cores and avoid blocking the main event loop. It provides a pool-based\n * approach with intelligent thread reuse and automatic lifecycle management.\n *\n * **Key Features**\n *\n * - **Thread Pool Management**: Automatically manages a pool of worker threads with configurable limits\n * - **Thread Reuse**: Reuses existing threads to avoid expensive initialization overhead\n * - **Idle Cleanup**: Automatically terminates unused threads after a configurable timeout\n * - **Type-Safe Communication**: Optional TypeBox schema validation for data passed to threads\n * - **CPU-Aware Defaults**: Pool size defaults to CPU count × 2 for optimal performance\n * - **Error Handling**: Proper error propagation and thread cleanup on failures\n *\n * **Use Cases**\n *\n * Perfect for CPU-intensive tasks that would otherwise block the main thread:\n * - Image/video processing\n * - Data transformation and analysis\n * - Cryptographic operations\n * - Heavy computations and algorithms\n * - Background data processing\n *\n * @example\n * **Basic thread usage:**\n * ```ts\n * import { $thread } from \"alepha/thread\";\n *\n * class DataProcessor {\n * heavyComputation = $thread({\n * name: \"compute\",\n * handler: async () => {\n * // This runs in a separate worker thread\n * let result = 0;\n * for (let i = 0; i < 1000000; i++) {\n * result += Math.sqrt(i);\n * }\n * return { result, timestamp: Date.now() };\n * }\n * });\n *\n * async processData() {\n * // Execute in worker thread without blocking main thread\n * const result = await this.heavyComputation.execute();\n * console.log(`Computation result: ${result.result}`);\n * }\n * }\n * ```\n *\n * @example\n * **Configured thread pool with custom settings:**\n * ```ts\n * class ImageProcessor {\n * imageProcessor = $thread({\n * name: \"image-processing\",\n * maxPoolSize: 4, // Limit to 4 concurrent threads\n * idleTimeout: 30000, // Clean up idle threads after 30 seconds\n * handler: async () => {\n * // CPU-intensive image processing logic\n * return await processImageData();\n * }\n * });\n * }\n * ```\n *\n * @example\n * **Thread with data validation:**\n * ```ts\n * import { t } from \"alepha\";\n *\n * class CryptoService {\n * encrypt = $thread({\n * name: \"encryption\",\n * handler: async () => {\n * // Perform encryption operations\n * return await encryptData();\n * }\n * });\n *\n * async encryptSensitiveData(data: { text: string; key: string }) {\n * // Validate input data before sending to thread\n * const schema = t.object({\n * text: t.text(),\n * key: t.text()\n * });\n *\n * return await this.encrypt.execute(data, schema);\n * }\n * }\n * ```\n *\n * @example\n * **Parallel processing with multiple threads:**\n * ```ts\n * class BatchProcessor {\n * processor = $thread({\n * name: \"batch-worker\",\n * maxPoolSize: 8, // Allow up to 8 concurrent workers\n * handler: async () => {\n * return await processBatchItem();\n * }\n * });\n *\n * async processBatch(items: any[]) {\n * // Process multiple items in parallel across different threads\n * const promises = items.map(() => this.processor.execute());\n * const results = await Promise.all(promises);\n * return results;\n * }\n * }\n * ```\n */\nexport const $thread = (options: ThreadDescriptorOptions): ThreadDescriptor => {\n return createDescriptor(ThreadDescriptor, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ThreadDescriptorOptions {\n /**\n * Unique name for this thread worker.\n *\n * Used for:\n * - Thread pool identification (threads with same name share the same pool)\n * - Logging and debugging\n * - Error messages and stack traces\n *\n * If not provided, defaults to the property key of the descriptor.\n *\n * @example \"image-processor\"\n * @example \"crypto-worker\"\n * @example \"data-analysis\"\n */\n name?: string;\n\n /**\n * The function to execute in the worker thread.\n *\n * This function:\n * - Runs in a separate Node.js worker thread\n * - Should contain the CPU-intensive logic\n * - Can be async and return any serializable data\n * - Has access to standard Node.js APIs and modules\n * - Cannot directly access the main thread's memory or variables\n *\n * **Important**: The handler function is serialized and sent to the worker thread,\n * so it cannot reference variables from the parent scope (closures won't work).\n * All required data must be passed via the `execute()` method.\n *\n * @example\n * ```ts\n * handler: async () => {\n * // CPU-intensive work here\n * const result = performComplexCalculation();\n * return { result, completed: Date.now() };\n * }\n * ```\n */\n handler: () => any | Promise<any>;\n\n /**\n * Maximum number of worker threads in the pool.\n *\n * Controls how many threads can run concurrently for this named thread worker.\n * When all threads are busy, additional `execute()` calls will queue until a thread becomes available.\n *\n * **Default**: `cpus().length * 2` (number of CPU cores × 2)\n *\n * **Guidelines**:\n * - For CPU-bound tasks: Set to number of CPU cores\n * - For I/O-bound tasks in workers: Can be higher (2x CPU cores)\n * - For memory-intensive tasks: Set lower to avoid memory pressure\n * - For short-lived tasks: Can be higher for better throughput\n *\n * @default cpus().length * 2\n * @example 4 // Limit to 4 concurrent threads\n * @example 1 // Single worker thread (sequential processing)\n * @example 16 // High concurrency for lightweight tasks\n */\n maxPoolSize?: number;\n\n /**\n * Time in milliseconds before idle worker threads are terminated.\n *\n * When a worker thread has been idle (not executing any tasks) for this duration,\n * it will be automatically terminated to free up system resources. New threads\n * will be created as needed when new tasks are submitted.\n *\n * **Default**: 60000 (60 seconds)\n *\n * **Considerations**:\n * - Shorter timeouts: Save memory but increase thread creation overhead\n * - Longer timeouts: Keep threads ready but consume more memory\n * - Very short timeouts may cause constant thread creation/destruction\n *\n * @default 60000\n * @example 30000 // 30 seconds\n * @example 120000 // 2 minutes\n * @example 5000 // 5 seconds (for very memory-constrained environments)\n */\n idleTimeout?: number;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ThreadDescriptor extends Descriptor<ThreadDescriptorOptions> {\n protected readonly script = process.argv[1];\n static readonly globalPool = new Map<string, ThreadPool>();\n\n public get name(): string {\n return this.options.name || this.config.propertyKey;\n }\n\n public get maxPoolSize(): number {\n return this.options.maxPoolSize || cpus().length * 2;\n }\n\n public get idleTimeout(): number {\n return this.options.idleTimeout || 60000; // 1 minute default\n }\n\n private getPool(): ThreadPool {\n if (!ThreadDescriptor.globalPool.has(this.name)) {\n ThreadDescriptor.globalPool.set(\n this.name,\n new ThreadPool(\n this.name,\n this.maxPoolSize,\n this.idleTimeout,\n this.script,\n ),\n );\n }\n return ThreadDescriptor.globalPool.get(this.name)!;\n }\n\n public async execute<T = any>(data?: any, schema?: TSchema): Promise<T> {\n if (schema && data) {\n try {\n TypeBoxValue.Decode(schema, data);\n } catch (error) {\n throw new Error(\n `Invalid data: ${error instanceof Error ? error.message : error}`,\n );\n }\n }\n\n const pool = this.getPool();\n return await pool.execute<T>(data);\n }\n\n public async create(): Promise<void> {\n const pool = this.getPool();\n await pool.warmUp();\n }\n\n public async terminate(): Promise<void> {\n const pool = this.getPool();\n await pool.terminate();\n ThreadDescriptor.globalPool.delete(this.name);\n }\n}\n\n$thread[KIND] = ThreadDescriptor;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ninterface ThreadMessage<T = any> {\n id: string;\n type: \"execute\" | \"response\" | \"error\";\n data?: T;\n error?: string;\n}\n\ninterface ThreadInstance {\n worker: Worker;\n port: MessagePort;\n busy: boolean;\n lastUsed: number;\n pendingMessages: Map<\n string,\n { resolve: (value: any) => void; reject: (error: Error) => void }\n >;\n}\n\nclass ThreadPool {\n private instances: ThreadInstance[] = [];\n private queue: Array<{\n data: any;\n resolve: (value: any) => void;\n reject: (error: Error) => void;\n }> = [];\n private idleTimer?: NodeJS.Timeout;\n\n constructor(\n private readonly name: string,\n private readonly maxPoolSize: number,\n private readonly idleTimeout: number,\n private readonly script: string,\n ) {}\n\n async warmUp(): Promise<void> {\n if (this.instances.length === 0) {\n await this.createInstance();\n }\n }\n\n private async createInstance(): Promise<ThreadInstance> {\n const { port1, port2 } = new MessageChannel();\n\n const worker = new Worker(this.script, {\n env: {\n ...process.env,\n ALEPHA_WORKER: this.name,\n APP_NAME: \"WORKER\",\n },\n workerData: { port: port2 },\n transferList: [port2],\n });\n\n const instance: ThreadInstance = {\n worker,\n port: port1,\n busy: false,\n lastUsed: Date.now(),\n pendingMessages: new Map(),\n };\n\n instance.port.on(\"message\", (message: ThreadMessage) => {\n if (message.type === \"response\" || message.type === \"error\") {\n const pending = instance.pendingMessages.get(message.id);\n if (pending) {\n instance.pendingMessages.delete(message.id);\n instance.busy = false;\n instance.lastUsed = Date.now();\n\n if (message.type === \"error\") {\n pending.reject(new Error(message.error));\n } else {\n pending.resolve(message.data);\n }\n\n this.processQueue();\n }\n }\n });\n\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(\n () => reject(new Error(\"Thread initialization timeout\")),\n 5000,\n );\n\n worker.once(\"online\", () => {\n clearTimeout(timeout);\n resolve();\n });\n\n worker.once(\"error\", (error) => {\n clearTimeout(timeout);\n reject(error);\n });\n });\n\n this.instances.push(instance);\n this.resetIdleTimer();\n\n return instance;\n }\n\n async execute<T = any>(data?: any): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n this.queue.push({ data, resolve, reject });\n this.processQueue();\n });\n }\n\n private async processQueue(): Promise<void> {\n if (this.queue.length === 0) {\n return;\n }\n\n let instance = this.instances.find((i) => !i.busy);\n\n if (!instance && this.instances.length < this.maxPoolSize) {\n try {\n instance = await this.createInstance();\n } catch (error) {\n const { reject } = this.queue.shift()!;\n reject(\n error instanceof Error\n ? error\n : new Error(\"Failed to create thread instance\"),\n );\n return;\n }\n }\n\n if (!instance) {\n return; // Wait for an instance to become available\n }\n\n const { data, resolve, reject } = this.queue.shift()!;\n const messageId = `${Date.now()}-${Math.random()}`;\n\n instance.busy = true;\n instance.pendingMessages.set(messageId, { resolve, reject });\n\n const message: ThreadMessage = {\n id: messageId,\n type: \"execute\",\n data,\n };\n\n instance.port.postMessage(message);\n }\n\n private resetIdleTimer(): void {\n if (this.idleTimer) {\n clearTimeout(this.idleTimer);\n }\n\n this.idleTimer = setTimeout(() => {\n this.cleanupIdleInstances();\n }, this.idleTimeout);\n }\n\n private cleanupIdleInstances(): void {\n const now = Date.now();\n const instancesToRemove = this.instances.filter(\n (instance) =>\n !instance.busy && now - instance.lastUsed > this.idleTimeout,\n );\n\n for (const instance of instancesToRemove) {\n const index = this.instances.indexOf(instance);\n if (index > -1) {\n this.instances.splice(index, 1);\n instance.port.close();\n void instance.worker.terminate();\n }\n }\n\n if (this.instances.length > 0) {\n this.resetIdleTimer();\n }\n }\n\n async terminate(): Promise<void> {\n if (this.idleTimer) {\n clearTimeout(this.idleTimer);\n }\n\n await Promise.all(\n this.instances.map(async (instance) => {\n instance.port.close();\n await instance.worker.terminate();\n }),\n );\n\n this.instances = [];\n this.queue = [];\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n","import { parentPort, workerData } from \"node:worker_threads\";\nimport { $env, $hook, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { $thread } from \"../descriptors/$thread.ts\";\n\ninterface ThreadMessage<T = any> {\n id: string;\n type: \"execute\" | \"response\" | \"error\" | \"ready\";\n data?: T;\n error?: string;\n}\n\nexport class ThreadProvider {\n protected readonly log = $logger();\n protected readonly env = $env(\n t.object({\n ALEPHA_WORKER: t.optional(t.text()),\n }),\n );\n\n protected readonly ready = $hook({\n on: \"ready\",\n handler: async (alepha) => {\n const worker = this.env.ALEPHA_WORKER;\n if (!worker) {\n return;\n }\n\n const threads = alepha.descriptors($thread);\n const threadDescriptor = threads.find((thread) => thread.name === worker);\n\n if (!threadDescriptor) {\n this.log.error(`Thread not found: ${worker}`);\n return;\n }\n\n this.log.info(`Thread ready: ${threadDescriptor.name}`);\n\n // Use the message channel port from worker data if available, fallback to parentPort\n const communicationPort = workerData?.port || parentPort;\n\n if (!communicationPort) {\n this.log.error(\"No communication port available\");\n return;\n }\n\n // Set up message handling\n communicationPort.on(\"message\", async (message: ThreadMessage) => {\n if (message.type === \"execute\") {\n try {\n this.log.debug(\n `Executing thread handler: ${threadDescriptor.name}`,\n );\n const result = await threadDescriptor.options.handler();\n\n communicationPort.postMessage({\n id: message.id,\n type: \"response\",\n data: result,\n } as ThreadMessage);\n } catch (error) {\n this.log.error(`Thread execution error: ${error}`);\n\n communicationPort.postMessage({\n id: message.id,\n type: \"error\",\n error: error instanceof Error ? error.message : String(error),\n } as ThreadMessage);\n }\n }\n });\n\n // Signal that the worker is ready\n communicationPort.postMessage({ type: \"ready\" } as ThreadMessage);\n },\n });\n\n public static async cleanup(): Promise<void> {\n if (parentPort) {\n parentPort.removeAllListeners();\n }\n }\n}\n","import { $module, Alepha } from \"alepha\";\nimport { $thread } from \"./descriptors/$thread.ts\";\nimport { ThreadProvider } from \"./providers/ThreadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./descriptors/$thread.ts\";\nexport * from \"./providers/ThreadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Alepha {\n isWorkerThread(): boolean;\n }\n}\n\nAlepha.prototype.isWorkerThread = function (this: Alepha): boolean {\n return !!this.env.ALEPHA_WORKER;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Simple interface for managing worker threads in Alepha.\n *\n * @see {@link $thread}\n * @module alepha.thread\n */\nexport const AlephaThread = $module({\n name: \"alepha.thread\",\n descriptors: [$thread],\n services: [ThreadProvider],\n});\n"],"mappings":"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"}
|
package/dist/thread/index.d.cts
DELETED
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
import * as alepha1 from "alepha";
|
|
2
|
-
import { Descriptor, KIND, TSchema } from "alepha";
|
|
3
|
-
import * as alepha_logger0 from "alepha/logger";
|
|
4
|
-
|
|
5
|
-
//#region src/thread/descriptors/$thread.d.ts
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Creates a worker thread descriptor for offloading CPU-intensive tasks to separate threads.
|
|
9
|
-
*
|
|
10
|
-
* This descriptor enables you to run JavaScript code in Node.js worker threads, allowing you to
|
|
11
|
-
* leverage multiple CPU cores and avoid blocking the main event loop. It provides a pool-based
|
|
12
|
-
* approach with intelligent thread reuse and automatic lifecycle management.
|
|
13
|
-
*
|
|
14
|
-
* **Key Features**
|
|
15
|
-
*
|
|
16
|
-
* - **Thread Pool Management**: Automatically manages a pool of worker threads with configurable limits
|
|
17
|
-
* - **Thread Reuse**: Reuses existing threads to avoid expensive initialization overhead
|
|
18
|
-
* - **Idle Cleanup**: Automatically terminates unused threads after a configurable timeout
|
|
19
|
-
* - **Type-Safe Communication**: Optional TypeBox schema validation for data passed to threads
|
|
20
|
-
* - **CPU-Aware Defaults**: Pool size defaults to CPU count × 2 for optimal performance
|
|
21
|
-
* - **Error Handling**: Proper error propagation and thread cleanup on failures
|
|
22
|
-
*
|
|
23
|
-
* **Use Cases**
|
|
24
|
-
*
|
|
25
|
-
* Perfect for CPU-intensive tasks that would otherwise block the main thread:
|
|
26
|
-
* - Image/video processing
|
|
27
|
-
* - Data transformation and analysis
|
|
28
|
-
* - Cryptographic operations
|
|
29
|
-
* - Heavy computations and algorithms
|
|
30
|
-
* - Background data processing
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* **Basic thread usage:**
|
|
34
|
-
* ```ts
|
|
35
|
-
* import { $thread } from "alepha/thread";
|
|
36
|
-
*
|
|
37
|
-
* class DataProcessor {
|
|
38
|
-
* heavyComputation = $thread({
|
|
39
|
-
* name: "compute",
|
|
40
|
-
* handler: async () => {
|
|
41
|
-
* // This runs in a separate worker thread
|
|
42
|
-
* let result = 0;
|
|
43
|
-
* for (let i = 0; i < 1000000; i++) {
|
|
44
|
-
* result += Math.sqrt(i);
|
|
45
|
-
* }
|
|
46
|
-
* return { result, timestamp: Date.now() };
|
|
47
|
-
* }
|
|
48
|
-
* });
|
|
49
|
-
*
|
|
50
|
-
* async processData() {
|
|
51
|
-
* // Execute in worker thread without blocking main thread
|
|
52
|
-
* const result = await this.heavyComputation.execute();
|
|
53
|
-
* console.log(`Computation result: ${result.result}`);
|
|
54
|
-
* }
|
|
55
|
-
* }
|
|
56
|
-
* ```
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* **Configured thread pool with custom settings:**
|
|
60
|
-
* ```ts
|
|
61
|
-
* class ImageProcessor {
|
|
62
|
-
* imageProcessor = $thread({
|
|
63
|
-
* name: "image-processing",
|
|
64
|
-
* maxPoolSize: 4, // Limit to 4 concurrent threads
|
|
65
|
-
* idleTimeout: 30000, // Clean up idle threads after 30 seconds
|
|
66
|
-
* handler: async () => {
|
|
67
|
-
* // CPU-intensive image processing logic
|
|
68
|
-
* return await processImageData();
|
|
69
|
-
* }
|
|
70
|
-
* });
|
|
71
|
-
* }
|
|
72
|
-
* ```
|
|
73
|
-
*
|
|
74
|
-
* @example
|
|
75
|
-
* **Thread with data validation:**
|
|
76
|
-
* ```ts
|
|
77
|
-
* import { t } from "alepha";
|
|
78
|
-
*
|
|
79
|
-
* class CryptoService {
|
|
80
|
-
* encrypt = $thread({
|
|
81
|
-
* name: "encryption",
|
|
82
|
-
* handler: async () => {
|
|
83
|
-
* // Perform encryption operations
|
|
84
|
-
* return await encryptData();
|
|
85
|
-
* }
|
|
86
|
-
* });
|
|
87
|
-
*
|
|
88
|
-
* async encryptSensitiveData(data: { text: string; key: string }) {
|
|
89
|
-
* // Validate input data before sending to thread
|
|
90
|
-
* const schema = t.object({
|
|
91
|
-
* text: t.text(),
|
|
92
|
-
* key: t.text()
|
|
93
|
-
* });
|
|
94
|
-
*
|
|
95
|
-
* return await this.encrypt.execute(data, schema);
|
|
96
|
-
* }
|
|
97
|
-
* }
|
|
98
|
-
* ```
|
|
99
|
-
*
|
|
100
|
-
* @example
|
|
101
|
-
* **Parallel processing with multiple threads:**
|
|
102
|
-
* ```ts
|
|
103
|
-
* class BatchProcessor {
|
|
104
|
-
* processor = $thread({
|
|
105
|
-
* name: "batch-worker",
|
|
106
|
-
* maxPoolSize: 8, // Allow up to 8 concurrent workers
|
|
107
|
-
* handler: async () => {
|
|
108
|
-
* return await processBatchItem();
|
|
109
|
-
* }
|
|
110
|
-
* });
|
|
111
|
-
*
|
|
112
|
-
* async processBatch(items: any[]) {
|
|
113
|
-
* // Process multiple items in parallel across different threads
|
|
114
|
-
* const promises = items.map(() => this.processor.execute());
|
|
115
|
-
* const results = await Promise.all(promises);
|
|
116
|
-
* return results;
|
|
117
|
-
* }
|
|
118
|
-
* }
|
|
119
|
-
* ```
|
|
120
|
-
*/
|
|
121
|
-
declare const $thread: {
|
|
122
|
-
(options: ThreadDescriptorOptions): ThreadDescriptor;
|
|
123
|
-
[KIND]: typeof ThreadDescriptor;
|
|
124
|
-
};
|
|
125
|
-
interface ThreadDescriptorOptions {
|
|
126
|
-
/**
|
|
127
|
-
* Unique name for this thread worker.
|
|
128
|
-
*
|
|
129
|
-
* Used for:
|
|
130
|
-
* - Thread pool identification (threads with same name share the same pool)
|
|
131
|
-
* - Logging and debugging
|
|
132
|
-
* - Error messages and stack traces
|
|
133
|
-
*
|
|
134
|
-
* If not provided, defaults to the property key of the descriptor.
|
|
135
|
-
*
|
|
136
|
-
* @example "image-processor"
|
|
137
|
-
* @example "crypto-worker"
|
|
138
|
-
* @example "data-analysis"
|
|
139
|
-
*/
|
|
140
|
-
name?: string;
|
|
141
|
-
/**
|
|
142
|
-
* The function to execute in the worker thread.
|
|
143
|
-
*
|
|
144
|
-
* This function:
|
|
145
|
-
* - Runs in a separate Node.js worker thread
|
|
146
|
-
* - Should contain the CPU-intensive logic
|
|
147
|
-
* - Can be async and return any serializable data
|
|
148
|
-
* - Has access to standard Node.js APIs and modules
|
|
149
|
-
* - Cannot directly access the main thread's memory or variables
|
|
150
|
-
*
|
|
151
|
-
* **Important**: The handler function is serialized and sent to the worker thread,
|
|
152
|
-
* so it cannot reference variables from the parent scope (closures won't work).
|
|
153
|
-
* All required data must be passed via the `execute()` method.
|
|
154
|
-
*
|
|
155
|
-
* @example
|
|
156
|
-
* ```ts
|
|
157
|
-
* handler: async () => {
|
|
158
|
-
* // CPU-intensive work here
|
|
159
|
-
* const result = performComplexCalculation();
|
|
160
|
-
* return { result, completed: Date.now() };
|
|
161
|
-
* }
|
|
162
|
-
* ```
|
|
163
|
-
*/
|
|
164
|
-
handler: () => any | Promise<any>;
|
|
165
|
-
/**
|
|
166
|
-
* Maximum number of worker threads in the pool.
|
|
167
|
-
*
|
|
168
|
-
* Controls how many threads can run concurrently for this named thread worker.
|
|
169
|
-
* When all threads are busy, additional `execute()` calls will queue until a thread becomes available.
|
|
170
|
-
*
|
|
171
|
-
* **Default**: `cpus().length * 2` (number of CPU cores × 2)
|
|
172
|
-
*
|
|
173
|
-
* **Guidelines**:
|
|
174
|
-
* - For CPU-bound tasks: Set to number of CPU cores
|
|
175
|
-
* - For I/O-bound tasks in workers: Can be higher (2x CPU cores)
|
|
176
|
-
* - For memory-intensive tasks: Set lower to avoid memory pressure
|
|
177
|
-
* - For short-lived tasks: Can be higher for better throughput
|
|
178
|
-
*
|
|
179
|
-
* @default cpus().length * 2
|
|
180
|
-
* @example 4 // Limit to 4 concurrent threads
|
|
181
|
-
* @example 1 // Single worker thread (sequential processing)
|
|
182
|
-
* @example 16 // High concurrency for lightweight tasks
|
|
183
|
-
*/
|
|
184
|
-
maxPoolSize?: number;
|
|
185
|
-
/**
|
|
186
|
-
* Time in milliseconds before idle worker threads are terminated.
|
|
187
|
-
*
|
|
188
|
-
* When a worker thread has been idle (not executing any tasks) for this duration,
|
|
189
|
-
* it will be automatically terminated to free up system resources. New threads
|
|
190
|
-
* will be created as needed when new tasks are submitted.
|
|
191
|
-
*
|
|
192
|
-
* **Default**: 60000 (60 seconds)
|
|
193
|
-
*
|
|
194
|
-
* **Considerations**:
|
|
195
|
-
* - Shorter timeouts: Save memory but increase thread creation overhead
|
|
196
|
-
* - Longer timeouts: Keep threads ready but consume more memory
|
|
197
|
-
* - Very short timeouts may cause constant thread creation/destruction
|
|
198
|
-
*
|
|
199
|
-
* @default 60000
|
|
200
|
-
* @example 30000 // 30 seconds
|
|
201
|
-
* @example 120000 // 2 minutes
|
|
202
|
-
* @example 5000 // 5 seconds (for very memory-constrained environments)
|
|
203
|
-
*/
|
|
204
|
-
idleTimeout?: number;
|
|
205
|
-
}
|
|
206
|
-
declare class ThreadDescriptor extends Descriptor<ThreadDescriptorOptions> {
|
|
207
|
-
protected readonly script: string;
|
|
208
|
-
static readonly globalPool: Map<string, ThreadPool>;
|
|
209
|
-
get name(): string;
|
|
210
|
-
get maxPoolSize(): number;
|
|
211
|
-
get idleTimeout(): number;
|
|
212
|
-
private getPool;
|
|
213
|
-
execute<T = any>(data?: any, schema?: TSchema): Promise<T>;
|
|
214
|
-
create(): Promise<void>;
|
|
215
|
-
terminate(): Promise<void>;
|
|
216
|
-
}
|
|
217
|
-
declare class ThreadPool {
|
|
218
|
-
private readonly name;
|
|
219
|
-
private readonly maxPoolSize;
|
|
220
|
-
private readonly idleTimeout;
|
|
221
|
-
private readonly script;
|
|
222
|
-
private instances;
|
|
223
|
-
private queue;
|
|
224
|
-
private idleTimer?;
|
|
225
|
-
constructor(name: string, maxPoolSize: number, idleTimeout: number, script: string);
|
|
226
|
-
warmUp(): Promise<void>;
|
|
227
|
-
private createInstance;
|
|
228
|
-
execute<T = any>(data?: any): Promise<T>;
|
|
229
|
-
private processQueue;
|
|
230
|
-
private resetIdleTimer;
|
|
231
|
-
private cleanupIdleInstances;
|
|
232
|
-
terminate(): Promise<void>;
|
|
233
|
-
}
|
|
234
|
-
//#endregion
|
|
235
|
-
//#region src/thread/providers/ThreadProvider.d.ts
|
|
236
|
-
declare class ThreadProvider {
|
|
237
|
-
protected readonly log: alepha_logger0.Logger;
|
|
238
|
-
protected readonly env: {
|
|
239
|
-
ALEPHA_WORKER?: string | undefined;
|
|
240
|
-
};
|
|
241
|
-
protected readonly ready: alepha1.HookDescriptor<"ready">;
|
|
242
|
-
static cleanup(): Promise<void>;
|
|
243
|
-
}
|
|
244
|
-
//#endregion
|
|
245
|
-
//#region src/thread/index.d.ts
|
|
246
|
-
declare module "alepha" {
|
|
247
|
-
interface Alepha {
|
|
248
|
-
isWorkerThread(): boolean;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
|
-
* Simple interface for managing worker threads in Alepha.
|
|
253
|
-
*
|
|
254
|
-
* @see {@link $thread}
|
|
255
|
-
* @module alepha.thread
|
|
256
|
-
*/
|
|
257
|
-
declare const AlephaThread: alepha1.Service<alepha1.Module>;
|
|
258
|
-
//#endregion
|
|
259
|
-
export { $thread, AlephaThread, ThreadDescriptor, ThreadDescriptorOptions, ThreadProvider };
|
|
260
|
-
//# sourceMappingURL=index.d.cts.map
|