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.
Files changed (222) hide show
  1. package/README.md +43 -98
  2. package/dist/api/audits/index.d.ts +240 -240
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js +2 -2
  5. package/dist/api/audits/index.js.map +1 -1
  6. package/dist/api/files/index.d.ts +185 -185
  7. package/dist/api/files/index.d.ts.map +1 -1
  8. package/dist/api/files/index.js +2 -2
  9. package/dist/api/files/index.js.map +1 -1
  10. package/dist/api/jobs/index.d.ts +245 -245
  11. package/dist/api/jobs/index.d.ts.map +1 -1
  12. package/dist/api/notifications/index.browser.js +4 -4
  13. package/dist/api/notifications/index.browser.js.map +1 -1
  14. package/dist/api/notifications/index.d.ts +74 -74
  15. package/dist/api/notifications/index.d.ts.map +1 -1
  16. package/dist/api/notifications/index.js +4 -4
  17. package/dist/api/notifications/index.js.map +1 -1
  18. package/dist/api/parameters/index.d.ts +221 -221
  19. package/dist/api/parameters/index.d.ts.map +1 -1
  20. package/dist/api/users/index.d.ts +1632 -1631
  21. package/dist/api/users/index.d.ts.map +1 -1
  22. package/dist/api/users/index.js +26 -34
  23. package/dist/api/users/index.js.map +1 -1
  24. package/dist/api/verifications/index.d.ts +132 -132
  25. package/dist/api/verifications/index.d.ts.map +1 -1
  26. package/dist/batch/index.d.ts +122 -122
  27. package/dist/batch/index.d.ts.map +1 -1
  28. package/dist/bucket/index.d.ts +163 -163
  29. package/dist/bucket/index.d.ts.map +1 -1
  30. package/dist/cache/core/index.d.ts +46 -46
  31. package/dist/cache/core/index.d.ts.map +1 -1
  32. package/dist/cache/redis/index.d.ts.map +1 -1
  33. package/dist/cache/redis/index.js +2 -2
  34. package/dist/cache/redis/index.js.map +1 -1
  35. package/dist/cli/index.d.ts +5933 -201
  36. package/dist/cli/index.d.ts.map +1 -1
  37. package/dist/cli/index.js +609 -169
  38. package/dist/cli/index.js.map +1 -1
  39. package/dist/command/index.d.ts +296 -296
  40. package/dist/command/index.d.ts.map +1 -1
  41. package/dist/command/index.js +19 -19
  42. package/dist/command/index.js.map +1 -1
  43. package/dist/core/index.browser.js +268 -79
  44. package/dist/core/index.browser.js.map +1 -1
  45. package/dist/core/index.d.ts +768 -694
  46. package/dist/core/index.d.ts.map +1 -1
  47. package/dist/core/index.js +268 -79
  48. package/dist/core/index.js.map +1 -1
  49. package/dist/core/index.native.js +268 -79
  50. package/dist/core/index.native.js.map +1 -1
  51. package/dist/datetime/index.d.ts +44 -44
  52. package/dist/datetime/index.d.ts.map +1 -1
  53. package/dist/email/index.d.ts +25 -25
  54. package/dist/email/index.d.ts.map +1 -1
  55. package/dist/fake/index.d.ts +5409 -5409
  56. package/dist/fake/index.d.ts.map +1 -1
  57. package/dist/fake/index.js +22 -22
  58. package/dist/fake/index.js.map +1 -1
  59. package/dist/file/index.d.ts +435 -435
  60. package/dist/file/index.d.ts.map +1 -1
  61. package/dist/lock/core/index.d.ts +208 -208
  62. package/dist/lock/core/index.d.ts.map +1 -1
  63. package/dist/lock/redis/index.d.ts.map +1 -1
  64. package/dist/logger/index.d.ts +24 -24
  65. package/dist/logger/index.d.ts.map +1 -1
  66. package/dist/logger/index.js +1 -5
  67. package/dist/logger/index.js.map +1 -1
  68. package/dist/mcp/index.d.ts +216 -198
  69. package/dist/mcp/index.d.ts.map +1 -1
  70. package/dist/mcp/index.js +28 -4
  71. package/dist/mcp/index.js.map +1 -1
  72. package/dist/orm/index.browser.js +9 -9
  73. package/dist/orm/index.browser.js.map +1 -1
  74. package/dist/orm/index.bun.js +83 -76
  75. package/dist/orm/index.bun.js.map +1 -1
  76. package/dist/orm/index.d.ts +961 -960
  77. package/dist/orm/index.d.ts.map +1 -1
  78. package/dist/orm/index.js +88 -81
  79. package/dist/orm/index.js.map +1 -1
  80. package/dist/queue/core/index.d.ts +244 -244
  81. package/dist/queue/core/index.d.ts.map +1 -1
  82. package/dist/queue/redis/index.d.ts.map +1 -1
  83. package/dist/redis/index.d.ts +105 -105
  84. package/dist/redis/index.d.ts.map +1 -1
  85. package/dist/retry/index.d.ts +69 -69
  86. package/dist/retry/index.d.ts.map +1 -1
  87. package/dist/router/index.d.ts +6 -6
  88. package/dist/router/index.d.ts.map +1 -1
  89. package/dist/scheduler/index.d.ts +108 -26
  90. package/dist/scheduler/index.d.ts.map +1 -1
  91. package/dist/scheduler/index.js +393 -1
  92. package/dist/scheduler/index.js.map +1 -1
  93. package/dist/security/index.d.ts +532 -209
  94. package/dist/security/index.d.ts.map +1 -1
  95. package/dist/security/index.js +1422 -11
  96. package/dist/security/index.js.map +1 -1
  97. package/dist/server/auth/index.d.ts +1296 -271
  98. package/dist/server/auth/index.d.ts.map +1 -1
  99. package/dist/server/auth/index.js +1249 -18
  100. package/dist/server/auth/index.js.map +1 -1
  101. package/dist/server/cache/index.d.ts +56 -56
  102. package/dist/server/cache/index.d.ts.map +1 -1
  103. package/dist/server/compress/index.d.ts +3 -3
  104. package/dist/server/compress/index.d.ts.map +1 -1
  105. package/dist/server/cookies/index.d.ts +6 -6
  106. package/dist/server/cookies/index.d.ts.map +1 -1
  107. package/dist/server/core/index.d.ts +196 -186
  108. package/dist/server/core/index.d.ts.map +1 -1
  109. package/dist/server/core/index.js +43 -27
  110. package/dist/server/core/index.js.map +1 -1
  111. package/dist/server/cors/index.d.ts +11 -11
  112. package/dist/server/cors/index.d.ts.map +1 -1
  113. package/dist/server/health/index.d.ts.map +1 -1
  114. package/dist/server/helmet/index.d.ts +2 -2
  115. package/dist/server/helmet/index.d.ts.map +1 -1
  116. package/dist/server/links/index.browser.js +9 -1
  117. package/dist/server/links/index.browser.js.map +1 -1
  118. package/dist/server/links/index.d.ts +83 -83
  119. package/dist/server/links/index.d.ts.map +1 -1
  120. package/dist/server/links/index.js +13 -5
  121. package/dist/server/links/index.js.map +1 -1
  122. package/dist/server/metrics/index.d.ts +514 -1
  123. package/dist/server/metrics/index.d.ts.map +1 -1
  124. package/dist/server/metrics/index.js +4462 -4
  125. package/dist/server/metrics/index.js.map +1 -1
  126. package/dist/server/multipart/index.d.ts +6 -6
  127. package/dist/server/multipart/index.d.ts.map +1 -1
  128. package/dist/server/proxy/index.d.ts +102 -102
  129. package/dist/server/proxy/index.d.ts.map +1 -1
  130. package/dist/server/rate-limit/index.d.ts +16 -16
  131. package/dist/server/rate-limit/index.d.ts.map +1 -1
  132. package/dist/server/static/index.d.ts +44 -44
  133. package/dist/server/static/index.d.ts.map +1 -1
  134. package/dist/server/swagger/index.d.ts +47 -47
  135. package/dist/server/swagger/index.d.ts.map +1 -1
  136. package/dist/sms/index.d.ts +11 -11
  137. package/dist/sms/index.d.ts.map +1 -1
  138. package/dist/sms/index.js +3 -3
  139. package/dist/sms/index.js.map +1 -1
  140. package/dist/thread/index.d.ts +71 -71
  141. package/dist/thread/index.d.ts.map +1 -1
  142. package/dist/thread/index.js +2 -2
  143. package/dist/thread/index.js.map +1 -1
  144. package/dist/topic/core/index.d.ts +318 -318
  145. package/dist/topic/core/index.d.ts.map +1 -1
  146. package/dist/topic/redis/index.d.ts +6 -6
  147. package/dist/topic/redis/index.d.ts.map +1 -1
  148. package/dist/vite/index.d.ts +2324 -1719
  149. package/dist/vite/index.d.ts.map +1 -1
  150. package/dist/vite/index.js +123 -475
  151. package/dist/vite/index.js.map +1 -1
  152. package/dist/websocket/index.browser.js +3 -3
  153. package/dist/websocket/index.browser.js.map +1 -1
  154. package/dist/websocket/index.d.ts +275 -275
  155. package/dist/websocket/index.d.ts.map +1 -1
  156. package/dist/websocket/index.js +3 -3
  157. package/dist/websocket/index.js.map +1 -1
  158. package/package.json +9 -9
  159. package/src/api/users/services/SessionService.ts +0 -10
  160. package/src/cli/apps/AlephaCli.ts +2 -2
  161. package/src/cli/apps/AlephaPackageBuilderCli.ts +9 -1
  162. package/src/cli/assets/apiHelloControllerTs.ts +2 -1
  163. package/src/cli/assets/biomeJson.ts +2 -1
  164. package/src/cli/assets/claudeMd.ts +9 -4
  165. package/src/cli/assets/dummySpecTs.ts +2 -1
  166. package/src/cli/assets/editorconfig.ts +2 -1
  167. package/src/cli/assets/mainBrowserTs.ts +2 -1
  168. package/src/cli/assets/mainCss.ts +24 -0
  169. package/src/cli/assets/tsconfigJson.ts +2 -1
  170. package/src/cli/assets/webAppRouterTs.ts +2 -1
  171. package/src/cli/assets/webHelloComponentTsx.ts +6 -2
  172. package/src/cli/atoms/appEntryOptions.ts +13 -0
  173. package/src/cli/atoms/buildOptions.ts +1 -1
  174. package/src/cli/atoms/changelogOptions.ts +1 -1
  175. package/src/cli/commands/build.ts +63 -47
  176. package/src/cli/commands/dev.ts +16 -33
  177. package/src/cli/commands/gen/env.ts +1 -1
  178. package/src/cli/commands/init.ts +17 -8
  179. package/src/cli/commands/lint.ts +1 -1
  180. package/src/cli/defineConfig.ts +9 -0
  181. package/src/cli/index.ts +2 -1
  182. package/src/cli/providers/AppEntryProvider.ts +131 -0
  183. package/src/cli/providers/ViteBuildProvider.ts +82 -0
  184. package/src/cli/providers/ViteDevServerProvider.ts +350 -0
  185. package/src/cli/providers/ViteTemplateProvider.ts +27 -0
  186. package/src/cli/services/AlephaCliUtils.ts +33 -2
  187. package/src/cli/services/PackageManagerUtils.ts +13 -6
  188. package/src/cli/services/ProjectScaffolder.ts +72 -49
  189. package/src/core/Alepha.ts +2 -8
  190. package/src/core/primitives/$module.ts +12 -0
  191. package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +257 -0
  192. package/src/core/providers/KeylessJsonSchemaCodec.ts +396 -14
  193. package/src/core/providers/SchemaValidator.spec.ts +236 -0
  194. package/src/logger/providers/PrettyFormatterProvider.ts +0 -9
  195. package/src/mcp/errors/McpError.ts +30 -0
  196. package/src/mcp/index.ts +3 -0
  197. package/src/mcp/transports/SseMcpTransport.ts +16 -6
  198. package/src/orm/providers/DrizzleKitProvider.ts +3 -5
  199. package/src/orm/services/Repository.ts +11 -0
  200. package/src/server/core/index.ts +1 -1
  201. package/src/server/core/providers/BunHttpServerProvider.ts +1 -1
  202. package/src/server/core/providers/NodeHttpServerProvider.spec.ts +125 -0
  203. package/src/server/core/providers/NodeHttpServerProvider.ts +71 -22
  204. package/src/server/core/providers/ServerLoggerProvider.ts +2 -2
  205. package/src/server/core/providers/ServerProvider.ts +9 -12
  206. package/src/server/links/atoms/apiLinksAtom.ts +7 -0
  207. package/src/server/links/index.browser.ts +2 -0
  208. package/src/server/links/index.ts +2 -0
  209. package/src/vite/index.ts +3 -2
  210. package/src/vite/tasks/buildClient.ts +0 -1
  211. package/src/vite/tasks/buildServer.ts +68 -21
  212. package/src/vite/tasks/copyAssets.ts +5 -4
  213. package/src/vite/tasks/generateSitemap.ts +64 -23
  214. package/src/vite/tasks/index.ts +0 -2
  215. package/src/vite/tasks/prerenderPages.ts +49 -24
  216. package/src/cli/assets/indexHtml.ts +0 -15
  217. package/src/cli/commands/format.ts +0 -23
  218. package/src/vite/helpers/boot.ts +0 -117
  219. package/src/vite/plugins/viteAlephaDev.ts +0 -177
  220. package/src/vite/tasks/devServer.ts +0 -71
  221. package/src/vite/tasks/runAlepha.ts +0 -270
  222. /package/dist/orm/{chunk-DtkW-qnP.js → chunk-DH6iiROE.js} +0 -0
