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
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,1846 @@ 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
+ const p = mkdir(path, {
1457
+ recursive: options.recursive ?? true,
1458
+ mode: options.mode
1459
+ });
1460
+ if (options.force === false) await p;
1461
+ else await p.catch(() => {});
1462
+ }
1463
+ /**
1464
+ * Lists files in a directory.
1465
+ *
1466
+ * @param path - The directory path to list
1467
+ * @param options - List options
1468
+ * @returns Array of filenames
1469
+ *
1470
+ * @example
1471
+ * ```typescript
1472
+ * const fs = alepha.inject(NodeFileSystemProvider);
1473
+ *
1474
+ * // List files in a directory
1475
+ * const files = await fs.ls("/tmp");
1476
+ * console.log(files); // ["file1.txt", "file2.txt", "subdir"]
1477
+ *
1478
+ * // List with hidden files
1479
+ * const allFiles = await fs.ls("/tmp", { hidden: true });
1480
+ *
1481
+ * // List recursively
1482
+ * const allFilesRecursive = await fs.ls("/tmp", { recursive: true });
1483
+ * ```
1484
+ */
1485
+ async ls(path, options) {
1486
+ const entries = await readdir(path);
1487
+ const filteredEntries = options?.hidden ? entries : entries.filter((e) => !e.startsWith("."));
1488
+ if (options?.recursive) {
1489
+ const allFiles = [];
1490
+ for (const entry of filteredEntries) {
1491
+ const fullPath = join(path, entry);
1492
+ if ((await stat(fullPath)).isDirectory()) {
1493
+ allFiles.push(entry);
1494
+ const subFiles = await this.ls(fullPath, options);
1495
+ allFiles.push(...subFiles.map((f) => join(entry, f)));
1496
+ } else allFiles.push(entry);
1497
+ }
1498
+ return allFiles;
1499
+ }
1500
+ return filteredEntries;
1501
+ }
1502
+ /**
1503
+ * Checks if a file or directory exists.
1504
+ *
1505
+ * @param path - The path to check
1506
+ * @returns True if the path exists, false otherwise
1507
+ *
1508
+ * @example
1509
+ * ```typescript
1510
+ * const fs = alepha.inject(NodeFileSystemProvider);
1511
+ *
1512
+ * if (await fs.exists("/tmp/file.txt")) {
1513
+ * console.log("File exists");
1514
+ * }
1515
+ * ```
1516
+ */
1517
+ async exists(path) {
1518
+ try {
1519
+ await access(path);
1520
+ return true;
1521
+ } catch {
1522
+ return false;
1523
+ }
1524
+ }
1525
+ /**
1526
+ * Reads the content of a file.
1527
+ *
1528
+ * @param path - The file path to read
1529
+ * @returns The file content as a Buffer
1530
+ *
1531
+ * @example
1532
+ * ```typescript
1533
+ * const fs = alepha.inject(NodeFileSystemProvider);
1534
+ *
1535
+ * const buffer = await fs.readFile("/tmp/file.txt");
1536
+ * console.log(buffer.toString("utf-8"));
1537
+ * ```
1538
+ */
1539
+ async readFile(path) {
1540
+ return await readFile(path);
1541
+ }
1542
+ /**
1543
+ * Writes data to a file.
1544
+ *
1545
+ * @param path - The file path to write to
1546
+ * @param data - The data to write (Buffer or string)
1547
+ *
1548
+ * @example
1549
+ * ```typescript
1550
+ * const fs = alepha.inject(NodeFileSystemProvider);
1551
+ *
1552
+ * // Write string
1553
+ * await fs.writeFile("/tmp/file.txt", "Hello, world!");
1554
+ *
1555
+ * // Write Buffer
1556
+ * await fs.writeFile("/tmp/file.bin", Buffer.from([0x01, 0x02, 0x03]));
1557
+ * ```
1558
+ */
1559
+ async writeFile(path, data) {
1560
+ if (isFileLike(data)) {
1561
+ await writeFile(path, Readable.from(data.stream()));
1562
+ return;
1563
+ }
1564
+ await writeFile(path, data);
1565
+ }
1566
+ /**
1567
+ * Reads the content of a file as a string.
1568
+ *
1569
+ * @param path - The file path to read
1570
+ * @returns The file content as a string
1571
+ *
1572
+ * @example
1573
+ * ```typescript
1574
+ * const fs = alepha.inject(NodeFileSystemProvider);
1575
+ * const content = await fs.readTextFile("/tmp/file.txt");
1576
+ * ```
1577
+ */
1578
+ async readTextFile(path) {
1579
+ return (await this.readFile(path)).toString("utf-8");
1580
+ }
1581
+ /**
1582
+ * Reads the content of a file as JSON.
1583
+ *
1584
+ * @param path - The file path to read
1585
+ * @returns The parsed JSON content
1586
+ *
1587
+ * @example
1588
+ * ```typescript
1589
+ * const fs = alepha.inject(NodeFileSystemProvider);
1590
+ * const config = await fs.readJsonFile<{ name: string }>("/tmp/config.json");
1591
+ * ```
1592
+ */
1593
+ async readJsonFile(path) {
1594
+ const text = await this.readTextFile(path);
1595
+ return this.json.parse(text);
1596
+ }
1597
+ /**
1598
+ * Creates a FileLike object from a Web File.
1599
+ *
1600
+ * @protected
1601
+ */
1602
+ createFileFromWebFile(source, options = {}) {
1603
+ const name = options.name ?? source.name;
1604
+ return {
1605
+ name,
1606
+ type: options.type ?? (source.type || this.detector.getContentType(name)),
1607
+ size: options.size ?? source.size ?? 0,
1608
+ lastModified: source.lastModified || Date.now(),
1609
+ stream: () => source.stream(),
1610
+ arrayBuffer: async () => {
1611
+ return await source.arrayBuffer();
1612
+ },
1613
+ text: async () => {
1614
+ return await source.text();
1615
+ }
1616
+ };
1617
+ }
1618
+ /**
1619
+ * Creates a FileLike object from a Buffer.
1620
+ *
1621
+ * @protected
1622
+ */
1623
+ createFileFromBuffer(source, options = {}) {
1624
+ const name = options.name ?? "file";
1625
+ return {
1626
+ name,
1627
+ type: options.type ?? this.detector.getContentType(options.name ?? name),
1628
+ size: source.byteLength,
1629
+ lastModified: Date.now(),
1630
+ stream: () => Readable.from(source),
1631
+ arrayBuffer: async () => {
1632
+ return this.bufferToArrayBuffer(source);
1633
+ },
1634
+ text: async () => {
1635
+ return source.toString("utf-8");
1636
+ }
1637
+ };
1638
+ }
1639
+ /**
1640
+ * Creates a FileLike object from a stream.
1641
+ *
1642
+ * @protected
1643
+ */
1644
+ createFileFromStream(source, options = {}) {
1645
+ let buffer = null;
1646
+ return {
1647
+ name: options.name ?? "file",
1648
+ type: options.type ?? this.detector.getContentType(options.name ?? "file"),
1649
+ size: options.size ?? 0,
1650
+ lastModified: Date.now(),
1651
+ stream: () => source,
1652
+ _buffer: null,
1653
+ arrayBuffer: async () => {
1654
+ buffer ??= await this.streamToBuffer(source);
1655
+ return this.bufferToArrayBuffer(buffer);
1656
+ },
1657
+ text: async () => {
1658
+ buffer ??= await this.streamToBuffer(source);
1659
+ return buffer.toString("utf-8");
1660
+ }
1661
+ };
1662
+ }
1663
+ /**
1664
+ * Creates a FileLike object from a URL.
1665
+ *
1666
+ * @protected
1667
+ */
1668
+ createFileFromUrl(url, options = {}) {
1669
+ const parsedUrl = new URL(url);
1670
+ const filename = options.name || parsedUrl.pathname.split("/").pop() || "file";
1671
+ let buffer = null;
1672
+ return {
1673
+ name: filename,
1674
+ type: options.type ?? this.detector.getContentType(filename),
1675
+ size: 0,
1676
+ lastModified: Date.now(),
1677
+ stream: () => this.createStreamFromUrl(url),
1678
+ arrayBuffer: async () => {
1679
+ buffer ??= await this.loadFromUrl(url);
1680
+ return this.bufferToArrayBuffer(buffer);
1681
+ },
1682
+ text: async () => {
1683
+ buffer ??= await this.loadFromUrl(url);
1684
+ return buffer.toString("utf-8");
1685
+ },
1686
+ filepath: url
1687
+ };
1688
+ }
1689
+ /**
1690
+ * Gets a streaming response from a URL.
1691
+ *
1692
+ * @protected
1693
+ */
1694
+ getStreamingResponse(url) {
1695
+ const stream = new PassThrough();
1696
+ fetch(url).then((res) => Readable.fromWeb(res.body).pipe(stream)).catch((err) => stream.destroy(err));
1697
+ return stream;
1698
+ }
1699
+ /**
1700
+ * Loads data from a URL.
1701
+ *
1702
+ * @protected
1703
+ */
1704
+ async loadFromUrl(url) {
1705
+ const parsedUrl = new URL(url);
1706
+ if (parsedUrl.protocol === "file:") return await readFile(fileURLToPath(url));
1707
+ else if (parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:") {
1708
+ const response = await fetch(url);
1709
+ if (!response.ok) throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
1710
+ const arrayBuffer = await response.arrayBuffer();
1711
+ return Buffer.from(arrayBuffer);
1712
+ } else throw new Error(`Unsupported protocol: ${parsedUrl.protocol}`);
1713
+ }
1714
+ /**
1715
+ * Creates a stream from a URL.
1716
+ *
1717
+ * @protected
1718
+ */
1719
+ createStreamFromUrl(url) {
1720
+ const parsedUrl = new URL(url);
1721
+ if (parsedUrl.protocol === "file:") return createReadStream(fileURLToPath(url));
1722
+ else if (parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:") return this.getStreamingResponse(url);
1723
+ else throw new AlephaError(`Unsupported protocol: ${parsedUrl.protocol}`);
1724
+ }
1725
+ /**
1726
+ * Converts a stream-like object to a Buffer.
1727
+ *
1728
+ * @protected
1729
+ */
1730
+ async streamToBuffer(streamLike) {
1731
+ const stream = streamLike instanceof Readable ? streamLike : Readable.fromWeb(streamLike);
1732
+ return new Promise((resolve, reject) => {
1733
+ const buffer = [];
1734
+ stream.on("data", (chunk) => buffer.push(Buffer.from(chunk)));
1735
+ stream.on("end", () => resolve(Buffer.concat(buffer)));
1736
+ stream.on("error", (err) => reject(new AlephaError("Error converting stream", { cause: err })));
1737
+ });
1738
+ }
1739
+ /**
1740
+ * Converts a Node.js Buffer to an ArrayBuffer.
1741
+ *
1742
+ * @protected
1743
+ */
1744
+ bufferToArrayBuffer(buffer) {
1745
+ return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
1746
+ }
1747
+ };
1748
+
1749
+ //#endregion
1750
+ //#region ../../src/system/providers/NodeShellProvider.ts
1751
+ /**
1752
+ * Node.js implementation of ShellProvider.
1753
+ *
1754
+ * Executes shell commands using Node.js child_process module.
1755
+ * Supports binary resolution from node_modules/.bin for local packages.
1756
+ */
1757
+ var NodeShellProvider = class {
1758
+ log = $logger();
1759
+ fs = $inject(FileSystemProvider);
1760
+ /**
1761
+ * Run a shell command or binary.
1762
+ */
1763
+ async run(command, options = {}) {
1764
+ const { resolve = false, capture = false, root, env } = options;
1765
+ const cwd = root ?? process.cwd();
1766
+ this.log.debug(`Shell: ${command}`, {
1767
+ cwd,
1768
+ resolve,
1769
+ capture
1770
+ });
1771
+ let executable;
1772
+ let args;
1773
+ if (resolve) {
1774
+ const [bin, ...rest] = command.split(" ");
1775
+ executable = await this.resolveExecutable(bin, cwd);
1776
+ args = rest;
1777
+ } else [executable, ...args] = command.split(" ");
1778
+ if (capture) return this.execCapture(command, {
1779
+ cwd,
1780
+ env
1781
+ });
1782
+ return this.execInherit(executable, args, {
1783
+ cwd,
1784
+ env
1785
+ });
1786
+ }
1787
+ /**
1788
+ * Execute command with inherited stdio (streams to terminal).
1789
+ */
1790
+ async execInherit(executable, args, options) {
1791
+ const proc = spawn(executable, args, {
1792
+ stdio: "inherit",
1793
+ cwd: options.cwd,
1794
+ env: {
1795
+ ...process.env,
1796
+ ...options.env
1797
+ }
1798
+ });
1799
+ return new Promise((resolve, reject) => {
1800
+ proc.on("exit", (code) => {
1801
+ if (code === 0 || code === null) resolve("");
1802
+ else reject(new AlephaError(`Command exited with code ${code}`));
1803
+ });
1804
+ proc.on("error", reject);
1805
+ });
1806
+ }
1807
+ /**
1808
+ * Execute command and capture stdout.
1809
+ */
1810
+ execCapture(command, options) {
1811
+ return new Promise((resolve, reject) => {
1812
+ exec(command, {
1813
+ cwd: options.cwd,
1814
+ env: {
1815
+ ...process.env,
1816
+ LOG_FORMAT: "pretty",
1817
+ ...options.env
1818
+ }
1819
+ }, (err, stdout) => {
1820
+ if (err) {
1821
+ err.stdout = stdout;
1822
+ reject(err);
1823
+ } else resolve(stdout);
1824
+ });
1825
+ });
1826
+ }
1827
+ /**
1828
+ * Resolve executable path from node_modules/.bin.
1829
+ *
1830
+ * Search order:
1831
+ * 1. Local: node_modules/.bin/
1832
+ * 2. Pnpm nested: node_modules/alepha/node_modules/.bin/
1833
+ * 3. Monorepo: Walk up to 3 parent directories
1834
+ */
1835
+ async resolveExecutable(name, root) {
1836
+ const suffix = process.platform === "win32" ? ".cmd" : "";
1837
+ let execPath = await this.findExecutable(root, `node_modules/.bin/${name}${suffix}`);
1838
+ if (!execPath) execPath = await this.findExecutable(root, `node_modules/alepha/node_modules/.bin/${name}${suffix}`);
1839
+ if (!execPath) {
1840
+ let parentDir = this.fs.join(root, "..");
1841
+ for (let i = 0; i < 3; i++) {
1842
+ execPath = await this.findExecutable(parentDir, `node_modules/.bin/${name}${suffix}`);
1843
+ if (execPath) break;
1844
+ parentDir = this.fs.join(parentDir, "..");
1845
+ }
1846
+ }
1847
+ if (!execPath) throw new AlephaError(`Could not find executable for '${name}'. Make sure the package is installed.`);
1848
+ return execPath;
1849
+ }
1850
+ /**
1851
+ * Check if executable exists at path.
1852
+ */
1853
+ async findExecutable(root, relativePath) {
1854
+ const fullPath = this.fs.join(root, relativePath);
1855
+ if (await this.fs.exists(fullPath)) return fullPath;
1856
+ }
1857
+ /**
1858
+ * Check if a command is installed and available in the system PATH.
1859
+ */
1860
+ isInstalled(command) {
1861
+ return new Promise((resolve) => {
1862
+ exec(process.platform === "win32" ? `where ${command}` : `command -v ${command}`, (error) => resolve(!error));
1863
+ });
1864
+ }
1865
+ };
1866
+
1867
+ //#endregion
1868
+ //#region ../../src/system/providers/ShellProvider.ts
1869
+ /**
1870
+ * Abstract provider for executing shell commands and binaries.
1871
+ *
1872
+ * Implementations:
1873
+ * - `NodeShellProvider` - Real shell execution using Node.js child_process
1874
+ * - `MemoryShellProvider` - In-memory mock for testing
1875
+ *
1876
+ * @example
1877
+ * ```typescript
1878
+ * class MyService {
1879
+ * protected readonly shell = $inject(ShellProvider);
1880
+ *
1881
+ * async build() {
1882
+ * // Run shell command directly
1883
+ * await this.shell.run("yarn install");
1884
+ *
1885
+ * // Run local binary with resolution
1886
+ * await this.shell.run("vite build", { resolve: true });
1887
+ *
1888
+ * // Capture output
1889
+ * const output = await this.shell.run("echo hello", { capture: true });
1890
+ * }
1891
+ * }
1892
+ * ```
1893
+ */
1894
+ var ShellProvider = class {};
1895
+
1896
+ //#endregion
1897
+ //#region ../../src/system/index.ts
1898
+ /**
1899
+ * | type | quality | stability |
1900
+ * |------|---------|-----------|
1901
+ * | tooling | standard | stable |
1902
+ *
1903
+ * System-level abstractions for portable code across runtimes.
1904
+ *
1905
+ * **Features:**
1906
+ * - File system operations (read, write, exists, etc.)
1907
+ * - Shell command execution
1908
+ * - File type detection and MIME utilities
1909
+ * - Memory implementations for testing
1910
+ *
1911
+ * @module alepha.system
1912
+ */
1913
+ const AlephaSystem = $module({
1914
+ name: "alepha.system",
1915
+ primitives: [],
1916
+ services: [
1917
+ FileDetector,
1918
+ FileSystemProvider,
1919
+ MemoryFileSystemProvider,
1920
+ NodeFileSystemProvider,
1921
+ ShellProvider,
1922
+ MemoryShellProvider,
1923
+ NodeShellProvider
1924
+ ],
1925
+ register: (alepha) => alepha.with({
1926
+ optional: true,
1927
+ provide: FileSystemProvider,
1928
+ use: NodeFileSystemProvider
1929
+ }).with({
1930
+ optional: true,
1931
+ provide: ShellProvider,
1932
+ use: alepha.isTest() ? MemoryShellProvider : NodeShellProvider
1933
+ })
1934
+ });
1935
+
91
1936
  //#endregion
92
1937
  //#region ../../src/sms/errors/SmsError.ts
93
1938
  var SmsError = class extends Error {
@@ -157,13 +2002,17 @@ ${message}
157
2002
  //#endregion
158
2003
  //#region ../../src/sms/index.ts
159
2004
  /**
160
- * Provides SMS sending capabilities for Alepha applications with multiple provider backends.
2005
+ * | type | quality | stability |
2006
+ * |------|---------|-----------|
2007
+ * | backend | rare | stable |
2008
+ *
2009
+ * SMS delivery with multiple provider support.
161
2010
  *
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.
2011
+ * **Features:**
2012
+ * - Send SMS with templates
2013
+ * - Multiple recipients
2014
+ * - Provider abstraction
165
2015
  *
166
- * @see {@link SmsProvider}
167
2016
  * @module alepha.sms
168
2017
  */
169
2018
  const AlephaSms = $module({