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