alepha 0.14.4 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/README.md +44 -102
  2. package/dist/api/audits/index.d.ts +331 -443
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js +2 -2
  5. package/dist/api/audits/index.js.map +1 -1
  6. package/dist/api/files/index.d.ts +0 -113
  7. package/dist/api/files/index.d.ts.map +1 -1
  8. package/dist/api/files/index.js +2 -3
  9. package/dist/api/files/index.js.map +1 -1
  10. package/dist/api/jobs/index.d.ts +151 -262
  11. package/dist/api/jobs/index.d.ts.map +1 -1
  12. package/dist/api/notifications/index.browser.js +4 -4
  13. package/dist/api/notifications/index.browser.js.map +1 -1
  14. package/dist/api/notifications/index.d.ts +164 -276
  15. package/dist/api/notifications/index.d.ts.map +1 -1
  16. package/dist/api/notifications/index.js +4 -4
  17. package/dist/api/notifications/index.js.map +1 -1
  18. package/dist/api/parameters/index.d.ts +265 -377
  19. package/dist/api/parameters/index.d.ts.map +1 -1
  20. package/dist/api/users/index.browser.js +1 -2
  21. package/dist/api/users/index.browser.js.map +1 -1
  22. package/dist/api/users/index.d.ts +195 -301
  23. package/dist/api/users/index.d.ts.map +1 -1
  24. package/dist/api/users/index.js +203 -184
  25. package/dist/api/users/index.js.map +1 -1
  26. package/dist/api/verifications/index.d.ts.map +1 -1
  27. package/dist/batch/index.d.ts.map +1 -1
  28. package/dist/batch/index.js +1 -2
  29. package/dist/batch/index.js.map +1 -1
  30. package/dist/bucket/index.d.ts.map +1 -1
  31. package/dist/cache/core/index.d.ts.map +1 -1
  32. package/dist/cache/redis/index.d.ts.map +1 -1
  33. package/dist/cache/redis/index.js +2 -2
  34. package/dist/cache/redis/index.js.map +1 -1
  35. package/dist/cli/index.d.ts +5900 -165
  36. package/dist/cli/index.d.ts.map +1 -1
  37. package/dist/cli/index.js +1481 -639
  38. package/dist/cli/index.js.map +1 -1
  39. package/dist/command/index.d.ts +8 -4
  40. package/dist/command/index.d.ts.map +1 -1
  41. package/dist/command/index.js +29 -25
  42. package/dist/command/index.js.map +1 -1
  43. package/dist/core/index.browser.js +563 -54
  44. package/dist/core/index.browser.js.map +1 -1
  45. package/dist/core/index.d.ts +175 -8
  46. package/dist/core/index.d.ts.map +1 -1
  47. package/dist/core/index.js +564 -54
  48. package/dist/core/index.js.map +1 -1
  49. package/dist/core/index.native.js +563 -54
  50. package/dist/core/index.native.js.map +1 -1
  51. package/dist/datetime/index.d.ts.map +1 -1
  52. package/dist/datetime/index.js +4 -4
  53. package/dist/datetime/index.js.map +1 -1
  54. package/dist/email/index.d.ts +89 -42
  55. package/dist/email/index.d.ts.map +1 -1
  56. package/dist/email/index.js +129 -33
  57. package/dist/email/index.js.map +1 -1
  58. package/dist/fake/index.d.ts +7969 -2
  59. package/dist/fake/index.d.ts.map +1 -1
  60. package/dist/fake/index.js +22 -22
  61. package/dist/fake/index.js.map +1 -1
  62. package/dist/file/index.d.ts +134 -1
  63. package/dist/file/index.d.ts.map +1 -1
  64. package/dist/file/index.js +253 -1
  65. package/dist/file/index.js.map +1 -1
  66. package/dist/lock/core/index.d.ts.map +1 -1
  67. package/dist/lock/redis/index.d.ts.map +1 -1
  68. package/dist/logger/index.d.ts +1 -2
  69. package/dist/logger/index.d.ts.map +1 -1
  70. package/dist/logger/index.js +1 -5
  71. package/dist/logger/index.js.map +1 -1
  72. package/dist/mcp/index.d.ts +19 -1
  73. package/dist/mcp/index.d.ts.map +1 -1
  74. package/dist/mcp/index.js +28 -4
  75. package/dist/mcp/index.js.map +1 -1
  76. package/dist/orm/chunk-DH6iiROE.js +38 -0
  77. package/dist/orm/index.browser.js +9 -9
  78. package/dist/orm/index.browser.js.map +1 -1
  79. package/dist/orm/index.bun.js +2821 -0
  80. package/dist/orm/index.bun.js.map +1 -0
  81. package/dist/orm/index.d.ts +318 -169
  82. package/dist/orm/index.d.ts.map +1 -1
  83. package/dist/orm/index.js +2086 -1776
  84. package/dist/orm/index.js.map +1 -1
  85. package/dist/queue/core/index.d.ts +4 -4
  86. package/dist/queue/core/index.d.ts.map +1 -1
  87. package/dist/queue/redis/index.d.ts.map +1 -1
  88. package/dist/redis/index.bun.js +285 -0
  89. package/dist/redis/index.bun.js.map +1 -0
  90. package/dist/redis/index.d.ts +13 -31
  91. package/dist/redis/index.d.ts.map +1 -1
  92. package/dist/redis/index.js +18 -38
  93. package/dist/redis/index.js.map +1 -1
  94. package/dist/retry/index.d.ts.map +1 -1
  95. package/dist/router/index.d.ts.map +1 -1
  96. package/dist/scheduler/index.d.ts +83 -1
  97. package/dist/scheduler/index.d.ts.map +1 -1
  98. package/dist/scheduler/index.js +393 -1
  99. package/dist/scheduler/index.js.map +1 -1
  100. package/dist/security/index.browser.js +5 -1
  101. package/dist/security/index.browser.js.map +1 -1
  102. package/dist/security/index.d.ts +598 -112
  103. package/dist/security/index.d.ts.map +1 -1
  104. package/dist/security/index.js +1808 -97
  105. package/dist/security/index.js.map +1 -1
  106. package/dist/server/auth/index.d.ts +1200 -175
  107. package/dist/server/auth/index.d.ts.map +1 -1
  108. package/dist/server/auth/index.js +1268 -37
  109. package/dist/server/auth/index.js.map +1 -1
  110. package/dist/server/cache/index.d.ts +6 -3
  111. package/dist/server/cache/index.d.ts.map +1 -1
  112. package/dist/server/cache/index.js +1 -1
  113. package/dist/server/cache/index.js.map +1 -1
  114. package/dist/server/compress/index.d.ts.map +1 -1
  115. package/dist/server/cookies/index.d.ts.map +1 -1
  116. package/dist/server/cookies/index.js +3 -3
  117. package/dist/server/cookies/index.js.map +1 -1
  118. package/dist/server/core/index.d.ts +115 -13
  119. package/dist/server/core/index.d.ts.map +1 -1
  120. package/dist/server/core/index.js +321 -139
  121. package/dist/server/core/index.js.map +1 -1
  122. package/dist/server/cors/index.d.ts +0 -1
  123. package/dist/server/cors/index.d.ts.map +1 -1
  124. package/dist/server/health/index.d.ts +0 -1
  125. package/dist/server/health/index.d.ts.map +1 -1
  126. package/dist/server/helmet/index.d.ts.map +1 -1
  127. package/dist/server/links/index.browser.js +9 -1
  128. package/dist/server/links/index.browser.js.map +1 -1
  129. package/dist/server/links/index.d.ts +1 -2
  130. package/dist/server/links/index.d.ts.map +1 -1
  131. package/dist/server/links/index.js +14 -7
  132. package/dist/server/links/index.js.map +1 -1
  133. package/dist/server/metrics/index.d.ts +514 -1
  134. package/dist/server/metrics/index.d.ts.map +1 -1
  135. package/dist/server/metrics/index.js +4462 -4
  136. package/dist/server/metrics/index.js.map +1 -1
  137. package/dist/server/multipart/index.d.ts.map +1 -1
  138. package/dist/server/proxy/index.d.ts +0 -1
  139. package/dist/server/proxy/index.d.ts.map +1 -1
  140. package/dist/server/rate-limit/index.d.ts.map +1 -1
  141. package/dist/server/static/index.d.ts.map +1 -1
  142. package/dist/server/swagger/index.d.ts +1 -2
  143. package/dist/server/swagger/index.d.ts.map +1 -1
  144. package/dist/server/swagger/index.js +1 -2
  145. package/dist/server/swagger/index.js.map +1 -1
  146. package/dist/sms/index.d.ts +3 -1
  147. package/dist/sms/index.d.ts.map +1 -1
  148. package/dist/sms/index.js +10 -10
  149. package/dist/sms/index.js.map +1 -1
  150. package/dist/thread/index.d.ts +0 -1
  151. package/dist/thread/index.d.ts.map +1 -1
  152. package/dist/thread/index.js +2 -2
  153. package/dist/thread/index.js.map +1 -1
  154. package/dist/topic/core/index.d.ts.map +1 -1
  155. package/dist/topic/redis/index.d.ts.map +1 -1
  156. package/dist/vite/index.d.ts +6315 -149
  157. package/dist/vite/index.d.ts.map +1 -1
  158. package/dist/vite/index.js +140 -469
  159. package/dist/vite/index.js.map +1 -1
  160. package/dist/websocket/index.browser.js +9 -9
  161. package/dist/websocket/index.browser.js.map +1 -1
  162. package/dist/websocket/index.d.ts +28 -28
  163. package/dist/websocket/index.d.ts.map +1 -1
  164. package/dist/websocket/index.js +9 -9
  165. package/dist/websocket/index.js.map +1 -1
  166. package/package.json +13 -18
  167. package/src/api/files/controllers/AdminFileStatsController.ts +0 -1
  168. package/src/api/users/atoms/realmAuthSettingsAtom.ts +5 -0
  169. package/src/api/users/controllers/{UserRealmController.ts → RealmController.ts} +11 -11
  170. package/src/api/users/entities/users.ts +1 -1
  171. package/src/api/users/index.ts +8 -8
  172. package/src/api/users/primitives/{$userRealm.ts → $realm.ts} +17 -19
  173. package/src/api/users/providers/{UserRealmProvider.ts → RealmProvider.ts} +26 -30
  174. package/src/api/users/schemas/{userRealmConfigSchema.ts → realmConfigSchema.ts} +2 -2
  175. package/src/api/users/services/CredentialService.ts +7 -7
  176. package/src/api/users/services/IdentityService.ts +4 -4
  177. package/src/api/users/services/RegistrationService.spec.ts +25 -27
  178. package/src/api/users/services/RegistrationService.ts +38 -27
  179. package/src/api/users/services/SessionCrudService.ts +3 -3
  180. package/src/api/users/services/SessionService.spec.ts +3 -3
  181. package/src/api/users/services/SessionService.ts +27 -18
  182. package/src/api/users/services/UserService.ts +7 -7
  183. package/src/batch/providers/BatchProvider.ts +1 -2
  184. package/src/cli/apps/AlephaCli.ts +2 -2
  185. package/src/cli/apps/AlephaPackageBuilderCli.ts +47 -20
  186. package/src/cli/assets/apiHelloControllerTs.ts +19 -0
  187. package/src/cli/assets/apiIndexTs.ts +16 -0
  188. package/src/cli/assets/biomeJson.ts +2 -1
  189. package/src/cli/assets/claudeMd.ts +308 -0
  190. package/src/cli/assets/dummySpecTs.ts +2 -1
  191. package/src/cli/assets/editorconfig.ts +2 -1
  192. package/src/cli/assets/mainBrowserTs.ts +4 -3
  193. package/src/cli/assets/mainCss.ts +24 -0
  194. package/src/cli/assets/mainServerTs.ts +24 -0
  195. package/src/cli/assets/tsconfigJson.ts +2 -1
  196. package/src/cli/assets/webAppRouterTs.ts +16 -0
  197. package/src/cli/assets/webHelloComponentTsx.ts +20 -0
  198. package/src/cli/assets/webIndexTs.ts +16 -0
  199. package/src/cli/atoms/appEntryOptions.ts +13 -0
  200. package/src/cli/atoms/buildOptions.ts +1 -1
  201. package/src/cli/atoms/changelogOptions.ts +1 -1
  202. package/src/cli/commands/build.ts +97 -61
  203. package/src/cli/commands/db.ts +21 -18
  204. package/src/cli/commands/deploy.ts +17 -5
  205. package/src/cli/commands/dev.ts +26 -47
  206. package/src/cli/commands/gen/env.ts +1 -1
  207. package/src/cli/commands/init.ts +79 -25
  208. package/src/cli/commands/lint.ts +9 -3
  209. package/src/cli/commands/test.ts +8 -2
  210. package/src/cli/commands/typecheck.ts +5 -1
  211. package/src/cli/commands/verify.ts +4 -2
  212. package/src/cli/defineConfig.ts +9 -0
  213. package/src/cli/index.ts +2 -1
  214. package/src/cli/providers/AppEntryProvider.ts +131 -0
  215. package/src/cli/providers/ViteBuildProvider.ts +82 -0
  216. package/src/cli/providers/ViteDevServerProvider.ts +350 -0
  217. package/src/cli/providers/ViteTemplateProvider.ts +27 -0
  218. package/src/cli/services/AlephaCliUtils.ts +72 -602
  219. package/src/cli/services/PackageManagerUtils.ts +308 -0
  220. package/src/cli/services/ProjectScaffolder.ts +329 -0
  221. package/src/command/helpers/Runner.ts +15 -3
  222. package/src/core/Alepha.ts +2 -8
  223. package/src/core/__tests__/Alepha-graph.spec.ts +4 -0
  224. package/src/core/index.shared.ts +1 -0
  225. package/src/core/index.ts +2 -0
  226. package/src/core/primitives/$hook.ts +6 -2
  227. package/src/core/primitives/$module.spec.ts +4 -0
  228. package/src/core/primitives/$module.ts +12 -0
  229. package/src/core/providers/AlsProvider.ts +1 -1
  230. package/src/core/providers/CodecManager.spec.ts +12 -6
  231. package/src/core/providers/CodecManager.ts +26 -6
  232. package/src/core/providers/EventManager.ts +169 -13
  233. package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +878 -0
  234. package/src/core/providers/KeylessJsonSchemaCodec.ts +789 -0
  235. package/src/core/providers/SchemaValidator.spec.ts +236 -0
  236. package/src/core/providers/StateManager.spec.ts +27 -16
  237. package/src/email/providers/LocalEmailProvider.spec.ts +111 -87
  238. package/src/email/providers/LocalEmailProvider.ts +52 -15
  239. package/src/email/providers/NodemailerEmailProvider.ts +167 -56
  240. package/src/file/errors/FileError.ts +7 -0
  241. package/src/file/index.ts +9 -1
  242. package/src/file/providers/MemoryFileSystemProvider.ts +393 -0
  243. package/src/logger/providers/PrettyFormatterProvider.ts +0 -9
  244. package/src/mcp/errors/McpError.ts +30 -0
  245. package/src/mcp/index.ts +3 -0
  246. package/src/mcp/transports/SseMcpTransport.ts +16 -6
  247. package/src/orm/index.browser.ts +1 -19
  248. package/src/orm/index.bun.ts +77 -0
  249. package/src/orm/index.shared-server.ts +22 -0
  250. package/src/orm/index.shared.ts +15 -0
  251. package/src/orm/index.ts +19 -39
  252. package/src/orm/providers/DrizzleKitProvider.ts +3 -5
  253. package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -5
  254. package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
  255. package/src/orm/providers/drivers/CloudflareD1Provider.ts +4 -0
  256. package/src/orm/providers/drivers/DatabaseProvider.ts +4 -0
  257. package/src/orm/providers/drivers/PglitePostgresProvider.ts +4 -0
  258. package/src/orm/services/Repository.ts +19 -0
  259. package/src/redis/index.bun.ts +35 -0
  260. package/src/redis/providers/BunRedisProvider.ts +12 -43
  261. package/src/redis/providers/BunRedisSubscriberProvider.ts +2 -3
  262. package/src/redis/providers/NodeRedisProvider.ts +16 -34
  263. package/src/{server/security → security}/__tests__/BasicAuth.spec.ts +11 -11
  264. package/src/{server/security → security}/__tests__/ServerSecurityProvider-realm.spec.ts +21 -16
  265. package/src/{server/security/providers → security/__tests__}/ServerSecurityProvider.spec.ts +5 -5
  266. package/src/security/index.browser.ts +5 -0
  267. package/src/security/index.ts +90 -7
  268. package/src/security/primitives/{$realm.spec.ts → $issuer.spec.ts} +11 -11
  269. package/src/security/primitives/{$realm.ts → $issuer.ts} +20 -17
  270. package/src/security/primitives/$role.ts +5 -5
  271. package/src/security/primitives/$serviceAccount.spec.ts +5 -5
  272. package/src/security/primitives/$serviceAccount.ts +3 -3
  273. package/src/{server/security → security}/providers/ServerSecurityProvider.ts +5 -7
  274. package/src/server/auth/primitives/$auth.ts +10 -10
  275. package/src/server/auth/primitives/$authCredentials.ts +3 -3
  276. package/src/server/auth/primitives/$authGithub.ts +3 -3
  277. package/src/server/auth/primitives/$authGoogle.ts +3 -3
  278. package/src/server/auth/providers/ServerAuthProvider.ts +13 -13
  279. package/src/server/cache/providers/ServerCacheProvider.ts +1 -1
  280. package/src/server/cookies/providers/ServerCookiesProvider.ts +3 -3
  281. package/src/server/core/index.ts +1 -1
  282. package/src/server/core/providers/BunHttpServerProvider.ts +1 -1
  283. package/src/server/core/providers/NodeHttpServerProvider.spec.ts +125 -0
  284. package/src/server/core/providers/NodeHttpServerProvider.ts +92 -24
  285. package/src/server/core/providers/ServerBodyParserProvider.ts +19 -23
  286. package/src/server/core/providers/ServerLoggerProvider.ts +23 -19
  287. package/src/server/core/providers/ServerProvider.ts +144 -24
  288. package/src/server/core/providers/ServerRouterProvider.ts +259 -115
  289. package/src/server/core/providers/ServerTimingProvider.ts +2 -2
  290. package/src/server/links/atoms/apiLinksAtom.ts +7 -0
  291. package/src/server/links/index.browser.ts +2 -0
  292. package/src/server/links/index.ts +3 -1
  293. package/src/server/links/providers/LinkProvider.ts +1 -1
  294. package/src/server/swagger/index.ts +1 -1
  295. package/src/sms/providers/LocalSmsProvider.spec.ts +153 -111
  296. package/src/sms/providers/LocalSmsProvider.ts +8 -7
  297. package/src/vite/index.ts +3 -2
  298. package/src/vite/tasks/buildClient.ts +0 -1
  299. package/src/vite/tasks/buildServer.ts +80 -22
  300. package/src/vite/tasks/copyAssets.ts +5 -4
  301. package/src/vite/tasks/generateCloudflare.ts +7 -0
  302. package/src/vite/tasks/generateSitemap.ts +64 -23
  303. package/src/vite/tasks/index.ts +0 -2
  304. package/src/vite/tasks/prerenderPages.ts +49 -24
  305. package/dist/server/security/index.browser.js +0 -13
  306. package/dist/server/security/index.browser.js.map +0 -1
  307. package/dist/server/security/index.d.ts +0 -173
  308. package/dist/server/security/index.d.ts.map +0 -1
  309. package/dist/server/security/index.js +0 -311
  310. package/dist/server/security/index.js.map +0 -1
  311. package/src/cli/assets/appRouterTs.ts +0 -9
  312. package/src/cli/assets/indexHtml.ts +0 -15
  313. package/src/cli/assets/mainTs.ts +0 -13
  314. package/src/cli/commands/format.ts +0 -17
  315. package/src/server/security/index.browser.ts +0 -10
  316. package/src/server/security/index.ts +0 -94
  317. package/src/vite/helpers/boot.ts +0 -106
  318. package/src/vite/plugins/viteAlephaDev.ts +0 -177
  319. package/src/vite/tasks/devServer.ts +0 -69
  320. package/src/vite/tasks/runAlepha.ts +0 -270
  321. /package/src/{server/security → security}/primitives/$basicAuth.ts +0 -0
  322. /package/src/{server/security → security}/providers/ServerBasicAuthProvider.ts +0 -0