@@ -11,10 +11,10 @@ import { FileSystemProvider } from "alepha/file";
11
11
  */
12
12
  declare abstract class SmsProvider {
13
13
  /**
14
- * Send an SMS.
15
- *
16
- * @return Promise that resolves when the SMS is sent.
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
- * Send an SMS using the configured provider.
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
- * Directory to save SMS files.
70
- * @default "node_modules/.alepha/sms" (relative to project root)
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
- * Get the last SMS sent (for testing purposes).
98
- */
97
+ * Get the last SMS sent (for testing purposes).
98
+ */
99
99
  get last(): SmsRecord | undefined;
100
100
  }
101
101
  //#endregion
@@ -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":";;;;;;;AAKA;AASA;;;uBATsB,WAAA;EAAA;AAStB;;;;EATsB,SAAA,KAAA,OAAA,EAMU,cAAA,GAAiB,OAAA;AAAA;AAAA,KAGrC,cAAA;EAAA,EAAA;EAAA,OAAA;AAAA;;;cCdC,QAAA,SAAiB,KAAA;EAAA,YAAA,OAAA,UAAA,KAAA,GACS,KAAA;AAAA;;;cCW1B,IAAA;EAAA,CAAA,OAAA,GAAiB,mBAAA,GAAmB,YAAA;EAAA;;UAKhC,mBAAA;EAAA,IAAA;EAAA,QAAA,GAEJ,iBAAA,CAAkB,WAAA;AAAA;AAAA;AAsB/B;;;;;;;;;;;ACnCA;AAQA;;;;ADK+B,cAsBlB,YAAA,SAAqB,SAAA,CAAU,mBAAA;EAAA,mBAAA,QAAA,EACf,WAAA;EAAA,IAAA,KAAA;EAAA;;;EAAA,KAAA,OAAA,EASA,cAAA,GAAiB,OAAA;EAAA,UAAA,UAAA,GAoBrB,WAAA;AAAA;;;UCjER,uBAAA;EAAA;AAQjB;;;EARiB,SAAA;AAAA;AAAA,cAQJ,gBAAA,YAA4B,WAAA;EAAA,mBAAA,GAAA,EAAX,cAAA,CACN,MAAA;EAAA,mBAAA,EAAA,EACD,kBAAA;EAAA,mBAAA,SAAA;EAAA,YAAA,OAAA,GAGA,uBAAA;EAAA,KAAA,OAAA,EAIM,cAAA,GAAiB,OAAA;EAAA,cAAA,OAAA;IAAA,EAAA;IAAA,OAAA;EAAA;AAAA;;;UCpB7B,SAAA;EAAA,EAAA;EAAA,OAAA;EAAA,MAAA,EAGP,IAAA;AAAA;AAAA,cAGG,iBAAA,YAA6B,WAAA;EAAA,mBAAA,GAAA,EAAX,cAAA,CACP,MAAA;EAAA,OAAA,EACN,SAAA;EAAA,KAAA,OAAA,EAEW,cAAA,GAAiB,OAAA;EAAA;;;EAAA,IAAA,KAAA,GAgBzB,SAAA;AAAA;;;;;;;;iBCRJ,MAAA;MAAA,QAAA,EACD,WAAA;MAAA,KAAA;IAAA;IAAA;MAAA,EAAA;MAAA,QAAA;MAAA,QAAA,EAMA,WAAA;IAAA;EAAA;AAAA;AAAA;;;;AAiBhB;;;;;;AAjBgB,cAiBH,SAAA,EAAS,OAAA,CAAA,OAAA,CAUpB,OAAA,CAVoB,MAAA"}
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$1 = `Failed to save SMS to local file: ${error instanceof Error ? error.message : String(error)}`;
135
- this.log.error(message$1, {
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$1, error instanceof Error ? error : void 0);
139
+ throw new SmsError(message, error instanceof Error ? error : void 0);
140
140
  }
