alepha 0.14.3 → 0.15.0

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 (317) hide show
  1. package/README.md +2 -5
  2. package/dist/api/audits/index.d.ts +620 -811
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/files/index.d.ts +185 -377
  5. package/dist/api/files/index.d.ts.map +1 -1
  6. package/dist/api/files/index.js +0 -1
  7. package/dist/api/files/index.js.map +1 -1
  8. package/dist/api/jobs/index.d.ts +245 -435
  9. package/dist/api/jobs/index.d.ts.map +1 -1
  10. package/dist/api/notifications/index.d.ts +238 -429
  11. package/dist/api/notifications/index.d.ts.map +1 -1
  12. package/dist/api/parameters/index.d.ts +236 -427
  13. package/dist/api/parameters/index.d.ts.map +1 -1
  14. package/dist/api/users/index.browser.js +1 -2
  15. package/dist/api/users/index.browser.js.map +1 -1
  16. package/dist/api/users/index.d.ts +1010 -1196
  17. package/dist/api/users/index.d.ts.map +1 -1
  18. package/dist/api/users/index.js +178 -151
  19. package/dist/api/users/index.js.map +1 -1
  20. package/dist/api/verifications/index.d.ts +17 -17
  21. package/dist/api/verifications/index.d.ts.map +1 -1
  22. package/dist/batch/index.d.ts +122 -122
  23. package/dist/batch/index.d.ts.map +1 -1
  24. package/dist/batch/index.js +1 -2
  25. package/dist/batch/index.js.map +1 -1
  26. package/dist/bucket/index.d.ts +163 -163
  27. package/dist/bucket/index.d.ts.map +1 -1
  28. package/dist/cache/core/index.d.ts +46 -46
  29. package/dist/cache/core/index.d.ts.map +1 -1
  30. package/dist/cache/redis/index.d.ts.map +1 -1
  31. package/dist/cli/index.d.ts +384 -285
  32. package/dist/cli/index.d.ts.map +1 -1
  33. package/dist/cli/index.js +1113 -623
  34. package/dist/cli/index.js.map +1 -1
  35. package/dist/command/index.d.ts +299 -300
  36. package/dist/command/index.d.ts.map +1 -1
  37. package/dist/command/index.js +13 -9
  38. package/dist/command/index.js.map +1 -1
  39. package/dist/core/index.browser.js +445 -103
  40. package/dist/core/index.browser.js.map +1 -1
  41. package/dist/core/index.d.ts +733 -625
  42. package/dist/core/index.d.ts.map +1 -1
  43. package/dist/core/index.js +446 -103
  44. package/dist/core/index.js.map +1 -1
  45. package/dist/core/index.native.js +445 -103
  46. package/dist/core/index.native.js.map +1 -1
  47. package/dist/datetime/index.d.ts +44 -44
  48. package/dist/datetime/index.d.ts.map +1 -1
  49. package/dist/datetime/index.js +4 -4
  50. package/dist/datetime/index.js.map +1 -1
  51. package/dist/email/index.d.ts +97 -50
  52. package/dist/email/index.d.ts.map +1 -1
  53. package/dist/email/index.js +129 -33
  54. package/dist/email/index.js.map +1 -1
  55. package/dist/fake/index.d.ts +7981 -14
  56. package/dist/fake/index.d.ts.map +1 -1
  57. package/dist/file/index.d.ts +523 -390
  58. package/dist/file/index.d.ts.map +1 -1
  59. package/dist/file/index.js +253 -1
  60. package/dist/file/index.js.map +1 -1
  61. package/dist/lock/core/index.d.ts +208 -208
  62. package/dist/lock/core/index.d.ts.map +1 -1
  63. package/dist/lock/redis/index.d.ts.map +1 -1
  64. package/dist/logger/index.d.ts +25 -26
  65. package/dist/logger/index.d.ts.map +1 -1
  66. package/dist/logger/index.js +12 -2
  67. package/dist/logger/index.js.map +1 -1
  68. package/dist/mcp/index.d.ts +197 -197
  69. package/dist/mcp/index.d.ts.map +1 -1
  70. package/dist/mcp/index.js +1 -1
  71. package/dist/mcp/index.js.map +1 -1
  72. package/dist/orm/chunk-DtkW-qnP.js +38 -0
  73. package/dist/orm/index.browser.js.map +1 -1
  74. package/dist/orm/index.bun.js +2814 -0
  75. package/dist/orm/index.bun.js.map +1 -0
  76. package/dist/orm/index.d.ts +1228 -1216
  77. package/dist/orm/index.d.ts.map +1 -1
  78. package/dist/orm/index.js +2041 -1967
  79. package/dist/orm/index.js.map +1 -1
  80. package/dist/queue/core/index.d.ts +248 -248
  81. package/dist/queue/core/index.d.ts.map +1 -1
  82. package/dist/queue/redis/index.d.ts.map +1 -1
  83. package/dist/redis/index.bun.js +285 -0
  84. package/dist/redis/index.bun.js.map +1 -0
  85. package/dist/redis/index.d.ts +118 -136
  86. package/dist/redis/index.d.ts.map +1 -1
  87. package/dist/redis/index.js +18 -38
  88. package/dist/redis/index.js.map +1 -1
  89. package/dist/retry/index.d.ts +69 -69
  90. package/dist/retry/index.d.ts.map +1 -1
  91. package/dist/router/index.d.ts +6 -6
  92. package/dist/router/index.d.ts.map +1 -1
  93. package/dist/scheduler/index.d.ts +25 -25
  94. package/dist/scheduler/index.d.ts.map +1 -1
  95. package/dist/security/index.browser.js +5 -1
  96. package/dist/security/index.browser.js.map +1 -1
  97. package/dist/security/index.d.ts +417 -254
  98. package/dist/security/index.d.ts.map +1 -1
  99. package/dist/security/index.js +386 -86
  100. package/dist/security/index.js.map +1 -1
  101. package/dist/server/auth/index.d.ts +110 -110
  102. package/dist/server/auth/index.d.ts.map +1 -1
  103. package/dist/server/auth/index.js +20 -20
  104. package/dist/server/auth/index.js.map +1 -1
  105. package/dist/server/cache/index.d.ts +62 -47
  106. package/dist/server/cache/index.d.ts.map +1 -1
  107. package/dist/server/cache/index.js +56 -3
  108. package/dist/server/cache/index.js.map +1 -1
  109. package/dist/server/compress/index.d.ts +6 -0
  110. package/dist/server/compress/index.d.ts.map +1 -1
  111. package/dist/server/compress/index.js +36 -1
  112. package/dist/server/compress/index.js.map +1 -1
  113. package/dist/server/cookies/index.d.ts +6 -6
  114. package/dist/server/cookies/index.d.ts.map +1 -1
  115. package/dist/server/cookies/index.js +3 -3
  116. package/dist/server/cookies/index.js.map +1 -1
  117. package/dist/server/core/index.browser.js +2 -2
  118. package/dist/server/core/index.browser.js.map +1 -1
  119. package/dist/server/core/index.d.ts +242 -150
  120. package/dist/server/core/index.d.ts.map +1 -1
  121. package/dist/server/core/index.js +294 -125
  122. package/dist/server/core/index.js.map +1 -1
  123. package/dist/server/cors/index.d.ts +11 -12
  124. package/dist/server/cors/index.d.ts.map +1 -1
  125. package/dist/server/health/index.d.ts +0 -1
  126. package/dist/server/health/index.d.ts.map +1 -1
  127. package/dist/server/helmet/index.d.ts +2 -2
  128. package/dist/server/helmet/index.d.ts.map +1 -1
  129. package/dist/server/links/index.browser.js.map +1 -1
  130. package/dist/server/links/index.d.ts +123 -124
  131. package/dist/server/links/index.d.ts.map +1 -1
  132. package/dist/server/links/index.js +1 -2
  133. package/dist/server/links/index.js.map +1 -1
  134. package/dist/server/metrics/index.d.ts.map +1 -1
  135. package/dist/server/multipart/index.d.ts +6 -6
  136. package/dist/server/multipart/index.d.ts.map +1 -1
  137. package/dist/server/proxy/index.d.ts +102 -103
  138. package/dist/server/proxy/index.d.ts.map +1 -1
  139. package/dist/server/rate-limit/index.d.ts +16 -16
  140. package/dist/server/rate-limit/index.d.ts.map +1 -1
  141. package/dist/server/static/index.d.ts +44 -44
  142. package/dist/server/static/index.d.ts.map +1 -1
  143. package/dist/server/static/index.js +4 -0
  144. package/dist/server/static/index.js.map +1 -1
  145. package/dist/server/swagger/index.d.ts +48 -49
  146. package/dist/server/swagger/index.d.ts.map +1 -1
  147. package/dist/server/swagger/index.js +3 -5
  148. package/dist/server/swagger/index.js.map +1 -1
  149. package/dist/sms/index.d.ts +13 -11
  150. package/dist/sms/index.d.ts.map +1 -1
  151. package/dist/sms/index.js +7 -7
  152. package/dist/sms/index.js.map +1 -1
  153. package/dist/thread/index.d.ts +71 -72
  154. package/dist/thread/index.d.ts.map +1 -1
  155. package/dist/topic/core/index.d.ts +318 -318
  156. package/dist/topic/core/index.d.ts.map +1 -1
  157. package/dist/topic/redis/index.d.ts +6 -6
  158. package/dist/topic/redis/index.d.ts.map +1 -1
  159. package/dist/vite/index.d.ts +5805 -249
  160. package/dist/vite/index.d.ts.map +1 -1
  161. package/dist/vite/index.js +599 -513
  162. package/dist/vite/index.js.map +1 -1
  163. package/dist/websocket/index.browser.js +6 -6
  164. package/dist/websocket/index.browser.js.map +1 -1
  165. package/dist/websocket/index.d.ts +247 -247
  166. package/dist/websocket/index.d.ts.map +1 -1
  167. package/dist/websocket/index.js +6 -6
  168. package/dist/websocket/index.js.map +1 -1
  169. package/package.json +9 -14
  170. package/src/api/files/controllers/AdminFileStatsController.ts +0 -1
  171. package/src/api/users/atoms/realmAuthSettingsAtom.ts +5 -0
  172. package/src/api/users/controllers/{UserRealmController.ts → RealmController.ts} +11 -11
  173. package/src/api/users/entities/users.ts +1 -1
  174. package/src/api/users/index.ts +8 -8
  175. package/src/api/users/primitives/{$userRealm.ts → $realm.ts} +17 -19
  176. package/src/api/users/providers/{UserRealmProvider.ts → RealmProvider.ts} +26 -30
  177. package/src/api/users/schemas/{userRealmConfigSchema.ts → realmConfigSchema.ts} +2 -2
  178. package/src/api/users/services/CredentialService.ts +7 -7
  179. package/src/api/users/services/IdentityService.ts +4 -4
  180. package/src/api/users/services/RegistrationService.spec.ts +25 -27
  181. package/src/api/users/services/RegistrationService.ts +38 -27
  182. package/src/api/users/services/SessionCrudService.ts +3 -3
  183. package/src/api/users/services/SessionService.spec.ts +3 -3
  184. package/src/api/users/services/SessionService.ts +28 -9
  185. package/src/api/users/services/UserService.ts +7 -7
  186. package/src/batch/providers/BatchProvider.ts +1 -2
  187. package/src/cli/apps/AlephaCli.ts +0 -2
  188. package/src/cli/apps/AlephaPackageBuilderCli.ts +38 -19
  189. package/src/cli/assets/apiHelloControllerTs.ts +18 -0
  190. package/src/cli/assets/apiIndexTs.ts +16 -0
  191. package/src/cli/assets/claudeMd.ts +303 -0
  192. package/src/cli/assets/mainBrowserTs.ts +2 -2
  193. package/src/cli/assets/mainServerTs.ts +24 -0
  194. package/src/cli/assets/webAppRouterTs.ts +15 -0
  195. package/src/cli/assets/webHelloComponentTsx.ts +16 -0
  196. package/src/cli/assets/webIndexTs.ts +16 -0
  197. package/src/cli/atoms/buildOptions.ts +88 -0
  198. package/src/cli/commands/build.ts +70 -87
  199. package/src/cli/commands/db.ts +21 -22
  200. package/src/cli/commands/deploy.ts +17 -5
  201. package/src/cli/commands/dev.ts +22 -14
  202. package/src/cli/commands/format.ts +8 -2
  203. package/src/cli/commands/gen/env.ts +53 -0
  204. package/src/cli/commands/gen/openapi.ts +1 -1
  205. package/src/cli/commands/gen/resource.ts +15 -0
  206. package/src/cli/commands/gen.ts +7 -1
  207. package/src/cli/commands/init.ts +74 -30
  208. package/src/cli/commands/lint.ts +8 -2
  209. package/src/cli/commands/test.ts +8 -3
  210. package/src/cli/commands/typecheck.ts +5 -1
  211. package/src/cli/commands/verify.ts +5 -3
  212. package/src/cli/defineConfig.ts +49 -7
  213. package/src/cli/index.ts +0 -1
  214. package/src/cli/services/AlephaCliUtils.ts +39 -589
  215. package/src/cli/services/PackageManagerUtils.ts +301 -0
  216. package/src/cli/services/ProjectScaffolder.ts +306 -0
  217. package/src/command/helpers/Runner.spec.ts +2 -2
  218. package/src/command/helpers/Runner.ts +16 -4
  219. package/src/command/primitives/$command.ts +0 -6
  220. package/src/command/providers/CliProvider.ts +1 -3
  221. package/src/core/Alepha.ts +42 -0
  222. package/src/core/__tests__/Alepha-graph.spec.ts +4 -0
  223. package/src/core/index.shared.ts +1 -0
  224. package/src/core/index.ts +2 -0
  225. package/src/core/primitives/$hook.ts +6 -2
  226. package/src/core/primitives/$module.spec.ts +4 -0
  227. package/src/core/providers/AlsProvider.ts +1 -1
  228. package/src/core/providers/CodecManager.spec.ts +12 -6
  229. package/src/core/providers/CodecManager.ts +26 -6
  230. package/src/core/providers/EventManager.ts +169 -13
  231. package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +621 -0
  232. package/src/core/providers/KeylessJsonSchemaCodec.ts +407 -0
  233. package/src/core/providers/StateManager.spec.ts +27 -16
  234. package/src/email/providers/LocalEmailProvider.spec.ts +111 -87
  235. package/src/email/providers/LocalEmailProvider.ts +52 -15
  236. package/src/email/providers/NodemailerEmailProvider.ts +167 -56
  237. package/src/file/errors/FileError.ts +7 -0
  238. package/src/file/index.ts +9 -1
  239. package/src/file/providers/MemoryFileSystemProvider.ts +393 -0
  240. package/src/logger/index.ts +15 -3
  241. package/src/mcp/transports/StdioMcpTransport.ts +1 -1
  242. package/src/orm/index.browser.ts +1 -19
  243. package/src/orm/index.bun.ts +77 -0
  244. package/src/orm/index.shared-server.ts +22 -0
  245. package/src/orm/index.shared.ts +15 -0
  246. package/src/orm/index.ts +13 -39
  247. package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -5
  248. package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
  249. package/src/orm/providers/drivers/CloudflareD1Provider.ts +4 -0
  250. package/src/orm/providers/drivers/DatabaseProvider.ts +4 -0
  251. package/src/orm/providers/drivers/PglitePostgresProvider.ts +4 -0
  252. package/src/orm/services/Repository.ts +8 -0
  253. package/src/queue/core/providers/WorkerProvider.spec.ts +48 -32
  254. package/src/redis/index.bun.ts +35 -0
  255. package/src/redis/providers/BunRedisProvider.ts +12 -43
  256. package/src/redis/providers/BunRedisSubscriberProvider.ts +2 -3
  257. package/src/redis/providers/NodeRedisProvider.ts +16 -34
  258. package/src/{server/security → security}/__tests__/BasicAuth.spec.ts +11 -11
  259. package/src/{server/security → security}/__tests__/ServerSecurityProvider-realm.spec.ts +21 -16
  260. package/src/{server/security/providers → security/__tests__}/ServerSecurityProvider.spec.ts +5 -5
  261. package/src/security/index.browser.ts +5 -0
  262. package/src/security/index.ts +90 -7
  263. package/src/security/primitives/{$realm.spec.ts → $issuer.spec.ts} +11 -11
  264. package/src/security/primitives/{$realm.ts → $issuer.ts} +20 -17
  265. package/src/security/primitives/$role.ts +5 -5
  266. package/src/security/primitives/$serviceAccount.spec.ts +5 -5
  267. package/src/security/primitives/$serviceAccount.ts +3 -3
  268. package/src/{server/security → security}/providers/ServerSecurityProvider.ts +5 -7
  269. package/src/server/auth/primitives/$auth.ts +10 -10
  270. package/src/server/auth/primitives/$authCredentials.ts +3 -3
  271. package/src/server/auth/primitives/$authGithub.ts +3 -3
  272. package/src/server/auth/primitives/$authGoogle.ts +3 -3
  273. package/src/server/auth/providers/ServerAuthProvider.ts +13 -13
  274. package/src/server/cache/providers/ServerCacheProvider.spec.ts +183 -0
  275. package/src/server/cache/providers/ServerCacheProvider.ts +95 -10
  276. package/src/server/compress/providers/ServerCompressProvider.ts +61 -2
  277. package/src/server/cookies/providers/ServerCookiesProvider.ts +3 -3
  278. package/src/server/core/helpers/ServerReply.ts +2 -2
  279. package/src/server/core/providers/NodeHttpServerProvider.ts +25 -6
  280. package/src/server/core/providers/ServerBodyParserProvider.ts +19 -23
  281. package/src/server/core/providers/ServerLoggerProvider.ts +23 -19
  282. package/src/server/core/providers/ServerProvider.ts +155 -22
  283. package/src/server/core/providers/ServerRouterProvider.ts +259 -115
  284. package/src/server/core/providers/ServerTimingProvider.ts +2 -2
  285. package/src/server/links/index.ts +1 -1
  286. package/src/server/links/providers/LinkProvider.ts +1 -1
  287. package/src/server/static/providers/ServerStaticProvider.ts +10 -0
  288. package/src/server/swagger/index.ts +1 -1
  289. package/src/server/swagger/providers/ServerSwaggerProvider.ts +5 -8
  290. package/src/sms/providers/LocalSmsProvider.spec.ts +153 -111
  291. package/src/sms/providers/LocalSmsProvider.ts +8 -7
  292. package/src/vite/helpers/boot.ts +28 -17
  293. package/src/vite/helpers/importViteReact.ts +13 -0
  294. package/src/vite/index.ts +1 -21
  295. package/src/vite/plugins/viteAlephaDev.ts +16 -1
  296. package/src/vite/plugins/viteAlephaSsrPreload.ts +222 -0
  297. package/src/vite/tasks/buildClient.ts +11 -0
  298. package/src/vite/tasks/buildServer.ts +59 -4
  299. package/src/vite/tasks/devServer.ts +71 -0
  300. package/src/vite/tasks/generateCloudflare.ts +7 -0
  301. package/src/vite/tasks/index.ts +2 -1
  302. package/dist/server/security/index.browser.js +0 -13
  303. package/dist/server/security/index.browser.js.map +0 -1
  304. package/dist/server/security/index.d.ts +0 -173
  305. package/dist/server/security/index.d.ts.map +0 -1
  306. package/dist/server/security/index.js +0 -311
  307. package/dist/server/security/index.js.map +0 -1
  308. package/src/cli/assets/appRouterTs.ts +0 -9
  309. package/src/cli/assets/mainTs.ts +0 -13
  310. package/src/cli/assets/viteConfigTs.ts +0 -14
  311. package/src/cli/commands/run.ts +0 -24
  312. package/src/server/security/index.browser.ts +0 -10
  313. package/src/server/security/index.ts +0 -94
  314. package/src/vite/plugins/viteAlepha.ts +0 -37
  315. package/src/vite/plugins/viteAlephaBuild.ts +0 -281
  316. /package/src/{server/security → security}/primitives/$basicAuth.ts +0 -0
  317. /package/src/{server/security → security}/providers/ServerBasicAuthProvider.ts +0 -0
