alepha 0.15.0 → 0.15.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/README.md +43 -98
- package/dist/api/audits/index.d.ts +240 -240
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +2 -2
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.d.ts +185 -185
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +2 -2
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +245 -245
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/notifications/index.browser.js +4 -4
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +74 -74
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +4 -4
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.d.ts +221 -221
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/users/index.d.ts +1632 -1631
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +26 -34
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +132 -132
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/batch/index.d.ts +122 -122
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/bucket/index.d.ts +163 -163
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/cache/core/index.d.ts +46 -46
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cache/redis/index.js +2 -2
- package/dist/cache/redis/index.js.map +1 -1
- package/dist/cli/index.d.ts +5933 -201
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +609 -169
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +296 -296
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +19 -19
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +268 -79
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +768 -694
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +268 -79
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +268 -79
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts +44 -44
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/email/index.d.ts +25 -25
- package/dist/email/index.d.ts.map +1 -1
- package/dist/fake/index.d.ts +5409 -5409
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/fake/index.js +22 -22
- package/dist/fake/index.js.map +1 -1
- package/dist/file/index.d.ts +435 -435
- package/dist/file/index.d.ts.map +1 -1
- package/dist/lock/core/index.d.ts +208 -208
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/redis/index.d.ts.map +1 -1
- package/dist/logger/index.d.ts +24 -24
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +1 -5
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +216 -198
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +28 -4
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/index.browser.js +9 -9
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.bun.js +83 -76
- package/dist/orm/index.bun.js.map +1 -1
- package/dist/orm/index.d.ts +961 -960
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +88 -81
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +244 -244
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/redis/index.d.ts.map +1 -1
- package/dist/redis/index.d.ts +105 -105
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/retry/index.d.ts +69 -69
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/router/index.d.ts +6 -6
- package/dist/router/index.d.ts.map +1 -1
- package/dist/scheduler/index.d.ts +108 -26
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +393 -1
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.d.ts +532 -209
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +1422 -11
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +1296 -271
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +1249 -18
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.d.ts +56 -56
- package/dist/server/cache/index.d.ts.map +1 -1
- package/dist/server/compress/index.d.ts +3 -3
- package/dist/server/compress/index.d.ts.map +1 -1
- package/dist/server/cookies/index.d.ts +6 -6
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/core/index.d.ts +196 -186
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +43 -27
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +11 -11
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/helmet/index.d.ts +2 -2
- package/dist/server/helmet/index.d.ts.map +1 -1
- package/dist/server/links/index.browser.js +9 -1
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +83 -83
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +13 -5
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +514 -1
- package/dist/server/metrics/index.d.ts.map +1 -1
- package/dist/server/metrics/index.js +4462 -4
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/multipart/index.d.ts +6 -6
- package/dist/server/multipart/index.d.ts.map +1 -1
- package/dist/server/proxy/index.d.ts +102 -102
- package/dist/server/proxy/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.d.ts +16 -16
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/static/index.d.ts +44 -44
- package/dist/server/static/index.d.ts.map +1 -1
- package/dist/server/swagger/index.d.ts +47 -47
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/sms/index.d.ts +11 -11
- package/dist/sms/index.d.ts.map +1 -1
- package/dist/sms/index.js +3 -3
- package/dist/sms/index.js.map +1 -1
- package/dist/thread/index.d.ts +71 -71
- package/dist/thread/index.d.ts.map +1 -1
- package/dist/thread/index.js +2 -2
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/core/index.d.ts +318 -318
- package/dist/topic/core/index.d.ts.map +1 -1
- package/dist/topic/redis/index.d.ts +6 -6
- package/dist/topic/redis/index.d.ts.map +1 -1
- package/dist/vite/index.d.ts +2324 -1719
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +123 -475
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.browser.js +3 -3
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.d.ts +275 -275
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +3 -3
- package/dist/websocket/index.js.map +1 -1
- package/package.json +9 -9
- package/src/api/users/services/SessionService.ts +0 -10
- package/src/cli/apps/AlephaCli.ts +2 -2
- package/src/cli/apps/AlephaPackageBuilderCli.ts +9 -1
- package/src/cli/assets/apiHelloControllerTs.ts +2 -1
- package/src/cli/assets/biomeJson.ts +2 -1
- package/src/cli/assets/claudeMd.ts +9 -4
- package/src/cli/assets/dummySpecTs.ts +2 -1
- package/src/cli/assets/editorconfig.ts +2 -1
- package/src/cli/assets/mainBrowserTs.ts +2 -1
- package/src/cli/assets/mainCss.ts +24 -0
- package/src/cli/assets/tsconfigJson.ts +2 -1
- package/src/cli/assets/webAppRouterTs.ts +2 -1
- package/src/cli/assets/webHelloComponentTsx.ts +6 -2
- package/src/cli/atoms/appEntryOptions.ts +13 -0
- package/src/cli/atoms/buildOptions.ts +1 -1
- package/src/cli/atoms/changelogOptions.ts +1 -1
- package/src/cli/commands/build.ts +63 -47
- package/src/cli/commands/dev.ts +16 -33
- package/src/cli/commands/gen/env.ts +1 -1
- package/src/cli/commands/init.ts +17 -8
- package/src/cli/commands/lint.ts +1 -1
- package/src/cli/defineConfig.ts +9 -0
- package/src/cli/index.ts +2 -1
- package/src/cli/providers/AppEntryProvider.ts +131 -0
- package/src/cli/providers/ViteBuildProvider.ts +82 -0
- package/src/cli/providers/ViteDevServerProvider.ts +350 -0
- package/src/cli/providers/ViteTemplateProvider.ts +27 -0
- package/src/cli/services/AlephaCliUtils.ts +33 -2
- package/src/cli/services/PackageManagerUtils.ts +13 -6
- package/src/cli/services/ProjectScaffolder.ts +72 -49
- package/src/core/Alepha.ts +2 -8
- package/src/core/primitives/$module.ts +12 -0
- package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +257 -0
- package/src/core/providers/KeylessJsonSchemaCodec.ts +396 -14
- package/src/core/providers/SchemaValidator.spec.ts +236 -0
- package/src/logger/providers/PrettyFormatterProvider.ts +0 -9
- package/src/mcp/errors/McpError.ts +30 -0
- package/src/mcp/index.ts +3 -0
- package/src/mcp/transports/SseMcpTransport.ts +16 -6
- package/src/orm/providers/DrizzleKitProvider.ts +3 -5
- package/src/orm/services/Repository.ts +11 -0
- package/src/server/core/index.ts +1 -1
- package/src/server/core/providers/BunHttpServerProvider.ts +1 -1
- package/src/server/core/providers/NodeHttpServerProvider.spec.ts +125 -0
- package/src/server/core/providers/NodeHttpServerProvider.ts +71 -22
- package/src/server/core/providers/ServerLoggerProvider.ts +2 -2
- package/src/server/core/providers/ServerProvider.ts +9 -12
- package/src/server/links/atoms/apiLinksAtom.ts +7 -0
- package/src/server/links/index.browser.ts +2 -0
- package/src/server/links/index.ts +2 -0
- package/src/vite/index.ts +3 -2
- package/src/vite/tasks/buildClient.ts +0 -1
- package/src/vite/tasks/buildServer.ts +68 -21
- package/src/vite/tasks/copyAssets.ts +5 -4
- package/src/vite/tasks/generateSitemap.ts +64 -23
- package/src/vite/tasks/index.ts +0 -2
- package/src/vite/tasks/prerenderPages.ts +49 -24
- package/src/cli/assets/indexHtml.ts +0 -15
- package/src/cli/commands/format.ts +0 -23
- package/src/vite/helpers/boot.ts +0 -117
- package/src/vite/plugins/viteAlephaDev.ts +0 -177
- package/src/vite/tasks/devServer.ts +0 -71
- package/src/vite/tasks/runAlepha.ts +0 -270
- /package/dist/orm/{chunk-DtkW-qnP.js → chunk-DH6iiROE.js} +0 -0
package/dist/sms/index.d.ts
CHANGED
|
@@ -11,10 +11,10 @@ import { FileSystemProvider } from "alepha/file";
|
|
|
11
11
|
*/
|
|
12
12
|
declare abstract class SmsProvider {
|
|
13
13
|
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
* Send an SMS.
|
|
15
|
+
*
|
|
16
|
+
* @return Promise that resolves when the SMS is sent.
|
|
17
|
+
*/
|
|
18
18
|
abstract send(options: SmsSendOptions): Promise<void>;
|
|
19
19
|
}
|
|
20
20
|
type SmsSendOptions = {
|
|
@@ -57,8 +57,8 @@ declare class SmsPrimitive extends Primitive<SmsPrimitiveOptions> {
|
|
|
57
57
|
protected readonly provider: SmsProvider;
|
|
58
58
|
get name(): string;
|
|
59
59
|
/**
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
* Send an SMS using the configured provider.
|
|
61
|
+
*/
|
|
62
62
|
send(options: SmsSendOptions): Promise<void>;
|
|
63
63
|
protected $provider(): SmsProvider;
|
|
64
64
|
}
|
|
@@ -66,9 +66,9 @@ declare class SmsPrimitive extends Primitive<SmsPrimitiveOptions> {
|
|
|
66
66
|
//#region ../../src/sms/providers/LocalSmsProvider.d.ts
|
|
67
67
|
interface LocalSmsProviderOptions {
|
|
68
68
|
/**
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
* Directory to save SMS files.
|
|
70
|
+
* @default "node_modules/.alepha/sms" (relative to project root)
|
|
71
|
+
*/
|
|
72
72
|
directory?: string;
|
|
73
73
|
}
|
|
74
74
|
declare class LocalSmsProvider implements SmsProvider {
|
|
@@ -94,8 +94,8 @@ declare class MemorySmsProvider implements SmsProvider {
|
|
|
94
94
|
records: SmsRecord[];
|
|
95
95
|
send(options: SmsSendOptions): Promise<void>;
|
|
96
96
|
/**
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
* Get the last SMS sent (for testing purposes).
|
|
98
|
+
*/
|
|
99
99
|
get last(): SmsRecord | undefined;
|
|
100
100
|
}
|
|
101
101
|
//#endregion
|
package/dist/sms/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/sms/providers/SmsProvider.ts","../../src/sms/errors/SmsError.ts","../../src/sms/primitives/$sms.ts","../../src/sms/providers/LocalSmsProvider.ts","../../src/sms/providers/MemorySmsProvider.ts","../../src/sms/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/sms/providers/SmsProvider.ts","../../src/sms/errors/SmsError.ts","../../src/sms/primitives/$sms.ts","../../src/sms/providers/LocalSmsProvider.ts","../../src/sms/providers/MemorySmsProvider.ts","../../src/sms/index.ts"],"mappings":";;;;;;;;;;;uBAKsB,WAAA;EAAA;;;;;EAAA,SAMJ,IAAA,CAAK,OAAA,EAAS,cAAA,GAAiB,OAAA;AAAA;AAAA,KAGrC,cAAA;EACV,EAAA;EACA,OAAA;AAAA;;;cChBW,QAAA,SAAiB,KAAA;cAChB,OAAA,UAAiB,KAAA,GAAQ,KAAA;AAAA;;;cCW1B,IAAA;EAAA,WAAiB,mBAAA,GAAmB,YAAA;EAAA;;UAKhC,mBAAA;EACf,IAAA;EACA,QAAA,GAAW,iBAAA,CAAkB,WAAA;AAAA;;;;;;AFL/B;;;;;;;;ACdA;;;;cCyCa,YAAA,SAAqB,SAAA,CAAU,mBAAA;EAAA,mBACvB,QAAA,EAAQ,WAAA;EAAA,IAEhB,IAAA,CAAA;ED3C0B;;;ECkDxB,IAAA,CAAK,OAAA,EAAS,cAAA,GAAiB,OAAA;EAAA,UAoBlC,SAAA,CAAA,GAAa,WAAA;AAAA;;;UCjER,uBAAA;;;;AHDjB;EGME,SAAA;AAAA;AAAA,cAGW,gBAAA,YAA4B,WAAA;EAAA,mBACpB,GAAA,EADS,cAAA,CACN,MAAA;EAAA,mBACH,EAAA,EAAE,kBAAA;EAAA,mBACF,SAAA;cAEP,OAAA,GAAS,uBAAA;EAIR,IAAA,CAAK,OAAA,EAAS,cAAA,GAAiB,OAAA;EAsCrC,aAAA,CAAc,OAAA;IAAW,EAAA;IAAY,OAAA;EAAA;AAAA;;;UC1D7B,SAAA;EACf,EAAA;EACA,OAAA;EACA,MAAA,EAAQ,IAAA;AAAA;AAAA,cAGG,iBAAA,YAA6B,WAAA;EAAA,mBACrB,GAAA,EADU,cAAA,CACP,MAAA;EACf,OAAA,EAAS,SAAA;EAEH,IAAA,CAAK,OAAA,EAAS,cAAA,GAAiB,OAAA;EJF5B;;;EAAA,IIkBL,IAAA,CAAA,GAAQ,SAAA;AAAA;;;;YCZT,KAAA;IACR,aAAA;MACE,EAAA;MACA,QAAA;MACA,SAAA,EAAW,MAAA;MACX,QAAA,EAAU,WAAA;MACV,KAAA;IAAA;IAEF,UAAA;MACE,EAAA;MACA,QAAA;MACA,QAAA,EAAU,WAAA;IAAA;EAAA;AAAA;;;AJ5BhB;;;;;;;;cI6Ca,SAAA,EAAS,OAAA,CAAA,OAAA,CAUpB,OAAA,CAVoB,MAAA"}
|
package/dist/sms/index.js
CHANGED
|
@@ -131,12 +131,12 @@ var LocalSmsProvider = class {
|
|
|
131
131
|
});
|
|
132
132
|
}
|
|
133
133
|
} catch (error) {
|
|
134
|
-
const message
|
|
135
|
-
this.log.error(message
|
|
134
|
+
const message = `Failed to save SMS to local file: ${error instanceof Error ? error.message : String(error)}`;
|
|
135
|
+
this.log.error(message, {
|
|
136
136
|
to,
|
|
137
137
|
directory: this.directory
|
|
138
138
|
});
|
|
139
|
-
throw new SmsError(message
|
|
139
|
+
throw new SmsError(message, error instanceof Error ? error : void 0);
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
createSmsText(options) {
|
package/dist/sms/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["message"],"sources":["../../src/sms/providers/MemorySmsProvider.ts","../../src/sms/providers/SmsProvider.ts","../../src/sms/primitives/$sms.ts","../../src/sms/errors/SmsError.ts","../../src/sms/providers/LocalSmsProvider.ts","../../src/sms/index.ts"],"sourcesContent":["import { $logger } from \"alepha/logger\";\nimport type { SmsProvider, SmsSendOptions } from \"./SmsProvider.ts\";\n\nexport interface SmsRecord {\n to: string;\n message: string;\n sentAt: Date;\n}\n\nexport class MemorySmsProvider implements SmsProvider {\n protected readonly log = $logger();\n public records: SmsRecord[] = [];\n\n public async send(options: SmsSendOptions): Promise<void> {\n const { to, message } = options;\n this.log.debug(\"Sending SMS to memory store\", { to, message });\n\n for (const recipient of Array.isArray(to) ? to : [to]) {\n this.records.push({\n to: recipient,\n message,\n sentAt: new Date(),\n });\n }\n }\n\n /**\n * Get the last SMS sent (for testing purposes).\n */\n public get last(): SmsRecord | undefined {\n return this.records[this.records.length - 1];\n }\n}\n","/**\n * SMS provider interface.\n *\n * All methods are asynchronous and return promises.\n */\nexport abstract class SmsProvider {\n /**\n * Send an SMS.\n *\n * @return Promise that resolves when the SMS is sent.\n */\n public abstract send(options: SmsSendOptions): Promise<void>;\n}\n\nexport type SmsSendOptions = {\n to: string | string[];\n message: string;\n};\n","import {\n createPrimitive,\n type InstantiableClass,\n KIND,\n Primitive,\n} from \"alepha\";\nimport { MemorySmsProvider } from \"../providers/MemorySmsProvider.ts\";\nimport type { SmsSendOptions } from \"../providers/SmsProvider.ts\";\nimport { SmsProvider } from \"../providers/SmsProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const $sms = (options: SmsPrimitiveOptions = {}) =>\n createPrimitive(SmsPrimitive, options);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface SmsPrimitiveOptions {\n name?: string;\n provider?: InstantiableClass<SmsProvider> | \"memory\";\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * SMS primitive for sending SMS messages through various providers.\n *\n * Usage:\n * ```typescript\n * class MyService {\n * private readonly welcomeSms = $sms({ name: \"welcome\" });\n *\n * async sendWelcome(phoneNumber: string, userName: string) {\n * await this.welcomeSms.send({\n * to: phoneNumber,\n * message: `Hello ${userName}! Welcome to our service.`\n * });\n * }\n * }\n * ```\n */\nexport class SmsPrimitive extends Primitive<SmsPrimitiveOptions> {\n protected readonly provider = this.$provider();\n\n public get name() {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n /**\n * Send an SMS using the configured provider.\n */\n public async send(options: SmsSendOptions): Promise<void> {\n await this.alepha.events.emit(\"sms:sending\", {\n to: options.to,\n template: this.name,\n variables: {},\n provider: this.provider,\n abort: () => {\n throw new Error(\"SMS sending aborted by hook\");\n },\n });\n\n await this.provider.send(options);\n\n await this.alepha.events.emit(\"sms:sent\", {\n to: options.to,\n template: this.name,\n provider: this.provider,\n });\n }\n\n protected $provider(): SmsProvider {\n if (!this.options.provider) {\n return this.alepha.inject(SmsProvider);\n }\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemorySmsProvider);\n }\n return this.alepha.inject(this.options.provider);\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n$sms[KIND] = SmsPrimitive;\n","export class SmsError extends Error {\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"SmsError\";\n this.cause = cause;\n }\n}\n","import { $inject } from \"alepha\";\nimport { FileSystemProvider } from \"alepha/file\";\nimport { $logger } from \"alepha/logger\";\nimport { SmsError } from \"../errors/SmsError.ts\";\nimport type { SmsProvider, SmsSendOptions } from \"./SmsProvider.ts\";\n\nexport interface LocalSmsProviderOptions {\n /**\n * Directory to save SMS files.\n * @default \"node_modules/.alepha/sms\" (relative to project root)\n */\n directory?: string;\n}\n\nexport class LocalSmsProvider implements SmsProvider {\n protected readonly log = $logger();\n protected readonly fs = $inject(FileSystemProvider);\n protected readonly directory: string;\n\n constructor(options: LocalSmsProviderOptions = {}) {\n this.directory = options.directory ?? \"node_modules/.alepha/sms\";\n }\n\n public async send(options: SmsSendOptions): Promise<void> {\n const { to, message } = options;\n\n this.log.debug(\"Sending SMS to local file\", {\n to,\n message,\n directory: this.directory,\n });\n\n try {\n // Ensure directory exists\n await this.fs.mkdir(this.directory, { recursive: true });\n\n // Create filename: phone+date\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n for (const recipient of Array.isArray(to) ? to : [to]) {\n const sanitizedPhone = recipient.replace(/[^0-9+]/g, \"_\");\n const filename = `${sanitizedPhone}+${timestamp}.txt`;\n const filepath = this.fs.join(this.directory, filename);\n\n // Create text content\n const textContent = this.createSmsText({\n to: recipient,\n message,\n });\n\n // Write to file\n await this.fs.writeFile(filepath, textContent);\n\n this.log.info(\"SMS saved to local file\", { filepath, to });\n }\n } catch (error) {\n const message = `Failed to save SMS to local file: ${error instanceof Error ? error.message : String(error)}`;\n this.log.error(message, { to, directory: this.directory });\n throw new SmsError(message, error instanceof Error ? error : undefined);\n }\n }\n\n public createSmsText(options: { to: string; message: string }): string {\n const { to, message } = options;\n const timestamp = new Date().toISOString();\n\n return `SMS Message\n===========\n\nSent: ${timestamp}\nTo: ${to}\n\nMessage:\n--------\n${message}\n`;\n }\n}\n","import { $module } from \"alepha\";\nimport { $sms } from \"./primitives/$sms.ts\";\nimport { LocalSmsProvider } from \"./providers/LocalSmsProvider.ts\";\nimport { MemorySmsProvider } from \"./providers/MemorySmsProvider.ts\";\nimport { SmsProvider } from \"./providers/SmsProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./errors/SmsError.ts\";\nexport * from \"./primitives/$sms.ts\";\nexport * from \"./providers/LocalSmsProvider.ts\";\nexport * from \"./providers/MemorySmsProvider.ts\";\nexport * from \"./providers/SmsProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Hooks {\n \"sms:sending\": {\n to: string | string[];\n template: string;\n variables: Record<string, unknown>;\n provider: SmsProvider;\n abort(): void;\n };\n \"sms:sent\": {\n to: string | string[];\n template: string;\n provider: SmsProvider;\n };\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Provides SMS sending capabilities for Alepha applications with multiple provider backends.\n *\n * The SMS module enables declarative SMS sending through the `$sms` primitive, allowing you to send\n * text messages through different providers: memory (for testing) or local file system.\n * It supports automatic provider selection based on environment configuration.\n *\n * @see {@link SmsProvider}\n * @module alepha.sms\n */\nexport const AlephaSms = $module({\n name: \"alepha.sms\",\n primitives: [$sms],\n services: [SmsProvider, MemorySmsProvider, LocalSmsProvider],\n register: (alepha) =>\n alepha.with({\n optional: true,\n provide: SmsProvider,\n use: MemorySmsProvider,\n }),\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/sms/providers/MemorySmsProvider.ts","../../src/sms/providers/SmsProvider.ts","../../src/sms/primitives/$sms.ts","../../src/sms/errors/SmsError.ts","../../src/sms/providers/LocalSmsProvider.ts","../../src/sms/index.ts"],"sourcesContent":["import { $logger } from \"alepha/logger\";\nimport type { SmsProvider, SmsSendOptions } from \"./SmsProvider.ts\";\n\nexport interface SmsRecord {\n to: string;\n message: string;\n sentAt: Date;\n}\n\nexport class MemorySmsProvider implements SmsProvider {\n protected readonly log = $logger();\n public records: SmsRecord[] = [];\n\n public async send(options: SmsSendOptions): Promise<void> {\n const { to, message } = options;\n this.log.debug(\"Sending SMS to memory store\", { to, message });\n\n for (const recipient of Array.isArray(to) ? to : [to]) {\n this.records.push({\n to: recipient,\n message,\n sentAt: new Date(),\n });\n }\n }\n\n /**\n * Get the last SMS sent (for testing purposes).\n */\n public get last(): SmsRecord | undefined {\n return this.records[this.records.length - 1];\n }\n}\n","/**\n * SMS provider interface.\n *\n * All methods are asynchronous and return promises.\n */\nexport abstract class SmsProvider {\n /**\n * Send an SMS.\n *\n * @return Promise that resolves when the SMS is sent.\n */\n public abstract send(options: SmsSendOptions): Promise<void>;\n}\n\nexport type SmsSendOptions = {\n to: string | string[];\n message: string;\n};\n","import {\n createPrimitive,\n type InstantiableClass,\n KIND,\n Primitive,\n} from \"alepha\";\nimport { MemorySmsProvider } from \"../providers/MemorySmsProvider.ts\";\nimport type { SmsSendOptions } from \"../providers/SmsProvider.ts\";\nimport { SmsProvider } from \"../providers/SmsProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const $sms = (options: SmsPrimitiveOptions = {}) =>\n createPrimitive(SmsPrimitive, options);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface SmsPrimitiveOptions {\n name?: string;\n provider?: InstantiableClass<SmsProvider> | \"memory\";\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * SMS primitive for sending SMS messages through various providers.\n *\n * Usage:\n * ```typescript\n * class MyService {\n * private readonly welcomeSms = $sms({ name: \"welcome\" });\n *\n * async sendWelcome(phoneNumber: string, userName: string) {\n * await this.welcomeSms.send({\n * to: phoneNumber,\n * message: `Hello ${userName}! Welcome to our service.`\n * });\n * }\n * }\n * ```\n */\nexport class SmsPrimitive extends Primitive<SmsPrimitiveOptions> {\n protected readonly provider = this.$provider();\n\n public get name() {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n /**\n * Send an SMS using the configured provider.\n */\n public async send(options: SmsSendOptions): Promise<void> {\n await this.alepha.events.emit(\"sms:sending\", {\n to: options.to,\n template: this.name,\n variables: {},\n provider: this.provider,\n abort: () => {\n throw new Error(\"SMS sending aborted by hook\");\n },\n });\n\n await this.provider.send(options);\n\n await this.alepha.events.emit(\"sms:sent\", {\n to: options.to,\n template: this.name,\n provider: this.provider,\n });\n }\n\n protected $provider(): SmsProvider {\n if (!this.options.provider) {\n return this.alepha.inject(SmsProvider);\n }\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemorySmsProvider);\n }\n return this.alepha.inject(this.options.provider);\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n$sms[KIND] = SmsPrimitive;\n","export class SmsError extends Error {\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"SmsError\";\n this.cause = cause;\n }\n}\n","import { $inject } from \"alepha\";\nimport { FileSystemProvider } from \"alepha/file\";\nimport { $logger } from \"alepha/logger\";\nimport { SmsError } from \"../errors/SmsError.ts\";\nimport type { SmsProvider, SmsSendOptions } from \"./SmsProvider.ts\";\n\nexport interface LocalSmsProviderOptions {\n /**\n * Directory to save SMS files.\n * @default \"node_modules/.alepha/sms\" (relative to project root)\n */\n directory?: string;\n}\n\nexport class LocalSmsProvider implements SmsProvider {\n protected readonly log = $logger();\n protected readonly fs = $inject(FileSystemProvider);\n protected readonly directory: string;\n\n constructor(options: LocalSmsProviderOptions = {}) {\n this.directory = options.directory ?? \"node_modules/.alepha/sms\";\n }\n\n public async send(options: SmsSendOptions): Promise<void> {\n const { to, message } = options;\n\n this.log.debug(\"Sending SMS to local file\", {\n to,\n message,\n directory: this.directory,\n });\n\n try {\n // Ensure directory exists\n await this.fs.mkdir(this.directory, { recursive: true });\n\n // Create filename: phone+date\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n for (const recipient of Array.isArray(to) ? to : [to]) {\n const sanitizedPhone = recipient.replace(/[^0-9+]/g, \"_\");\n const filename = `${sanitizedPhone}+${timestamp}.txt`;\n const filepath = this.fs.join(this.directory, filename);\n\n // Create text content\n const textContent = this.createSmsText({\n to: recipient,\n message,\n });\n\n // Write to file\n await this.fs.writeFile(filepath, textContent);\n\n this.log.info(\"SMS saved to local file\", { filepath, to });\n }\n } catch (error) {\n const message = `Failed to save SMS to local file: ${error instanceof Error ? error.message : String(error)}`;\n this.log.error(message, { to, directory: this.directory });\n throw new SmsError(message, error instanceof Error ? error : undefined);\n }\n }\n\n public createSmsText(options: { to: string; message: string }): string {\n const { to, message } = options;\n const timestamp = new Date().toISOString();\n\n return `SMS Message\n===========\n\nSent: ${timestamp}\nTo: ${to}\n\nMessage:\n--------\n${message}\n`;\n }\n}\n","import { $module } from \"alepha\";\nimport { $sms } from \"./primitives/$sms.ts\";\nimport { LocalSmsProvider } from \"./providers/LocalSmsProvider.ts\";\nimport { MemorySmsProvider } from \"./providers/MemorySmsProvider.ts\";\nimport { SmsProvider } from \"./providers/SmsProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./errors/SmsError.ts\";\nexport * from \"./primitives/$sms.ts\";\nexport * from \"./providers/LocalSmsProvider.ts\";\nexport * from \"./providers/MemorySmsProvider.ts\";\nexport * from \"./providers/SmsProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Hooks {\n \"sms:sending\": {\n to: string | string[];\n template: string;\n variables: Record<string, unknown>;\n provider: SmsProvider;\n abort(): void;\n };\n \"sms:sent\": {\n to: string | string[];\n template: string;\n provider: SmsProvider;\n };\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Provides SMS sending capabilities for Alepha applications with multiple provider backends.\n *\n * The SMS module enables declarative SMS sending through the `$sms` primitive, allowing you to send\n * text messages through different providers: memory (for testing) or local file system.\n * It supports automatic provider selection based on environment configuration.\n *\n * @see {@link SmsProvider}\n * @module alepha.sms\n */\nexport const AlephaSms = $module({\n name: \"alepha.sms\",\n primitives: [$sms],\n services: [SmsProvider, MemorySmsProvider, LocalSmsProvider],\n register: (alepha) =>\n alepha.with({\n optional: true,\n provide: SmsProvider,\n use: MemorySmsProvider,\n }),\n});\n"],"mappings":"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"}
|
package/dist/thread/index.d.ts
CHANGED
|
@@ -123,83 +123,83 @@ declare const $thread: {
|
|
|
123
123
|
};
|
|
124
124
|
interface ThreadPrimitiveOptions {
|
|
125
125
|
/**
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
126
|
+
* Unique name for this thread worker.
|
|
127
|
+
*
|
|
128
|
+
* Used for:
|
|
129
|
+
* - Thread pool identification (threads with same name share the same pool)
|
|
130
|
+
* - Logging and debugging
|
|
131
|
+
* - Error messages and stack traces
|
|
132
|
+
*
|
|
133
|
+
* If not provided, defaults to the property key of the primitive.
|
|
134
|
+
*
|
|
135
|
+
* @example "image-processor"
|
|
136
|
+
* @example "crypto-worker"
|
|
137
|
+
* @example "data-analysis"
|
|
138
|
+
*/
|
|
139
139
|
name?: string;
|
|
140
140
|
/**
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
141
|
+
* The function to execute in the worker thread.
|
|
142
|
+
*
|
|
143
|
+
* This function:
|
|
144
|
+
* - Runs in a separate Node.js worker thread
|
|
145
|
+
* - Should contain the CPU-intensive logic
|
|
146
|
+
* - Can be async and return any serializable data
|
|
147
|
+
* - Has access to standard Node.js APIs and modules
|
|
148
|
+
* - Cannot directly access the main thread's memory or variables
|
|
149
|
+
*
|
|
150
|
+
* **Important**: The handler function is serialized and sent to the worker thread,
|
|
151
|
+
* so it cannot reference variables from the parent scope (closures won't work).
|
|
152
|
+
* All required data must be passed via the `execute()` method.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* handler: async () => {
|
|
157
|
+
* // CPU-intensive work here
|
|
158
|
+
* const result = performComplexCalculation();
|
|
159
|
+
* return { result, completed: Date.now() };
|
|
160
|
+
* }
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
163
|
handler: () => any | Promise<any>;
|
|
164
164
|
/**
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
165
|
+
* Maximum number of worker threads in the pool.
|
|
166
|
+
*
|
|
167
|
+
* Controls how many threads can run concurrently for this named thread worker.
|
|
168
|
+
* When all threads are busy, additional `execute()` calls will queue until a thread becomes available.
|
|
169
|
+
*
|
|
170
|
+
* **Default**: `cpus().length * 2` (number of CPU cores × 2)
|
|
171
|
+
*
|
|
172
|
+
* **Guidelines**:
|
|
173
|
+
* - For CPU-bound tasks: Set to number of CPU cores
|
|
174
|
+
* - For I/O-bound tasks in workers: Can be higher (2x CPU cores)
|
|
175
|
+
* - For memory-intensive tasks: Set lower to avoid memory pressure
|
|
176
|
+
* - For short-lived tasks: Can be higher for better throughput
|
|
177
|
+
*
|
|
178
|
+
* @default cpus().length * 2
|
|
179
|
+
* @example 4 // Limit to 4 concurrent threads
|
|
180
|
+
* @example 1 // Single worker thread (sequential processing)
|
|
181
|
+
* @example 16 // High concurrency for lightweight tasks
|
|
182
|
+
*/
|
|
183
183
|
maxPoolSize?: number;
|
|
184
184
|
/**
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
185
|
+
* Time in milliseconds before idle worker threads are terminated.
|
|
186
|
+
*
|
|
187
|
+
* When a worker thread has been idle (not executing any tasks) for this duration,
|
|
188
|
+
* it will be automatically terminated to free up system resources. New threads
|
|
189
|
+
* will be created as needed when new tasks are submitted.
|
|
190
|
+
*
|
|
191
|
+
* **Default**: 60000 (60 seconds)
|
|
192
|
+
*
|
|
193
|
+
* **Considerations**:
|
|
194
|
+
* - Shorter timeouts: Save memory but increase thread creation overhead
|
|
195
|
+
* - Longer timeouts: Keep threads ready but consume more memory
|
|
196
|
+
* - Very short timeouts may cause constant thread creation/destruction
|
|
197
|
+
*
|
|
198
|
+
* @default 60000
|
|
199
|
+
* @example 30000 // 30 seconds
|
|
200
|
+
* @example 120000 // 2 minutes
|
|
201
|
+
* @example 5000 // 5 seconds (for very memory-constrained environments)
|
|
202
|
+
*/
|
|
203
203
|
idleTimeout?: number;
|
|
204
204
|
}
|
|
205
205
|
declare class ThreadPrimitive extends Primitive<ThreadPrimitiveOptions> {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/thread/primitives/$thread.ts","../../src/thread/providers/ThreadProvider.ts","../../src/thread/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/thread/primitives/$thread.ts","../../src/thread/providers/ThreadProvider.ts","../../src/thread/index.ts"],"mappings":";;;;;;;;AAuHA;;;;;;;;;;;;;;;;AAMA;;;;;;;;;;;AAuFA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChQD;;;;;;;;cD2Ga,OAAA;EAAA,UAAoB,sBAAA,GAAyB,eAAA;EAAA;;UAMzC,sBAAA;ECzGS;;;;;;;;ACboB;;;;;;EFqI5C,IAAA;EE3GA;;;;;;;;;;;;;;;;;;;;;;;EFoIA,OAAA,cAAqB,OAAA;;;;;;;;;;;;;;;;;;;;EAqBrB,WAAA;;;;;;;;;;;;;;;;;;;;EAqBA,WAAA;AAAA;AAAA,cAKW,eAAA,SAAwB,SAAA,CAAU,sBAAA;EAAA,mBAC1B,MAAA;EAAA,gBACH,UAAA,EAAU,GAAA,SAAA,UAAA;EAAA,IAEf,IAAA,CAAA;EAAA,IAIA,WAAA,CAAA;EAAA,IAIA,WAAA,CAAA;EAAA,QAIH,OAAA;EAeK,OAAA,SAAA,CAAiB,IAAA,QAAY,MAAA,GAAS,OAAA,GAAU,OAAA,CAAQ,CAAA;EAexD,MAAA,CAAA,GAAU,OAAA;EAKV,SAAA,CAAA,GAAa,OAAA;AAAA;AAAA,cA6BtB,UAAA;EAAA,iBAUe,IAAA;EAAA,iBACA,WAAA;EAAA,iBACA,WAAA;EAAA,iBACA,MAAA;EAAA,QAZX,SAAA;EAAA,QACA,KAAA;EAAA,QAKA,SAAA;cAGW,IAAA,UACA,WAAA,UACA,WAAA,UACA,MAAA;EAGb,MAAA,CAAA,GAAU,OAAA;EAAA,QAMF,cAAA;EA+DR,OAAA,SAAA,CAAiB,IAAA,SAAa,OAAA,CAAQ,CAAA;EAAA,QAO9B,YAAA;EAAA,QAwCN,cAAA;EAAA,QAUA,oBAAA;EAqBF,SAAA,CAAA,GAAa,OAAA;AAAA;;;cC3bR,cAAA;EAAA,mBACQ,GAAA,EADM,cAAA,CACH,MAAA;EAAA,mBACH,GAAA;;;qBAMA,KAAA,EAPG,OAAA,CAOE,aAAA;EAAA,OAuDJ,OAAA,CAAA,GAAW,OAAA;AAAA;;;;YC/DrB,MAAA;IACR,cAAA;EAAA;AAAA;;;;;;;cAgBS,YAAA,EAAY,OAAA,CAAA,OAAA,CAIvB,OAAA,CAJuB,MAAA"}
|
package/dist/thread/index.js
CHANGED
|
@@ -229,8 +229,8 @@ var ThreadPool = class {
|
|
|
229
229
|
if (!instance && this.instances.length < this.maxPoolSize) try {
|
|
230
230
|
instance = await this.createInstance();
|
|
231
231
|
} catch (error) {
|
|
232
|
-
const { reject
|
|
233
|
-
reject
|
|
232
|
+
const { reject } = this.queue.shift();
|
|
233
|
+
reject(error instanceof Error ? error : /* @__PURE__ */ new Error("Failed to create thread instance"));
|
|
234
234
|
return;
|
|
235
235
|
}
|
|
236
236
|
if (!instance) return;
|
package/dist/thread/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/thread/primitives/$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 { createPrimitive, KIND, Primitive, Value } from \"alepha\";\n\n/**\n * Creates a worker thread primitive for offloading CPU-intensive tasks to separate threads.\n *\n * This primitive 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: ThreadPrimitiveOptions): ThreadPrimitive => {\n return createPrimitive(ThreadPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ThreadPrimitiveOptions {\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 primitive.\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 ThreadPrimitive extends Primitive<ThreadPrimitiveOptions> {\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 (!ThreadPrimitive.globalPool.has(this.name)) {\n ThreadPrimitive.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 ThreadPrimitive.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 Value.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 ThreadPrimitive.globalPool.delete(this.name);\n }\n}\n\n$thread[KIND] = ThreadPrimitive;\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 \"../primitives/$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.primitives($thread);\n const threadPrimitive = threads.find((thread) => thread.name === worker);\n\n if (!threadPrimitive) {\n this.log.error(`Thread not found: ${worker}`);\n return;\n }\n\n this.log.info(`Thread ready: ${threadPrimitive.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(`Executing thread handler: ${threadPrimitive.name}`);\n const result = await threadPrimitive.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 \"./primitives/$thread.ts\";\nimport { ThreadProvider } from \"./providers/ThreadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$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 primitives: [$thread],\n services: [ThreadProvider],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/thread/primitives/$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 { createPrimitive, KIND, Primitive, Value } from \"alepha\";\n\n/**\n * Creates a worker thread primitive for offloading CPU-intensive tasks to separate threads.\n *\n * This primitive 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: ThreadPrimitiveOptions): ThreadPrimitive => {\n return createPrimitive(ThreadPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ThreadPrimitiveOptions {\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 primitive.\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 ThreadPrimitive extends Primitive<ThreadPrimitiveOptions> {\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 (!ThreadPrimitive.globalPool.has(this.name)) {\n ThreadPrimitive.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 ThreadPrimitive.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 Value.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 ThreadPrimitive.globalPool.delete(this.name);\n }\n}\n\n$thread[KIND] = ThreadPrimitive;\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 \"../primitives/$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.primitives($thread);\n const threadPrimitive = threads.find((thread) => thread.name === worker);\n\n if (!threadPrimitive) {\n this.log.error(`Thread not found: ${worker}`);\n return;\n }\n\n this.log.info(`Thread ready: ${threadPrimitive.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(`Executing thread handler: ${threadPrimitive.name}`);\n const result = await threadPrimitive.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 \"./primitives/$thread.ts\";\nimport { ThreadProvider } from \"./providers/ThreadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$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 primitives: [$thread],\n services: [ThreadProvider],\n});\n"],"mappings":"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"}
|