alepha 0.15.1 → 0.15.3

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 (523) hide show
  1. package/README.md +68 -80
  2. package/dist/api/audits/index.d.ts +10 -33
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js +10 -33
  5. package/dist/api/audits/index.js.map +1 -1
  6. package/dist/api/files/index.d.ts +10 -3
  7. package/dist/api/files/index.d.ts.map +1 -1
  8. package/dist/api/files/index.js +10 -3
  9. package/dist/api/files/index.js.map +1 -1
  10. package/dist/api/jobs/index.d.ts +162 -155
  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.d.ts +10 -4
  19. package/dist/api/notifications/index.d.ts.map +1 -1
  20. package/dist/api/notifications/index.js +10 -4
  21. package/dist/api/notifications/index.js.map +1 -1
  22. package/dist/api/parameters/index.d.ts +43 -50
  23. package/dist/api/parameters/index.d.ts.map +1 -1
  24. package/dist/api/parameters/index.js +30 -37
  25. package/dist/api/parameters/index.js.map +1 -1
  26. package/dist/api/users/index.d.ts +1081 -760
  27. package/dist/api/users/index.d.ts.map +1 -1
  28. package/dist/api/users/index.js +2539 -218
  29. package/dist/api/users/index.js.map +1 -1
  30. package/dist/api/verifications/index.d.ts +138 -132
  31. package/dist/api/verifications/index.d.ts.map +1 -1
  32. package/dist/api/verifications/index.js +12 -4
  33. package/dist/api/verifications/index.js.map +1 -1
  34. package/dist/batch/index.d.ts +20 -40
  35. package/dist/batch/index.d.ts.map +1 -1
  36. package/dist/batch/index.js +31 -44
  37. package/dist/batch/index.js.map +1 -1
  38. package/dist/bucket/index.d.ts +440 -8
  39. package/dist/bucket/index.d.ts.map +1 -1
  40. package/dist/bucket/index.js +1861 -12
  41. package/dist/bucket/index.js.map +1 -1
  42. package/dist/cache/core/index.d.ts +179 -7
  43. package/dist/cache/core/index.d.ts.map +1 -1
  44. package/dist/cache/core/index.js +213 -7
  45. package/dist/cache/core/index.js.map +1 -1
  46. package/dist/cache/redis/index.d.ts +1 -0
  47. package/dist/cache/redis/index.d.ts.map +1 -1
  48. package/dist/cache/redis/index.js +4 -0
  49. package/dist/cache/redis/index.js.map +1 -1
  50. package/dist/cli/index.d.ts +638 -5645
  51. package/dist/cli/index.d.ts.map +1 -1
  52. package/dist/cli/index.js +2550 -368
  53. package/dist/cli/index.js.map +1 -1
  54. package/dist/command/index.d.ts +203 -45
  55. package/dist/command/index.d.ts.map +1 -1
  56. package/dist/command/index.js +2060 -71
  57. package/dist/command/index.js.map +1 -1
  58. package/dist/core/index.browser.js +70 -40
  59. package/dist/core/index.browser.js.map +1 -1
  60. package/dist/core/index.d.ts +34 -13
  61. package/dist/core/index.d.ts.map +1 -1
  62. package/dist/core/index.js +90 -40
  63. package/dist/core/index.js.map +1 -1
  64. package/dist/core/index.native.js +70 -40
  65. package/dist/core/index.native.js.map +1 -1
  66. package/dist/datetime/index.d.ts +15 -0
  67. package/dist/datetime/index.d.ts.map +1 -1
  68. package/dist/datetime/index.js +15 -0
  69. package/dist/datetime/index.js.map +1 -1
  70. package/dist/email/index.d.ts +323 -20
  71. package/dist/email/index.d.ts.map +1 -1
  72. package/dist/email/index.js +1857 -7
  73. package/dist/email/index.js.map +1 -1
  74. package/dist/fake/index.d.ts +90 -8
  75. package/dist/fake/index.d.ts.map +1 -1
  76. package/dist/fake/index.js +91 -20
  77. package/dist/fake/index.js.map +1 -1
  78. package/dist/lock/core/index.d.ts +11 -4
  79. package/dist/lock/core/index.d.ts.map +1 -1
  80. package/dist/lock/core/index.js +11 -4
  81. package/dist/lock/core/index.js.map +1 -1
  82. package/dist/logger/index.d.ts +17 -66
  83. package/dist/logger/index.d.ts.map +1 -1
  84. package/dist/logger/index.js +14 -63
  85. package/dist/logger/index.js.map +1 -1
  86. package/dist/mcp/index.d.ts +10 -30
  87. package/dist/mcp/index.d.ts.map +1 -1
  88. package/dist/mcp/index.js +12 -35
  89. package/dist/mcp/index.js.map +1 -1
  90. package/dist/orm/index.browser.js +3 -3
  91. package/dist/orm/index.browser.js.map +1 -1
  92. package/dist/orm/index.bun.js +39 -20
  93. package/dist/orm/index.bun.js.map +1 -1
  94. package/dist/orm/index.d.ts +517 -540
  95. package/dist/orm/index.d.ts.map +1 -1
  96. package/dist/orm/index.js +58 -71
  97. package/dist/orm/index.js.map +1 -1
  98. package/dist/queue/core/index.d.ts +18 -10
  99. package/dist/queue/core/index.d.ts.map +1 -1
  100. package/dist/queue/core/index.js +14 -6
  101. package/dist/queue/core/index.js.map +1 -1
  102. package/dist/react/auth/index.browser.js +108 -0
  103. package/dist/react/auth/index.browser.js.map +1 -0
  104. package/dist/react/auth/index.d.ts +100 -0
  105. package/dist/react/auth/index.d.ts.map +1 -0
  106. package/dist/react/auth/index.js +145 -0
  107. package/dist/react/auth/index.js.map +1 -0
  108. package/dist/react/core/index.d.ts +469 -0
  109. package/dist/react/core/index.d.ts.map +1 -0
  110. package/dist/react/core/index.js +464 -0
  111. package/dist/react/core/index.js.map +1 -0
  112. package/dist/react/form/index.d.ts +232 -0
  113. package/dist/react/form/index.d.ts.map +1 -0
  114. package/dist/react/form/index.js +432 -0
  115. package/dist/react/form/index.js.map +1 -0
  116. package/dist/react/head/index.browser.js +423 -0
  117. package/dist/react/head/index.browser.js.map +1 -0
  118. package/dist/react/head/index.d.ts +288 -0
  119. package/dist/react/head/index.d.ts.map +1 -0
  120. package/dist/react/head/index.js +465 -0
  121. package/dist/react/head/index.js.map +1 -0
  122. package/dist/react/i18n/index.d.ts +175 -0
  123. package/dist/react/i18n/index.d.ts.map +1 -0
  124. package/dist/react/i18n/index.js +224 -0
  125. package/dist/react/i18n/index.js.map +1 -0
  126. package/dist/react/router/index.browser.js +1974 -0
  127. package/dist/react/router/index.browser.js.map +1 -0
  128. package/dist/react/router/index.d.ts +1956 -0
  129. package/dist/react/router/index.d.ts.map +1 -0
  130. package/dist/react/router/index.js +4722 -0
  131. package/dist/react/router/index.js.map +1 -0
  132. package/dist/react/websocket/index.d.ts +117 -0
  133. package/dist/react/websocket/index.d.ts.map +1 -0
  134. package/dist/react/websocket/index.js +107 -0
  135. package/dist/react/websocket/index.js.map +1 -0
  136. package/dist/redis/index.bun.js +4 -0
  137. package/dist/redis/index.bun.js.map +1 -1
  138. package/dist/redis/index.d.ts +41 -44
  139. package/dist/redis/index.d.ts.map +1 -1
  140. package/dist/redis/index.js +16 -25
  141. package/dist/redis/index.js.map +1 -1
  142. package/dist/retry/index.d.ts +11 -2
  143. package/dist/retry/index.d.ts.map +1 -1
  144. package/dist/retry/index.js +11 -2
  145. package/dist/retry/index.js.map +1 -1
  146. package/dist/scheduler/index.d.ts +11 -2
  147. package/dist/scheduler/index.d.ts.map +1 -1
  148. package/dist/scheduler/index.js +11 -2
  149. package/dist/scheduler/index.js.map +1 -1
  150. package/dist/security/index.d.ts +140 -49
  151. package/dist/security/index.d.ts.map +1 -1
  152. package/dist/security/index.js +164 -32
  153. package/dist/security/index.js.map +1 -1
  154. package/dist/server/auth/index.d.ts +12 -7
  155. package/dist/server/auth/index.d.ts.map +1 -1
  156. package/dist/server/auth/index.js +12 -7
  157. package/dist/server/auth/index.js.map +1 -1
  158. package/dist/server/cache/index.d.ts +7 -22
  159. package/dist/server/cache/index.d.ts.map +1 -1
  160. package/dist/server/cache/index.js +7 -22
  161. package/dist/server/cache/index.js.map +1 -1
  162. package/dist/server/compress/index.d.ts +10 -2
  163. package/dist/server/compress/index.d.ts.map +1 -1
  164. package/dist/server/compress/index.js +10 -2
  165. package/dist/server/compress/index.js.map +1 -1
  166. package/dist/server/cookies/index.d.ts +40 -16
  167. package/dist/server/cookies/index.d.ts.map +1 -1
  168. package/dist/server/cookies/index.js +7 -5
  169. package/dist/server/cookies/index.js.map +1 -1
  170. package/dist/server/core/index.d.ts +124 -23
  171. package/dist/server/core/index.d.ts.map +1 -1
  172. package/dist/server/core/index.js +231 -14
  173. package/dist/server/core/index.js.map +1 -1
  174. package/dist/server/cors/index.d.ts +13 -23
  175. package/dist/server/cors/index.d.ts.map +1 -1
  176. package/dist/server/cors/index.js +7 -21
  177. package/dist/server/cors/index.js.map +1 -1
  178. package/dist/server/health/index.d.ts +8 -2
  179. package/dist/server/health/index.d.ts.map +1 -1
  180. package/dist/server/health/index.js +8 -2
  181. package/dist/server/health/index.js.map +1 -1
  182. package/dist/server/helmet/index.d.ts +11 -3
  183. package/dist/server/helmet/index.d.ts.map +1 -1
  184. package/dist/server/helmet/index.js +11 -3
  185. package/dist/server/helmet/index.js.map +1 -1
  186. package/dist/server/links/index.d.ts +11 -6
  187. package/dist/server/links/index.d.ts.map +1 -1
  188. package/dist/server/links/index.js +11 -6
  189. package/dist/server/links/index.js.map +1 -1
  190. package/dist/server/metrics/index.d.ts +10 -3
  191. package/dist/server/metrics/index.d.ts.map +1 -1
  192. package/dist/server/metrics/index.js +10 -3
  193. package/dist/server/metrics/index.js.map +1 -1
  194. package/dist/server/multipart/index.d.ts +9 -3
  195. package/dist/server/multipart/index.d.ts.map +1 -1
  196. package/dist/server/multipart/index.js +9 -3
  197. package/dist/server/multipart/index.js.map +1 -1
  198. package/dist/server/proxy/index.d.ts +8 -2
  199. package/dist/server/proxy/index.d.ts.map +1 -1
  200. package/dist/server/proxy/index.js +8 -2
  201. package/dist/server/proxy/index.js.map +1 -1
  202. package/dist/server/rate-limit/index.d.ts +30 -35
  203. package/dist/server/rate-limit/index.d.ts.map +1 -1
  204. package/dist/server/rate-limit/index.js +18 -55
  205. package/dist/server/rate-limit/index.js.map +1 -1
  206. package/dist/server/static/index.d.ts +137 -4
  207. package/dist/server/static/index.d.ts.map +1 -1
  208. package/dist/server/static/index.js +1853 -5
  209. package/dist/server/static/index.js.map +1 -1
  210. package/dist/server/swagger/index.d.ts +309 -6
  211. package/dist/server/swagger/index.d.ts.map +1 -1
  212. package/dist/server/swagger/index.js +1854 -6
  213. package/dist/server/swagger/index.js.map +1 -1
  214. package/dist/sms/index.d.ts +309 -7
  215. package/dist/sms/index.d.ts.map +1 -1
  216. package/dist/sms/index.js +1856 -7
  217. package/dist/sms/index.js.map +1 -1
  218. package/dist/system/index.browser.js +1218 -0
  219. package/dist/system/index.browser.js.map +1 -0
  220. package/dist/{file → system}/index.d.ts +343 -16
  221. package/dist/system/index.d.ts.map +1 -0
  222. package/dist/{file → system}/index.js +419 -22
  223. package/dist/system/index.js.map +1 -0
  224. package/dist/thread/index.d.ts +11 -2
  225. package/dist/thread/index.d.ts.map +1 -1
  226. package/dist/thread/index.js +11 -2
  227. package/dist/thread/index.js.map +1 -1
  228. package/dist/topic/core/index.d.ts +12 -5
  229. package/dist/topic/core/index.d.ts.map +1 -1
  230. package/dist/topic/core/index.js +12 -5
  231. package/dist/topic/core/index.js.map +1 -1
  232. package/dist/vite/index.d.ts +5 -6272
  233. package/dist/vite/index.d.ts.map +1 -1
  234. package/dist/vite/index.js +23 -10
  235. package/dist/vite/index.js.map +1 -1
  236. package/dist/websocket/index.d.ts +12 -8
  237. package/dist/websocket/index.d.ts.map +1 -1
  238. package/dist/websocket/index.js +12 -8
  239. package/dist/websocket/index.js.map +1 -1
  240. package/package.json +82 -11
  241. package/src/api/audits/index.ts +10 -33
  242. package/src/api/files/__tests__/$bucket.spec.ts +1 -1
  243. package/src/api/files/controllers/AdminFileStatsController.spec.ts +1 -1
  244. package/src/api/files/controllers/FileController.spec.ts +1 -1
  245. package/src/api/files/index.ts +10 -3
  246. package/src/api/files/jobs/FileJobs.spec.ts +1 -1
  247. package/src/api/files/services/FileService.spec.ts +1 -1
  248. package/src/api/jobs/index.ts +10 -3
  249. package/src/api/keys/controllers/AdminApiKeyController.ts +75 -0
  250. package/src/api/keys/controllers/ApiKeyController.ts +103 -0
  251. package/src/api/keys/entities/apiKeyEntity.ts +41 -0
  252. package/src/api/keys/index.ts +49 -0
  253. package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +7 -0
  254. package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +17 -0
  255. package/src/api/keys/schemas/createApiKeyBodySchema.ts +7 -0
  256. package/src/api/keys/schemas/createApiKeyResponseSchema.ts +11 -0
  257. package/src/api/keys/schemas/listApiKeyResponseSchema.ts +15 -0
  258. package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +5 -0
  259. package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +5 -0
  260. package/src/api/keys/services/ApiKeyService.spec.ts +553 -0
  261. package/src/api/keys/services/ApiKeyService.ts +306 -0
  262. package/src/api/logs/TODO.md +55 -0
  263. package/src/api/notifications/index.ts +10 -4
  264. package/src/api/parameters/index.ts +9 -30
  265. package/src/api/parameters/primitives/$config.ts +12 -4
  266. package/src/api/parameters/services/ConfigStore.ts +9 -3
  267. package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1035 -0
  268. package/src/api/users/__tests__/ApiKeys.spec.ts +401 -0
  269. package/src/api/users/index.ts +14 -3
  270. package/src/api/users/primitives/$realm.ts +33 -5
  271. package/src/api/users/providers/RealmProvider.ts +1 -12
  272. package/src/api/users/services/SessionService.ts +1 -1
  273. package/src/api/verifications/controllers/VerificationController.ts +2 -0
  274. package/src/api/verifications/index.ts +10 -4
  275. package/src/batch/index.ts +9 -36
  276. package/src/batch/primitives/$batch.ts +0 -8
  277. package/src/batch/providers/BatchProvider.ts +29 -2
  278. package/src/bucket/__tests__/shared.ts +1 -1
  279. package/src/bucket/index.ts +13 -6
  280. package/src/bucket/primitives/$bucket.ts +1 -1
  281. package/src/bucket/providers/LocalFileStorageProvider.ts +1 -1
  282. package/src/bucket/providers/MemoryFileStorageProvider.ts +1 -1
  283. package/src/cache/core/__tests__/shared.ts +30 -0
  284. package/src/cache/core/index.ts +11 -6
  285. package/src/cache/core/primitives/$cache.spec.ts +5 -0
  286. package/src/cache/core/providers/CacheProvider.ts +17 -0
  287. package/src/cache/core/providers/MemoryCacheProvider.ts +300 -1
  288. package/src/cache/redis/__tests__/cache-redis.spec.ts +5 -0
  289. package/src/cache/redis/providers/RedisCacheProvider.ts +9 -0
  290. package/src/cli/apps/AlephaCli.ts +1 -14
  291. package/src/cli/apps/AlephaPackageBuilderCli.ts +10 -1
  292. package/src/cli/atoms/buildOptions.ts +99 -9
  293. package/src/cli/commands/build.ts +150 -37
  294. package/src/cli/commands/db.ts +22 -18
  295. package/src/cli/commands/deploy.ts +1 -1
  296. package/src/cli/commands/dev.ts +1 -20
  297. package/src/cli/commands/gen/env.ts +5 -2
  298. package/src/cli/commands/gen/openapi.ts +5 -2
  299. package/src/cli/commands/init.spec.ts +588 -0
  300. package/src/cli/commands/init.ts +115 -58
  301. package/src/cli/commands/lint.ts +7 -1
  302. package/src/cli/commands/typecheck.ts +11 -0
  303. package/src/cli/providers/AppEntryProvider.ts +1 -1
  304. package/src/cli/providers/ViteBuildProvider.ts +8 -50
  305. package/src/cli/providers/ViteDevServerProvider.ts +35 -16
  306. package/src/cli/services/AlephaCliUtils.ts +52 -121
  307. package/src/cli/services/PackageManagerUtils.ts +129 -11
  308. package/src/cli/services/ProjectScaffolder.spec.ts +97 -0
  309. package/src/cli/services/ProjectScaffolder.ts +148 -81
  310. package/src/cli/services/ViteUtils.ts +82 -0
  311. package/src/cli/{assets/claudeMd.ts → templates/agentMd.ts} +37 -24
  312. package/src/cli/templates/apiAppSecurityTs.ts +11 -0
  313. package/src/cli/templates/apiIndexTs.ts +30 -0
  314. package/src/cli/templates/gitignore.ts +39 -0
  315. package/src/cli/{assets → templates}/mainCss.ts +11 -2
  316. package/src/cli/templates/mainServerTs.ts +33 -0
  317. package/src/cli/templates/webAppRouterTs.ts +74 -0
  318. package/src/cli/templates/webHelloComponentTsx.ts +30 -0
  319. package/src/command/helpers/Runner.spec.ts +139 -0
  320. package/src/command/helpers/Runner.ts +7 -22
  321. package/src/command/index.ts +12 -4
  322. package/src/command/providers/CliProvider.spec.ts +1392 -0
  323. package/src/command/providers/CliProvider.ts +320 -47
  324. package/src/core/Alepha.ts +34 -27
  325. package/src/core/__tests__/Alepha-start.spec.ts +4 -4
  326. package/src/core/helpers/jsonSchemaToTypeBox.spec.ts +771 -0
  327. package/src/core/helpers/jsonSchemaToTypeBox.ts +62 -10
  328. package/src/core/index.shared.ts +1 -0
  329. package/src/core/index.ts +20 -0
  330. package/src/core/providers/EventManager.spec.ts +0 -71
  331. package/src/core/providers/EventManager.ts +3 -15
  332. package/src/core/providers/Json.ts +2 -14
  333. package/src/datetime/index.ts +15 -0
  334. package/src/email/index.ts +10 -5
  335. package/src/email/providers/LocalEmailProvider.spec.ts +1 -1
  336. package/src/email/providers/LocalEmailProvider.ts +1 -1
  337. package/src/fake/__tests__/keyName.example.ts +1 -1
  338. package/src/fake/__tests__/keyName.spec.ts +5 -5
  339. package/src/fake/index.ts +9 -6
  340. package/src/fake/providers/FakeProvider.spec.ts +258 -40
  341. package/src/fake/providers/FakeProvider.ts +133 -19
  342. package/src/lock/core/index.ts +11 -4
  343. package/src/logger/index.ts +17 -66
  344. package/src/mcp/index.ts +10 -27
  345. package/src/mcp/transports/SseMcpTransport.ts +0 -11
  346. package/src/orm/__tests__/PostgresProvider.spec.ts +2 -2
  347. package/src/orm/index.browser.ts +2 -2
  348. package/src/orm/index.bun.ts +5 -3
  349. package/src/orm/index.ts +23 -53
  350. package/src/orm/providers/drivers/BunSqliteProvider.ts +5 -1
  351. package/src/orm/providers/drivers/CloudflareD1Provider.ts +57 -30
  352. package/src/orm/providers/drivers/DatabaseProvider.ts +9 -1
  353. package/src/orm/providers/drivers/NodeSqliteProvider.ts +4 -1
  354. package/src/orm/services/Repository.ts +7 -3
  355. package/src/queue/core/index.ts +14 -6
  356. package/src/react/auth/__tests__/$auth.spec.ts +202 -0
  357. package/src/react/auth/hooks/useAuth.ts +32 -0
  358. package/src/react/auth/index.browser.ts +13 -0
  359. package/src/react/auth/index.shared.ts +2 -0
  360. package/src/react/auth/index.ts +48 -0
  361. package/src/react/auth/providers/ReactAuthProvider.ts +16 -0
  362. package/src/react/auth/services/ReactAuth.ts +135 -0
  363. package/src/react/core/__tests__/Router.spec.tsx +169 -0
  364. package/src/react/core/components/ClientOnly.tsx +49 -0
  365. package/src/react/core/components/ErrorBoundary.tsx +73 -0
  366. package/src/react/core/contexts/AlephaContext.ts +7 -0
  367. package/src/react/core/contexts/AlephaProvider.tsx +42 -0
  368. package/src/react/core/hooks/useAction.browser.spec.tsx +569 -0
  369. package/src/react/core/hooks/useAction.ts +480 -0
  370. package/src/react/core/hooks/useAlepha.ts +26 -0
  371. package/src/react/core/hooks/useClient.ts +17 -0
  372. package/src/react/core/hooks/useEvents.ts +51 -0
  373. package/src/react/core/hooks/useInject.ts +12 -0
  374. package/src/react/core/hooks/useStore.ts +52 -0
  375. package/src/react/core/index.ts +90 -0
  376. package/src/react/form/components/FormState.tsx +17 -0
  377. package/src/react/form/errors/FormValidationError.ts +18 -0
  378. package/src/react/form/hooks/useForm.browser.spec.tsx +366 -0
  379. package/src/react/form/hooks/useForm.ts +47 -0
  380. package/src/react/form/hooks/useFormState.ts +130 -0
  381. package/src/react/form/index.ts +44 -0
  382. package/src/react/form/services/FormModel.ts +614 -0
  383. package/src/react/head/helpers/SeoExpander.spec.ts +203 -0
  384. package/src/react/head/helpers/SeoExpander.ts +142 -0
  385. package/src/react/head/hooks/useHead.spec.tsx +288 -0
  386. package/src/react/head/hooks/useHead.ts +62 -0
  387. package/src/react/head/index.browser.ts +26 -0
  388. package/src/react/head/index.ts +44 -0
  389. package/src/react/head/interfaces/Head.ts +105 -0
  390. package/src/react/head/primitives/$head.ts +25 -0
  391. package/src/react/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
  392. package/src/react/head/providers/BrowserHeadProvider.ts +212 -0
  393. package/src/react/head/providers/HeadProvider.ts +168 -0
  394. package/src/react/head/providers/ServerHeadProvider.ts +31 -0
  395. package/src/react/i18n/__tests__/integration.spec.tsx +239 -0
  396. package/src/react/i18n/components/Localize.spec.tsx +357 -0
  397. package/src/react/i18n/components/Localize.tsx +35 -0
  398. package/src/react/i18n/hooks/useI18n.browser.spec.tsx +438 -0
  399. package/src/react/i18n/hooks/useI18n.ts +18 -0
  400. package/src/react/i18n/index.ts +41 -0
  401. package/src/react/i18n/primitives/$dictionary.ts +69 -0
  402. package/src/react/i18n/providers/I18nProvider.spec.ts +389 -0
  403. package/src/react/i18n/providers/I18nProvider.ts +278 -0
  404. package/src/react/router/__tests__/page-head-browser.browser.spec.ts +95 -0
  405. package/src/react/router/__tests__/page-head.spec.ts +48 -0
  406. package/src/react/router/__tests__/seo-head.spec.ts +125 -0
  407. package/src/react/router/atoms/ssrManifestAtom.ts +58 -0
  408. package/src/react/router/components/ErrorViewer.tsx +872 -0
  409. package/src/react/router/components/Link.tsx +23 -0
  410. package/src/react/router/components/NestedView.tsx +223 -0
  411. package/src/react/router/components/NotFound.tsx +30 -0
  412. package/src/react/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
  413. package/src/react/router/contexts/RouterLayerContext.ts +12 -0
  414. package/src/react/router/errors/Redirection.ts +28 -0
  415. package/src/react/router/hooks/useActive.ts +52 -0
  416. package/src/react/router/hooks/useQueryParams.ts +63 -0
  417. package/src/react/router/hooks/useRouter.ts +20 -0
  418. package/src/react/router/hooks/useRouterState.ts +11 -0
  419. package/src/react/router/index.browser.ts +45 -0
  420. package/src/react/router/index.shared.ts +19 -0
  421. package/src/react/router/index.ts +146 -0
  422. package/src/react/router/primitives/$page.browser.spec.tsx +851 -0
  423. package/src/react/router/primitives/$page.spec.tsx +676 -0
  424. package/src/react/router/primitives/$page.ts +489 -0
  425. package/src/react/router/providers/ReactBrowserProvider.ts +312 -0
  426. package/src/react/router/providers/ReactBrowserRendererProvider.ts +25 -0
  427. package/src/react/router/providers/ReactBrowserRouterProvider.ts +168 -0
  428. package/src/react/router/providers/ReactPageProvider.ts +726 -0
  429. package/src/react/router/providers/ReactPreloadProvider.spec.ts +142 -0
  430. package/src/react/router/providers/ReactPreloadProvider.ts +85 -0
  431. package/src/react/router/providers/ReactServerProvider.spec.tsx +316 -0
  432. package/src/react/router/providers/ReactServerProvider.ts +487 -0
  433. package/src/react/router/providers/ReactServerTemplateProvider.spec.ts +210 -0
  434. package/src/react/router/providers/ReactServerTemplateProvider.ts +542 -0
  435. package/src/react/router/providers/SSRManifestProvider.ts +334 -0
  436. package/src/react/router/services/ReactPageServerService.ts +48 -0
  437. package/src/react/router/services/ReactPageService.ts +27 -0
  438. package/src/react/router/services/ReactRouter.ts +262 -0
  439. package/src/react/websocket/hooks/useRoom.tsx +242 -0
  440. package/src/react/websocket/index.ts +7 -0
  441. package/src/redis/__tests__/redis.spec.ts +13 -0
  442. package/src/redis/index.ts +9 -25
  443. package/src/redis/providers/BunRedisProvider.ts +9 -0
  444. package/src/redis/providers/NodeRedisProvider.ts +8 -0
  445. package/src/redis/providers/RedisProvider.ts +16 -0
  446. package/src/retry/index.ts +11 -2
  447. package/src/router/index.ts +15 -0
  448. package/src/scheduler/index.ts +11 -2
  449. package/src/security/__tests__/BasicAuth.spec.ts +2 -0
  450. package/src/security/__tests__/ServerSecurityProvider.spec.ts +90 -5
  451. package/src/security/index.ts +15 -10
  452. package/src/security/interfaces/IssuerResolver.ts +27 -0
  453. package/src/security/primitives/$issuer.ts +55 -0
  454. package/src/security/providers/SecurityProvider.ts +179 -0
  455. package/src/security/providers/ServerBasicAuthProvider.ts +6 -2
  456. package/src/security/providers/ServerSecurityProvider.ts +63 -41
  457. package/src/server/auth/index.ts +12 -7
  458. package/src/server/cache/index.ts +7 -22
  459. package/src/server/compress/index.ts +10 -2
  460. package/src/server/cookies/index.ts +7 -5
  461. package/src/server/cookies/primitives/$cookie.ts +33 -11
  462. package/src/server/core/index.ts +16 -6
  463. package/src/server/core/interfaces/ServerRequest.ts +83 -1
  464. package/src/server/core/primitives/$action.spec.ts +1 -1
  465. package/src/server/core/primitives/$action.ts +8 -3
  466. package/src/server/core/providers/NodeHttpServerProvider.spec.ts +9 -3
  467. package/src/server/core/providers/NodeHttpServerProvider.ts +9 -3
  468. package/src/server/core/services/ServerRequestParser.spec.ts +520 -0
  469. package/src/server/core/services/ServerRequestParser.ts +306 -13
  470. package/src/server/cors/index.ts +7 -21
  471. package/src/server/cors/primitives/$cors.ts +6 -2
  472. package/src/server/health/index.ts +8 -2
  473. package/src/server/helmet/index.ts +11 -3
  474. package/src/server/links/index.ts +11 -6
  475. package/src/server/metrics/index.ts +10 -3
  476. package/src/server/multipart/index.ts +9 -3
  477. package/src/server/proxy/index.ts +8 -2
  478. package/src/server/rate-limit/index.ts +21 -25
  479. package/src/server/rate-limit/primitives/$rateLimit.ts +6 -2
  480. package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +38 -14
  481. package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +22 -56
  482. package/src/server/static/index.ts +8 -2
  483. package/src/server/static/providers/ServerStaticProvider.ts +1 -1
  484. package/src/server/swagger/index.ts +9 -4
  485. package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
  486. package/src/sms/index.ts +9 -5
  487. package/src/sms/providers/LocalSmsProvider.spec.ts +1 -1
  488. package/src/sms/providers/LocalSmsProvider.ts +1 -1
  489. package/src/system/index.browser.ts +36 -0
  490. package/src/system/index.ts +62 -0
  491. package/src/system/index.workerd.ts +1 -0
  492. package/src/{file → system}/providers/FileSystemProvider.ts +24 -0
  493. package/src/{file → system}/providers/MemoryFileSystemProvider.ts +116 -3
  494. package/src/system/providers/MemoryShellProvider.ts +164 -0
  495. package/src/{file → system}/providers/NodeFileSystemProvider.spec.ts +2 -2
  496. package/src/{file → system}/providers/NodeFileSystemProvider.ts +47 -2
  497. package/src/system/providers/NodeShellProvider.ts +184 -0
  498. package/src/system/providers/ShellProvider.ts +74 -0
  499. package/src/{file → system}/services/FileDetector.spec.ts +2 -2
  500. package/src/thread/index.ts +11 -2
  501. package/src/topic/core/index.ts +12 -5
  502. package/src/vite/tasks/buildClient.ts +2 -7
  503. package/src/vite/tasks/buildServer.ts +19 -13
  504. package/src/vite/tasks/generateCloudflare.ts +10 -7
  505. package/src/vite/tasks/generateDocker.ts +4 -0
  506. package/src/websocket/index.ts +12 -8
  507. package/dist/file/index.d.ts.map +0 -1
  508. package/dist/file/index.js.map +0 -1
  509. package/src/cli/assets/apiIndexTs.ts +0 -16
  510. package/src/cli/assets/mainServerTs.ts +0 -24
  511. package/src/cli/assets/webAppRouterTs.ts +0 -16
  512. package/src/cli/assets/webHelloComponentTsx.ts +0 -20
  513. package/src/cli/providers/ViteTemplateProvider.ts +0 -27
  514. package/src/file/index.ts +0 -43
  515. /package/src/cli/{assets → templates}/apiHelloControllerTs.ts +0 -0
  516. /package/src/cli/{assets → templates}/biomeJson.ts +0 -0
  517. /package/src/cli/{assets → templates}/dummySpecTs.ts +0 -0
  518. /package/src/cli/{assets → templates}/editorconfig.ts +0 -0
  519. /package/src/cli/{assets → templates}/mainBrowserTs.ts +0 -0
  520. /package/src/cli/{assets → templates}/tsconfigJson.ts +0 -0
  521. /package/src/cli/{assets → templates}/webIndexTs.ts +0 -0
  522. /package/src/{file → system}/errors/FileError.ts +0 -0
  523. /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,1846 @@ 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