@@ -1,54 +1,41 @@
1
1
  import { spawn } from "node:child_process";
2
- import { access, mkdir, readFile, writeFile } from "node:fs/promises";
3
- import { join } from "node:path";
4
2
  import { $inject, Alepha, AlephaError } from "alepha";
5
- import { EnvUtils, type RunnerMethod } from "alepha/command";
3
+ import { EnvUtils } from "alepha/command";
6
4
  import { FileSystemProvider } from "alepha/file";
7
5
  import { $logger } from "alepha/logger";
8
- import { boot } from "alepha/vite";
9
- import { appRouterTs } from "../assets/appRouterTs.ts";
10
- import { biomeJson } from "../assets/biomeJson.ts";
11
- import { dummySpecTs } from "../assets/dummySpecTs.ts";
12
- import { editorconfig } from "../assets/editorconfig.ts";
13
- import { indexHtml } from "../assets/indexHtml.ts";
14
- import { mainBrowserTs } from "../assets/mainBrowserTs.ts";
15
- import { mainTs } from "../assets/mainTs.ts";
16
- import { tsconfigJson } from "../assets/tsconfigJson.ts";
17
- import { version } from "../version.ts";
6
+ import { AppEntryProvider } from "../providers/AppEntryProvider.ts";
18
7
 
