alepha 0.15.0 → 0.15.2

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 (551) hide show
  1. package/README.md +43 -98
  2. package/dist/api/audits/index.d.ts +630 -653
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js +12 -35
  5. package/dist/api/audits/index.js.map +1 -1
  6. package/dist/api/files/index.d.ts +365 -358
  7. package/dist/api/files/index.d.ts.map +1 -1
  8. package/dist/api/files/index.js +12 -5
  9. package/dist/api/files/index.js.map +1 -1
  10. package/dist/api/jobs/index.d.ts +255 -248
  11. package/dist/api/jobs/index.d.ts.map +1 -1
  12. package/dist/api/jobs/index.js +10 -3
  13. package/dist/api/jobs/index.js.map +1 -1
  14. package/dist/api/keys/index.d.ts +413 -0
  15. package/dist/api/keys/index.d.ts.map +1 -0
  16. package/dist/api/keys/index.js +476 -0
  17. package/dist/api/keys/index.js.map +1 -0
  18. package/dist/api/notifications/index.browser.js +4 -4
  19. package/dist/api/notifications/index.browser.js.map +1 -1
  20. package/dist/api/notifications/index.d.ts +84 -78
  21. package/dist/api/notifications/index.d.ts.map +1 -1
  22. package/dist/api/notifications/index.js +14 -8
  23. package/dist/api/notifications/index.js.map +1 -1
  24. package/dist/api/parameters/index.d.ts +528 -535
  25. package/dist/api/parameters/index.d.ts.map +1 -1
  26. package/dist/api/parameters/index.js +30 -37
  27. package/dist/api/parameters/index.js.map +1 -1
  28. package/dist/api/users/index.d.ts +1221 -910
  29. package/dist/api/users/index.d.ts.map +1 -1
  30. package/dist/api/users/index.js +2556 -248
  31. package/dist/api/users/index.js.map +1 -1
  32. package/dist/api/verifications/index.d.ts +142 -136
  33. package/dist/api/verifications/index.d.ts.map +1 -1
  34. package/dist/api/verifications/index.js +12 -4
  35. package/dist/api/verifications/index.js.map +1 -1
  36. package/dist/batch/index.d.ts +142 -162
  37. package/dist/batch/index.d.ts.map +1 -1
  38. package/dist/batch/index.js +31 -44
  39. package/dist/batch/index.js.map +1 -1
  40. package/dist/bucket/index.d.ts +595 -171
  41. package/dist/bucket/index.d.ts.map +1 -1
  42. package/dist/bucket/index.js +1856 -12
  43. package/dist/bucket/index.js.map +1 -1
  44. package/dist/cache/core/index.d.ts +225 -53
  45. package/dist/cache/core/index.d.ts.map +1 -1
  46. package/dist/cache/core/index.js +213 -7
  47. package/dist/cache/core/index.js.map +1 -1
  48. package/dist/cache/redis/index.d.ts +1 -0
  49. package/dist/cache/redis/index.d.ts.map +1 -1
  50. package/dist/cache/redis/index.js +6 -2
  51. package/dist/cache/redis/index.js.map +1 -1
  52. package/dist/cli/index.d.ts +834 -226
  53. package/dist/cli/index.d.ts.map +1 -1
  54. package/dist/cli/index.js +2872 -417
  55. package/dist/cli/index.js.map +1 -1
  56. package/dist/command/index.d.ts +458 -310
  57. package/dist/command/index.d.ts.map +1 -1
  58. package/dist/command/index.js +2011 -76
  59. package/dist/command/index.js.map +1 -1
  60. package/dist/core/index.browser.js +309 -97
  61. package/dist/core/index.browser.js.map +1 -1
  62. package/dist/core/index.d.ts +796 -701
  63. package/dist/core/index.d.ts.map +1 -1
  64. package/dist/core/index.js +329 -97
  65. package/dist/core/index.js.map +1 -1
  66. package/dist/core/index.native.js +309 -97
  67. package/dist/core/index.native.js.map +1 -1
  68. package/dist/datetime/index.d.ts +59 -44
  69. package/dist/datetime/index.d.ts.map +1 -1
  70. package/dist/datetime/index.js +15 -0
  71. package/dist/datetime/index.js.map +1 -1
  72. package/dist/email/index.d.ts +314 -19
  73. package/dist/email/index.d.ts.map +1 -1
  74. package/dist/email/index.js +1852 -7
  75. package/dist/email/index.js.map +1 -1
  76. package/dist/fake/index.d.ts +5500 -5418
  77. package/dist/fake/index.d.ts.map +1 -1
  78. package/dist/fake/index.js +113 -42
  79. package/dist/fake/index.js.map +1 -1
  80. package/dist/lock/core/index.d.ts +219 -212
  81. package/dist/lock/core/index.d.ts.map +1 -1
  82. package/dist/lock/core/index.js +11 -4
  83. package/dist/lock/core/index.js.map +1 -1
  84. package/dist/lock/redis/index.d.ts.map +1 -1
  85. package/dist/logger/index.d.ts +41 -90
  86. package/dist/logger/index.d.ts.map +1 -1
  87. package/dist/logger/index.js +15 -68
  88. package/dist/logger/index.js.map +1 -1
  89. package/dist/mcp/index.d.ts +228 -230
  90. package/dist/mcp/index.d.ts.map +1 -1
  91. package/dist/mcp/index.js +32 -31
  92. package/dist/mcp/index.js.map +1 -1
  93. package/dist/orm/index.browser.js +12 -12
  94. package/dist/orm/index.browser.js.map +1 -1
  95. package/dist/orm/index.bun.js +90 -80
  96. package/dist/orm/index.bun.js.map +1 -1
  97. package/dist/orm/index.d.ts +1434 -1459
  98. package/dist/orm/index.d.ts.map +1 -1
  99. package/dist/orm/index.js +112 -130
  100. package/dist/orm/index.js.map +1 -1
  101. package/dist/queue/core/index.d.ts +262 -254
  102. package/dist/queue/core/index.d.ts.map +1 -1
  103. package/dist/queue/core/index.js +14 -6
  104. package/dist/queue/core/index.js.map +1 -1
  105. package/dist/queue/redis/index.d.ts.map +1 -1
  106. package/dist/react/auth/index.browser.js +108 -0
  107. package/dist/react/auth/index.browser.js.map +1 -0
  108. package/dist/react/auth/index.d.ts +100 -0
  109. package/dist/react/auth/index.d.ts.map +1 -0
  110. package/dist/react/auth/index.js +145 -0
  111. package/dist/react/auth/index.js.map +1 -0
  112. package/dist/react/core/index.d.ts +469 -0
  113. package/dist/react/core/index.d.ts.map +1 -0
  114. package/dist/react/core/index.js +464 -0
  115. package/dist/react/core/index.js.map +1 -0
  116. package/dist/react/form/index.d.ts +232 -0
  117. package/dist/react/form/index.d.ts.map +1 -0
  118. package/dist/react/form/index.js +432 -0
  119. package/dist/react/form/index.js.map +1 -0
  120. package/dist/react/head/index.browser.js +423 -0
  121. package/dist/react/head/index.browser.js.map +1 -0
  122. package/dist/react/head/index.d.ts +288 -0
  123. package/dist/react/head/index.d.ts.map +1 -0
  124. package/dist/react/head/index.js +465 -0
  125. package/dist/react/head/index.js.map +1 -0
  126. package/dist/react/i18n/index.d.ts +175 -0
  127. package/dist/react/i18n/index.d.ts.map +1 -0
  128. package/dist/react/i18n/index.js +224 -0
  129. package/dist/react/i18n/index.js.map +1 -0
  130. package/dist/react/router/index.browser.js +1980 -0
  131. package/dist/react/router/index.browser.js.map +1 -0
  132. package/dist/react/router/index.d.ts +2068 -0
  133. package/dist/react/router/index.d.ts.map +1 -0
  134. package/dist/react/router/index.js +4932 -0
  135. package/dist/react/router/index.js.map +1 -0
  136. package/dist/react/websocket/index.d.ts +117 -0
  137. package/dist/react/websocket/index.d.ts.map +1 -0
  138. package/dist/react/websocket/index.js +107 -0
  139. package/dist/react/websocket/index.js.map +1 -0
  140. package/dist/redis/index.bun.js +4 -0
  141. package/dist/redis/index.bun.js.map +1 -1
  142. package/dist/redis/index.d.ts +127 -130
  143. package/dist/redis/index.d.ts.map +1 -1
  144. package/dist/redis/index.js +16 -25
  145. package/dist/redis/index.js.map +1 -1
  146. package/dist/retry/index.d.ts +80 -71
  147. package/dist/retry/index.d.ts.map +1 -1
  148. package/dist/retry/index.js +11 -2
  149. package/dist/retry/index.js.map +1 -1
  150. package/dist/router/index.d.ts +6 -6
  151. package/dist/router/index.d.ts.map +1 -1
  152. package/dist/scheduler/index.d.ts +119 -28
  153. package/dist/scheduler/index.d.ts.map +1 -1
  154. package/dist/scheduler/index.js +404 -3
  155. package/dist/scheduler/index.js.map +1 -1
  156. package/dist/security/index.d.ts +642 -228
  157. package/dist/security/index.d.ts.map +1 -1
  158. package/dist/security/index.js +1579 -37
  159. package/dist/security/index.js.map +1 -1
  160. package/dist/server/auth/index.d.ts +1141 -111
  161. package/dist/server/auth/index.d.ts.map +1 -1
  162. package/dist/server/auth/index.js +1261 -25
  163. package/dist/server/auth/index.js.map +1 -1
  164. package/dist/server/cache/index.d.ts +63 -78
  165. package/dist/server/cache/index.d.ts.map +1 -1
  166. package/dist/server/cache/index.js +7 -22
  167. package/dist/server/cache/index.js.map +1 -1
  168. package/dist/server/compress/index.d.ts +13 -5
  169. package/dist/server/compress/index.d.ts.map +1 -1
  170. package/dist/server/compress/index.js +10 -2
  171. package/dist/server/compress/index.js.map +1 -1
  172. package/dist/server/cookies/index.d.ts +46 -22
  173. package/dist/server/cookies/index.d.ts.map +1 -1
  174. package/dist/server/cookies/index.js +7 -5
  175. package/dist/server/cookies/index.js.map +1 -1
  176. package/dist/server/core/index.d.ts +307 -196
  177. package/dist/server/core/index.d.ts.map +1 -1
  178. package/dist/server/core/index.js +271 -38
  179. package/dist/server/core/index.js.map +1 -1
  180. package/dist/server/cors/index.d.ts +24 -34
  181. package/dist/server/cors/index.d.ts.map +1 -1
  182. package/dist/server/cors/index.js +7 -21
  183. package/dist/server/cors/index.js.map +1 -1
  184. package/dist/server/health/index.d.ts +25 -19
  185. package/dist/server/health/index.d.ts.map +1 -1
  186. package/dist/server/health/index.js +8 -2
  187. package/dist/server/health/index.js.map +1 -1
  188. package/dist/server/helmet/index.d.ts +13 -5
  189. package/dist/server/helmet/index.d.ts.map +1 -1
  190. package/dist/server/helmet/index.js +11 -3
  191. package/dist/server/helmet/index.js.map +1 -1
  192. package/dist/server/links/index.browser.js +9 -1
  193. package/dist/server/links/index.browser.js.map +1 -1
  194. package/dist/server/links/index.d.ts +133 -128
  195. package/dist/server/links/index.d.ts.map +1 -1
  196. package/dist/server/links/index.js +24 -11
  197. package/dist/server/links/index.js.map +1 -1
  198. package/dist/server/metrics/index.d.ts +524 -4
  199. package/dist/server/metrics/index.d.ts.map +1 -1
  200. package/dist/server/metrics/index.js +4472 -7
  201. package/dist/server/metrics/index.js.map +1 -1
  202. package/dist/server/multipart/index.d.ts +15 -9
  203. package/dist/server/multipart/index.d.ts.map +1 -1
  204. package/dist/server/multipart/index.js +9 -3
  205. package/dist/server/multipart/index.js.map +1 -1
  206. package/dist/server/proxy/index.d.ts +110 -104
  207. package/dist/server/proxy/index.d.ts.map +1 -1
  208. package/dist/server/proxy/index.js +8 -2
  209. package/dist/server/proxy/index.js.map +1 -1
  210. package/dist/server/rate-limit/index.d.ts +46 -51
  211. package/dist/server/rate-limit/index.d.ts.map +1 -1
  212. package/dist/server/rate-limit/index.js +18 -55
  213. package/dist/server/rate-limit/index.js.map +1 -1
  214. package/dist/server/static/index.d.ts +181 -48
  215. package/dist/server/static/index.d.ts.map +1 -1
  216. package/dist/server/static/index.js +1848 -5
  217. package/dist/server/static/index.js.map +1 -1
  218. package/dist/server/swagger/index.d.ts +348 -53
  219. package/dist/server/swagger/index.d.ts.map +1 -1
  220. package/dist/server/swagger/index.js +1849 -6
  221. package/dist/server/swagger/index.js.map +1 -1
  222. package/dist/sms/index.d.ts +312 -18
  223. package/dist/sms/index.d.ts.map +1 -1
  224. package/dist/sms/index.js +1854 -10
  225. package/dist/sms/index.js.map +1 -1
  226. package/dist/system/index.browser.js +496 -0
  227. package/dist/system/index.browser.js.map +1 -0
  228. package/dist/system/index.d.ts +1158 -0
  229. package/dist/system/index.d.ts.map +1 -0
  230. package/dist/{file → system}/index.js +412 -20
  231. package/dist/system/index.js.map +1 -0
  232. package/dist/thread/index.d.ts +82 -73
  233. package/dist/thread/index.d.ts.map +1 -1
  234. package/dist/thread/index.js +13 -4
  235. package/dist/thread/index.js.map +1 -1
  236. package/dist/topic/core/index.d.ts +330 -323
  237. package/dist/topic/core/index.d.ts.map +1 -1
  238. package/dist/topic/core/index.js +12 -5
  239. package/dist/topic/core/index.js.map +1 -1
  240. package/dist/topic/redis/index.d.ts +6 -6
  241. package/dist/topic/redis/index.d.ts.map +1 -1
  242. package/dist/vite/index.d.ts +163 -5825
  243. package/dist/vite/index.d.ts.map +1 -1
  244. package/dist/vite/index.js +130 -477
  245. package/dist/vite/index.js.map +1 -1
  246. package/dist/websocket/index.browser.js +3 -3
  247. package/dist/websocket/index.browser.js.map +1 -1
  248. package/dist/websocket/index.d.ts +287 -283
  249. package/dist/websocket/index.d.ts.map +1 -1
  250. package/dist/websocket/index.js +15 -11
  251. package/dist/websocket/index.js.map +1 -1
  252. package/package.json +86 -17
  253. package/src/api/audits/index.ts +10 -33
  254. package/src/api/files/__tests__/$bucket.spec.ts +1 -1
  255. package/src/api/files/controllers/AdminFileStatsController.spec.ts +1 -1
  256. package/src/api/files/controllers/FileController.spec.ts +1 -1
  257. package/src/api/files/index.ts +10 -3
  258. package/src/api/files/jobs/FileJobs.spec.ts +1 -1
  259. package/src/api/files/services/FileService.spec.ts +1 -1
  260. package/src/api/jobs/index.ts +10 -3
  261. package/src/api/keys/controllers/AdminApiKeyController.ts +75 -0
  262. package/src/api/keys/controllers/ApiKeyController.ts +103 -0
  263. package/src/api/keys/entities/apiKeyEntity.ts +41 -0
  264. package/src/api/keys/index.ts +49 -0
  265. package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +7 -0
  266. package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +17 -0
  267. package/src/api/keys/schemas/createApiKeyBodySchema.ts +7 -0
  268. package/src/api/keys/schemas/createApiKeyResponseSchema.ts +11 -0
  269. package/src/api/keys/schemas/listApiKeyResponseSchema.ts +15 -0
  270. package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +5 -0
  271. package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +5 -0
  272. package/src/api/keys/services/ApiKeyService.spec.ts +553 -0
  273. package/src/api/keys/services/ApiKeyService.ts +306 -0
  274. package/src/api/logs/TODO.md +52 -0
  275. package/src/api/notifications/index.ts +10 -4
  276. package/src/api/parameters/index.ts +9 -30
  277. package/src/api/parameters/primitives/$config.ts +12 -4
  278. package/src/api/parameters/services/ConfigStore.ts +9 -3
  279. package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1035 -0
  280. package/src/api/users/__tests__/ApiKeys.spec.ts +401 -0
  281. package/src/api/users/index.ts +14 -3
  282. package/src/api/users/primitives/$realm.ts +33 -5
  283. package/src/api/users/providers/RealmProvider.ts +1 -12
  284. package/src/api/users/services/SessionService.ts +1 -11
  285. package/src/api/verifications/controllers/VerificationController.ts +2 -0
  286. package/src/api/verifications/index.ts +10 -4
  287. package/src/batch/index.ts +9 -36
  288. package/src/batch/primitives/$batch.ts +0 -8
  289. package/src/batch/providers/BatchProvider.ts +29 -2
  290. package/src/bucket/__tests__/shared.ts +1 -1
  291. package/src/bucket/index.ts +13 -6
  292. package/src/bucket/primitives/$bucket.ts +1 -1
  293. package/src/bucket/providers/LocalFileStorageProvider.ts +1 -1
  294. package/src/bucket/providers/MemoryFileStorageProvider.ts +1 -1
  295. package/src/cache/core/__tests__/shared.ts +30 -0
  296. package/src/cache/core/index.ts +11 -6
  297. package/src/cache/core/primitives/$cache.spec.ts +5 -0
  298. package/src/cache/core/providers/CacheProvider.ts +17 -0
  299. package/src/cache/core/providers/MemoryCacheProvider.ts +300 -1
  300. package/src/cache/redis/__tests__/cache-redis.spec.ts +5 -0
  301. package/src/cache/redis/providers/RedisCacheProvider.ts +9 -0
  302. package/src/cli/apps/AlephaCli.ts +3 -16
  303. package/src/cli/apps/AlephaPackageBuilderCli.ts +10 -2
  304. package/src/cli/atoms/appEntryOptions.ts +13 -0
  305. package/src/cli/atoms/buildOptions.ts +1 -1
  306. package/src/cli/atoms/changelogOptions.ts +1 -1
  307. package/src/cli/commands/build.ts +64 -52
  308. package/src/cli/commands/db.ts +17 -11
  309. package/src/cli/commands/deploy.ts +1 -1
  310. package/src/cli/commands/dev.ts +13 -49
  311. package/src/cli/commands/gen/env.ts +6 -3
  312. package/src/cli/commands/gen/openapi.ts +5 -2
  313. package/src/cli/commands/init.spec.ts +544 -0
  314. package/src/cli/commands/init.ts +101 -58
  315. package/src/cli/commands/lint.ts +8 -2
  316. package/src/cli/commands/typecheck.ts +11 -0
  317. package/src/cli/defineConfig.ts +9 -0
  318. package/src/cli/index.ts +2 -1
  319. package/src/cli/providers/AppEntryProvider.ts +131 -0
  320. package/src/cli/providers/ViteBuildProvider.ts +40 -0
  321. package/src/cli/providers/ViteDevServerProvider.ts +378 -0
  322. package/src/cli/services/AlephaCliUtils.ts +39 -93
  323. package/src/cli/services/PackageManagerUtils.ts +140 -17
  324. package/src/cli/services/ProjectScaffolder.ts +169 -101
  325. package/src/cli/services/ViteUtils.ts +82 -0
  326. package/src/cli/{assets/claudeMd.ts → templates/agentMd.ts} +41 -28
  327. package/src/cli/{assets → templates}/apiHelloControllerTs.ts +2 -1
  328. package/src/cli/{assets → templates}/biomeJson.ts +2 -1
  329. package/src/cli/{assets → templates}/dummySpecTs.ts +2 -1
  330. package/src/cli/{assets → templates}/editorconfig.ts +2 -1
  331. package/src/cli/templates/gitignore.ts +39 -0
  332. package/src/cli/{assets → templates}/mainBrowserTs.ts +2 -1
  333. package/src/cli/templates/mainCss.ts +33 -0
  334. package/src/cli/templates/mainServerTs.ts +33 -0
  335. package/src/cli/{assets → templates}/tsconfigJson.ts +2 -1
  336. package/src/cli/templates/webAppRouterTs.ts +50 -0
  337. package/src/cli/templates/webHelloComponentTsx.ts +20 -0
  338. package/src/command/helpers/Runner.spec.ts +4 -0
  339. package/src/command/helpers/Runner.ts +3 -21
  340. package/src/command/index.ts +12 -4
  341. package/src/command/providers/CliProvider.spec.ts +1067 -0
  342. package/src/command/providers/CliProvider.ts +203 -40
  343. package/src/core/Alepha.ts +3 -9
  344. package/src/core/__tests__/Alepha-start.spec.ts +4 -4
  345. package/src/core/helpers/jsonSchemaToTypeBox.spec.ts +771 -0
  346. package/src/core/helpers/jsonSchemaToTypeBox.ts +62 -10
  347. package/src/core/index.shared.ts +1 -0
  348. package/src/core/index.ts +20 -0
  349. package/src/core/primitives/$module.ts +12 -0
  350. package/src/core/providers/EventManager.spec.ts +0 -71
  351. package/src/core/providers/EventManager.ts +3 -15
  352. package/src/core/providers/Json.ts +2 -14
  353. package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +257 -0
  354. package/src/core/providers/KeylessJsonSchemaCodec.ts +396 -14
  355. package/src/core/providers/SchemaValidator.spec.ts +236 -0
  356. package/src/datetime/index.ts +15 -0
  357. package/src/email/index.ts +10 -5
  358. package/src/email/providers/LocalEmailProvider.spec.ts +1 -1
  359. package/src/email/providers/LocalEmailProvider.ts +1 -1
  360. package/src/fake/__tests__/keyName.example.ts +1 -1
  361. package/src/fake/__tests__/keyName.spec.ts +5 -5
  362. package/src/fake/index.ts +9 -6
  363. package/src/fake/providers/FakeProvider.spec.ts +258 -40
  364. package/src/fake/providers/FakeProvider.ts +133 -19
  365. package/src/lock/core/index.ts +11 -4
  366. package/src/logger/index.ts +17 -66
  367. package/src/logger/providers/PrettyFormatterProvider.ts +0 -9
  368. package/src/mcp/errors/McpError.ts +30 -0
  369. package/src/mcp/index.ts +13 -27
  370. package/src/mcp/transports/SseMcpTransport.ts +6 -7
  371. package/src/orm/__tests__/PostgresProvider.spec.ts +2 -2
  372. package/src/orm/index.browser.ts +2 -2
  373. package/src/orm/index.bun.ts +4 -2
  374. package/src/orm/index.ts +21 -47
  375. package/src/orm/providers/DrizzleKitProvider.ts +3 -5
  376. package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -0
  377. package/src/orm/services/Repository.ts +18 -3
  378. package/src/queue/core/index.ts +14 -6
  379. package/src/react/auth/__tests__/$auth.spec.ts +202 -0
  380. package/src/react/auth/hooks/useAuth.ts +32 -0
  381. package/src/react/auth/index.browser.ts +13 -0
  382. package/src/react/auth/index.shared.ts +2 -0
  383. package/src/react/auth/index.ts +48 -0
  384. package/src/react/auth/providers/ReactAuthProvider.ts +16 -0
  385. package/src/react/auth/services/ReactAuth.ts +135 -0
  386. package/src/react/core/__tests__/Router.spec.tsx +169 -0
  387. package/src/react/core/components/ClientOnly.tsx +49 -0
  388. package/src/react/core/components/ErrorBoundary.tsx +73 -0
  389. package/src/react/core/contexts/AlephaContext.ts +7 -0
  390. package/src/react/core/contexts/AlephaProvider.tsx +42 -0
  391. package/src/react/core/hooks/useAction.browser.spec.tsx +569 -0
  392. package/src/react/core/hooks/useAction.ts +480 -0
  393. package/src/react/core/hooks/useAlepha.ts +26 -0
  394. package/src/react/core/hooks/useClient.ts +17 -0
  395. package/src/react/core/hooks/useEvents.ts +51 -0
  396. package/src/react/core/hooks/useInject.ts +12 -0
  397. package/src/react/core/hooks/useStore.ts +52 -0
  398. package/src/react/core/index.ts +90 -0
  399. package/src/react/form/components/FormState.tsx +17 -0
  400. package/src/react/form/errors/FormValidationError.ts +18 -0
  401. package/src/react/form/hooks/useForm.browser.spec.tsx +366 -0
  402. package/src/react/form/hooks/useForm.ts +47 -0
  403. package/src/react/form/hooks/useFormState.ts +130 -0
  404. package/src/react/form/index.ts +44 -0
  405. package/src/react/form/services/FormModel.ts +614 -0
  406. package/src/react/head/helpers/SeoExpander.spec.ts +203 -0
  407. package/src/react/head/helpers/SeoExpander.ts +142 -0
  408. package/src/react/head/hooks/useHead.spec.tsx +288 -0
  409. package/src/react/head/hooks/useHead.ts +62 -0
  410. package/src/react/head/index.browser.ts +26 -0
  411. package/src/react/head/index.ts +44 -0
  412. package/src/react/head/interfaces/Head.ts +105 -0
  413. package/src/react/head/primitives/$head.ts +25 -0
  414. package/src/react/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
  415. package/src/react/head/providers/BrowserHeadProvider.ts +212 -0
  416. package/src/react/head/providers/HeadProvider.ts +168 -0
  417. package/src/react/head/providers/ServerHeadProvider.ts +31 -0
  418. package/src/react/i18n/__tests__/integration.spec.tsx +239 -0
  419. package/src/react/i18n/components/Localize.spec.tsx +357 -0
  420. package/src/react/i18n/components/Localize.tsx +35 -0
  421. package/src/react/i18n/hooks/useI18n.browser.spec.tsx +438 -0
  422. package/src/react/i18n/hooks/useI18n.ts +18 -0
  423. package/src/react/i18n/index.ts +41 -0
  424. package/src/react/i18n/primitives/$dictionary.ts +69 -0
  425. package/src/react/i18n/providers/I18nProvider.spec.ts +389 -0
  426. package/src/react/i18n/providers/I18nProvider.ts +278 -0
  427. package/src/react/router/__tests__/page-head-browser.browser.spec.ts +95 -0
  428. package/src/react/router/__tests__/page-head.spec.ts +48 -0
  429. package/src/react/router/__tests__/seo-head.spec.ts +125 -0
  430. package/src/react/router/atoms/ssrManifestAtom.ts +58 -0
  431. package/src/react/router/components/ErrorViewer.tsx +872 -0
  432. package/src/react/router/components/Link.tsx +23 -0
  433. package/src/react/router/components/NestedView.tsx +223 -0
  434. package/src/react/router/components/NotFound.tsx +30 -0
  435. package/src/react/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
  436. package/src/react/router/contexts/RouterLayerContext.ts +12 -0
  437. package/src/react/router/errors/Redirection.ts +28 -0
  438. package/src/react/router/hooks/useActive.ts +52 -0
  439. package/src/react/router/hooks/useQueryParams.ts +63 -0
  440. package/src/react/router/hooks/useRouter.ts +20 -0
  441. package/src/react/router/hooks/useRouterState.ts +11 -0
  442. package/src/react/router/index.browser.ts +45 -0
  443. package/src/react/router/index.shared.ts +19 -0
  444. package/src/react/router/index.ts +142 -0
  445. package/src/react/router/primitives/$page.browser.spec.tsx +851 -0
  446. package/src/react/router/primitives/$page.spec.tsx +708 -0
  447. package/src/react/router/primitives/$page.ts +497 -0
  448. package/src/react/router/providers/ReactBrowserProvider.ts +309 -0
  449. package/src/react/router/providers/ReactBrowserRendererProvider.ts +25 -0
  450. package/src/react/router/providers/ReactBrowserRouterProvider.ts +168 -0
  451. package/src/react/router/providers/ReactPageProvider.ts +726 -0
  452. package/src/react/router/providers/ReactServerProvider.spec.tsx +316 -0
  453. package/src/react/router/providers/ReactServerProvider.ts +558 -0
  454. package/src/react/router/providers/ReactServerTemplateProvider.ts +979 -0
  455. package/src/react/router/providers/SSRManifestProvider.ts +334 -0
  456. package/src/react/router/services/ReactPageServerService.ts +48 -0
  457. package/src/react/router/services/ReactPageService.ts +27 -0
  458. package/src/react/router/services/ReactRouter.ts +262 -0
  459. package/src/react/websocket/hooks/useRoom.tsx +242 -0
  460. package/src/react/websocket/index.ts +7 -0
  461. package/src/redis/__tests__/redis.spec.ts +13 -0
  462. package/src/redis/index.ts +9 -25
  463. package/src/redis/providers/BunRedisProvider.ts +9 -0
  464. package/src/redis/providers/NodeRedisProvider.ts +8 -0
  465. package/src/redis/providers/RedisProvider.ts +16 -0
  466. package/src/retry/index.ts +11 -2
  467. package/src/router/index.ts +15 -0
  468. package/src/scheduler/index.ts +11 -2
  469. package/src/security/__tests__/BasicAuth.spec.ts +2 -0
  470. package/src/security/__tests__/ServerSecurityProvider.spec.ts +13 -5
  471. package/src/security/index.ts +15 -10
  472. package/src/security/interfaces/IssuerResolver.ts +27 -0
  473. package/src/security/primitives/$issuer.ts +55 -0
  474. package/src/security/providers/SecurityProvider.ts +179 -0
  475. package/src/security/providers/ServerBasicAuthProvider.ts +6 -2
  476. package/src/security/providers/ServerSecurityProvider.ts +36 -22
  477. package/src/server/auth/index.ts +12 -7
  478. package/src/server/cache/index.ts +7 -22
  479. package/src/server/compress/index.ts +10 -2
  480. package/src/server/cookies/index.ts +7 -5
  481. package/src/server/cookies/primitives/$cookie.ts +33 -11
  482. package/src/server/core/index.ts +17 -7
  483. package/src/server/core/interfaces/ServerRequest.ts +83 -1
  484. package/src/server/core/primitives/$action.spec.ts +1 -1
  485. package/src/server/core/primitives/$action.ts +8 -3
  486. package/src/server/core/providers/BunHttpServerProvider.ts +1 -1
  487. package/src/server/core/providers/NodeHttpServerProvider.spec.ts +125 -0
  488. package/src/server/core/providers/NodeHttpServerProvider.ts +77 -22
  489. package/src/server/core/providers/ServerLoggerProvider.ts +2 -2
  490. package/src/server/core/providers/ServerProvider.ts +9 -12
  491. package/src/server/core/services/ServerRequestParser.spec.ts +520 -0
  492. package/src/server/core/services/ServerRequestParser.ts +306 -13
  493. package/src/server/cors/index.ts +7 -21
  494. package/src/server/cors/primitives/$cors.ts +6 -2
  495. package/src/server/health/index.ts +8 -2
  496. package/src/server/helmet/index.ts +11 -3
  497. package/src/server/links/atoms/apiLinksAtom.ts +7 -0
  498. package/src/server/links/index.browser.ts +2 -0
  499. package/src/server/links/index.ts +13 -6
  500. package/src/server/metrics/index.ts +10 -3
  501. package/src/server/multipart/index.ts +9 -3
  502. package/src/server/proxy/index.ts +8 -2
  503. package/src/server/rate-limit/index.ts +21 -25
  504. package/src/server/rate-limit/primitives/$rateLimit.ts +6 -2
  505. package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +38 -14
  506. package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +22 -56
  507. package/src/server/static/index.ts +8 -2
  508. package/src/server/static/providers/ServerStaticProvider.ts +1 -1
  509. package/src/server/swagger/index.ts +9 -4
  510. package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
  511. package/src/sms/index.ts +9 -5
  512. package/src/sms/providers/LocalSmsProvider.spec.ts +1 -1
  513. package/src/sms/providers/LocalSmsProvider.ts +1 -1
  514. package/src/system/index.browser.ts +11 -0
  515. package/src/system/index.ts +62 -0
  516. package/src/{file → system}/providers/FileSystemProvider.ts +16 -0
  517. package/src/{file → system}/providers/MemoryFileSystemProvider.ts +116 -3
  518. package/src/system/providers/MemoryShellProvider.ts +164 -0
  519. package/src/{file → system}/providers/NodeFileSystemProvider.spec.ts +2 -2
  520. package/src/{file → system}/providers/NodeFileSystemProvider.ts +36 -0
  521. package/src/system/providers/NodeShellProvider.ts +184 -0
  522. package/src/system/providers/ShellProvider.ts +74 -0
  523. package/src/{file → system}/services/FileDetector.spec.ts +2 -2
  524. package/src/thread/index.ts +11 -2
  525. package/src/topic/core/index.ts +12 -5
  526. package/src/vite/index.ts +3 -2
  527. package/src/vite/tasks/buildClient.ts +2 -8
  528. package/src/vite/tasks/buildServer.ts +84 -21
  529. package/src/vite/tasks/copyAssets.ts +5 -4
  530. package/src/vite/tasks/generateSitemap.ts +64 -23
  531. package/src/vite/tasks/index.ts +0 -2
  532. package/src/vite/tasks/prerenderPages.ts +49 -24
  533. package/src/websocket/index.ts +12 -8
  534. package/dist/file/index.d.ts +0 -839
  535. package/dist/file/index.d.ts.map +0 -1
  536. package/dist/file/index.js.map +0 -1
  537. package/src/cli/assets/indexHtml.ts +0 -15
  538. package/src/cli/assets/mainServerTs.ts +0 -24
  539. package/src/cli/assets/webAppRouterTs.ts +0 -15
  540. package/src/cli/assets/webHelloComponentTsx.ts +0 -16
  541. package/src/cli/commands/format.ts +0 -23
  542. package/src/file/index.ts +0 -43
  543. package/src/vite/helpers/boot.ts +0 -117
  544. package/src/vite/plugins/viteAlephaDev.ts +0 -177
  545. package/src/vite/tasks/devServer.ts +0 -71
  546. package/src/vite/tasks/runAlepha.ts +0 -270
  547. /package/dist/orm/{chunk-DtkW-qnP.js → chunk-DH6iiROE.js} +0 -0
  548. /package/src/cli/{assets → templates}/apiIndexTs.ts +0 -0
  549. /package/src/cli/{assets → templates}/webIndexTs.ts +0 -0
  550. /package/src/{file → system}/errors/FileError.ts +0 -0
  551. /package/src/{file → system}/services/FileDetector.ts +0 -0