+ const p = mkdir(path, {
1460
+ recursive: options.recursive ?? true,
1461
+ mode: options.mode
1462
+ });
1463
+ if (options.force === false) await p;
1464
+ else await p.catch(() => {});
1465
+ }
1466
+ /**
1467
+ * Lists files in a directory.
1468
+ *
1469
+ * @param path - The directory path to list
1470
+ * @param options - List options
1471
+ * @returns Array of filenames
1472
+ *
1473
+ * @example
1474
+ * ```typescript
1475
+ * const fs = alepha.inject(NodeFileSystemProvider);
1476
+ *
1477
+ * // List files in a directory
1478
+ * const files = await fs.ls("/tmp");
1479
+ * console.log(files); // ["file1.txt", "file2.txt", "subdir"]
1480
+ *
1481
+ * // List with hidden files
1482
+ * const allFiles = await fs.ls("/tmp", { hidden: true });
1483
+ *
1484
+ * // List recursively
1485
+ * const allFilesRecursive = await fs.ls("/tmp", { recursive: true });
1486
+ * ```
1487
+ */
1488
+ async ls(path, options) {
1489
+ const entries = await readdir(path);
1490
+ const filteredEntries = options?.hidden ? entries : entries.filter((e) => !e.startsWith("."));
1491
+ if (options?.recursive) {
1492
+ const allFiles = [];
1493
+ for (const entry of filteredEntries) {
1494
+ const fullPath = join(path, entry);
1495
+ if ((await stat(fullPath)).isDirectory()) {
1496
+ allFiles.push(entry);
1497
+ const subFiles = await this.ls(fullPath, options);
1498
+ allFiles.push(...subFiles.map((f) => join(entry, f)));
1499
+ } else allFiles.push(entry);
1500
+ }
1501
+ return allFiles;
1502
+ }
1503
+ return filteredEntries;
1504
+ }
1505
+ /**
1506
+ * Checks if a file or directory exists.
1507
+ *
1508
+ * @param path - The path to check
1509
+ * @returns True if the path exists, false otherwise
1510
+ *
1511
+ * @example
1512
+ * ```typescript
1513
+ * const fs = alepha.inject(NodeFileSystemProvider);
1514
+ *
1515
+ * if (await fs.exists("/tmp/file.txt")) {
1516
+ * console.log("File exists");
1517
+ * }
1518
+ * ```
1519
+ */
1520
+ async exists(path) {
1521
+ try {
1522
+ await access(path);
1523
+ return true;
1524
+ } catch {
1525
+ return false;
1526
+ }
1527
+ }
1528
+ /**
1529
+ * Reads the content of a file.
1530
+ *
1531
+ * @param path - The file path to read
1532
+ * @returns The file content as a Buffer
1533
+ *
1534
+ * @example
1535
+ * ```typescript
1536
+ * const fs = alepha.inject(NodeFileSystemProvider);
1537
+ *
1538
+ * const buffer = await fs.readFile("/tmp/file.txt");
1539
+ * console.log(buffer.toString("utf-8"));
1540
+ * ```
1541
+ */
1542
+ async readFile(path) {
1543
+ return await readFile(path);
1544
+ }
1545
+ /**
1546
+ * Writes data to a file.
1547
+ *
1548
+ * @param path - The file path to write to
1549
+ * @param data - The data to write (Buffer or string)
1550
+ *
1551
+ * @example
1552
+ * ```typescript
1553
+ * const fs = alepha.inject(NodeFileSystemProvider);
1554
+ *
1555
+ * // Write string
1556
+ * await fs.writeFile("/tmp/file.txt", "Hello, world!");
1557
+ *
1558
+ * // Write Buffer
1559
+ * await fs.writeFile("/tmp/file.bin", Buffer.from([0x01, 0x02, 0x03]));
1560
+ * ```
1561
+ */
1562
+ async writeFile(path, data) {
1563
+ if (isFileLike(data)) {
1564
+ await writeFile(path, Readable.from(data.stream()));
1565
+ return;
1566
+ }
1567
+ await writeFile(path, data);
1568
+ }
1569
+ /**
1570
+ * Reads the content of a file as a string.
1571
+ *
1572
+ * @param path - The file path to read
1573
+ * @returns The file content as a string
1574
+ *
1575
+ * @example
1576
+ * ```typescript
1577
+ * const fs = alepha.inject(NodeFileSystemProvider);
1578
+ * const content = await fs.readTextFile("/tmp/file.txt");
1579
+ * ```
1580
+ */
1581
+ async readTextFile(path) {
1582
+ return (await this.readFile(path)).toString("utf-8");
1583
+ }
1584
+ /**
1585
+ * Reads the content of a file as JSON.
1586
+ *
1587
+ * @param path - The file path to read
1588
+ * @returns The parsed JSON content
1589
+ *
1590
+ * @example
1591
+ * ```typescript
1592
+ * const fs = alepha.inject(NodeFileSystemProvider);
1593
+ * const config = await fs.readJsonFile<{ name: string }>("/tmp/config.json");
1594
+ * ```
1595
+ */
1596
+ async readJsonFile(path) {
1597
+ const text = await this.readTextFile(path);
1598
+ return this.json.parse(text);
1599
+ }
1600
+ /**
1601
+ * Creates a FileLike object from a Web File.
1602
+ *
1603
+ * @protected
1604
+ */
1605
+ createFileFromWebFile(source, options = {}) {
1606
+ const name = options.name ?? source.name;
1607
+ return {
1608
+ name,
1609
+ type: options.type ?? (source.type || this.detector.getContentType(name)),
1610
+ size: options.size ?? source.size ?? 0,
1611
+ lastModified: source.lastModified || Date.now(),
1612
+ stream: () => source.stream(),
1613
+ arrayBuffer: async () => {
1614
+ return await source.arrayBuffer();
1615
+ },
1616
+ text: async () => {
1617
+ return await source.text();
1618
+ }
1619
+ };
1620
+ }
1621
+ /**
1622
+ * Creates a FileLike object from a Buffer.
1623
+ *
1624
+ * @protected
1625
+ */
1626
+ createFileFromBuffer(source, options = {}) {
1627
+ const name = options.name ?? "file";
1628
+ return {
1629
+ name,
1630
+ type: options.type ?? this.detector.getContentType(options.name ?? name),
1631
+ size: source.byteLength,
1632
+ lastModified: Date.now(),
1633
+ stream: () => Readable.from(source),
1634
+ arrayBuffer: async () => {
1635
+ return this.bufferToArrayBuffer(source);
1636
+ },
1637
+ text: async () => {
1638
+ return source.toString("utf-8");
1639
+ }
1640
+ };
1641
+ }
1642
+ /**
1643
+ * Creates a FileLike object from a stream.
1644
+ *
1645
+ * @protected
1646
+ */
1647
+ createFileFromStream(source, options = {}) {
1648
+ let buffer = null;
1649
+ return {
1650
+ name: options.name ?? "file",
1651
+ type: options.type ?? this.detector.getContentType(options.name ?? "file"),
1652
+ size: options.size ?? 0,
1653
+ lastModified: Date.now(),
1654
+ stream: () => source,
1655
+ _buffer: null,
1656
+ arrayBuffer: async () => {
1657
+ buffer ??= await this.streamToBuffer(source);
1658
+ return this.bufferToArrayBuffer(buffer);
1659
+ },
1660
+ text: async () => {
1661
+ buffer ??= await this.streamToBuffer(source);
1662
+ return buffer.toString("utf-8");
1663
+ }
1664
+ };
1665
+ }
1666
+ /**
1667
+ * Creates a FileLike object from a URL.
1668
+ *
1669
+ * @protected
1670
+ */
1671
+ createFileFromUrl(url, options = {}) {
1672
+ const parsedUrl = new URL(url);
1673
+ const filename = options.name || parsedUrl.pathname.split("/").pop() || "file";
1674
+ let buffer = null;
1675
+ return {
1676
+ name: filename,
1677
+ type: options.type ?? this.detector.getContentType(filename),
1678
+ size: 0,
1679
+ lastModified: Date.now(),
1680
+ stream: () => this.createStreamFromUrl(url),
1681
+ arrayBuffer: async () => {
1682
+ buffer ??= await this.loadFromUrl(url);
1683
+ return this.bufferToArrayBuffer(buffer);
1684
+ },
1685
+ text: async () => {
1686
+ buffer ??= await this.loadFromUrl(url);
1687
+ return buffer.toString("utf-8");
1688
+ },
1689
+ filepath: url
1690
+ };
1691
+ }
1692
+ /**
1693
+ * Gets a streaming response from a URL.
1694
+ *
1695
+ * @protected
1696
+ */
1697
+ getStreamingResponse(url) {
1698
+ const stream = new PassThrough();
1699
+ fetch(url).then((res) => Readable.fromWeb(res.body).pipe(stream)).catch((err) => stream.destroy(err));
1700
+ return stream;
1701
+ }
1702
+ /**
1703
+ * Loads data from a URL.
1704
+ *
1705
+ * @protected
1706
+ */
1707
+ async loadFromUrl(url) {
1708
+ const parsedUrl = new URL(url);
1709
+ if (parsedUrl.protocol === "file:") return await readFile(fileURLToPath(url));
1710
+ else if (parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:") {
1711
+ const response = await fetch(url);
1712
+ if (!response.ok) throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
1713
+ const arrayBuffer = await response.arrayBuffer();
1714
+ return Buffer.from(arrayBuffer);
1715
+ } else throw new Error(`Unsupported protocol: ${parsedUrl.protocol}`);
1716
+ }
1717
+ /**
1718
+ * Creates a stream from a URL.
1719
+ *
1720
+ * @protected
1721
+ */
1722
+ createStreamFromUrl(url) {
1723
+ const parsedUrl = new URL(url);
1724
+ if (parsedUrl.protocol === "file:") return createReadStream(fileURLToPath(url));
1725
+ else if (parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:") return this.getStreamingResponse(url);
1726
+ else throw new AlephaError(`Unsupported protocol: ${parsedUrl.protocol}`);
1727
+ }
1728
+ /**
1729
+ * Converts a stream-like object to a Buffer.
1730
+ *
1731
+ * @protected
1732
+ */
1733
+ async streamToBuffer(streamLike) {
1734
+ const stream = streamLike instanceof Readable ? streamLike : Readable.fromWeb(streamLike);
1735
+ return new Promise((resolve, reject) => {
1736
+ const buffer = [];
1737
+ stream.on("data", (chunk) => buffer.push(Buffer.from(chunk)));
1738
+ stream.on("end", () => resolve(Buffer.concat(buffer)));
1739
+ stream.on("error", (err) => reject(new AlephaError("Error converting stream", { cause: err })));
1740
+ });
1741
+ }
1742
+ /**
1743
+ * Converts a Node.js Buffer to an ArrayBuffer.
1744
+ *
1745
+ * @protected
1746
+ */
1747
+ bufferToArrayBuffer(buffer) {
1748
+ return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
1749
+ }
1750
+ };
1751
+
1752
+ //#endregion
1753
+ //#region ../../src/system/providers/NodeShellProvider.ts
1754
+ /**
1755
+ * Node.js implementation of ShellProvider.
1756
+ *
1757
+ * Executes shell commands using Node.js child_process module.
1758
+ * Supports binary resolution from node_modules/.bin for local packages.
1759
+ */
1760
+ var NodeShellProvider = class {
1761
+ log = $logger();
1762
+ fs = $inject(FileSystemProvider);
1763
+ /**
1764
+ * Run a shell command or binary.
1765
+ */
1766
+ async run(command, options = {}) {
1767
+ const { resolve = false, capture = false, root, env } = options;
1768
+ const cwd = root ?? process.cwd();
1769
+ this.log.debug(`Shell: ${command}`, {
1770
+ cwd,
1771
+ resolve,
1772
+ capture
1773
+ });
1774
+ let executable;
1775
+ let args;
1776
+ if (resolve) {
1777
+ const [bin, ...rest] = command.split(" ");
1778
+ executable = await this.resolveExecutable(bin, cwd);
1779
+ args = rest;
1780
+ } else [executable, ...args] = command.split(" ");
1781
+ if (capture) return this.execCapture(command, {
1782
+ cwd,
1783
+ env
1784
+ });
1785
+ return this.execInherit(executable, args, {
1786
+ cwd,
1787
+ env
1788
+ });
1789
+ }
1790
+ /**
1791
+ * Execute command with inherited stdio (streams to terminal).
1792
+ */
1793
+ async execInherit(executable, args, options) {
1794
+ const proc = spawn(executable, args, {
1795
+ stdio: "inherit",
1796
+ cwd: options.cwd,
1797
+ env: {
1798
+ ...process.env,
1799
+ ...options.env
1800
+ }
1801
+ });
1802
+ return new Promise((resolve, reject) => {
1803
+ proc.on("exit", (code) => {
1804
+ if (code === 0 || code === null) resolve("");
1805
+ else reject(new AlephaError(`Command exited with code ${code}`));
1806
+ });
1807
+ proc.on("error", reject);
1808
+ });
1809
+ }
1810
+ /**
1811
+ * Execute command and capture stdout.
1812
+ */
1813
+ execCapture(command, options) {
1814
+ return new Promise((resolve, reject) => {
1815
+ exec(command, {
1816
+ cwd: options.cwd,
1817
+ env: {
1818
+ ...process.env,
1819
+ LOG_FORMAT: "pretty",
1820
+ ...options.env
1821
+ }
1822
+ }, (err, stdout) => {
1823
+ if (err) {
1824
+ err.stdout = stdout;
1825
+ reject(err);
1826
+ } else resolve(stdout);
1827
+ });
1828
+ });
1829
+ }
1830
+ /**
1831
+ * Resolve executable path from node_modules/.bin.
1832
+ *
1833
+ * Search order:
1834
+ * 1. Local: node_modules/.bin/
1835
+ * 2. Pnpm nested: node_modules/alepha/node_modules/.bin/
1836
+ * 3. Monorepo: Walk up to 3 parent directories
1837
+ */
1838
+ async resolveExecutable(name, root) {
1839
+ const suffix = process.platform === "win32" ? ".cmd" : "";
1840
+ let execPath = await this.findExecutable(root, `node_modules/.bin/${name}${suffix}`);
1841
+ if (!execPath) execPath = await this.findExecutable(root, `node_modules/alepha/node_modules/.bin/${name}${suffix}`);
1842
+ if (!execPath) {
1843
+ let parentDir = this.fs.join(root, "..");
1844
+ for (let i = 0; i < 3; i++) {
1845
+ execPath = await this.findExecutable(parentDir, `node_modules/.bin/${name}${suffix}`);
1846
+ if (execPath) break;
1847
+ parentDir = this.fs.join(parentDir, "..");
1848
+ }
1849
+ }
1850
+ if (!execPath) throw new AlephaError(`Could not find executable for '${name}'. Make sure the package is installed.`);
1851
+ return execPath;
1852
+ }
1853
+ /**
1854
+ * Check if executable exists at path.
1855
+ */
1856
+ async findExecutable(root, relativePath) {
1857
+ const fullPath = this.fs.join(root, relativePath);
1858
+ if (await this.fs.exists(fullPath)) return fullPath;
1859
+ }
1860
+ /**
1861
+ * Check if a command is installed and available in the system PATH.
1862
+ */
1863
+ isInstalled(command) {
1864
+ return new Promise((resolve) => {
1865
+ exec(process.platform === "win32" ? `where ${command}` : `command -v ${command}`, (error) => resolve(!error));
1866
+ });
1867
+ }
1868
+ };
1869
+
1870
+ //#endregion
1871
+ //#region ../../src/system/providers/ShellProvider.ts
1872
+ /**
1873
+ * Abstract provider for executing shell commands and binaries.
1874
+ *
1875
+ * Implementations:
1876
+ * - `NodeShellProvider` - Real shell execution using Node.js child_process
1877
+ * - `MemoryShellProvider` - In-memory mock for testing
1878
+ *
1879
+ * @example
1880
+ * ```typescript
1881
+ * class MyService {
1882
+ * protected readonly shell = $inject(ShellProvider);
1883
+ *
1884
+ * async build() {
1885
+ * // Run shell command directly
1886
+ * await this.shell.run("yarn install");
1887
+ *
1888
+ * // Run local binary with resolution
1889
+ * await this.shell.run("vite build", { resolve: true });
1890
+ *
1891
+ * // Capture output
1892
+ * const output = await this.shell.run("echo hello", { capture: true });
1893
+ * }
1894
+ * }
1895
+ * ```
1896
+ */
1897
+ var ShellProvider = class {};
1898
+
1899
+ //#endregion
1900
+ //#region ../../src/system/index.ts
1901
+ /**
1902
+ * | type | quality | stability |
1903
+ * |------|---------|-----------|
1904
+ * | tooling | standard | stable |
1905
+ *
1906
+ * System-level abstractions for portable code across runtimes.
1907
+ *
1908
+ * **Features:**
1909
+ * - File system operations (read, write, exists, etc.)
1910
+ * - Shell command execution
1911
+ * - File type detection and MIME utilities
1912
+ * - Memory implementations for testing
1913
+ *
1914
+ * @module alepha.system
1915
+ */
1916
+ const AlephaSystem = $module({
1917
+ name: "alepha.system",
1918
+ primitives: [],
1919
+ services: [
1920
+ FileDetector,
1921
+ FileSystemProvider,
1922
+ MemoryFileSystemProvider,
1923
+ NodeFileSystemProvider,
1924
+ ShellProvider,
1925
+ MemoryShellProvider,
1926
+ NodeShellProvider
1927
+ ],
1928
+ register: (alepha) => alepha.with({
1929
+ optional: true,
1930
+ provide: FileSystemProvider,
1931
+ use: NodeFileSystemProvider
1932
+ }).with({
1933
+ optional: true,
1934
+ provide: ShellProvider,
1935
+ use: alepha.isTest() ? MemoryShellProvider : NodeShellProvider
1936
+ })
1937
+ });
1938
+
94
1939
  //#endregion
95
1940
  //#region ../../src/email/errors/EmailError.ts
96
1941
  var EmailError = class extends Error {
@@ -380,13 +2225,18 @@ var NodemailerEmailProvider = class {
380
2225
  //#endregion
381
2226
  //#region ../../src/email/index.ts
382
2227
  /**
383
- * Provides email sending capabilities for Alepha applications with multiple provider backends.
2228
+ * | type | quality | stability |
2229
+ * |------|---------|-----------|
2230
+ * | backend | rare | stable |
2231
+ *
2232
+ * Email delivery with template support.
384
2233
  *
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.
2234
+ * **Features:**
2235
+ * - Send emails with templates
2236
+ * - Multiple recipients
2237
+ * - SMTP via Nodemailer
2238
+ * - Local file provider for development
388
2239
  *
389
- * @see {@link EmailProvider}
390
2240
  * @module alepha.email
391
2241
  */
392
2242
  const AlephaEmail = $module({