141
141
  }
142
142
  createSmsText(options) {
@@ -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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"}
@@ -123,83 +123,83 @@ declare const $thread: {
123
123
  };
124
124
  interface ThreadPrimitiveOptions {
125
125
  /**
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
- */
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
- * 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
- */
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
- * 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
- */
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
- * 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
- */
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":";;;;;;AAuHA;;;;;;AAMA;AAuFA;;;;;;;;;;;AAwDC;;;;;;;;;AChQD;;;;;;;;ACL8C;;;;;AAsB9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cF0Fa,OAAA;EAAA,CAAA,OAAA,EAAoB,sBAAA,GAAyB,eAAA;EAAA;;UAMzC,sBAAA;EAAA;AAuFjB;;;;;;;;;;;AAwDC;;EA/IgB,IAAA;EAAA;AAuFjB;;;;;;;;;;;AAwDC;;;;;;;;;AChQD;;EDiHiB,OAAA,cAwCM,OAAA;EAAA;AA+CvB;;;;;;;;;;;AAwDC;;;;;;;EAvGsB,WAAA;EAAA;AA+CvB;;;;;;;;;;;AAwDC;;;;;;;EAvGsB,WAAA;AAAA;AAAA,cA+CV,eAAA,SAAwB,SAAA,CAAU,sBAAA;EAAA,mBAAA,MAAA;EAAA,gBAAA,UAAA,EAEnB,GAAA,SAAA,UAAA;EAAA,IAAA,KAAA;EAAA,IAAA,YAAA;EAAA,IAAA,YAAA;EAAA,QAAA,OAAA;EAAA,OAAA,SAAA,CAAA,IAAA,QAAA,MAAA,GA6ByB,OAAA,GAAU,OAAA,CAAQ,CAAA;EAAA,OAAA,GAe9C,OAAA;EAAA,UAAA,GAKG,OAAA;AAAA;AAAA,cA6BtB,UAAA;EAAA,iBAAA,IAAA;EAAA,iBAAA,WAAA;EAAA,iBAAA,WAAA;EAAA,iBAAA,MAAA;EAAA,QAAA,SAAA;EAAA,QAAA,KAAA;EAAA,QAAA,SAAA;EAAA,YAAA,IAAA,UAAA,WAAA,UAAA,WAAA,UAAA,MAAA;EAAA,OAAA,GAgBY,OAAA;EAAA,QAAA,cAAA;EAAA,OAAA,SAAA,CAAA,IAAA,SAqEoB,OAAA,CAAQ,CAAA;EAAA,QAAA,YAAA;EAAA,QAAA,cAAA;EAAA,QAAA,oBAAA;EAAA,UAAA,GA8EzB,OAAA;AAAA;;;cC3bR,cAAA;EAAA,mBAAA,GAAA,EAAc,cAAA,CACH,MAAA;EAAA,mBAAA,GAAA;IAAA,aAAA;EAAA;EAAA,mBAAA,KAAA,EAAA,OAAA,CAOE,aAAA;EAAA,OAAA,QAAA,GAuDO,OAAA;AAAA;;;;;;;;;ACpEa;;;;;cAsBjC,YAAA,EAAY,OAAA,CAAA,OAAA,CAIvB,OAAA,CAJuB,MAAA"}
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"}
@@ -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: reject$1 } = this.queue.shift();
233
- reject$1(error instanceof Error ? error : /* @__PURE__ */ new Error("Failed to create thread instance"));
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;
@@ -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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"}