@@ -1,8 +1,6 @@
1
- import { access, readFile, unlink, writeFile } from "node:fs/promises";
2
- import { createRequire } from "node:module";
3
- import { join } from "node:path";
4
- import { $inject, OPTIONS, t } from "alepha";
1
+ import { $inject, $use, t } from "alepha";
5
2
  import { $command } from "alepha/command";
3
+ import { FileSystemProvider } from "alepha/file";
6
4
  import { $logger } from "alepha/logger";
7
5
  import {
8
6
  boot,
@@ -15,15 +13,22 @@ import {
15
13
  generateVercel,
16
14
  prerenderPages,
17
15
  } from "alepha/vite";
18
- import type * as Vite from "vite";
16
+ import { buildOptions } from "../atoms/buildOptions.ts";
19
17
  import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
18
+ import { PackageManagerUtils } from "../services/PackageManagerUtils.ts";
19
+ import { ProjectScaffolder } from "../services/ProjectScaffolder.ts";
20
20
 
21
21
  export class BuildCommand {
22
22
  protected readonly log = $logger();
23
+ protected readonly fs = $inject(FileSystemProvider);
23
24
  protected readonly utils = $inject(AlephaCliUtils);
25
+ protected readonly pm = $inject(PackageManagerUtils);
26
+ protected readonly scaffolder = $inject(ProjectScaffolder);
27
+ protected readonly options = $use(buildOptions);
24
28
 
25
29
  public readonly build = $command({
26
30
  name: "build",
31
+ mode: "production",
27
32
  description: "Build the project for production",
28
33
  args: t.optional(
29
34
  t.text({ title: "path", description: "Filepath to build" }),
@@ -54,20 +59,23 @@ export class BuildCommand {
54
59
  description: "Generate sitemap.xml with base URL",
55
60
  }),
56
61
  ),
62
+ bun: t.optional(
63
+ t.boolean({
64
+ description: "Prioritize .bun.ts entry files for Bun runtime",
65
+ }),
66
+ ),
57
67
  }),
58
68
  handler: async ({ flags, args, run, root }) => {
59
69
  // Tell viteAlephaBuild plugin to skip - CLI handles all tasks
60
70
  process.env.ALEPHA_BUILD_MODE = "cli";
61
71
  process.env.NODE_ENV = "production";
62
72
 
63
- if (await this.utils.hasExpo(root)) {
64
- // will coming soon
65
- // 1. ensure "expo prebuild" is run
73
+ if (await this.pm.hasExpo(root)) {
74
+ // will come soon
66
75
  return;
67
76
  }
68
77
 
69
- await this.utils.ensureConfig(root, {
70
- viteConfigTs: true,
78
+ await this.scaffolder.ensureConfig(root, {
71
79
  tsconfigJson: true,
72
80
  });
73
81
 
@@ -77,41 +85,19 @@ export class BuildCommand {
77
85
  const distDir = "dist";
78
86
  const clientDir = "public";
79
87
 
80
- await this.utils.ensureDependency(root, "vite", {
88
+ await this.pm.ensureDependency(root, "vite", {
81
89
  run,
90
+ exec: (cmd, opts) => this.utils.exec(cmd, opts),
82
91
  });
92
+ await run.rm("dist", { alias: "clean dist" });
83
93
 
84
- await run.rm("dist", {
85
- alias: "clean dist",
86
- });
87
-
88
- const vite: typeof Vite = createRequire(import.meta.url)("vite");
89
- const config = await vite.resolveConfig({}, "build", "production");
90
- const alephaPlugin: any = config.plugins.find(
91
- (it) => it.name === "alepha:build",
92
- );
93
- const viteAlephaBuildOptions = alephaPlugin?.[OPTIONS] || {};
94
-
94
+ const options = this.options;
95
95
  await this.utils.loadEnv(root, [".env", ".env.production"]);
96
96
 
97
- const stats = flags.stats ?? viteAlephaBuildOptions.stats ?? false;
98
- const hasServer = viteAlephaBuildOptions.serverEntry !== false;
97
+ const stats = flags.stats ?? options.stats ?? false;
98
+ const hasClient = await this.fs.exists(this.fs.join(root, "index.html"));
99
99
 
100
- let hasClient = false;
101
- try {
102
- await access(join(root, "index.html"));
103
- hasClient = true;
104
- } catch {
105
- // No index.html
106
- }
107
-
108
- // Extract client options
109
- const clientOptions =
110
- typeof viteAlephaBuildOptions.client === "object"
111
- ? viteAlephaBuildOptions.client
112
- : {};
113
-
114
- // Build client
100
+ // Build client (precompress always enabled)
115
101
  if (hasClient) {
116
102
  await run({
117
103
  name: "vite build client",
@@ -120,7 +106,7 @@ export class BuildCommand {
120
106
  silent: true,
121
107
  dist: `${distDir}/${clientDir}`,
122
108
  stats,
123
- precompress: clientOptions.precompress,
109
+ precompress: true,
124
110
  }),
125
111
  });
126
112
  }
@@ -129,13 +115,26 @@ export class BuildCommand {
129
115
  await run({
130
116
  name: "vite build server",
131
117
  handler: async () => {
132
- // Check if client template exists
133
- let clientBuilt = false;
134
- try {
135
- await readFile(`${distDir}/${clientDir}/index.html`, "utf-8");
136
- clientBuilt = true;
137
- } catch {
138
- // No client build
118
+ const clientIndexPath = `${distDir}/${clientDir}/index.html`;
119
+ const clientBuilt = await this.fs.exists(clientIndexPath);
120
+
121
+ const conditions: string[] = [];
122
+
123
+ // bun:
124
+ // - alepha
125
+ // - react-dom
126
+
127
+ if (flags.bun) {
128
+ conditions.push("bun");
129
+ }
130
+
131
+ // workerd:
132
+ // - react-dom
133
+ // - postgres
134
+
135
+ // TODO: investigate if we have more conditions like 'edge' to add here
136
+ if (options.cloudflare) {
137
+ conditions.push("workerd");
139
138
  }
140
139
 
141
140
  await buildServer({
@@ -144,11 +143,12 @@ export class BuildCommand {
144
143
  distDir,
145
144
  clientDir: clientBuilt ? clientDir : undefined,
146
145
  stats,
146
+ conditions,
147
147
  });
148
148
 
149
149
  // Server will handle index.html if both client & server are built
150
- if (clientBuilt && hasServer) {
151
- await unlink(`${distDir}/${clientDir}/index.html`);
150
+ if (clientBuilt) {
151
+ await this.fs.rm(clientIndexPath);
152
152
  }
153
153
  },
154
154
  });
@@ -163,83 +163,66 @@ export class BuildCommand {
163
163
 
164
164
  if (hasClient) {
165
165
  // Generate sitemap
166
- const sitemapBaseUrl = flags.sitemap ?? clientOptions.sitemap?.hostname;
167
-
168
- if (sitemapBaseUrl) {
166
+ const sitemapHostname = flags.sitemap ?? options.sitemap?.hostname;
167
+ if (sitemapHostname) {
169
168
  await run({
170
169
  name: "add sitemap",
171
170
  handler: async () => {
172
- await writeFile(
171
+ await this.fs.writeFile(
173
172
  `${distDir}/${clientDir}/sitemap.xml`,
174
173
  await generateSitemap({
175
174
  entry: `${distDir}/index.js`,
176
- baseUrl: sitemapBaseUrl,
175
+ baseUrl: sitemapHostname,
177
176
  }),
178
177
  );
179
178
  },
180
179
  });
181
180
  }
182
181
 
183
- // Pre-render static pages
184
- const shouldPrerender = clientOptions.prerender;
185
-
186
- if (shouldPrerender) {
187
- await run({
188
- name: "pre-render pages",
189
- handler: async () => {
190
- await prerenderPages({
191
- dist: `${distDir}/${clientDir}`,
192
- entry: `${distDir}/index.js`,
193
- compress: clientOptions.precompress,
194
- });
195
- },
196
- });
197
- }
182
+ // Pre-render static pages (always enabled)
183
+ await run({
184
+ name: "pre-render pages",
185
+ handler: async () => {
186
+ await prerenderPages({
187
+ dist: `${distDir}/${clientDir}`,
188
+ entry: `${distDir}/index.js`,
189
+ compress: true,
190
+ });
191
+ },
192
+ });
198
193
  }
199
194
 
200
195
  // Generate deployment configurations
201
- if (flags.vercel || viteAlephaBuildOptions.vercel) {
202
- const config =
203
- typeof viteAlephaBuildOptions.vercel === "object"
204
- ? viteAlephaBuildOptions.vercel
205
- : {};
196
+ if (flags.vercel || options.vercel) {
206
197
  await run({
207
198
  name: "add Vercel config",
208
199
  handler: () =>
209
200
  generateVercel({
210
201
  distDir,
211
202
  clientDir,
212
- config,
203
+ config: options.vercel,
213
204
  }),
214
205
  });
215
206
  }
216
207
 
217
- if (flags.cloudflare || viteAlephaBuildOptions.cloudflare) {
218
- const config =
219
- typeof viteAlephaBuildOptions.cloudflare === "boolean"
220
- ? {}
221
- : viteAlephaBuildOptions.cloudflare;
208
+ if (flags.cloudflare || options.cloudflare) {
222
209
  await run({
223
210
  name: "add Cloudflare config",
224
211
  handler: () =>
225
212
  generateCloudflare({
226
213
  distDir,
227
- config,
214
+ config: options.cloudflare?.config,
228
215
  }),
229
216
  });
230
217
  }
231
218
 
232
- if (flags.docker || viteAlephaBuildOptions.docker) {
233
- const dockerConfig =
234
- typeof viteAlephaBuildOptions.docker === "object"
235
- ? viteAlephaBuildOptions.docker
236
- : {};
219
+ if (flags.docker || options.docker) {
237
220
  await run({
238
221
  name: "add Docker config",
239
222
  handler: () =>
240
223
  generateDocker({
241
224
  distDir,
242
- ...dockerConfig,
225
+ ...options.docker,
243
226
  }),
244
227
  });
245
228
  }
@@ -1,7 +1,6 @@
1
- import { readFile } from "node:fs/promises";
2
- import { join } from "node:path";
3
1
  import { $inject, AlephaError, t } from "alepha";
4
2
  import { $command } from "alepha/command";
3
+ import { FileSystemProvider } from "alepha/file";
5
4
  import { $logger } from "alepha/logger";
6
5
  import type {
7
6
  DatabaseProvider,
@@ -27,6 +26,7 @@ const drizzleCommandFlags = t.object({
27
26
 
28
27
  export class DbCommand {
29
28
  protected readonly log = $logger();
29
+ protected readonly fs = $inject(FileSystemProvider);
30
30
  protected readonly utils = $inject(AlephaCliUtils);
31
31
 
32
32
  /**
@@ -66,26 +66,23 @@ export class DbCommand {
66
66
 
67
67
  accepted.add(providerName);
68
68
 
69
- const migrationDir = join(rootDir, "migrations", providerName);
69
+ const migrationDir = this.fs.join(rootDir, "migrations", providerName);
70
70
 
71
- const journalFile = await readFile(
72
- `${migrationDir}/meta/_journal.json`,
73
- "utf-8",
74
- ).catch(() => null);
71
+ const journalBuffer = await this.fs
72
+ .readFile(`${migrationDir}/meta/_journal.json`)
73
+ .catch(() => null);
75
74
 
76
- if (!journalFile) {
75
+ if (!journalBuffer) {
77
76
  this.log.info("No migration journal found.");
78
77
  return;
79
78
  }
80
79
 
81
- const journal = JSON.parse(journalFile);
80
+ const journal = JSON.parse(journalBuffer.toString("utf-8"));
82
81
  const lastMigration = journal.entries[journal.entries.length - 1];
83
- const lastSnapshot = JSON.parse(
84
- await readFile(
85
- `${migrationDir}/meta/${String(lastMigration.idx).padStart(4, "0")}_snapshot.json`,
86
- "utf-8",
87
- ),
82
+ const snapshotBuffer = await this.fs.readFile(
83
+ `${migrationDir}/meta/${String(lastMigration.idx).padStart(4, "0")}_snapshot.json`,
88
84
  );
85
+ const lastSnapshot = JSON.parse(snapshotBuffer.toString("utf-8"));
89
86
 
90
87
  const models = drizzleKitProvider.getModels(provider);
91
88
  const kit = drizzleKitProvider.importDrizzleKit();
@@ -134,7 +131,6 @@ export class DbCommand {
134
131
  protected readonly generate = $command({
135
132
  name: "generate",
136
133
  description: "Generate migration files based on current database schema",
137
- summary: false,
138
134
  args: t.optional(
139
135
  t.text({
140
136
  title: "path",
@@ -173,7 +169,6 @@ export class DbCommand {
173
169
  protected readonly push = $command({
174
170
  name: "push",
175
171
  description: "Push database schema changes directly to the database",
176
- summary: false,
177
172
  args: t.optional(
178
173
  t.text({
179
174
  title: "path",
@@ -200,7 +195,6 @@ export class DbCommand {
200
195
  protected readonly migrate = $command({
201
196
  name: "migrate",
202
197
  description: "Apply pending database migrations",
203
- summary: false,
204
198
  args: t.optional(
205
199
  t.text({
206
200
  title: "path",
@@ -227,7 +221,6 @@ export class DbCommand {
227
221
  protected readonly studio = $command({
228
222
  name: "studio",
229
223
  description: "Launch Drizzle Studio database browser",
230
- summary: false,
231
224
  args: t.optional(
232
225
  t.text({
233
226
  title: "path",
@@ -299,6 +292,10 @@ export class DbCommand {
299
292
  const providerName = provider.name;
300
293
  const dialect = provider.dialect;
301
294
 
295
+ if (providerName === "") {
296
+ continue;
297
+ }
298
+
302
299
  if (accepted.has(providerName)) {
303
300
  continue;
304
301
  }
@@ -320,6 +317,7 @@ export class DbCommand {
320
317
  provider,
321
318
  providerName,
322
319
  providerUrl: provider.url,
320
+ providerDriver: provider.driver,
323
321
  dialect,
324
322
  entry,
325
323
  rootDir,
@@ -345,6 +343,7 @@ export class DbCommand {
345
343
  provider: DatabaseProvider;
346
344
  providerName: string;
347
345
  providerUrl: string;
346
+ providerDriver: string;
348
347
  dialect: string;
349
348
  entry: string;
350
349
  rootDir: string;
@@ -375,16 +374,16 @@ export class DbCommand {
375
374
  config.schemaFilter = options.provider.schema;
376
375
  }
377
376
 
378
- if (options.providerName === "d1") {
377
+ if (options.providerDriver === "d1") {
379
378
  config.driver = "d1-http";
380
379
  }
381
380
 
382
- if (options.providerName === "pglite") {
381
+ if (options.providerDriver === "pglite") {
383
382
  config.driver = "pglite";
384
383
  }
385
384
 
386
385
  if (options.dialect === "sqlite") {
387
- if (options.providerName === "d1") {
386
+ if (options.providerDriver === "d1") {
388
387
  const token = process.env.CLOUDFLARE_API_TOKEN;
389
388
  if (!token) {
390
389
  throw new AlephaError(
@@ -425,7 +424,7 @@ export class DbCommand {
425
424
  } else {
426
425
  let url = options.providerUrl;
427
426
  url = url.replace("sqlite://", "").replace("file://", "");
428
- url = join(options.rootDir, url);
427
+ url = this.fs.join(options.rootDir, url);
429
428
 
430
429
  config.dbCredentials = {
431
430
  url,
@@ -1,12 +1,15 @@
1
- import { join } from "node:path";
2
1
  import { $inject, AlephaError, t } from "alepha";
3
2
  import { $command } from "alepha/command";
3
+ import { FileSystemProvider } from "alepha/file";
4
4
  import { $logger } from "alepha/logger";
5
5
  import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
6
+ import { PackageManagerUtils } from "../services/PackageManagerUtils.ts";
6
7
 
7
8
  export class DeployCommand {
8
9
  protected readonly log = $logger();
10
+ protected readonly fs = $inject(FileSystemProvider);
9
11
  protected readonly utils = $inject(AlephaCliUtils);
12
+ protected readonly pm = $inject(PackageManagerUtils);
10
13
 
11
14
  /**
12
15
  * Deploy the project to a hosting platform (e.g., Vercel, Cloudflare, Surge)
@@ -79,7 +82,10 @@ export class DeployCommand {
79
82
  this.log.debug("Running database migrations before deployment...");
80
83
  await this.utils.exec(`alepha db migrate --mode=${mode}`);
81
84
  }
82
- await this.utils.ensureDependency(root, "vercel", { dev: true });
85
+ await this.pm.ensureDependency(root, "vercel", {
86
+ dev: true,
87
+ exec: (cmd, opts) => this.utils.exec(cmd, opts),
88
+ });
83
89
  const command =
84
90
  `vercel . --cwd=dist ${mode === "production" ? "--prod" : ""}`.trim();
85
91
  this.log.debug(`Deploying to Vercel with command: ${command}`);
@@ -93,7 +99,10 @@ export class DeployCommand {
93
99
  this.log.debug("Running database migrations before deployment...");
94
100
  await this.utils.exec(`alepha db migrate --mode=${mode}`);
95
101
  }
96
- await this.utils.ensureDependency(root, "wrangler", { dev: true });
102
+ await this.pm.ensureDependency(root, "wrangler", {
103
+ dev: true,
104
+ exec: (cmd, opts) => this.utils.exec(cmd, opts),
105
+ });
97
106
  const command =
98
107
  `wrangler deploy ${mode === "production" ? "" : "--env preview"} --config=dist/wrangler.jsonc`.trim();
99
108
  this.log.info(`Deploying to Cloudflare with command: ${command}`);
@@ -103,8 +112,11 @@ export class DeployCommand {
103
112
 
104
113
  // Surge deployment
105
114
  if (await this.utils.exists(root, "dist/public/404.html")) {
106
- await this.utils.ensureDependency(root, "surge", { dev: true });
107
- const distPath = join(root, "dist/public");
115
+ await this.pm.ensureDependency(root, "surge", {
116
+ dev: true,
117
+ exec: (cmd, opts) => this.utils.exec(cmd, opts),
118
+ });
119
+ const distPath = this.fs.join(root, "dist/public");
108
120
  this.log.debug(`Deploying to Surge from directory: ${distPath}`);
109
121
  await this.utils.exec(`surge ${distPath}`);
110
122
  return;
@@ -1,14 +1,18 @@
1
- import { access } from "node:fs/promises";
2
- import { join } from "node:path";
3
1
  import { $inject, Alepha, t } from "alepha";
4
2
  import { $command } from "alepha/command";
3
+ import { FileSystemProvider } from "alepha/file";
5
4
  import { $logger } from "alepha/logger";
6
- import { boot } from "alepha/vite";
5
+ import { boot, devServer } from "alepha/vite";
7
6
  import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
7
+ import { PackageManagerUtils } from "../services/PackageManagerUtils.ts";
8
+ import { ProjectScaffolder } from "../services/ProjectScaffolder.ts";
8
9
 
9
10
  export class DevCommand {
10
11
  protected readonly log = $logger();
12
+ protected readonly fs = $inject(FileSystemProvider);
11
13
  protected readonly utils = $inject(AlephaCliUtils);
14
+ protected readonly pm = $inject(PackageManagerUtils);
15
+ protected readonly scaffolder = $inject(ProjectScaffolder);
12
16
  protected readonly alepha = $inject(Alepha);
13
17
 
14
18
  /**
@@ -22,10 +26,9 @@ export class DevCommand {
22
26
  description: "Run the project in development mode",
23
27
  args: t.optional(t.text({ title: "path", description: "Filepath to run" })),
24
28
  handler: async ({ args, root }) => {
25
- const expo = await this.utils.hasExpo(root);
29
+ const expo = await this.pm.hasExpo(root);
26
30
 
27
- await this.utils.ensureConfig(root, {
28
- viteConfigTs: !expo,
31
+ await this.scaffolder.ensureConfig(root, {
29
32
  tsconfigJson: true,
30
33
  });
31
34
 
@@ -40,7 +43,7 @@ export class DevCommand {
40
43
  const isFullstack = await this.isFullstackProject(root);
41
44
 
42
45
  if (!isFullstack) {
43
- const exe = this.alepha.isBun() ? "bun" : "tsx";
46
+ const exe = (await this.isBunProject(root)) ? "bun" : "tsx";
44
47
  let cmd = `${exe} --watch`;
45
48
  if (await this.utils.exists(root, ".env")) {
46
49
  cmd += " --env-file=./.env";
@@ -53,17 +56,22 @@ export class DevCommand {
53
56
  }
54
57
 
55
58
  // Ensure vite is installed before running
56
- await this.utils.ensureDependency(root, "vite");
57
- await this.utils.exec("vite");
59
+ await this.pm.ensureDependency(root, "vite", {
60
+ exec: (cmd, opts) => this.utils.exec(cmd, opts),
61
+ });
62
+
63
+ await devServer();
58
64
  },
59
65
  });
60
66
 
61
- protected async isFullstackProject(root: string): Promise<boolean> {
62
- try {
63
- await access(join(root, "index.html"));
67
+ protected async isBunProject(root: string): Promise<boolean> {
68
+ if (this.alepha.isBun()) {
64
69
  return true;
65
- } catch {
66
- return false;
67
70
  }
71
+ return this.fs.exists(this.fs.join(root, "bun.lock"));
72
+ }
73
+
74
+ protected async isFullstackProject(root: string): Promise<boolean> {
75
+ return this.fs.exists(this.fs.join(root, "index.html"));
68
76
  }
69
77
  }
@@ -1,16 +1,22 @@
1
1
  import { $inject } from "alepha";
2
2
  import { $command } from "alepha/command";
3
3
  import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
4
+ import { PackageManagerUtils } from "../services/PackageManagerUtils.ts";
5
+ import { ProjectScaffolder } from "../services/ProjectScaffolder.ts";
4
6
 
5
7
  export class FormatCommand {
6
8
  protected readonly utils = $inject(AlephaCliUtils);
9
+ protected readonly pm = $inject(PackageManagerUtils);
10
+ protected readonly scaffolder = $inject(ProjectScaffolder);
7
11
 
8
12
  public readonly format = $command({
9
13
  name: "format",
10
14
  description: "Format the codebase using Biome",
11
15
  handler: async ({ root }) => {
12
- await this.utils.ensureConfig(root, { biomeJson: true });
13
- await this.utils.ensureDependency(root, "@biomejs/biome");
16
+ await this.scaffolder.ensureConfig(root, { biomeJson: true });
17
+ await this.pm.ensureDependency(root, "@biomejs/biome", {
18
+ exec: (cmd, opts) => this.utils.exec(cmd, opts),
19
+ });
14
20
  await this.utils.exec("biome format --fix");
15
21
  },
16
22
  });
@@ -0,0 +1,53 @@
1
+ import { $inject, t } from "alepha";
2
+ import { $command } from "alepha/command";
3
+ import { FileSystemProvider } from "alepha/file";
4
+ import { $logger } from "alepha/logger";
5
+ import { AlephaCliUtils } from "../../services/AlephaCliUtils.ts";
6
+
7
+ export class GenEnvCommand {
8
+ protected readonly log = $logger();
9
+ protected readonly utils = $inject(AlephaCliUtils);
10
+ protected readonly fs = $inject(FileSystemProvider);
11
+
12
+ public readonly command = $command({
13
+ name: "env",
14
+ description: "Extract environment variables from server entry file",
15
+ flags: t.object({
16
+ out: t.optional(
17
+ t.text({
18
+ aliases: ["o"],
19
+ description: "Output file path (e.g., .env)",
20
+ }),
21
+ ),
22
+ }),
23
+ handler: async ({ root, flags }) => {
24
+ const { alepha } = await this.utils.loadAlephaFromServerEntryFile(root);
25
+
26
+ try {
27
+ const { env } = alepha.dump();
28
+
29
+ let dotEnvFile = "";
30
+ for (const [key, value] of Object.entries(env)) {
31
+ if (value.description) {
32
+ dotEnvFile += `# ${value.description.split("\n").join("\n# ")}\n`;
33
+ }
34
+ if (value.required && !value.default) {
35
+ dotEnvFile += `# (required)\n`;
36
+ }
37
+ if (value.enum) {
38
+ dotEnvFile += `# Possible values: ${value.enum.join(", ")}\n`;
39
+ }
40
+ dotEnvFile += `${key}=${value.default || ""}\n\n`;
41
+ }
42
+
43
+ if (flags.out) {
44
+ await this.fs.writeFile(this.fs.join(root, flags.out), dotEnvFile);
45
+ } else {
46
+ this.log.info(dotEnvFile);
47
+ }
48
+ } catch (err) {
49
+ this.log.error("Failed to extract environment variables", err);
50
+ }
51
+ },
52
+ });
53
+ }
@@ -64,7 +64,7 @@ export class OpenApiCommand {
64
64
  return;
65
65
  }
66
66
 
67
- this.log.error(`OpenAPI generation failed - ${message}`, { err });
67
+ this.log.error(`OpenAPI generation failed - ${message}`, err);
68
68
  }
69
69
  },
70
70
  });
@@ -0,0 +1,15 @@
1
+ /**
2
+ * TODO:
3
+ *
4
+ * alepha gen resource <name>
5
+ *
6
+ * will generate:
7
+ *
8
+ * src/api/controllers/<name>Controller.ts
9
+ * src/api/entity/<name>Entity.ts
10
+ * (maybe) src/api/services/<name>Service.ts
11
+ * (maybe) src/api/repositories/<name>Repository.ts
12
+ *
13
+ * Each file will contain a basic scaffold for the respective component.
14
+ *
15
+ */
@@ -1,16 +1,22 @@
1
1
  import { $inject } from "alepha";
2
2
  import { $command } from "alepha/command";
3
3
  import { ChangelogCommand } from "./gen/changelog.ts";
4
+ import { GenEnvCommand } from "./gen/env.ts";
4
5
  import { OpenApiCommand } from "./gen/openapi.ts";
5
6
 
6
7
  export class GenCommand {
7
8
  protected readonly changelog = $inject(ChangelogCommand);
8
9
  protected readonly openapi = $inject(OpenApiCommand);
10
+ protected readonly genEnv = $inject(GenEnvCommand);
9
11
 
10
12
  public readonly gen = $command({
11
13
  name: "gen",
12
14
  description: "Generate code, documentation, ...",
13
- children: [this.changelog.command, this.openapi.command],
15
+ children: [
16
+ this.changelog.command,
17
+ this.openapi.command,
18
+ this.genEnv.command,
19
+ ],
14
20
  handler: async ({ help }) => {
15
21
  help();
16
22
  },