19
8
  /**
20
- * Utility service for common project operations used by CLI commands.
9
+ * Core utility service for CLI commands.
21
10
  *
22
- * This service provides helper methods for:
23
- * - Project configuration file management (tsconfig.json, package.json, etc.)
24
- * - Package manager setup (Yarn, npm, pnpm)
25
- * - Sample project downloading
26
- * - Drizzle ORM/Kit utilities
27
- * - Alepha instance loading
11
+ * Provides:
12
+ * - Command execution
13
+ * - File editing helpers
14
+ * - Drizzle/ORM utilities
15
+ * - Environment loading
28
16
  */
29
17
  export class AlephaCliUtils {
30
18
  protected readonly log = $logger();
31
19
  protected readonly fs = $inject(FileSystemProvider);
32
20
  protected readonly envUtils = $inject(EnvUtils);
33
- protected readonly alepha = $inject(Alepha);
21
+ protected readonly boot = $inject(AppEntryProvider);
22
+
23
+ // ===========================================
24
+ // Command Execution
25
+ // ===========================================
34
26
 
35
27
  /**
36
- * Execute a command using npx with inherited stdio.
37
- *
38
- * @example
39
- * ```ts
40
- * const runner = alepha.inject(ProcessRunner);
41
- * await runner.exec("tsx watch src/index.ts");
42
- * ```
28
+ * Execute a command with inherited stdio.
43
29
  */
44
30
  public async exec(
45
31
  command: string,
46
32
  options: {
33
+ root?: string;
47
34
  env?: Record<string, string>;
48
35
  global?: boolean;
49
36
  } = {},
50
37
  ): Promise<void> {
51
- const root = process.cwd();
38
+ const root = options.root ?? process.cwd();
52
39
  this.log.debug(`Executing command: ${command}`, { cwd: root });
53
40
 
54
41
  const runExec = async (app: string, args: string[]) => {
@@ -81,7 +68,6 @@ export class AlephaCliUtils {
81
68
  let execPath = await this.checkFileExists(
82
69
  root,
83
70
  `node_modules/.bin/${app}${suffix}`,
84
- true,
85
71
  );
86
72
 
87
73
  // or, find executable inside alepha package node_modules (pnpm style)
@@ -89,10 +75,24 @@ export class AlephaCliUtils {
89
75
  execPath = await this.checkFileExists(
90
76
  root,
91
77
  `node_modules/alepha/node_modules/.bin/${app}${suffix}`,
92
- true,
93
78
  );
94
79
  }
95
80
 
81
+ // check if parent folder (monorepo) has the executable (check 3 times)
82
+ if (!execPath) {
83
+ let parentDir = this.fs.join(root, "..");
84
+ for (let i = 0; i < 3; i++) {
85
+ execPath = await this.checkFileExists(
86
+ parentDir,
87
+ `node_modules/.bin/${app}${suffix}`,
88
+ );
89
+ if (execPath) {
90
+ break;
91
+ }
92
+ parentDir = this.fs.join(parentDir, "..");
93
+ }
94
+ }
95
+
96
96
  if (!execPath) {
97
97
  throw new AlephaError(
98
98
  `Could not find executable for command '${app}'. Make sure the package is installed.`,
@@ -104,366 +104,26 @@ export class AlephaCliUtils {
104
104
 
105
105
  /**
106
106
  * Write a configuration file to node_modules/.alepha directory.
107
- *
108
- * Creates the .alepha directory if it doesn't exist and writes the file with the given content.
109
- *
110
- * @param name - The name of the config file to create
111
- * @param content - The content to write to the file
112
- * @param root - The root directory (defaults to process.cwd())
113
- * @returns The absolute path to the created file
114
- *
115
- * @example
116
- * ```ts
117
- * const runner = alepha.inject(ProcessRunner);
118
- * const configPath = await runner.writeConfigFile("biome.json", biomeConfig);
119
- * ```
120
107
  */
121
108
  public async writeConfigFile(
122
109
  name: string,
123
110
  content: string,
124
111
  root = process.cwd(),
125
112
  ): Promise<string> {
126
- const dir = join(root, "node_modules", ".alepha");
113
+ const dir = this.fs.join(root, "node_modules", ".alepha");
127
114
 
128
- await mkdir(dir, {
129
- recursive: true,
130
- }).catch(() => null);
115
+ await this.fs.mkdir(dir, { recursive: true }).catch(() => null);
131
116
 
132
- const path = join(dir, name);
133
- await writeFile(path, content);
117
+ const path = this.fs.join(dir, name);
118
+ await this.fs.writeFile(path, content);
134
119
 
135
120
  this.log.debug(`Config file written: ${path}`);
136
121
 
137
122
  return path;
138
123
  }
139
124
 
140
- // ===================================================================================================================
141
- // Package Manager & Project Setup
142
- // ===================================================================================================================
143
-
144
- public async editFile(
145
- root: string,
146
- name: string,
147
- editFn: (content: string) => string | Promise<string>,
148
- ): Promise<void> {
149
- const filePath = join(root, name);
150
- try {
151
- const content = await readFile(filePath, "utf8");
152
- const newContent = await editFn(content);
153
- await writeFile(filePath, newContent);
154
- } catch (error) {
155
- this.log.debug("Could not edit file", error);
156
- }
157
- }
158
-
159
- public async editJsonFile(
160
- root: string,
161
- name: string,
162
- editFn: (obj: any) => any | Promise<any>,
163
- ): Promise<void> {
164
- return await this.editFile(root, name, async (content) => {
165
- const obj = JSON.parse(content);
166
- const newObj = await editFn(obj);
167
- return JSON.stringify(newObj, null, 2);
168
- });
169
- }
170
-
171
- public async removeFiles(root: string, files: string[]): Promise<void> {
172
- await Promise.all(
173
- files.map((file) =>
174
- this.fs.rm(join(root, file), { force: true, recursive: true }),
175
- ),
176
- );
177
- }
178
-
179
- public async removeYarn(root: string): Promise<void> {
180
- await this.removeFiles(root, [".yarn", ".yarnrc.yml", "yarn.lock"]);
181
- await this.editJsonFile(root, "package.json", (pkg) => {
182
- delete pkg.packageManager;
183
- });
184
- }
185
-
186
- public async removePnpm(root: string): Promise<void> {
187
- await this.removeFiles(root, ["pnpm-lock.yaml", "pnpm-workspace.yaml"]);
188
- await this.editJsonFile(root, "package.json", (pkg) => {
189
- delete pkg.packageManager;
190
- });
191
- }
192
-
193
- public async removeNpm(root: string): Promise<void> {
194
- await this.removeFiles(root, ["package-lock.json"]);
195
- }
196
-
197
- public async removeBun(root: string): Promise<void> {
198
- await this.removeFiles(root, ["bun.lockb", "bun.lock"]);
199
- }
200
-
201
- public async removeAllPmFilesExcept(
202
- root: string,
203
- except: string,
204
- ): Promise<void> {
205
- if (except !== "yarn") await this.removeYarn(root);
206
- if (except !== "pnpm") await this.removePnpm(root);
207
- if (except !== "npm") await this.removeNpm(root);
208
- if (except !== "bun") await this.removeBun(root);
209
- }
210
-
211
- /**
212
- * Ensure Yarn is configured in the project directory.
213
- *
214
- * Creates a .yarnrc.yml file with node-modules linker if it doesn't exist.
215
- *
216
- * @param root - The root directory of the project
217
- */
218
- public async ensureYarn(root: string): Promise<void> {
219
- await this.ensureFileExists(
220
- root,
221
- ".yarnrc.yml",
222
- "nodeLinker: node-modules",
223
- false,
224
- );
225
-
226
- await this.removeAllPmFilesExcept(root, "yarn");
227
- }
228
-
229
- public async ensureBun(root: string): Promise<void> {
230
- await this.removeAllPmFilesExcept(root, "bun");
231
- }
232
-
233
- public async ensurePnpm(root: string): Promise<void> {
234
- await this.removeAllPmFilesExcept(root, "pnpm");
235
- }
236
-
237
- public async ensureNpm(root: string): Promise<void> {
238
- await this.removeAllPmFilesExcept(root, "npm");
239
- }
240
-
241
- /**
242
- * Generate package.json content with Alepha dependencies.
243
- *
244
- * @param modes - Configuration for which dependencies to include
245
- * @returns Package.json partial with dependencies, devDependencies, and scripts
246
- */
247
- public generatePackageJsonContent(modes: DependencyModes): {
248
- dependencies: Record<string, string>;
249
- devDependencies: Record<string, string>;
250
- scripts: Record<string, string>;
251
- type: "module";
252
- } {
253
- const dependencies: Record<string, string> = {
254
- alepha: `^${version}`,
255
- };
256
-
257
- const devDependencies: Record<string, string> = {};
258
-
259
- const scripts: Record<string, string> = {
260
- dev: "alepha dev",
261
- build: "alepha build",
262
- lint: "alepha lint",
263
- typecheck: "alepha typecheck",
264
- verify: "alepha verify",
265
- };
266
-
267
- if (modes.ui) {
268
- dependencies["@alepha/ui"] = `^${version}`;
269
- modes.react = true;
270
- }
271
-
272
- if (modes.react) {
273
- dependencies["@alepha/react"] = `^${version}`;
274
- dependencies.react = "^19.2.0";
275
- dependencies["react-dom"] = "^19.2.0";
276
- devDependencies["@types/react"] = "^19.2.0";
277
- }
278
-
279
- return {
280
- type: "module",
281
- dependencies,
282
- devDependencies,
283
- scripts,
284
- };
285
- }
286
-
287
- /**
288
- * Ensure package.json exists and has correct configuration.
289
- *
290
- * Creates a new package.json if none exists, or updates an existing one to:
291
- * - Set "type": "module"
292
- * - Add Alepha dependencies
293
- * - Add standard scripts
294
- *
295
- * @param root - The root directory of the project
296
- * @param modes - Configuration for which dependencies to include
297
- */
298
- public async ensurePackageJson(
299
- root: string,
300
- modes: DependencyModes,
301
- ): Promise<object> {
302
- const packageJsonPath = join(root, "package.json");
303
- try {
304
- await access(packageJsonPath);
305
- } catch (error) {
306
- const obj = this.generatePackageJsonContent(modes);
307
- await writeFile(packageJsonPath, JSON.stringify(obj, null, 2));
308
- return obj;
309
- }
310
-
311
- const content = await readFile(packageJsonPath, "utf8");
312
- const packageJson = JSON.parse(content);
313
-
314
- const newPackageJson = this.generatePackageJsonContent(modes);
315
-
316
- packageJson.type = "module";
317
- packageJson.dependencies ??= {};
318
- packageJson.devDependencies ??= {};
319
- packageJson.scripts ??= {};
320
-
321
- Object.assign(packageJson.dependencies, newPackageJson.dependencies);
322
- Object.assign(packageJson.devDependencies, newPackageJson.devDependencies);
323
- Object.assign(packageJson.scripts, newPackageJson.scripts);
324
-
325
- await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
326
-
327
- return packageJson;
328
- }
329
-
330
- public async ensureConfig(
331
- root: string,
332
- opts: {
333
- packageJson?: boolean | DependencyModes;
334
- tsconfigJson?: boolean;
335
- indexHtml?: boolean;
336
- biomeJson?: boolean;
337
- editorconfig?: boolean;
338
- },
339
- ): Promise<Array<void | object>> {
340
- const tasks: Promise<void | object>[] = [];
341
-
342
- if (opts.packageJson) {
343
- tasks.push(
344
- this.ensurePackageJson(
345
- root,
346
- typeof opts.packageJson === "boolean" ? {} : opts.packageJson,
347
- ),
348
- );
349
- }
350
- if (opts.tsconfigJson) {
351
- tasks.push(this.ensureTsConfig(root));
352
- }
353
- if (opts.indexHtml) {
354
- tasks.push(this.ensureIndexHtml(root));
355
- }
356
- if (opts.biomeJson) {
357
- tasks.push(this.ensureBiomeConfig(root));
358
- }
359
- if (opts.editorconfig) {
360
- tasks.push(this.ensureEditorConfig(root));
361
- }
362
-
363
- return await Promise.all(tasks);
364
- }
365
-
366
- /**
367
- * Ensure tsconfig.json exists in the project.
368
- *
369
- * Creates a standard Alepha tsconfig.json if none exists.
370
- *
371
- * @param root - The root directory of the project
372
- */
373
- public async ensureTsConfig(root: string): Promise<void> {
374
- await this.ensureFileExists(root, "tsconfig.json", tsconfigJson, true);
375
- }
376
-
377
- protected async checkFileExists(
378
- root: string,
379
- name: string,
380
- checkParentDirectories: boolean = false,
381
- ): Promise<string | undefined> {
382
- const configPath = join(root, name);
383
- if (!checkParentDirectories) {
384
- try {
385
- await access(configPath);
386
- return configPath;
387
- } catch {
388
- return;
389
- }
390
- }
391
-
392
- let currentDir = root;
393
- const maxIterations = 10; // safety to prevent infinite loops
394
- let level = 0;
395
-
396
- while (level < maxIterations) {
397
- try {
398
- const maybe = join(currentDir, name);
399
- await access(maybe);
400
- return maybe;
401
- } catch {
402
- const parentDir = join(currentDir, "..");
403
- if (parentDir === currentDir) {
404
- break;
405
- }
406
- currentDir = parentDir;
407
- }
408
- level += 1;
409
- }
410
- }
411
-
412
- protected async ensureFileExists(
413
- root: string,
414
- name: string,
415
- content: string,
416
- checkParentDirectories: boolean = false,
417
- ): Promise<void> {
418
- const found = await this.checkFileExists(
419
- root,
420
- name,
421
- checkParentDirectories,
422
- );
423
-
424
- if (!found) {
425
- await writeFile(join(root, name), content);
426
- }
427
- }
428
-
429
- // ===================================================================================================================
430
- // Biome Configuration
431
- // ===================================================================================================================
432
-
433
- /**
434
- * Get the path to Biome configuration file.
435
- *
436
- * Looks for an existing biome.json in the project root, or creates one if it doesn't exist.
437
- */
438
- public async ensureBiomeConfig(root: string): Promise<void> {
439
- await this.ensureFileExists(root, "biome.json", biomeJson, true);
440
- }
441
-
442
- /**
443
- * Ensure .editorconfig exists in the project.
444
- *
445
- * Creates a standard .editorconfig if none exists.
446
- *
447
- * @param root - The root directory of the project
448
- */
449
- public async ensureEditorConfig(root: string): Promise<void> {
450
- await this.ensureFileExists(root, ".editorconfig", editorconfig, true);
451
- }
452
-
453
- // ===================================================================================================================
454
- // Drizzle ORM & Kit Utilities
455
- // ===================================================================================================================
456
-
457
125
  /**
458
126
  * Load Alepha instance from a server entry file.
459
- *
460
- * Dynamically imports the server entry file and extracts the Alepha instance.
461
- * Skips the automatic start process.
462
- *
463
- * @param rootDir - The root directory of the project
464
- * @param explicitEntry - Optional explicit path to the entry file
465
- * @returns Object containing the Alepha instance and the entry file path
466
- * @throws {AlephaError} If the Alepha instance cannot be found
467
127
  */
468
128
  public async loadAlephaFromServerEntryFile(
469
129
  rootDir?: string,
@@ -474,7 +134,22 @@ export class AlephaCliUtils {
474
134
  }> {
475
135
  process.env.ALEPHA_CLI_IMPORT = "true";
476
136
 
477
- const entry = await boot.getServerEntry(rootDir, explicitEntry);
137
+ const root = rootDir ?? process.cwd();
138
+ let entry: string;
139
+
140
+ if (explicitEntry) {
141
+ // Explicit entry provided
142
+ entry = this.fs.join(root, explicitEntry);
143
+ if (!(await this.fs.exists(entry))) {
144
+ throw new AlephaError(
145
+ `Explicit server entry file "${explicitEntry}" not found.`,
146
+ );
147
+ }
148
+ } else {
149
+ // Auto-discover entry
150
+ const appEntry = await this.boot.getAppEntry(root);
151
+ entry = this.fs.join(root, appEntry.server);
152
+ }
478
153
 
479
154
  delete (global as any).__alepha;
480
155
 
@@ -484,19 +159,13 @@ export class AlephaCliUtils {
484
159
 
485
160
  // check if alepha is correctly exported
486
161
  if (mod.default instanceof Alepha) {
487
- return {
488
- alepha: mod.default,
489
- entry,
490
- };
162
+ return { alepha: mod.default, entry };
491
163
  }
492
164
 
493
165
  // else, try with global variable
494
166
  const g: any = global;
495
167
  if (g.__alepha) {
496
- return {
497
- alepha: g.__alepha,
498
- entry,
499
- };
168
+ return { alepha: g.__alepha, entry };
500
169
  }
501
170
 
502
171
  throw new AlephaError(
@@ -504,16 +173,12 @@ export class AlephaCliUtils {
504
173
  );
505
174
  }
506
175
 
176
+ // ===========================================
177
+ // Drizzle ORM & Kit Utilities
178
+ // ===========================================
179
+
507
180
  /**
508
181
  * Generate JavaScript code for Drizzle entities export.
509
- *
510
- * Creates a temporary entities.js file that imports from the entry file
511
- * and exports database models for Drizzle Kit to process.
512
- *
513
- * @param entry - Path to the server entry file
514
- * @param provider - Name of the database provider
515
- * @param models - Array of model names to export
516
- * @returns JavaScript code as a string
517
182
  */
518
183
  public generateEntitiesJs(
519
184
  entry: string,
@@ -534,11 +199,12 @@ ${models.map((it: string) => `export const ${it} = models["${it}"];`).join("\n")
534
199
  `.trim();
535
200
  }
536
201
 
202
+ // ===========================================
203
+ // Environment
204
+ // ===========================================
205
+
537
206
  /**
538
207
  * Load environment variables from a .env file.
539
- *
540
- * Reads the .env file in the specified root directory and sets
541
- * the environment variables in process.env.
542
208
  */
543
209
  public async loadEnv(
544
210
  root: string,
@@ -547,217 +213,21 @@ ${models.map((it: string) => `export const ${it} = models["${it}"];`).join("\n")
547
213
  await this.envUtils.loadEnv(root, files);
548
214
  }
549
215
 
550
- public async getPackageManager(
551
- root: string,
552
- flags?: { yarn?: boolean; pnpm?: boolean; npm?: boolean; bun?: boolean },
553
- ): Promise<"yarn" | "pnpm" | "npm" | "bun"> {
554
- if (flags?.yarn) {
555
- return "yarn";
556
- }
557
- if (flags?.pnpm) {
558
- return "pnpm";
559
- }
560
- if (flags?.npm) {
561
- return "npm";
562
- }
563
- if (flags?.bun) {
564
- return "bun";
565
- }
566
- if (this.alepha.isBun()) {
567
- return "bun";
568
- }
569
- if (await this.checkFileExists(root, "yarn.lock", true)) {
570
- return "yarn";
571
- }
572
- if (await this.checkFileExists(root, "pnpm-lock.yaml", true)) {
573
- return "pnpm";
574
- }
575
- return "npm";
576
- }
577
-
578
- public async ensureIndexHtml(root: string) {
579
- if (await this.fs.exists(join(root, "index.html"))) {
580
- return;
581
- }
582
-
583
- const serverEntry = "src/main.server.ts";
584
- const browserEntry = "src/main.browser.ts";
585
- const appRouter = "src/AppRouter.ts";
586
-
587
- await this.fs.writeFile(join(root, "index.html"), indexHtml(browserEntry));
588
-
589
- try {
590
- await this.fs.mkdir(join(root, "src"), { recursive: true });
591
- } catch {}
592
-
593
- if (!(await this.fs.exists(join(root, browserEntry)))) {
594
- await this.fs.writeFile(join(root, browserEntry), mainBrowserTs());
595
- }
596
-
597
- if (!(await this.fs.exists(join(root, serverEntry)))) {
598
- await this.fs.writeFile(join(root, serverEntry), mainBrowserTs());
599
- }
600
-
601
- if (!(await this.fs.exists(join(root, appRouter)))) {
602
- await this.fs.writeFile(join(root, appRouter), appRouterTs());
603
- }
604
- }
605
-
606
- public async exists(root: string, dirName: string): Promise<boolean> {
607
- return this.fs.exists(join(root, dirName));
608
- }
609
-
610
- /**
611
- * Ensure src/main.ts exists with a minimal Alepha bootstrap.
612
- *
613
- * Creates the src directory and main.ts file if the src directory
614
- * doesn't exist or is empty.
615
- *
616
- * @param root - The root directory of the project
617
- */
618
- public async ensureSrcMain(root: string): Promise<void> {
619
- const srcDir = join(root, "src");
620
- const mainPath = join(srcDir, "main.ts");
621
-
622
- // Check if src directory exists
623
- const srcExists = await this.fs.exists(srcDir);
624
-
625
- if (!srcExists) {
626
- // Create src directory and main.ts
627
- await this.fs.mkdir(srcDir, { recursive: true });
628
- await this.fs.writeFile(mainPath, mainTs());
629
- return;
630
- }
631
-
632
- // Check if src directory is empty
633
- const files = await this.fs.ls(srcDir);
634
- if (files.length === 0) {
635
- await this.fs.writeFile(mainPath, mainTs());
636
- }
637
- }
638
-
639
- /**
640
- * Ensure test directory exists with a dummy test file.
641
- *
642
- * Creates the test directory and a dummy.spec.ts file if the test directory
643
- * doesn't exist or is empty.
644
- *
645
- * @param root - The root directory of the project
646
- */
647
- public async ensureTestDir(root: string): Promise<void> {
648
- const testDir = join(root, "test");
649
- const dummyPath = join(testDir, "dummy.spec.ts");
650
-
651
- // Check if test directory exists
652
- const testExists = await this.fs.exists(testDir);
653
-
654
- if (!testExists) {
655
- // Create test directory and dummy.spec.ts
656
- await this.fs.mkdir(testDir, { recursive: true });
657
- await this.fs.writeFile(dummyPath, dummySpecTs());
658
- return;
659
- }
660
-
661
- // Check if test directory is empty
662
- const files = await this.fs.ls(testDir);
663
- if (files.length === 0) {
664
- await this.fs.writeFile(dummyPath, dummySpecTs());
665
- }
666
- }
667
-
668
- async readPackageJson(root: string): Promise<Record<string, any>> {
669
- const packageJson = await this.fs
670
- .createFile({
671
- path: this.fs.join(root, "package.json"),
672
- })
673
- .text();
674
- return JSON.parse(packageJson);
675
- }
676
-
677
- /**
678
- * Check if a dependency is installed in the project.
679
- *
680
- * @param root - The root directory of the project
681
- * @param packageName - The name of the package to check
682
- * @returns True if the package is in dependencies or devDependencies
683
- */
684
- async hasDependency(root: string, packageName: string): Promise<boolean> {
685
- try {
686
- const pkg = await this.readPackageJson(root);
687
- return !!(
688
- pkg.dependencies?.[packageName] || pkg.devDependencies?.[packageName]
689
- );
690
- } catch {
691
- return false;
692
- }
693
- }
216
+ // ===========================================
217
+ // Helpers
218
+ // ===========================================
694
219
 
695
- /**
696
- * Check if Expo is present in the project.
697
- *
698
- * @param root - The root directory of the project
699
- * @returns True if expo is in dependencies or devDependencies
700
- */
701
- async hasExpo(root: string): Promise<boolean> {
702
- return this.hasDependency(root, "expo");
220
+ public async exists(root: string, path: string): Promise<boolean> {
221
+ return this.fs.exists(this.fs.join(root, path));
703
222
  }
704
223
 
705
- async getInstallCommand(root: string, packageName: string, dev = true) {
706
- const pm = await this.getPackageManager(root);
707
- let cmd: string;
708
-
709
- switch (pm) {
710
- case "yarn":
711
- cmd = `yarn add ${dev ? "-D" : ""} ${packageName}`;
712
- break;
713
- case "pnpm":
714
- cmd = `pnpm add ${dev ? "-D" : ""} ${packageName}`;
715
- break;
716
- case "bun":
717
- cmd = `bun add ${dev ? "-d" : ""} ${packageName}`;
718
- break;
719
- default:
720
- cmd = `npm install ${dev ? "--save-dev" : ""} ${packageName}`;
721
- }
722
-
723
- return cmd.replace(/\s+/g, " ").trim();
724
- }
725
-
726
- /**
727
- * Install a dependency if it's missing from the project.
728
- *
729
- * Automatically detects the package manager (yarn, pnpm, npm) and installs
730
- * the package as a dev dependency if not already present.
731
- */
732
- async ensureDependency(
224
+ protected async checkFileExists(
733
225
  root: string,
734
- packageName: string,
735
- options: { dev?: boolean; run?: RunnerMethod } = {},
736
- ): Promise<void> {
737
- const { dev = true } = options;
738
-
739
- if (await this.hasDependency(root, packageName)) {
740
- this.log.debug(`Dependency '${packageName}' is already installed`);
741
- return;
742
- }
743
-
744
- const cmd = await this.getInstallCommand(root, packageName, dev);
745
-
746
- if (options.run) {
747
- // if it's during a Runner flow, just use the runner's run method
748
- await options.run(cmd, {
749
- alias: `installing ${packageName}`,
750
- });
751
- } else {
752
- // else, run directly with our util exec method
753
- this.log.debug(`Installing ${packageName}`);
754
- await this.exec(cmd, { global: true });
226
+ name: string,
227
+ ): Promise<string | undefined> {
228
+ const configPath = this.fs.join(root, name);
229
+ if (await this.fs.exists(configPath)) {
230
+ return configPath;
755
231
  }
756
232
  }
757
233
  }
758
-
759
- export interface DependencyModes {
760
- react?: boolean;
761
- ui?: boolean;
762
- expo?: boolean;
763
- }