@@ -1,6 +1,11 @@
1
- import { $atom, $env, $hook, $inject, $module, $use, KIND, Primitive, createPrimitive, t } from "alepha";
1
+ import { $atom, $env, $hook, $inject, $module, $use, AlephaError, Json, KIND, Primitive, createPrimitive, isFileLike, t } from "alepha";
2
2
  import { $logger } from "alepha/logger";
3
- import { FileSystemProvider } from "alepha/file";
3
+ import { join } from "node:path";
4
+ import { createReadStream } from "node:fs";
5
+ import { access, copyFile, cp, mkdir, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
6
+ import { PassThrough, Readable } from "node:stream";
7
+ import { fileURLToPath } from "node:url";
8
+ import { exec, spawn } from "node:child_process";
4
9
  import nodemailer from "nodemailer";
5
10
 
6
11
  //#region ../../src/email/providers/EmailProvider.ts
@@ -91,6 +96,1841 @@ var EmailPrimitive = class extends Primitive {
91
96
  };
92
97
  $email[KIND] = EmailPrimitive;
93
98
 
99
+ //#endregion
100
+ //#region ../../src/system/providers/FileSystemProvider.ts
101
+ /**
102
+ * FileSystem interface providing utilities for working with files.
103
+ */
104
+ var FileSystemProvider = class {};
105
+
106
+ //#endregion
107
+ //#region ../../src/system/providers/MemoryFileSystemProvider.ts
108
+ /**
109
+ * In-memory implementation of FileSystemProvider for testing.
110
+ *
111
+ * This provider stores all files and directories in memory, making it ideal for
112
+ * unit tests that need to verify file operations without touching the real file system.
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * // In tests, substitute the real FileSystemProvider with MemoryFileSystemProvider
117
+ * const alepha = Alepha.create().with({
118
+ * provide: FileSystemProvider,
119
+ * use: MemoryFileSystemProvider,
120
+ * });
121
+ *
122
+ * // Run code that uses FileSystemProvider
123
+ * const service = alepha.inject(MyService);
124
+ * await service.saveFile("test.txt", "Hello World");
125
+ *
126
+ * // Verify the file was written
127
+ * const memoryFs = alepha.inject(MemoryFileSystemProvider);
128
+ * expect(memoryFs.files.get("test.txt")?.toString()).toBe("Hello World");
129
+ * ```
130
+ */
131
+ var MemoryFileSystemProvider = class {
132
+ json = $inject(Json);
133
+ /**
134
+ * In-memory storage for files (path -> content)
135
+ */
136
+ files = /* @__PURE__ */ new Map();
137
+ /**
138
+ * In-memory storage for directories
139
+ */
140
+ directories = /* @__PURE__ */ new Set();
141
+ /**
142
+ * Track mkdir calls for test assertions
143
+ */
144
+ mkdirCalls = [];
145
+ /**
146
+ * Track writeFile calls for test assertions
147
+ */
148
+ writeFileCalls = [];
149
+ /**
150
+ * Track readFile calls for test assertions
151
+ */
152
+ readFileCalls = [];
153
+ /**
154
+ * Track rm calls for test assertions
155
+ */
156
+ rmCalls = [];
157
+ /**
158
+ * Track join calls for test assertions
159
+ */
160
+ joinCalls = [];
161
+ /**
162
+ * Error to throw on mkdir (for testing error handling)
163
+ */
164
+ mkdirError = null;
165
+ /**
166
+ * Error to throw on writeFile (for testing error handling)
167
+ */
168
+ writeFileError = null;
169
+ /**
170
+ * Error to throw on readFile (for testing error handling)
171
+ */
172
+ readFileError = null;
173
+ constructor(options = {}) {
174
+ this.mkdirError = options.mkdirError ?? null;
175
+ this.writeFileError = options.writeFileError ?? null;
176
+ this.readFileError = options.readFileError ?? null;
177
+ }
178
+ /**
179
+ * Join path segments using forward slashes.
180
+ * Uses Node's path.join for proper normalization (handles .. and .)
181
+ */
182
+ join(...paths) {
183
+ this.joinCalls.push(paths);
184
+ return join(...paths);
185
+ }
186
+ /**
187
+ * Create a FileLike object from various sources.
188
+ */
189
+ createFile(options) {
190
+ if ("path" in options) {
191
+ const filePath = options.path;
192
+ const buffer = this.files.get(filePath);
193
+ if (buffer === void 0) throw new Error(`ENOENT: no such file or directory, open '${filePath}'`);
194
+ return {
195
+ name: options.name ?? filePath.split("/").pop() ?? "file",
196
+ type: options.type ?? "application/octet-stream",
197
+ size: buffer.byteLength,
198
+ lastModified: Date.now(),
199
+ stream: () => {
200
+ throw new Error("Stream not implemented in MemoryFileSystemProvider");
201
+ },
202
+ arrayBuffer: async () => buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength),
203
+ text: async () => buffer.toString("utf-8")
204
+ };
205
+ }
206
+ if ("buffer" in options) {
207
+ const buffer = options.buffer;
208
+ return {
209
+ name: options.name ?? "file",
210
+ type: options.type ?? "application/octet-stream",
211
+ size: buffer.byteLength,
212
+ lastModified: Date.now(),
213
+ stream: () => {
214
+ throw new Error("Stream not implemented in MemoryFileSystemProvider");
215
+ },
216
+ arrayBuffer: async () => buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength),
217
+ text: async () => buffer.toString("utf-8")
218
+ };
219
+ }
220
+ if ("text" in options) {
221
+ const buffer = Buffer.from(options.text, "utf-8");
222
+ return {
223
+ name: options.name ?? "file.txt",
224
+ type: options.type ?? "text/plain",
225
+ size: buffer.byteLength,
226
+ lastModified: Date.now(),
227
+ stream: () => {
228
+ throw new Error("Stream not implemented in MemoryFileSystemProvider");
229
+ },
230
+ arrayBuffer: async () => buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength),
231
+ text: async () => options.text
232
+ };
233
+ }
234
+ throw new Error("MemoryFileSystemProvider.createFile: unsupported options. Only buffer and text are supported.");
235
+ }
236
+ /**
237
+ * Remove a file or directory from memory.
238
+ */
239
+ async rm(path, options) {
240
+ this.rmCalls.push({
241
+ path,
242
+ options
243
+ });
244
+ if (!(this.files.has(path) || this.directories.has(path)) && !options?.force) throw new Error(`ENOENT: no such file or directory, rm '${path}'`);
245
+ if (this.directories.has(path)) if (options?.recursive) {
246
+ this.directories.delete(path);
247
+ for (const filePath of this.files.keys()) if (filePath.startsWith(`${path}/`)) this.files.delete(filePath);
248
+ for (const dirPath of this.directories) if (dirPath.startsWith(`${path}/`)) this.directories.delete(dirPath);
249
+ } else throw new Error(`EISDIR: illegal operation on a directory, rm '${path}'`);
250
+ else this.files.delete(path);
251
+ }
252
+ /**
253
+ * Copy a file or directory in memory.
254
+ */
255
+ async cp(src, dest, options) {
256
+ if (this.directories.has(src)) {
257
+ if (!options?.recursive) throw new Error(`Cannot copy directory without recursive option: ${src}`);
258
+ this.directories.add(dest);
259
+ for (const [filePath, content] of this.files) if (filePath.startsWith(`${src}/`)) {
260
+ const newPath = filePath.replace(src, dest);
261
+ this.files.set(newPath, Buffer.from(content));
262
+ }
263
+ } else if (this.files.has(src)) {
264
+ const content = this.files.get(src);
265
+ this.files.set(dest, Buffer.from(content));
266
+ } else throw new Error(`ENOENT: no such file or directory, cp '${src}'`);
267
+ }
268
+ /**
269
+ * Move/rename a file or directory in memory.
270
+ */
271
+ async mv(src, dest) {
272
+ if (this.directories.has(src)) {
273
+ this.directories.delete(src);
274
+ this.directories.add(dest);
275
+ for (const [filePath, content] of this.files) if (filePath.startsWith(`${src}/`)) {
276
+ const newPath = filePath.replace(src, dest);
277
+ this.files.delete(filePath);
278
+ this.files.set(newPath, content);
279
+ }
280
+ } else if (this.files.has(src)) {
281
+ const content = this.files.get(src);
282
+ this.files.delete(src);
283
+ this.files.set(dest, content);
284
+ } else throw new Error(`ENOENT: no such file or directory, mv '${src}'`);
285
+ }
286
+ /**
287
+ * Create a directory in memory.
288
+ */
289
+ async mkdir(path, options) {
290
+ this.mkdirCalls.push({
291
+ path,
292
+ options
293
+ });
294
+ if (this.mkdirError) throw this.mkdirError;
295
+ if (this.directories.has(path) && !options?.recursive) throw new Error(`EEXIST: file already exists, mkdir '${path}'`);
296
+ this.directories.add(path);
297
+ if (options?.recursive) {
298
+ const parts = path.split("/").filter(Boolean);
299
+ let current = "";
300
+ for (const part of parts) {
301
+ current = current ? `${current}/${part}` : part;
302
+ this.directories.add(current);
303
+ }
304
+ }
305
+ }
306
+ /**
307
+ * List files in a directory.
308
+ */
309
+ async ls(path, options) {
310
+ const normalizedPath = path.replace(/\/$/, "");
311
+ const entries = /* @__PURE__ */ new Set();
312
+ for (const filePath of this.files.keys()) if (filePath.startsWith(`${normalizedPath}/`)) {
313
+ const relativePath = filePath.slice(normalizedPath.length + 1);
314
+ const parts = relativePath.split("/");
315
+ if (options?.recursive) entries.add(relativePath);
316
+ else entries.add(parts[0]);
317
+ }
318
+ for (const dirPath of this.directories) if (dirPath.startsWith(`${normalizedPath}/`) && dirPath !== normalizedPath) {
319
+ const relativePath = dirPath.slice(normalizedPath.length + 1);
320
+ const parts = relativePath.split("/");
321
+ if (options?.recursive) entries.add(relativePath);
322
+ else if (parts.length === 1) entries.add(parts[0]);
323
+ }
324
+ let result = Array.from(entries);
325
+ if (!options?.hidden) result = result.filter((entry) => !entry.startsWith("."));
326
+ return result.sort();
327
+ }
328
+ /**
329
+ * Check if a file or directory exists in memory.
330
+ */
331
+ async exists(path) {
332
+ return this.files.has(path) || this.directories.has(path);
333
+ }
334
+ /**
335
+ * Read a file from memory.
336
+ */
337
+ async readFile(path) {
338
+ this.readFileCalls.push(path);
339
+ if (this.readFileError) throw this.readFileError;
340
+ const content = this.files.get(path);
341
+ if (!content) throw new Error(`ENOENT: no such file or directory, open '${path}'`);
342
+ return content;
343
+ }
344
+ /**
345
+ * Read a file from memory as text.
346
+ */
347
+ async readTextFile(path) {
348
+ return (await this.readFile(path)).toString("utf-8");
349
+ }
350
+ /**
351
+ * Read a file from memory as JSON.
352
+ */
353
+ async readJsonFile(path) {
354
+ const text = await this.readTextFile(path);
355
+ return this.json.parse(text);
356
+ }
357
+ /**
358
+ * Write a file to memory.
359
+ */
360
+ async writeFile(path, data) {
361
+ const dataStr = typeof data === "string" ? data : data instanceof Buffer || data instanceof Uint8Array ? data.toString("utf-8") : await data.text();
362
+ this.writeFileCalls.push({
363
+ path,
364
+ data: dataStr
365
+ });
366
+ if (this.writeFileError) throw this.writeFileError;
367
+ const buffer = typeof data === "string" ? Buffer.from(data, "utf-8") : data instanceof Buffer ? data : data instanceof Uint8Array ? Buffer.from(data) : Buffer.from(await data.text(), "utf-8");
368
+ this.files.set(path, buffer);
369
+ }
370
+ /**
371
+ * Reset all in-memory state (useful between tests).
372
+ */
373
+ reset() {
374
+ this.files.clear();
375
+ this.directories.clear();
376
+ this.mkdirCalls = [];
377
+ this.writeFileCalls = [];
378
+ this.readFileCalls = [];
379
+ this.rmCalls = [];
380
+ this.joinCalls = [];
381
+ this.mkdirError = null;
382
+ this.writeFileError = null;
383
+ this.readFileError = null;
384
+ }
385
+ /**
386
+ * Check if a file was written during the test.
387
+ *
388
+ * @example
389
+ * ```typescript
390
+ * expect(fs.wasWritten("/project/tsconfig.json")).toBe(true);
391
+ * ```
392
+ */
393
+ wasWritten(path) {
394
+ return this.writeFileCalls.some((call) => call.path === path);
395
+ }
396
+ /**
397
+ * Check if a file was written with content matching a pattern.
398
+ *
399
+ * @example
400
+ * ```typescript
401
+ * expect(fs.wasWrittenMatching("/project/tsconfig.json", /extends/)).toBe(true);
402
+ * ```
403
+ */
404
+ wasWrittenMatching(path, pattern) {
405
+ const call = this.writeFileCalls.find((c) => c.path === path);
406
+ return call ? pattern.test(call.data) : false;
407
+ }
408
+ /**
409
+ * Check if a file was read during the test.
410
+ *
411
+ * @example
412
+ * ```typescript
413
+ * expect(fs.wasRead("/project/package.json")).toBe(true);
414
+ * ```
415
+ */
416
+ wasRead(path) {
417
+ return this.readFileCalls.includes(path);
418
+ }
419
+ /**
420
+ * Check if a file was deleted during the test.
421
+ *
422
+ * @example
423
+ * ```typescript
424
+ * expect(fs.wasDeleted("/project/old-file.txt")).toBe(true);
425
+ * ```
426
+ */
427
+ wasDeleted(path) {
428
+ return this.rmCalls.some((call) => call.path === path);
429
+ }
430
+ /**
431
+ * Get the content of a file as a string (convenience method for testing).
432
+ */
433
+ getFileContent(path) {
434
+ return this.files.get(path)?.toString("utf-8");
435
+ }
436
+ };
437
+
438
+ //#endregion
439
+ //#region ../../src/system/providers/MemoryShellProvider.ts
440
+ /**
441
+ * In-memory implementation of ShellProvider for testing.
442
+ *
443
+ * Records all commands that would be executed without actually running them.
444
+ * Can be configured to return specific outputs or throw errors for testing.
445
+ *
446
+ * @example
447
+ * ```typescript
448
+ * // In tests, substitute the real ShellProvider with MemoryShellProvider
449
+ * const alepha = Alepha.create().with({
450
+ * provide: ShellProvider,
451
+ * use: MemoryShellProvider,
452
+ * });
453
+ *
454
+ * // Configure mock behavior
455
+ * const shell = alepha.inject(MemoryShellProvider);
456
+ * shell.configure({
457
+ * outputs: { "echo hello": "hello\n" },
458
+ * errors: { "failing-cmd": "Command failed" },
459
+ * });
460
+ *
461
+ * // Or use the fluent API
462
+ * shell.outputs.set("another-cmd", "output");
463
+ * shell.errors.set("another-error", "Error message");
464
+ *
465
+ * // Run code that uses ShellProvider
466
+ * const service = alepha.inject(MyService);
467
+ * await service.doSomething();
468
+ *
469
+ * // Verify commands were called
470
+ * expect(shell.calls).toHaveLength(2);
471
+ * expect(shell.calls[0].command).toBe("yarn install");
472
+ * ```
473
+ */
474
+ var MemoryShellProvider = class {
475
+ /**
476
+ * All recorded shell calls.
477
+ */
478
+ calls = [];
479
+ /**
480
+ * Simulated outputs for specific commands.
481
+ */
482
+ outputs = /* @__PURE__ */ new Map();
483
+ /**
484
+ * Commands that should throw an error.
485
+ */
486
+ errors = /* @__PURE__ */ new Map();
487
+ /**
488
+ * Commands considered installed in the system PATH.
489
+ */
490
+ installedCommands = /* @__PURE__ */ new Set();
491
+ /**
492
+ * Configure the mock with predefined outputs, errors, and installed commands.
493
+ */
494
+ configure(options) {
495
+ if (options.outputs) for (const [cmd, output] of Object.entries(options.outputs)) this.outputs.set(cmd, output);
496
+ if (options.errors) for (const [cmd, error] of Object.entries(options.errors)) this.errors.set(cmd, error);
497
+ if (options.installedCommands) for (const cmd of options.installedCommands) this.installedCommands.add(cmd);
498
+ return this;
499
+ }
500
+ /**
501
+ * Record command and return simulated output.
502
+ */
503
+ async run(command, options = {}) {
504
+ this.calls.push({
505
+ command,
506
+ options
507
+ });
508
+ const errorMsg = this.errors.get(command);
509
+ if (errorMsg) throw new Error(errorMsg);
510
+ return this.outputs.get(command) ?? "";
511
+ }
512
+ /**
513
+ * Check if a specific command was called.
514
+ */
515
+ wasCalled(command) {
516
+ return this.calls.some((call) => call.command === command);
517
+ }
518
+ /**
519
+ * Check if a command matching a pattern was called.
520
+ */
521
+ wasCalledMatching(pattern) {
522
+ return this.calls.some((call) => pattern.test(call.command));
523
+ }
524
+ /**
525
+ * Get all calls matching a pattern.
526
+ */
527
+ getCallsMatching(pattern) {
528
+ return this.calls.filter((call) => pattern.test(call.command));
529
+ }
530
+ /**
531
+ * Check if a command is installed.
532
+ */
533
+ async isInstalled(command) {
534
+ return this.installedCommands.has(command);
535
+ }
536
+ /**
537
+ * Reset all recorded state.
538
+ */
539
+ reset() {
540
+ this.calls = [];
541
+ this.outputs.clear();
542
+ this.errors.clear();
543
+ this.installedCommands.clear();
544
+ }
545
+ };
546
+
547
+ //#endregion
548
+ //#region ../../src/system/services/FileDetector.ts
549
+ /**
550
+ * Service for detecting file types and getting content types.
551
+ *
552
+ * @example
553
+ * ```typescript
554
+ * const detector = alepha.inject(FileDetector);
555
+ *
556
+ * // Get content type from filename
557
+ * const mimeType = detector.getContentType("image.png"); // "image/png"
558
+ *
559
+ * // Detect file type by magic bytes
560
+ * const stream = createReadStream('image.png');
561
+ * const result = await detector.detectFileType(stream, 'image.png');
562
+ * console.log(result.mimeType); // 'image/png'
563
+ * console.log(result.verified); // true if magic bytes match
564
+ * ```
565
+ */
566
+ var FileDetector = class FileDetector {
567
+ /**
568
+ * Magic byte signatures for common file formats.
569
+ * Each signature is represented as an array of bytes or null (wildcard).
570
+ */
571
+ static MAGIC_BYTES = {
572
+ png: [{
573
+ signature: [
574
+ 137,
575
+ 80,
576
+ 78,
577
+ 71,
578
+ 13,
579
+ 10,
580
+ 26,
581
+ 10
582
+ ],
583
+ mimeType: "image/png"
584
+ }],
585
+ jpg: [
586
+ {
587
+ signature: [
588
+ 255,
589
+ 216,
590
+ 255,
591
+ 224
592
+ ],
593
+ mimeType: "image/jpeg"
594
+ },
595
+ {
596
+ signature: [
597
+ 255,
598
+ 216,
599
+ 255,
600
+ 225
601
+ ],
602
+ mimeType: "image/jpeg"
603
+ },
604
+ {
605
+ signature: [
606
+ 255,
607
+ 216,
608
+ 255,
609
+ 226
610
+ ],
611
+ mimeType: "image/jpeg"
612
+ },
613
+ {
614
+ signature: [
615
+ 255,
616
+ 216,
617
+ 255,
618
+ 227
619
+ ],
620
+ mimeType: "image/jpeg"
621
+ },
622
+ {
623
+ signature: [
624
+ 255,
625
+ 216,
626
+ 255,
627
+ 232
628
+ ],
629
+ mimeType: "image/jpeg"
630
+ }
631
+ ],
632
+ jpeg: [
633
+ {
634
+ signature: [
635
+ 255,
636
+ 216,
637
+ 255,
638
+ 224
639
+ ],
640
+ mimeType: "image/jpeg"
641
+ },
642
+ {
643
+ signature: [
644
+ 255,
645
+ 216,
646
+ 255,
647
+ 225
648
+ ],
649
+ mimeType: "image/jpeg"
650
+ },
651
+ {
652
+ signature: [
653
+ 255,
654
+ 216,
655
+ 255,
656
+ 226
657
+ ],
658
+ mimeType: "image/jpeg"
659
+ },
660
+ {
661
+ signature: [
662
+ 255,
663
+ 216,
664
+ 255,
665
+ 227
666
+ ],
667
+ mimeType: "image/jpeg"
668
+ },
669
+ {
670
+ signature: [
671
+ 255,
672
+ 216,
673
+ 255,
674
+ 232
675
+ ],
676
+ mimeType: "image/jpeg"
677
+ }
678
+ ],
679
+ gif: [{
680
+ signature: [
681
+ 71,
682
+ 73,
683
+ 70,
684
+ 56,
685
+ 55,
686
+ 97
687
+ ],
688
+ mimeType: "image/gif"
689
+ }, {
690
+ signature: [
691
+ 71,
692
+ 73,
693
+ 70,
694
+ 56,
695
+ 57,
696
+ 97
697
+ ],
698
+ mimeType: "image/gif"
699
+ }],
700
+ webp: [{
701
+ signature: [
702
+ 82,
703
+ 73,
704
+ 70,
705
+ 70,
706
+ null,
707
+ null,
708
+ null,
709
+ null,
710
+ 87,
711
+ 69,
712
+ 66,
713
+ 80
714
+ ],
715
+ mimeType: "image/webp"
716
+ }],
717
+ bmp: [{
718
+ signature: [66, 77],
719
+ mimeType: "image/bmp"
720
+ }],
721
+ ico: [{
722
+ signature: [
723
+ 0,
724
+ 0,
725
+ 1,
726
+ 0
727
+ ],
728
+ mimeType: "image/x-icon"
729
+ }],
730
+ tiff: [{
731
+ signature: [
732
+ 73,
733
+ 73,
734
+ 42,
735
+ 0
736
+ ],
737
+ mimeType: "image/tiff"
738
+ }, {
739
+ signature: [
740
+ 77,
741
+ 77,
742
+ 0,
743
+ 42
744
+ ],
745
+ mimeType: "image/tiff"
746
+ }],
747
+ tif: [{
748
+ signature: [
749
+ 73,
750
+ 73,
751
+ 42,
752
+ 0
753
+ ],
754
+ mimeType: "image/tiff"
755
+ }, {
756
+ signature: [
757
+ 77,
758
+ 77,
759
+ 0,
760
+ 42
761
+ ],
762
+ mimeType: "image/tiff"
763
+ }],
764
+ pdf: [{
765
+ signature: [
766
+ 37,
767
+ 80,
768
+ 68,
769
+ 70,
770
+ 45
771
+ ],
772
+ mimeType: "application/pdf"
773
+ }],
774
+ zip: [
775
+ {
776
+ signature: [
777
+ 80,
778
+ 75,
779
+ 3,
780
+ 4
781
+ ],
782
+ mimeType: "application/zip"
783
+ },
784
+ {
785
+ signature: [
786
+ 80,
787
+ 75,
788
+ 5,
789
+ 6
790
+ ],
791
+ mimeType: "application/zip"
792
+ },
793
+ {
794
+ signature: [
795
+ 80,
796
+ 75,
797
+ 7,
798
+ 8
799
+ ],
800
+ mimeType: "application/zip"
801
+ }
802
+ ],
803
+ rar: [{
804
+ signature: [
805
+ 82,
806
+ 97,
807
+ 114,
808
+ 33,
809
+ 26,
810
+ 7
811
+ ],
812
+ mimeType: "application/vnd.rar"
813
+ }],
814
+ "7z": [{
815
+ signature: [
816
+ 55,
817
+ 122,
818
+ 188,
819
+ 175,
820
+ 39,
821
+ 28
822
+ ],
823
+ mimeType: "application/x-7z-compressed"
824
+ }],
825
+ tar: [{
826
+ signature: [
827
+ 117,
828
+ 115,
829
+ 116,
830
+ 97,
831
+ 114
832
+ ],
833
+ mimeType: "application/x-tar"
834
+ }],
835
+ gz: [{
836
+ signature: [31, 139],
837
+ mimeType: "application/gzip"
838
+ }],
839
+ tgz: [{
840
+ signature: [31, 139],
841
+ mimeType: "application/gzip"
842
+ }],
843
+ mp3: [
844
+ {
845
+ signature: [255, 251],
846
+ mimeType: "audio/mpeg"
847
+ },
848
+ {
849
+ signature: [255, 243],
850
+ mimeType: "audio/mpeg"
851
+ },
852
+ {
853
+ signature: [255, 242],
854
+ mimeType: "audio/mpeg"
855
+ },
856
+ {
857
+ signature: [
858
+ 73,
859
+ 68,
860
+ 51
861
+ ],
862
+ mimeType: "audio/mpeg"
863
+ }
864
+ ],
865
+ wav: [{
866
+ signature: [
867
+ 82,
868
+ 73,
869
+ 70,
870
+ 70,
871
+ null,
872
+ null,
873
+ null,
874
+ null,
875
+ 87,
876
+ 65,
877
+ 86,
878
+ 69
879
+ ],
880
+ mimeType: "audio/wav"
881
+ }],
882
+ ogg: [{
883
+ signature: [
884
+ 79,
885
+ 103,
886
+ 103,
887
+ 83
888
+ ],
889
+ mimeType: "audio/ogg"
890
+ }],
891
+ flac: [{
892
+ signature: [
893
+ 102,
894
+ 76,
895
+ 97,
896
+ 67
897
+ ],
898
+ mimeType: "audio/flac"
899
+ }],
900
+ mp4: [
901
+ {
902
+ signature: [
903
+ null,
904
+ null,
905
+ null,
906
+ null,
907
+ 102,
908
+ 116,
909
+ 121,
910
+ 112
911
+ ],
912
+ mimeType: "video/mp4"
913
+ },
914
+ {
915
+ signature: [
916
+ null,
917
+ null,
918
+ null,
919
+ null,
920
+ 102,
921
+ 116,
922
+ 121,
923
+ 112,
924
+ 105,
925
+ 115,
926
+ 111,
927
+ 109
928
+ ],
929
+ mimeType: "video/mp4"
930
+ },
931
+ {
932
+ signature: [
933
+ null,
934
+ null,
935
+ null,
936
+ null,
937
+ 102,
938
+ 116,
939
+ 121,
940
+ 112,
941
+ 109,
942
+ 112,
943
+ 52,
944
+ 50
945
+ ],
946
+ mimeType: "video/mp4"
947
+ }
948
+ ],
949
+ webm: [{
950
+ signature: [
951
+ 26,
952
+ 69,
953
+ 223,
954
+ 163
955
+ ],
956
+ mimeType: "video/webm"
957
+ }],
958
+ avi: [{
959
+ signature: [
960
+ 82,
961
+ 73,
962
+ 70,
963
+ 70,
964
+ null,
965
+ null,
966
+ null,
967
+ null,
968
+ 65,
969
+ 86,
970
+ 73,
971
+ 32
972
+ ],
973
+ mimeType: "video/x-msvideo"
974
+ }],
975
+ mov: [{
976
+ signature: [
977
+ null,
978
+ null,
979
+ null,
980
+ null,
981
+ 102,
982
+ 116,
983
+ 121,
984
+ 112,
985
+ 113,
986
+ 116,
987
+ 32,
988
+ 32
989
+ ],
990
+ mimeType: "video/quicktime"
991
+ }],
992
+ mkv: [{
993
+ signature: [
994
+ 26,
995
+ 69,
996
+ 223,
997
+ 163
998
+ ],
999
+ mimeType: "video/x-matroska"
1000
+ }],
1001
+ docx: [{
1002
+ signature: [
1003
+ 80,
1004
+ 75,
1005
+ 3,
1006
+ 4
1007
+ ],
1008
+ mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
1009
+ }],
1010
+ xlsx: [{
1011
+ signature: [
1012
+ 80,
1013
+ 75,
1014
+ 3,
1015
+ 4
1016
+ ],
1017
+ mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
1018
+ }],
1019
+ pptx: [{
1020
+ signature: [
1021
+ 80,
1022
+ 75,
1023
+ 3,
1024
+ 4
1025
+ ],
1026
+ mimeType: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
1027
+ }],
1028
+ doc: [{
1029
+ signature: [
1030
+ 208,
1031
+ 207,
1032
+ 17,
1033
+ 224,
1034
+ 161,
1035
+ 177,
1036
+ 26,
1037
+ 225
1038
+ ],
1039
+ mimeType: "application/msword"
1040
+ }],
1041
+ xls: [{
1042
+ signature: [
1043
+ 208,
1044
+ 207,
1045
+ 17,
1046
+ 224,
1047
+ 161,
1048
+ 177,
1049
+ 26,
1050
+ 225
1051
+ ],
1052
+ mimeType: "application/vnd.ms-excel"
1053
+ }],
1054
+ ppt: [{
1055
+ signature: [
1056
+ 208,
1057
+ 207,
1058
+ 17,
1059
+ 224,
1060
+ 161,
1061
+ 177,
1062
+ 26,
1063
+ 225
1064
+ ],
1065
+ mimeType: "application/vnd.ms-powerpoint"
1066
+ }]
1067
+ };
1068
+ /**
1069
+ * All possible format signatures for checking against actual file content
1070
+ */
1071
+ static ALL_SIGNATURES = Object.entries(FileDetector.MAGIC_BYTES).flatMap(([ext, signatures]) => signatures.map((sig) => ({
1072
+ ext,
1073
+ ...sig
1074
+ })));
1075
+ /**
1076
+ * MIME type map for file extensions.
1077
+ *
1078
+ * Can be used to get the content type of file based on its extension.
1079
+ * Feel free to add more mime types in your project!
1080
+ */
1081
+ static mimeMap = {
1082
+ json: "application/json",
1083
+ txt: "text/plain",
1084
+ html: "text/html",
1085
+ htm: "text/html",
1086
+ xml: "application/xml",
1087
+ csv: "text/csv",
1088
+ pdf: "application/pdf",
1089
+ md: "text/markdown",
1090
+ markdown: "text/markdown",
1091
+ rtf: "application/rtf",
1092
+ css: "text/css",
1093
+ js: "application/javascript",
1094
+ mjs: "application/javascript",
1095
+ ts: "application/typescript",
1096
+ jsx: "text/jsx",
1097
+ tsx: "text/tsx",
1098
+ zip: "application/zip",
1099
+ rar: "application/vnd.rar",
1100
+ "7z": "application/x-7z-compressed",
1101
+ tar: "application/x-tar",
1102
+ gz: "application/gzip",
1103
+ tgz: "application/gzip",
1104
+ png: "image/png",
1105
+ jpg: "image/jpeg",
1106
+ jpeg: "image/jpeg",
1107
+ gif: "image/gif",
1108
+ webp: "image/webp",
1109
+ svg: "image/svg+xml",
1110
+ bmp: "image/bmp",
1111
+ ico: "image/x-icon",
1112
+ tiff: "image/tiff",
1113
+ tif: "image/tiff",
1114
+ mp3: "audio/mpeg",
1115
+ wav: "audio/wav",
1116
+ ogg: "audio/ogg",
1117
+ m4a: "audio/mp4",
1118
+ aac: "audio/aac",
1119
+ flac: "audio/flac",
1120
+ mp4: "video/mp4",
1121
+ webm: "video/webm",
1122
+ avi: "video/x-msvideo",
1123
+ mov: "video/quicktime",
1124
+ wmv: "video/x-ms-wmv",
1125
+ flv: "video/x-flv",
1126
+ mkv: "video/x-matroska",
1127
+ doc: "application/msword",
1128
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1129
+ xls: "application/vnd.ms-excel",
1130
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
1131
+ ppt: "application/vnd.ms-powerpoint",
1132
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
1133
+ woff: "font/woff",
1134
+ woff2: "font/woff2",
1135
+ ttf: "font/ttf",
1136
+ otf: "font/otf",
1137
+ eot: "application/vnd.ms-fontobject"
1138
+ };
1139
+ /**
1140
+ * Reverse MIME type map for looking up extensions from MIME types.
1141
+ * Prefers shorter, more common extensions when multiple exist.
1142
+ */
1143
+ static reverseMimeMap = (() => {
1144
+ const reverse = {};
1145
+ for (const [ext, mimeType] of Object.entries(FileDetector.mimeMap)) if (!reverse[mimeType]) reverse[mimeType] = ext;
1146
+ return reverse;
1147
+ })();
1148
+ /**
1149
+ * Returns the file extension for a given MIME type.
1150
+ *
1151
+ * @param mimeType - The MIME type to look up
1152
+ * @returns The file extension (without dot), or "bin" if not found
1153
+ *
1154
+ * @example
1155
+ * ```typescript
1156
+ * const detector = alepha.inject(FileDetector);
1157
+ * const ext = detector.getExtensionFromMimeType("image/png"); // "png"
1158
+ * const ext2 = detector.getExtensionFromMimeType("application/octet-stream"); // "bin"
1159
+ * ```
1160
+ */
1161
+ getExtensionFromMimeType(mimeType) {
1162
+ return FileDetector.reverseMimeMap[mimeType] || "bin";
1163
+ }
1164
+ /**
1165
+ * Returns the content type of file based on its filename.
1166
+ *
1167
+ * @param filename - The filename to check
1168
+ * @returns The MIME type
1169
+ *
1170
+ * @example
1171
+ * ```typescript
1172
+ * const detector = alepha.inject(FileDetector);
1173
+ * const mimeType = detector.getContentType("image.png"); // "image/png"
1174
+ * ```
1175
+ */
1176
+ getContentType(filename) {
1177
+ const ext = filename.toLowerCase().split(".").pop() || "";
1178
+ return FileDetector.mimeMap[ext] || "application/octet-stream";
1179
+ }
1180
+ /**
1181
+ * Detects the file type by checking magic bytes against the stream content.
1182
+ *
1183
+ * @param stream - The readable stream to check
1184
+ * @param filename - The filename (used to get the extension)
1185
+ * @returns File type information including MIME type, extension, and verification status
1186
+ *
1187
+ * @example
1188
+ * ```typescript
1189
+ * const detector = alepha.inject(FileDetector);
1190
+ * const stream = createReadStream('image.png');
1191
+ * const result = await detector.detectFileType(stream, 'image.png');
1192
+ * console.log(result.mimeType); // 'image/png'
1193
+ * console.log(result.verified); // true if magic bytes match
1194
+ * ```
1195
+ */
1196
+ async detectFileType(stream, filename) {
1197
+ const expectedMimeType = this.getContentType(filename);
1198
+ const lastDotIndex = filename.lastIndexOf(".");
1199
+ const ext = lastDotIndex > 0 ? filename.substring(lastDotIndex + 1).toLowerCase() : "";
1200
+ const { buffer, stream: newStream } = await this.peekBytes(stream, 16);
1201
+ const expectedSignatures = FileDetector.MAGIC_BYTES[ext];
1202
+ if (expectedSignatures) {
1203
+ for (const { signature, mimeType } of expectedSignatures) if (this.matchesSignature(buffer, signature)) return {
1204
+ mimeType,
1205
+ extension: ext,
1206
+ verified: true,
1207
+ stream: newStream
1208
+ };
1209
+ }
1210
+ for (const { ext: detectedExt, signature, mimeType } of FileDetector.ALL_SIGNATURES) if (detectedExt !== ext && this.matchesSignature(buffer, signature)) return {
1211
+ mimeType,
1212
+ extension: detectedExt,
1213
+ verified: true,
1214
+ stream: newStream
1215
+ };
1216
+ return {
1217
+ mimeType: expectedMimeType,
1218
+ extension: ext,
1219
+ verified: false,
1220
+ stream: newStream
1221
+ };
1222
+ }
1223
+ /**
1224
+ * Reads all bytes from a stream and returns the first N bytes along with a new stream containing all data.
1225
+ * This approach reads the entire stream upfront to avoid complex async handling issues.
1226
+ *
1227
+ * @protected
1228
+ */
1229
+ async peekBytes(stream, numBytes) {
1230
+ const chunks = [];
1231
+ for await (const chunk of stream) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
1232
+ const allData = Buffer.concat(chunks);
1233
+ return {
1234
+ buffer: allData.subarray(0, numBytes),
1235
+ stream: Readable.from(allData)
1236
+ };
1237
+ }
1238
+ /**
1239
+ * Checks if a buffer matches a magic byte signature.
1240
+ *
1241
+ * @protected
1242
+ */
1243
+ matchesSignature(buffer, signature) {
1244
+ if (buffer.length < signature.length) return false;
1245
+ for (let i = 0; i < signature.length; i++) if (signature[i] !== null && buffer[i] !== signature[i]) return false;
1246
+ return true;
1247
+ }
1248
+ };
1249
+
1250
+ //#endregion
1251
+ //#region ../../src/system/providers/NodeFileSystemProvider.ts
1252
+ /**
1253
+ * Node.js implementation of FileSystem interface.
1254
+ *
1255
+ * @example
1256
+ * ```typescript
1257
+ * const fs = alepha.inject(NodeFileSystemProvider);
1258
+ *
1259
+ * // Create from URL
1260
+ * const file1 = fs.createFile({ url: "file:///path/to/file.png" });
1261
+ *
1262
+ * // Create from Buffer
1263
+ * const file2 = fs.createFile({ buffer: Buffer.from("hello"), name: "hello.txt" });
1264
+ *
1265
+ * // Create from text
1266
+ * const file3 = fs.createFile({ text: "Hello, world!", name: "greeting.txt" });
1267
+ *
1268
+ * // File operations
1269
+ * await fs.mkdir("/tmp/mydir", { recursive: true });
1270
+ * await fs.cp("/src/file.txt", "/dest/file.txt");
1271
+ * await fs.mv("/old/path.txt", "/new/path.txt");
1272
+ * const files = await fs.ls("/tmp");
1273
+ * await fs.rm("/tmp/file.txt");
1274
+ * ```
1275
+ */
1276
+ var NodeFileSystemProvider = class {
1277
+ detector = $inject(FileDetector);
1278
+ json = $inject(Json);
1279
+ join(...paths) {
1280
+ return join(...paths);
1281
+ }
1282
+ /**
1283
+ * Creates a FileLike object from various sources.
1284
+ *
1285
+ * @param options - Options for creating the file
1286
+ * @returns A FileLike object
1287
+ *
1288
+ * @example
1289
+ * ```typescript
1290
+ * const fs = alepha.inject(NodeFileSystemProvider);
1291
+ *
1292
+ * // From URL
1293
+ * const file1 = fs.createFile({ url: "https://example.com/image.png" });
1294
+ *
1295
+ * // From Buffer
1296
+ * const file2 = fs.createFile({
1297
+ * buffer: Buffer.from("hello"),
1298
+ * name: "hello.txt",
1299
+ * type: "text/plain"
1300
+ * });
1301
+ *
1302
+ * // From text
1303
+ * const file3 = fs.createFile({ text: "Hello!", name: "greeting.txt" });
1304
+ *
1305
+ * // From stream with detection
1306
+ * const stream = createReadStream("/path/to/file.png");
1307
+ * const file4 = fs.createFile({ stream, name: "image.png" });
1308
+ * ```
1309
+ */
1310
+ createFile(options) {
1311
+ if ("path" in options) {
1312
+ const path = options.path;
1313
+ const filename = path.split("/").pop() || "file";
1314
+ return this.createFileFromUrl(`file://${path}`, {
1315
+ type: options.type,
1316
+ name: options.name || filename
1317
+ });
1318
+ }
1319
+ if ("url" in options) return this.createFileFromUrl(options.url, {
1320
+ type: options.type,
1321
+ name: options.name
1322
+ });
1323
+ if ("response" in options) {
1324
+ if (!options.response.body) throw new AlephaError("Response has no body stream");
1325
+ const res = options.response;
1326
+ const sizeHeader = res.headers.get("content-length");
1327
+ const size = sizeHeader ? parseInt(sizeHeader, 10) : void 0;
1328
+ let name = options.name;
1329
+ const contentDisposition = res.headers.get("content-disposition");
1330
+ if (contentDisposition && !name) {
1331
+ const match = contentDisposition.match(/filename="?([^"]+)"?/);
1332
+ if (match) name = match[1];
1333
+ }
1334
+ const type = options.type || res.headers.get("content-type") || void 0;
1335
+ return this.createFileFromStream(options.response.body, {
1336
+ type,
1337
+ name,
1338
+ size
1339
+ });
1340
+ }
1341
+ if ("file" in options) return this.createFileFromWebFile(options.file, {
1342
+ type: options.type,
1343
+ name: options.name,
1344
+ size: options.size
1345
+ });
1346
+ if ("buffer" in options) return this.createFileFromBuffer(options.buffer, {
1347
+ type: options.type,
1348
+ name: options.name
1349
+ });
1350
+ if ("arrayBuffer" in options) return this.createFileFromBuffer(Buffer.from(options.arrayBuffer), {
1351
+ type: options.type,
1352
+ name: options.name
1353
+ });
1354
+ if ("text" in options) return this.createFileFromBuffer(Buffer.from(options.text, "utf-8"), {
1355
+ type: options.type || "text/plain",
1356
+ name: options.name || "file.txt"
1357
+ });
1358
+ if ("stream" in options) return this.createFileFromStream(options.stream, {
1359
+ type: options.type,
1360
+ name: options.name,
1361
+ size: options.size
1362
+ });
1363
+ throw new AlephaError("Invalid createFile options: no valid source provided");
1364
+ }
1365
+ /**
1366
+ * Removes a file or directory.
1367
+ *
1368
+ * @param path - The path to remove
1369
+ * @param options - Remove options
1370
+ *
1371
+ * @example
1372
+ * ```typescript
1373
+ * const fs = alepha.inject(NodeFileSystemProvider);
1374
+ *
1375
+ * // Remove a file
1376
+ * await fs.rm("/tmp/file.txt");
1377
+ *
1378
+ * // Remove a directory recursively
1379
+ * await fs.rm("/tmp/mydir", { recursive: true });
1380
+ *
1381
+ * // Remove with force (no error if doesn't exist)
1382
+ * await fs.rm("/tmp/maybe-exists.txt", { force: true });
1383
+ * ```
1384
+ */
1385
+ async rm(path, options) {
1386
+ await rm(path, options);
1387
+ }
1388
+ /**
1389
+ * Copies a file or directory.
1390
+ *
1391
+ * @param src - Source path
1392
+ * @param dest - Destination path
1393
+ * @param options - Copy options
1394
+ *
1395
+ * @example
1396
+ * ```typescript
1397
+ * const fs = alepha.inject(NodeFileSystemProvider);
1398
+ *
1399
+ * // Copy a file
1400
+ * await fs.cp("/src/file.txt", "/dest/file.txt");
1401
+ *
1402
+ * // Copy a directory recursively
1403
+ * await fs.cp("/src/dir", "/dest/dir", { recursive: true });
1404
+ *
1405
+ * // Copy with force (overwrite existing)
1406
+ * await fs.cp("/src/file.txt", "/dest/file.txt", { force: true });
1407
+ * ```
1408
+ */
1409
+ async cp(src, dest, options) {
1410
+ if ((await stat(src)).isDirectory()) {
1411
+ if (!options?.recursive) throw new Error(`Cannot copy directory without recursive option: ${src}`);
1412
+ await cp(src, dest, {
1413
+ recursive: true,
1414
+ force: options?.force ?? false
1415
+ });
1416
+ } else await copyFile(src, dest);
1417
+ }
1418
+ /**
1419
+ * Moves/renames a file or directory.
1420
+ *
1421
+ * @param src - Source path
1422
+ * @param dest - Destination path
1423
+ *
1424
+ * @example
1425
+ * ```typescript
1426
+ * const fs = alepha.inject(NodeFileSystemProvider);
1427
+ *
1428
+ * // Move/rename a file
1429
+ * await fs.mv("/old/path.txt", "/new/path.txt");
1430
+ *
1431
+ * // Move a directory
1432
+ * await fs.mv("/old/dir", "/new/dir");
1433
+ * ```
1434
+ */
1435
+ async mv(src, dest) {
1436
+ await rename(src, dest);
1437
+ }
1438
+ /**
1439
+ * Creates a directory.
1440
+ *
1441
+ * @param path - The directory path to create
1442
+ * @param options - Mkdir options
1443
+ *
1444
+ * @example
1445
+ * ```typescript
1446
+ * const fs = alepha.inject(NodeFileSystemProvider);
1447
+ *
1448
+ * // Create a directory
1449
+ * await fs.mkdir("/tmp/mydir");
1450
+ *
1451
+ * // Create nested directories
1452
+ * await fs.mkdir("/tmp/path/to/dir", { recursive: true });
1453
+ *
1454
+ * // Create with specific permissions
1455
+ * await fs.mkdir("/tmp/mydir", { mode: 0o755 });
1456
+ * ```
1457
+ */
1458
+ async mkdir(path, options) {
1459
+ await mkdir(path, options);
1460
+ }
1461
+ /**
1462
+ * Lists files in a directory.
1463
+ *
1464
+ * @param path - The directory path to list
1465
+ * @param options - List options
1466
+ * @returns Array of filenames
1467
+ *
1468
+ * @example
1469
+ * ```typescript
1470
+ * const fs = alepha.inject(NodeFileSystemProvider);
1471
+ *
1472
+ * // List files in a directory
1473
+ * const files = await fs.ls("/tmp");
1474
+ * console.log(files); // ["file1.txt", "file2.txt", "subdir"]
1475
+ *
1476
+ * // List with hidden files
1477
+ * const allFiles = await fs.ls("/tmp", { hidden: true });
1478
+ *
1479
+ * // List recursively
1480
+ * const allFilesRecursive = await fs.ls("/tmp", { recursive: true });
1481
+ * ```
1482
+ */
1483
+ async ls(path, options) {
1484
+ const entries = await readdir(path);
1485
+ const filteredEntries = options?.hidden ? entries : entries.filter((e) => !e.startsWith("."));
1486
+ if (options?.recursive) {
1487
+ const allFiles = [];
1488
+ for (const entry of filteredEntries) {
1489
+ const fullPath = join(path, entry);
1490
+ if ((await stat(fullPath)).isDirectory()) {
1491
+ allFiles.push(entry);
1492
+ const subFiles = await this.ls(fullPath, options);
1493
+ allFiles.push(...subFiles.map((f) => join(entry, f)));
1494
+ } else allFiles.push(entry);
1495
+ }
1496
+ return allFiles;
1497
+ }
1498
+ return filteredEntries;
1499
+ }
1500
+ /**
1501
+ * Checks if a file or directory exists.
1502
+ *
1503
+ * @param path - The path to check
1504
+ * @returns True if the path exists, false otherwise
1505
+ *
1506
+ * @example
1507
+ * ```typescript
1508
+ * const fs = alepha.inject(NodeFileSystemProvider);
1509
+ *
1510
+ * if (await fs.exists("/tmp/file.txt")) {
1511
+ * console.log("File exists");
1512
+ * }
1513
+ * ```
1514
+ */
1515
+ async exists(path) {
1516
+ try {
1517
+ await access(path);
1518
+ return true;
1519
+ } catch {
1520
+ return false;
1521
+ }
1522
+ }
1523
+ /**
1524
+ * Reads the content of a file.
1525
+ *
1526
+ * @param path - The file path to read
1527
+ * @returns The file content as a Buffer
1528
+ *
1529
+ * @example
1530
+ * ```typescript
1531
+ * const fs = alepha.inject(NodeFileSystemProvider);
1532
+ *
1533
+ * const buffer = await fs.readFile("/tmp/file.txt");
1534
+ * console.log(buffer.toString("utf-8"));
1535
+ * ```
1536
+ */
1537
+ async readFile(path) {
1538
+ return await readFile(path);
1539
+ }
1540
+ /**
1541
+ * Writes data to a file.
1542
+ *
1543
+ * @param path - The file path to write to
1544
+ * @param data - The data to write (Buffer or string)
1545
+ *
1546
+ * @example
1547
+ * ```typescript
1548
+ * const fs = alepha.inject(NodeFileSystemProvider);
1549
+ *
1550
+ * // Write string
1551
+ * await fs.writeFile("/tmp/file.txt", "Hello, world!");
1552
+ *
1553
+ * // Write Buffer
1554
+ * await fs.writeFile("/tmp/file.bin", Buffer.from([0x01, 0x02, 0x03]));
1555
+ * ```
1556
+ */
1557
+ async writeFile(path, data) {
1558
+ if (isFileLike(data)) {
1559
+ await writeFile(path, Readable.from(data.stream()));
1560
+ return;
1561
+ }
1562
+ await writeFile(path, data);
1563
+ }
1564
+ /**
1565
+ * Reads the content of a file as a string.
1566
+ *
1567
+ * @param path - The file path to read
1568
+ * @returns The file content as a string
1569
+ *
1570
+ * @example
1571
+ * ```typescript
1572
+ * const fs = alepha.inject(NodeFileSystemProvider);
1573
+ * const content = await fs.readTextFile("/tmp/file.txt");
1574
+ * ```
1575
+ */
1576
+ async readTextFile(path) {
1577
+ return (await this.readFile(path)).toString("utf-8");
1578
+ }
1579
+ /**
1580
+ * Reads the content of a file as JSON.
1581
+ *
1582
+ * @param path - The file path to read
1583
+ * @returns The parsed JSON content
1584
+ *
1585
+ * @example
1586
+ * ```typescript
1587
+ * const fs = alepha.inject(NodeFileSystemProvider);
1588
+ * const config = await fs.readJsonFile<{ name: string }>("/tmp/config.json");
1589
+ * ```
1590
+ */
1591
+ async readJsonFile(path) {
1592
+ const text = await this.readTextFile(path);
1593
+ return this.json.parse(text);
1594
+ }
1595
+ /**
1596
+ * Creates a FileLike object from a Web File.
1597
+ *
1598
+ * @protected
1599
+ */
1600
+ createFileFromWebFile(source, options = {}) {
1601
+ const name = options.name ?? source.name;
1602
+ return {
1603
+ name,
1604
+ type: options.type ?? (source.type || this.detector.getContentType(name)),
1605
+ size: options.size ?? source.size ?? 0,
1606
+ lastModified: source.lastModified || Date.now(),
1607
+ stream: () => source.stream(),
1608
+ arrayBuffer: async () => {
1609
+ return await source.arrayBuffer();
1610
+ },
1611
+ text: async () => {
1612
+ return await source.text();
1613
+ }
1614
+ };
1615
+ }
1616
+ /**
1617
+ * Creates a FileLike object from a Buffer.
1618
+ *
1619
+ * @protected
1620
+ */
1621
+ createFileFromBuffer(source, options = {}) {
1622
+ const name = options.name ?? "file";
1623
+ return {
1624
+ name,
1625
+ type: options.type ?? this.detector.getContentType(options.name ?? name),
1626
+ size: source.byteLength,
1627
+ lastModified: Date.now(),
1628
+ stream: () => Readable.from(source),
1629
+ arrayBuffer: async () => {
1630
+ return this.bufferToArrayBuffer(source);
1631
+ },
1632
+ text: async () => {
1633
+ return source.toString("utf-8");
1634
+ }
1635
+ };
1636
+ }
1637
+ /**
1638
+ * Creates a FileLike object from a stream.
1639
+ *
1640
+ * @protected
1641
+ */
1642
+ createFileFromStream(source, options = {}) {
1643
+ let buffer = null;
1644
+ return {
1645
+ name: options.name ?? "file",
1646
+ type: options.type ?? this.detector.getContentType(options.name ?? "file"),
1647
+ size: options.size ?? 0,
1648
+ lastModified: Date.now(),
1649
+ stream: () => source,
1650
+ _buffer: null,
1651
+ arrayBuffer: async () => {
1652
+ buffer ??= await this.streamToBuffer(source);
1653
+ return this.bufferToArrayBuffer(buffer);
1654
+ },
1655
+ text: async () => {
1656
+ buffer ??= await this.streamToBuffer(source);
1657
+ return buffer.toString("utf-8");
1658
+ }
1659
+ };
1660
+ }
1661
+ /**
1662
+ * Creates a FileLike object from a URL.
1663
+ *
1664
+ * @protected
1665
+ */
1666
+ createFileFromUrl(url, options = {}) {
1667
+ const parsedUrl = new URL(url);
1668
+ const filename = options.name || parsedUrl.pathname.split("/").pop() || "file";
1669
+ let buffer = null;
1670
+ return {
1671
+ name: filename,
1672
+ type: options.type ?? this.detector.getContentType(filename),
1673
+ size: 0,
1674
+ lastModified: Date.now(),
1675
+ stream: () => this.createStreamFromUrl(url),
1676
+ arrayBuffer: async () => {
1677
+ buffer ??= await this.loadFromUrl(url);
1678
+ return this.bufferToArrayBuffer(buffer);
1679
+ },
1680
+ text: async () => {
1681
+ buffer ??= await this.loadFromUrl(url);
1682
+ return buffer.toString("utf-8");
1683
+ },
1684
+ filepath: url
1685
+ };
1686
+ }
1687
+ /**
1688
+ * Gets a streaming response from a URL.
1689
+ *
1690
+ * @protected
1691
+ */
1692
+ getStreamingResponse(url) {
1693
+ const stream = new PassThrough();
1694
+ fetch(url).then((res) => Readable.fromWeb(res.body).pipe(stream)).catch((err) => stream.destroy(err));
1695
+ return stream;
1696
+ }
1697
+ /**
1698
+ * Loads data from a URL.
1699
+ *
1700
+ * @protected
1701
+ */
1702
+ async loadFromUrl(url) {
1703
+ const parsedUrl = new URL(url);
1704
+ if (parsedUrl.protocol === "file:") return await readFile(fileURLToPath(url));
1705
+ else if (parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:") {
1706
+ const response = await fetch(url);
1707
+ if (!response.ok) throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
1708
+ const arrayBuffer = await response.arrayBuffer();
1709
+ return Buffer.from(arrayBuffer);
1710
+ } else throw new Error(`Unsupported protocol: ${parsedUrl.protocol}`);
1711
+ }
1712
+ /**
1713
+ * Creates a stream from a URL.
1714
+ *
1715
+ * @protected
1716
+ */
1717
+ createStreamFromUrl(url) {
1718
+ const parsedUrl = new URL(url);
1719
+ if (parsedUrl.protocol === "file:") return createReadStream(fileURLToPath(url));
1720
+ else if (parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:") return this.getStreamingResponse(url);
1721
+ else throw new AlephaError(`Unsupported protocol: ${parsedUrl.protocol}`);
1722
+ }
1723
+ /**
1724
+ * Converts a stream-like object to a Buffer.
1725
+ *
1726
+ * @protected
1727
+ */
1728
+ async streamToBuffer(streamLike) {
1729
+ const stream = streamLike instanceof Readable ? streamLike : Readable.fromWeb(streamLike);
1730
+ return new Promise((resolve, reject) => {
1731
+ const buffer = [];
1732
+ stream.on("data", (chunk) => buffer.push(Buffer.from(chunk)));
1733
+ stream.on("end", () => resolve(Buffer.concat(buffer)));
1734
+ stream.on("error", (err) => reject(new AlephaError("Error converting stream", { cause: err })));
1735
+ });
1736
+ }
1737
+ /**
1738
+ * Converts a Node.js Buffer to an ArrayBuffer.
1739
+ *
1740
+ * @protected
1741
+ */
1742
+ bufferToArrayBuffer(buffer) {
1743
+ return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
1744
+ }
1745
+ };
1746
+
1747
+ //#endregion
1748
+ //#region ../../src/system/providers/NodeShellProvider.ts
1749
+ /**
1750
+ * Node.js implementation of ShellProvider.
1751
+ *
1752
+ * Executes shell commands using Node.js child_process module.
1753
+ * Supports binary resolution from node_modules/.bin for local packages.
1754
+ */
1755
+ var NodeShellProvider = class {
1756
+ log = $logger();
1757
+ fs = $inject(FileSystemProvider);
1758
+ /**
1759
+ * Run a shell command or binary.
1760
+ */
1761
+ async run(command, options = {}) {
1762
+ const { resolve = false, capture = false, root, env } = options;
1763
+ const cwd = root ?? process.cwd();
1764
+ this.log.debug(`Shell: ${command}`, {
1765
+ cwd,
1766
+ resolve,
1767
+ capture
1768
+ });
1769
+ let executable;
1770
+ let args;
1771
+ if (resolve) {
1772
+ const [bin, ...rest] = command.split(" ");
1773
+ executable = await this.resolveExecutable(bin, cwd);
1774
+ args = rest;
1775
+ } else [executable, ...args] = command.split(" ");
1776
+ if (capture) return this.execCapture(command, {
1777
+ cwd,
1778
+ env
1779
+ });
1780
+ return this.execInherit(executable, args, {
1781
+ cwd,
1782
+ env
1783
+ });
1784
+ }
1785
+ /**
1786
+ * Execute command with inherited stdio (streams to terminal).
1787
+ */
1788
+ async execInherit(executable, args, options) {
1789
+ const proc = spawn(executable, args, {
1790
+ stdio: "inherit",
1791
+ cwd: options.cwd,
1792
+ env: {
1793
+ ...process.env,
1794
+ ...options.env
1795
+ }
1796
+ });
1797
+ return new Promise((resolve, reject) => {
1798
+ proc.on("exit", (code) => {
1799
+ if (code === 0 || code === null) resolve("");
1800
+ else reject(new AlephaError(`Command exited with code ${code}`));
1801
+ });
1802
+ proc.on("error", reject);
1803
+ });
1804
+ }
1805
+ /**
1806
+ * Execute command and capture stdout.
1807
+ */
1808
+ execCapture(command, options) {
1809
+ return new Promise((resolve, reject) => {
1810
+ exec(command, {
1811
+ cwd: options.cwd,
1812
+ env: {
1813
+ ...process.env,
1814
+ LOG_FORMAT: "pretty",
1815
+ ...options.env
1816
+ }
1817
+ }, (err, stdout) => {
1818
+ if (err) {
1819
+ err.stdout = stdout;
1820
+ reject(err);
1821
+ } else resolve(stdout);
1822
+ });
1823
+ });
1824
+ }
1825
+ /**
1826
+ * Resolve executable path from node_modules/.bin.
1827
+ *
1828
+ * Search order:
1829
+ * 1. Local: node_modules/.bin/
1830
+ * 2. Pnpm nested: node_modules/alepha/node_modules/.bin/
1831
+ * 3. Monorepo: Walk up to 3 parent directories
1832
+ */
1833
+ async resolveExecutable(name, root) {
1834
+ const suffix = process.platform === "win32" ? ".cmd" : "";
1835
+ let execPath = await this.findExecutable(root, `node_modules/.bin/${name}${suffix}`);
1836
+ if (!execPath) execPath = await this.findExecutable(root, `node_modules/alepha/node_modules/.bin/${name}${suffix}`);
1837
+ if (!execPath) {
1838
+ let parentDir = this.fs.join(root, "..");
1839
+ for (let i = 0; i < 3; i++) {
1840
+ execPath = await this.findExecutable(parentDir, `node_modules/.bin/${name}${suffix}`);
1841
+ if (execPath) break;
1842
+ parentDir = this.fs.join(parentDir, "..");
1843
+ }
1844
+ }
1845
+ if (!execPath) throw new AlephaError(`Could not find executable for '${name}'. Make sure the package is installed.`);
1846
+ return execPath;
1847
+ }
1848
+ /**
1849
+ * Check if executable exists at path.
1850
+ */
1851
+ async findExecutable(root, relativePath) {
1852
+ const fullPath = this.fs.join(root, relativePath);
1853
+ if (await this.fs.exists(fullPath)) return fullPath;
1854
+ }
1855
+ /**
1856
+ * Check if a command is installed and available in the system PATH.
1857
+ */
1858
+ isInstalled(command) {
1859
+ return new Promise((resolve) => {
1860
+ exec(process.platform === "win32" ? `where ${command}` : `command -v ${command}`, (error) => resolve(!error));
1861
+ });
1862
+ }
1863
+ };
1864
+
1865
+ //#endregion
1866
+ //#region ../../src/system/providers/ShellProvider.ts
1867
+ /**
1868
+ * Abstract provider for executing shell commands and binaries.
1869
+ *
1870
+ * Implementations:
1871
+ * - `NodeShellProvider` - Real shell execution using Node.js child_process
1872
+ * - `MemoryShellProvider` - In-memory mock for testing
1873
+ *
1874
+ * @example
1875
+ * ```typescript
1876
+ * class MyService {
1877
+ * protected readonly shell = $inject(ShellProvider);
1878
+ *
1879
+ * async build() {
1880
+ * // Run shell command directly
1881
+ * await this.shell.run("yarn install");
1882
+ *
1883
+ * // Run local binary with resolution
1884
+ * await this.shell.run("vite build", { resolve: true });
1885
+ *
1886
+ * // Capture output
1887
+ * const output = await this.shell.run("echo hello", { capture: true });
1888
+ * }
1889
+ * }
1890
+ * ```
1891
+ */
1892
+ var ShellProvider = class {};
1893
+
1894
+ //#endregion
1895
+ //#region ../../src/system/index.ts
1896
+ /**
1897
+ * | type | quality | stability |
1898
+ * |------|---------|-----------|
1899
+ * | tooling | standard | stable |
1900
+ *
1901
+ * System-level abstractions for portable code across runtimes.
1902
+ *
1903
+ * **Features:**
1904
+ * - File system operations (read, write, exists, etc.)
1905
+ * - Shell command execution
1906
+ * - File type detection and MIME utilities
1907
+ * - Memory implementations for testing
1908
+ *
1909
+ * @module alepha.system
1910
+ */
1911
+ const AlephaSystem = $module({
1912
+ name: "alepha.system",
1913
+ primitives: [],
1914
+ services: [
1915
+ FileDetector,
1916
+ FileSystemProvider,
1917
+ MemoryFileSystemProvider,
1918
+ NodeFileSystemProvider,
1919
+ ShellProvider,
1920
+ MemoryShellProvider,
1921
+ NodeShellProvider
1922
+ ],
1923
+ register: (alepha) => alepha.with({
1924
+ optional: true,
1925
+ provide: FileSystemProvider,
1926
+ use: NodeFileSystemProvider
1927
+ }).with({
1928
+ optional: true,
1929
+ provide: ShellProvider,
1930
+ use: alepha.isTest() ? MemoryShellProvider : NodeShellProvider
1931
+ })
1932
+ });
1933
+
94
1934
  //#endregion
95
1935
  //#region ../../src/email/errors/EmailError.ts
96
1936
  var EmailError = class extends Error {
@@ -380,13 +2220,18 @@ var NodemailerEmailProvider = class {
380
2220
  //#endregion
381
2221
  //#region ../../src/email/index.ts
382
2222
  /**
383
- * Provides email sending capabilities for Alepha applications with multiple provider backends.
2223
+ * | type | quality | stability |
2224
+ * |------|---------|-----------|
2225
+ * | backend | rare | stable |
2226
+ *
2227
+ * Email delivery with template support.
384
2228
  *
385
- * The email module enables declarative email sending through the `$email` primitive, allowing you to send
386
- * emails through different providers: memory (for testing), local file system, or SMTP via Nodemailer.
387
- * It supports HTML email content and automatic provider selection based on environment configuration.
2229
+ * **Features:**
2230
+ * - Send emails with templates
2231
+ * - Multiple recipients
2232
+ * - SMTP via Nodemailer
2233
+ * - Local file provider for development
388
2234
  *
389
- * @see {@link EmailProvider}
390
2235
  * @module alepha.email
391
2236
  */
392
2237
  const AlephaEmail = $module({