alepha 0.14.0 → 0.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/README.md +3 -3
  2. package/dist/api/audits/index.d.ts +80 -1
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js.map +1 -1
  5. package/dist/api/files/index.d.ts +80 -1
  6. package/dist/api/files/index.d.ts.map +1 -1
  7. package/dist/api/files/index.js.map +1 -1
  8. package/dist/api/jobs/index.d.ts +236 -157
  9. package/dist/api/jobs/index.d.ts.map +1 -1
  10. package/dist/api/jobs/index.js.map +1 -1
  11. package/dist/api/notifications/index.d.ts +21 -1
  12. package/dist/api/notifications/index.d.ts.map +1 -1
  13. package/dist/api/parameters/index.d.ts +451 -4
  14. package/dist/api/parameters/index.d.ts.map +1 -1
  15. package/dist/api/parameters/index.js.map +1 -1
  16. package/dist/api/users/index.d.ts +252 -249
  17. package/dist/api/users/index.d.ts.map +1 -1
  18. package/dist/api/users/index.js +4 -0
  19. package/dist/api/users/index.js.map +1 -1
  20. package/dist/api/verifications/index.d.ts +128 -128
  21. package/dist/api/verifications/index.d.ts.map +1 -1
  22. package/dist/batch/index.js.map +1 -1
  23. package/dist/cache/core/index.js.map +1 -1
  24. package/dist/cli/index.d.ts +304 -115
  25. package/dist/cli/index.d.ts.map +1 -1
  26. package/dist/cli/index.js +650 -531
  27. package/dist/cli/index.js.map +1 -1
  28. package/dist/command/index.d.ts +210 -13
  29. package/dist/command/index.d.ts.map +1 -1
  30. package/dist/command/index.js +306 -69
  31. package/dist/command/index.js.map +1 -1
  32. package/dist/core/index.browser.js.map +1 -1
  33. package/dist/core/index.d.ts +1 -1
  34. package/dist/core/index.d.ts.map +1 -1
  35. package/dist/core/index.js +7 -6
  36. package/dist/core/index.js.map +1 -1
  37. package/dist/core/index.native.js +7 -6
  38. package/dist/core/index.native.js.map +1 -1
  39. package/dist/datetime/index.js.map +1 -1
  40. package/dist/fake/index.js.map +1 -1
  41. package/dist/file/index.d.ts.map +1 -1
  42. package/dist/file/index.js.map +1 -1
  43. package/dist/lock/redis/index.js.map +1 -1
  44. package/dist/logger/index.js.map +1 -1
  45. package/dist/mcp/index.js.map +1 -1
  46. package/dist/orm/index.browser.js +26 -5
  47. package/dist/orm/index.browser.js.map +1 -1
  48. package/dist/orm/index.d.ts +294 -215
  49. package/dist/orm/index.d.ts.map +1 -1
  50. package/dist/orm/index.js +522 -523
  51. package/dist/orm/index.js.map +1 -1
  52. package/dist/queue/redis/index.js +2 -4
  53. package/dist/queue/redis/index.js.map +1 -1
  54. package/dist/redis/index.d.ts +400 -29
  55. package/dist/redis/index.d.ts.map +1 -1
  56. package/dist/redis/index.js +412 -21
  57. package/dist/redis/index.js.map +1 -1
  58. package/dist/retry/index.js.map +1 -1
  59. package/dist/router/index.js.map +1 -1
  60. package/dist/scheduler/index.js.map +1 -1
  61. package/dist/security/index.d.ts.map +1 -1
  62. package/dist/security/index.js.map +1 -1
  63. package/dist/server/auth/index.d.ts +155 -155
  64. package/dist/server/auth/index.js.map +1 -1
  65. package/dist/server/cache/index.js.map +1 -1
  66. package/dist/server/cookies/index.browser.js.map +1 -1
  67. package/dist/server/cookies/index.js.map +1 -1
  68. package/dist/server/core/index.browser.js.map +1 -1
  69. package/dist/server/core/index.d.ts +0 -1
  70. package/dist/server/core/index.d.ts.map +1 -1
  71. package/dist/server/core/index.js.map +1 -1
  72. package/dist/server/helmet/index.d.ts +4 -1
  73. package/dist/server/helmet/index.d.ts.map +1 -1
  74. package/dist/server/helmet/index.js.map +1 -1
  75. package/dist/server/links/index.browser.js.map +1 -1
  76. package/dist/server/links/index.js.map +1 -1
  77. package/dist/server/multipart/index.d.ts.map +1 -1
  78. package/dist/server/multipart/index.js.map +1 -1
  79. package/dist/server/proxy/index.js.map +1 -1
  80. package/dist/server/rate-limit/index.js.map +1 -1
  81. package/dist/server/security/index.d.ts +9 -9
  82. package/dist/server/security/index.js.map +1 -1
  83. package/dist/server/swagger/index.js.map +1 -1
  84. package/dist/thread/index.js.map +1 -1
  85. package/dist/topic/core/index.js.map +1 -1
  86. package/dist/topic/redis/index.js +3 -3
  87. package/dist/topic/redis/index.js.map +1 -1
  88. package/dist/vite/index.js +9 -6
  89. package/dist/vite/index.js.map +1 -1
  90. package/dist/websocket/index.browser.js.map +1 -1
  91. package/dist/websocket/index.d.ts +7 -7
  92. package/dist/websocket/index.js.map +1 -1
  93. package/package.json +3 -3
  94. package/src/api/users/index.ts +4 -0
  95. package/src/cli/apps/AlephaCli.ts +36 -14
  96. package/src/cli/apps/AlephaPackageBuilderCli.ts +5 -1
  97. package/src/cli/assets/appRouterTs.ts +1 -1
  98. package/src/cli/atoms/changelogOptions.ts +45 -0
  99. package/src/cli/commands/{ViteCommands.ts → build.ts} +4 -93
  100. package/src/cli/commands/changelog.ts +244 -0
  101. package/src/cli/commands/clean.ts +14 -0
  102. package/src/cli/commands/{DrizzleCommands.ts → db.ts} +37 -124
  103. package/src/cli/commands/deploy.ts +118 -0
  104. package/src/cli/commands/dev.ts +57 -0
  105. package/src/cli/commands/format.ts +17 -0
  106. package/src/cli/commands/{CoreCommands.ts → init.ts} +2 -40
  107. package/src/cli/commands/lint.ts +17 -0
  108. package/src/cli/commands/root.ts +32 -0
  109. package/src/cli/commands/run.ts +24 -0
  110. package/src/cli/commands/test.ts +42 -0
  111. package/src/cli/commands/typecheck.ts +19 -0
  112. package/src/cli/commands/{VerifyCommands.ts → verify.ts} +1 -13
  113. package/src/cli/defineConfig.ts +24 -0
  114. package/src/cli/index.ts +17 -5
  115. package/src/cli/services/AlephaCliUtils.ts +4 -21
  116. package/src/cli/services/GitMessageParser.ts +77 -0
  117. package/src/command/helpers/EnvUtils.ts +37 -0
  118. package/src/command/index.ts +3 -1
  119. package/src/command/primitives/$command.ts +172 -6
  120. package/src/command/providers/CliProvider.ts +424 -91
  121. package/src/core/Alepha.ts +8 -5
  122. package/src/file/providers/NodeFileSystemProvider.ts +3 -1
  123. package/src/orm/index.browser.ts +1 -1
  124. package/src/orm/index.ts +18 -10
  125. package/src/orm/interfaces/PgQueryWhere.ts +1 -26
  126. package/src/orm/providers/{PostgresTypeProvider.ts → DatabaseTypeProvider.ts} +25 -3
  127. package/src/orm/providers/drivers/BunPostgresProvider.ts +225 -0
  128. package/src/orm/providers/drivers/BunSqliteProvider.ts +180 -0
  129. package/src/orm/providers/drivers/DatabaseProvider.ts +25 -0
  130. package/src/orm/providers/drivers/NodePostgresProvider.ts +0 -25
  131. package/src/orm/services/QueryManager.ts +10 -125
  132. package/src/queue/redis/providers/RedisQueueProvider.ts +2 -7
  133. package/src/redis/index.ts +65 -3
  134. package/src/redis/providers/BunRedisProvider.ts +304 -0
  135. package/src/redis/providers/BunRedisSubscriberProvider.ts +94 -0
  136. package/src/redis/providers/NodeRedisProvider.ts +280 -0
  137. package/src/redis/providers/NodeRedisSubscriberProvider.ts +94 -0
  138. package/src/redis/providers/RedisProvider.ts +134 -140
  139. package/src/redis/providers/RedisSubscriberProvider.ts +58 -49
  140. package/src/server/core/providers/BunHttpServerProvider.ts +0 -3
  141. package/src/server/core/providers/ServerBodyParserProvider.ts +3 -1
  142. package/src/server/core/providers/ServerProvider.ts +7 -4
  143. package/src/server/multipart/providers/ServerMultipartProvider.ts +3 -1
  144. package/src/server/proxy/providers/ServerProxyProvider.ts +1 -1
  145. package/src/topic/redis/providers/RedisTopicProvider.ts +3 -3
  146. package/src/vite/tasks/buildServer.ts +1 -0
  147. package/src/cli/commands/BiomeCommands.ts +0 -29
  148. package/src/cli/commands/ChangelogCommands.ts +0 -389
  149. package/src/orm/services/PgJsonQueryManager.ts +0 -511
@@ -0,0 +1,118 @@
1
+ import { join } from "node:path";
2
+ import { $inject, AlephaError, t } from "alepha";
3
+ import { $command } from "alepha/command";
4
+ import { $logger } from "alepha/logger";
5
+ import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
6
+
7
+ export class DeployCommand {
8
+ protected readonly log = $logger();
9
+ protected readonly utils = $inject(AlephaCliUtils);
10
+
11
+ /**
12
+ * Deploy the project to a hosting platform (e.g., Vercel, Cloudflare, Surge)
13
+ *
14
+ * Deploy command can be overridden by creating a alepha.config.ts in the project root:
15
+ *
16
+ * ```ts
17
+ * import { defineConfig } from "alepha/cli";
18
+ *
19
+ * export default defineConfig({
20
+ * commands: {
21
+ * deploy: {
22
+ * handler: async ({ root, mode, flags }) => {
23
+ * // Custom deployment logic here
24
+ * },
25
+ * },
26
+ * },
27
+ * });
28
+ * ```
29
+ */
30
+ public readonly deploy = $command({
31
+ name: "deploy",
32
+ description:
33
+ "Deploy the project to a hosting platform (e.g., Vercel, Cloudflare, Surge)",
34
+ mode: true,
35
+ flags: t.object({
36
+ build: t.boolean({
37
+ description: "Build the project before deployment",
38
+ default: false,
39
+ }),
40
+ migrate: t.boolean({
41
+ description:
42
+ "Run database migrations before deployment (if applicable)",
43
+ default: false,
44
+ }),
45
+ }),
46
+ env: t.object({
47
+ VERCEL_TOKEN: t.optional(
48
+ t.text({
49
+ description: "Vercel API token (e.g., xxxxxxxxxxxxxxxxxxxx)",
50
+ }),
51
+ ),
52
+ VERCEL_ORG_ID: t.optional(
53
+ t.text({
54
+ description: "Vercel organization ID (e.g., team_abc123...)",
55
+ }),
56
+ ),
57
+ VERCEL_PROJECT_ID: t.optional(
58
+ t.text({ description: "Vercel project ID (e.g., prj_abc123...)" }),
59
+ ),
60
+ CLOUDFLARE_API_TOKEN: t.optional(
61
+ t.text({
62
+ description: "Cloudflare API token (e.g., xxxx-xxxx-xxxx-xxxx)",
63
+ }),
64
+ ),
65
+ CLOUDFLARE_ACCOUNT_ID: t.optional(
66
+ t.text({
67
+ description: "Cloudflare account ID (e.g., abc123def456...)",
68
+ }),
69
+ ),
70
+ }),
71
+ handler: async ({ root, mode, flags }) => {
72
+ if (flags.build) {
73
+ await this.utils.exec("alepha build");
74
+ }
75
+
76
+ // Vercel deployment
77
+ if (await this.utils.exists(root, "dist/vercel.json")) {
78
+ if (flags.migrate) {
79
+ this.log.debug("Running database migrations before deployment...");
80
+ await this.utils.exec(`alepha db migrate --mode=${mode}`);
81
+ }
82
+ await this.utils.ensureDependency(root, "vercel", { dev: true });
83
+ const command =
84
+ `vercel . --cwd=dist ${mode === "production" ? "--prod" : ""}`.trim();
85
+ this.log.debug(`Deploying to Vercel with command: ${command}`);
86
+ await this.utils.exec(command);
87
+ return;
88
+ }
89
+
90
+ // Cloudflare deployment
91
+ if (await this.utils.exists(root, "dist/wrangler.jsonc")) {
92
+ if (flags.migrate) {
93
+ this.log.debug("Running database migrations before deployment...");
94
+ await this.utils.exec(`alepha db migrate --mode=${mode}`);
95
+ }
96
+ await this.utils.ensureDependency(root, "wrangler", { dev: true });
97
+ const command =
98
+ `wrangler deploy ${mode === "production" ? "" : "--env preview"} --config=dist/wrangler.jsonc`.trim();
99
+ this.log.info(`Deploying to Cloudflare with command: ${command}`);
100
+ await this.utils.exec(command);
101
+ return;
102
+ }
103
+
104
+ // Surge deployment
105
+ 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");
108
+ this.log.debug(`Deploying to Surge from directory: ${distPath}`);
109
+ await this.utils.exec(`surge ${distPath}`);
110
+ return;
111
+ }
112
+
113
+ throw new AlephaError(
114
+ "No deployment configuration found in the dist folder.",
115
+ );
116
+ },
117
+ });
118
+ }
@@ -0,0 +1,57 @@
1
+ import { access } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { $inject, t } from "alepha";
4
+ import { $command } from "alepha/command";
5
+ import { $logger } from "alepha/logger";
6
+ import { boot } from "alepha/vite";
7
+ import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
8
+
9
+ export class DevCommand {
10
+ protected readonly log = $logger();
11
+ protected readonly utils = $inject(AlephaCliUtils);
12
+
13
+ /**
14
+ * Will run the project in watch mode.
15
+ *
16
+ * - If an index.html file is found in the project root, it will run Vite in dev mode.
17
+ * - Otherwise, it will look for a server entry file and run it with tsx in watch mode.
18
+ */
19
+ public readonly dev = $command({
20
+ name: "dev",
21
+ description: "Run the project in development mode",
22
+ args: t.optional(t.text({ title: "path", description: "Filepath to run" })),
23
+ handler: async ({ args, root }) => {
24
+ const expo = await this.utils.hasExpo(root);
25
+
26
+ await this.utils.ensureConfig(root, {
27
+ viteConfigTs: !expo,
28
+ tsconfigJson: true,
29
+ });
30
+
31
+ if (expo) {
32
+ await this.utils.exec("expo start");
33
+ return;
34
+ }
35
+
36
+ const entry = await boot.getServerEntry(root, args);
37
+ this.log.trace("Entry file found", { entry });
38
+
39
+ try {
40
+ await access(join(root, "index.html"));
41
+ } catch {
42
+ this.log.trace("No index.html found, running entry file with tsx");
43
+ let cmd = "tsx --watch";
44
+ if (await this.utils.exists(root, ".env")) {
45
+ cmd += " --env-file=./.env";
46
+ }
47
+ cmd += ` ${entry}`;
48
+ await this.utils.exec(cmd);
49
+ return;
50
+ }
51
+
52
+ // Ensure vite is installed before running
53
+ await this.utils.ensureDependency(root, "vite");
54
+ await this.utils.exec("vite");
55
+ },
56
+ });
57
+ }
@@ -0,0 +1,17 @@
1
+ import { $inject } from "alepha";
2
+ import { $command } from "alepha/command";
3
+ import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
4
+
5
+ export class FormatCommand {
6
+ protected readonly utils = $inject(AlephaCliUtils);
7
+
8
+ public readonly format = $command({
9
+ name: "format",
10
+ description: "Format the codebase using Biome",
11
+ handler: async ({ root }) => {
12
+ await this.utils.ensureConfig(root, { biomeJson: true });
13
+ await this.utils.ensureDependency(root, "@biomejs/biome");
14
+ await this.utils.exec("biome format --fix");
15
+ },
16
+ });
17
+ }
@@ -1,48 +1,10 @@
1
1
  import { $inject, t } from "alepha";
2
- import { $command, CliProvider } from "alepha/command";
3
- import { $logger } from "alepha/logger";
2
+ import { $command } from "alepha/command";
4
3
  import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
5
- import { version } from "../version.ts";
6
4
 
7
- export class CoreCommands {
8
- protected readonly log = $logger();
9
- protected readonly cli = $inject(CliProvider);
5
+ export class InitCommand {
10
6
  protected readonly utils = $inject(AlephaCliUtils);
11
7
 
12
- /**
13
- * Called when no command is provided
14
- */
15
- public readonly root = $command({
16
- root: true,
17
- flags: t.object({
18
- version: t.optional(
19
- t.boolean({
20
- description: "Show Alepha CLI version",
21
- aliases: ["v"],
22
- }),
23
- ),
24
- }),
25
- handler: async ({ flags }) => {
26
- if (flags.version) {
27
- this.log.info(version);
28
- return;
29
- }
30
-
31
- this.cli.printHelp();
32
- },
33
- });
34
-
35
- /**
36
- * Clean the project, removing the "dist" directory
37
- */
38
- public readonly clean = $command({
39
- name: "clean",
40
- description: "Clean the project",
41
- handler: async ({ run }) => {
42
- await run.rm("./dist");
43
- },
44
- });
45
-
46
8
  /**
47
9
  * Ensure the project has the necessary Alepha configuration files.
48
10
  * Add the correct dependencies to package.json and install them.
@@ -0,0 +1,17 @@
1
+ import { $inject } from "alepha";
2
+ import { $command } from "alepha/command";
3
+ import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
4
+
5
+ export class LintCommand {
6
+ protected readonly utils = $inject(AlephaCliUtils);
7
+
8
+ public readonly lint = $command({
9
+ name: "lint",
10
+ description: "Run linter across the codebase using Biome",
11
+ handler: async ({ root }) => {
12
+ await this.utils.ensureConfig(root, { biomeJson: true });
13
+ await this.utils.ensureDependency(root, "@biomejs/biome");
14
+ await this.utils.exec("biome check --formatter-enabled=false --fix");
15
+ },
16
+ });
17
+ }
@@ -0,0 +1,32 @@
1
+ import { $inject, t } from "alepha";
2
+ import { $command, CliProvider } from "alepha/command";
3
+ import { $logger } from "alepha/logger";
4
+ import { version } from "../version.ts";
5
+
6
+ export class RootCommand {
7
+ protected readonly log = $logger();
8
+ protected readonly cli = $inject(CliProvider);
9
+
10
+ /**
11
+ * Called when no command is provided
12
+ */
13
+ public readonly root = $command({
14
+ root: true,
15
+ flags: t.object({
16
+ version: t.optional(
17
+ t.boolean({
18
+ description: "Show Alepha CLI version",
19
+ aliases: ["v"],
20
+ }),
21
+ ),
22
+ }),
23
+ handler: async ({ flags }) => {
24
+ if (flags.version) {
25
+ this.log.info(version);
26
+ return;
27
+ }
28
+
29
+ this.cli.printHelp();
30
+ },
31
+ });
32
+ }
@@ -0,0 +1,24 @@
1
+ import { $inject, t } from "alepha";
2
+ import { $command } from "alepha/command";
3
+ import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
4
+
5
+ export class RunCommand {
6
+ protected readonly utils = $inject(AlephaCliUtils);
7
+
8
+ public readonly run = $command({
9
+ name: "run",
10
+ hide: true,
11
+ description: "Run a TypeScript file directly",
12
+ flags: t.object({
13
+ watch: t.optional(
14
+ t.boolean({ description: "Watch file for changes", alias: "w" }),
15
+ ),
16
+ }),
17
+ summary: false,
18
+ args: t.text({ title: "path", description: "Filepath to run" }),
19
+ handler: async ({ args, flags, root }) => {
20
+ await this.utils.ensureTsConfig(root);
21
+ await this.utils.exec(`tsx ${flags.watch ? "watch " : ""}${args}`);
22
+ },
23
+ });
24
+ }
@@ -0,0 +1,42 @@
1
+ import { $inject, t } from "alepha";
2
+ import { $command } from "alepha/command";
3
+ import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
4
+
5
+ export class TestCommand {
6
+ protected readonly utils = $inject(AlephaCliUtils);
7
+
8
+ public readonly test = $command({
9
+ name: "test",
10
+ description: "Run tests using Vitest",
11
+ flags: t.object({
12
+ config: t.optional(
13
+ t.string({
14
+ description: "Path to Vitest config file",
15
+ alias: "c",
16
+ }),
17
+ ),
18
+ }),
19
+ env: t.object({
20
+ VITEST_ARGS: t.optional(
21
+ t.string({
22
+ default: "",
23
+ description:
24
+ "Additional arguments to pass to Vitest. E.g., --coverage",
25
+ }),
26
+ ),
27
+ }),
28
+ handler: async ({ root, flags, env }) => {
29
+ await this.utils.ensureConfig(root, {
30
+ tsconfigJson: true,
31
+ viteConfigTs: true,
32
+ });
33
+
34
+ // Ensure vitest is installed before running
35
+ await this.utils.ensureDependency(root, "vitest");
36
+
37
+ const config = flags.config ? `--config=${flags.config}` : "";
38
+
39
+ await this.utils.exec(`vitest run ${config} ${env.VITEST_ARGS}`);
40
+ },
41
+ });
42
+ }
@@ -0,0 +1,19 @@
1
+ import { $inject } from "alepha";
2
+ import { $command } from "alepha/command";
3
+ import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
4
+
5
+ export class TypecheckCommand {
6
+ protected readonly utils = $inject(AlephaCliUtils);
7
+
8
+ /**
9
+ * Run TypeScript type checking across the codebase with no emit.
10
+ */
11
+ public readonly typecheck = $command({
12
+ name: "typecheck",
13
+ description: "Check TypeScript types across the codebase",
14
+ handler: async ({ root }) => {
15
+ await this.utils.ensureDependency(root, "typescript");
16
+ await this.utils.exec("tsc --noEmit");
17
+ },
18
+ });
19
+ }
@@ -2,7 +2,7 @@ import { $inject } from "alepha";
2
2
  import { $command } from "alepha/command";
3
3
  import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
4
4
 
5
- export class VerifyCommands {
5
+ export class VerifyCommand {
6
6
  protected readonly utils = $inject(AlephaCliUtils);
7
7
 
8
8
  /**
@@ -44,16 +44,4 @@ export class VerifyCommands {
44
44
  await run("alepha clean");
45
45
  },
46
46
  });
47
-
48
- /**
49
- * Run TypeScript type checking across the codebase with no emit.
50
- */
51
- public readonly typecheck = $command({
52
- name: "typecheck",
53
- description: "Check TypeScript types across the codebase",
54
- handler: async ({ root }) => {
55
- await this.utils.ensureDependency(root, "typescript");
56
- await this.utils.exec("tsc --noEmit");
57
- },
58
- });
59
47
  }
@@ -0,0 +1,24 @@
1
+ import type { Alepha } from "alepha";
2
+ import type { CommandPrimitive } from "alepha/command";
3
+
4
+ export type AlephaCliConfig = (alepha: Alepha) => {
5
+ commands?: Record<string, CommandPrimitive>;
6
+ services?: Array<any>;
7
+ };
8
+
9
+ export const defineConfig = (config: AlephaCliConfig) => {
10
+ return (alepha: Alepha) => {
11
+ const { commands, services = [] } = config(alepha);
12
+ for (const it of services) {
13
+ alepha.with(it);
14
+ }
15
+ return {
16
+ ...commands,
17
+ };
18
+ };
19
+ };
20
+
21
+ /**
22
+ * @alias defineConfig
23
+ */
24
+ export const defineAlephaConfig = defineConfig;
package/src/cli/index.ts CHANGED
@@ -1,9 +1,21 @@
1
1
  export * from "./apps/AlephaCli.ts";
2
2
  export * from "./apps/AlephaPackageBuilderCli.ts";
3
- export * from "./commands/BiomeCommands.ts";
4
- export * from "./commands/CoreCommands.ts";
5
- export * from "./commands/DrizzleCommands.ts";
6
- export * from "./commands/VerifyCommands.ts";
7
- export * from "./commands/ViteCommands.ts";
3
+ export * from "./atoms/changelogOptions.ts";
4
+ export * from "./commands/build.ts";
5
+ export * from "./commands/changelog.ts";
6
+ export * from "./commands/clean.ts";
7
+ export * from "./commands/db.ts";
8
+ export * from "./commands/deploy.ts";
9
+ export * from "./commands/dev.ts";
10
+ export * from "./commands/format.ts";
11
+ export * from "./commands/init.ts";
12
+ export * from "./commands/lint.ts";
13
+ export * from "./commands/root.ts";
14
+ export * from "./commands/run.ts";
15
+ export * from "./commands/test.ts";
16
+ export * from "./commands/typecheck.ts";
17
+ export * from "./commands/verify.ts";
18
+ export * from "./defineConfig.ts";
8
19
  export * from "./services/AlephaCliUtils.ts";
20
+ export * from "./services/GitMessageParser.ts";
9
21
  export * from "./version.ts";
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
2
2
  import { access, mkdir, readFile, writeFile } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
4
  import { $inject, Alepha, AlephaError } from "alepha";
5
- import type { RunnerMethod } from "alepha/command";
5
+ import { EnvUtils, type RunnerMethod } from "alepha/command";
6
6
  import { FileSystemProvider } from "alepha/file";
7
7
  import { $logger } from "alepha/logger";
8
8
  import { boot } from "alepha/vite";
@@ -31,6 +31,7 @@ import { version } from "../version.ts";
31
31
  export class AlephaCliUtils {
32
32
  protected readonly log = $logger();
33
33
  protected readonly fs = $inject(FileSystemProvider);
34
+ protected readonly envUtils = $inject(EnvUtils);
34
35
 
35
36
  /**
36
37
  * Execute a command using npx with inherited stdio.
@@ -499,29 +500,11 @@ ${models.map((it: string) => `export const ${it} = models["${it}"];`).join("\n")
499
500
  * Reads the .env file in the specified root directory and sets
500
501
  * the environment variables in process.env.
501
502
  */
502
- public async loadEnvFile(
503
+ public async loadEnv(
503
504
  root: string,
504
505
  files: string[] = [".env"],
505
506
  ): Promise<void> {
506
- for (const it of files) {
507
- for (const file of [it, `${it}.local`]) {
508
- const envPath = join(root, file);
509
- try {
510
- const envContent = await readFile(envPath, "utf8");
511
- const lines = envContent.split("\n");
512
- for (const line of lines) {
513
- const [key, ...rest] = line.split("=");
514
- if (key) {
515
- const value = rest.join("=");
516
- process.env[key.trim()] = value.trim();
517
- }
518
- }
519
- this.log.debug(`Loaded environment variables from ${envPath}`);
520
- } catch {
521
- this.log.debug(`No ${file} file found at ${envPath}, skipping load.`);
522
- }
523
- }
524
- }
507
+ await this.envUtils.loadEnv(root, files);
525
508
  }
526
509
 
527
510
  public async getPackageManager(
@@ -0,0 +1,77 @@
1
+ import { $logger } from "alepha/logger";
2
+ import {
3
+ type ChangelogOptions,
4
+ DEFAULT_IGNORE,
5
+ } from "../atoms/changelogOptions.ts";
6
+ import type { Commit } from "../commands/changelog.ts";
7
+
8
+ /**
9
+ * Service for parsing git commit messages into structured format.
10
+ *
11
+ * Only parses **conventional commits with a scope**:
12
+ * - `feat(scope): description` → feature
13
+ * - `fix(scope): description` → bug fix
14
+ * - `feat(scope)!: description` → breaking change
15
+ *
16
+ * Commits without scope are ignored, allowing developers to commit
17
+ * work-in-progress changes without polluting release notes:
18
+ * - `cli: work in progress` → ignored (no type)
19
+ * - `fix: quick patch` → ignored (no scope)
20
+ * - `feat(cli): add command` → included
21
+ */
22
+ export class GitMessageParser {
23
+ protected readonly log = $logger();
24
+
25
+ /**
26
+ * Parse a git commit line into a structured Commit object.
27
+ *
28
+ * **Format:** `type(scope): description` or `type(scope)!: description`
29
+ *
30
+ * **Supported types:** feat, fix, docs, refactor, perf, revert
31
+ *
32
+ * **Breaking changes:** Use `!` before `:` (e.g., `feat(api)!: remove endpoint`)
33
+ *
34
+ * @returns Commit object or null if not matching/ignored
35
+ */
36
+ parseCommit(line: string, config: ChangelogOptions): Commit | null {
37
+ // Extract hash and message from git log --oneline format
38
+ const match = line.match(/^([a-f0-9]+)\s+(.+)$/);
39
+ if (!match) return null;
40
+
41
+ const [, hash, message] = match;
42
+ const ignore = config.ignore ?? DEFAULT_IGNORE;
43
+
44
+ // Conventional commit with REQUIRED scope: type(scope): description
45
+ // The `!` before `:` marks a breaking change
46
+ const conventionalMatch = message.match(
47
+ /^(feat|fix|docs|refactor|perf|revert)\(([^)]+)\)(!)?:\s*(.+)$/i,
48
+ );
49
+
50
+ if (!conventionalMatch) {
51
+ // No match - commit doesn't follow required format
52
+ return null;
53
+ }
54
+
55
+ const [, type, scope, breakingMark, description] = conventionalMatch;
56
+
57
+ // Check if scope should be ignored
58
+ const baseScope = scope.split("/")[0];
59
+ if (ignore.includes(baseScope) || ignore.includes(scope)) {
60
+ return null;
61
+ }
62
+
63
+ // Breaking change detection:
64
+ // 1. Explicit `!` marker: feat(api)!: change
65
+ // 2. Word "breaking" in description: feat(api): breaking change to auth
66
+ const breaking =
67
+ breakingMark === "!" || description.toLowerCase().includes("breaking");
68
+
69
+ return {
70
+ hash: hash.substring(0, 8),
71
+ type: type.toLowerCase(),
72
+ scope,
73
+ description: description.trim(),
74
+ breaking,
75
+ };
76
+ }
77
+ }
@@ -0,0 +1,37 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { $logger } from "alepha/logger";
4
+
5
+ export class EnvUtils {
6
+ protected readonly log = $logger();
7
+
8
+ /**
9
+ * Load environment variables from .env files into process.env.
10
+ * By default, it loads from ".env" and ".env.local".
11
+ * You can specify additional files to load, e.g. [".env", ".env.production"].
12
+ */
13
+ public async loadEnv(
14
+ root: string,
15
+ files: string[] = [".env"],
16
+ ): Promise<void> {
17
+ for (const it of files) {
18
+ for (const file of [it, `${it}.local`]) {
19
+ const envPath = join(root, file);
20
+ try {
21
+ const envContent = await readFile(envPath, "utf8");
22
+ const lines = envContent.split("\n");
23
+ for (const line of lines) {
24
+ const [key, ...rest] = line.split("=");
25
+ if (key) {
26
+ const value = rest.join("=");
27
+ process.env[key.trim()] = value.trim();
28
+ }
29
+ }
30
+ this.log.debug(`Loaded environment variables from ${envPath}`);
31
+ } catch {
32
+ this.log.debug(`No ${file} file found at ${envPath}, skipping load.`);
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
@@ -1,5 +1,6 @@
1
1
  import { $module } from "alepha";
2
2
  import { Asker } from "./helpers/Asker.ts";
3
+ import { EnvUtils } from "./helpers/EnvUtils.ts";
3
4
  import { PrettyPrint } from "./helpers/PrettyPrint.ts";
4
5
  import { Runner } from "./helpers/Runner.ts";
5
6
  import { $command } from "./primitives/$command.ts";
@@ -9,6 +10,7 @@ import { CliProvider } from "./providers/CliProvider.ts";
9
10
 
10
11
  export * from "./errors/CommandError.ts";
11
12
  export * from "./helpers/Asker.ts";
13
+ export * from "./helpers/EnvUtils.ts";
12
14
  export * from "./helpers/PrettyPrint.ts";
13
15
  export * from "./helpers/Runner.ts";
14
16
  export * from "./primitives/$command.ts";
@@ -28,7 +30,7 @@ export * from "./providers/CliProvider.ts";
28
30
  export const AlephaCommand = $module({
29
31
  name: "alepha.command",
30
32
  primitives: [$command],
31
- services: [CliProvider, Runner, Asker, PrettyPrint],
33
+ services: [CliProvider, Runner, Asker, PrettyPrint, EnvUtils],
32
34
  });
33
35
 
34
36
  // ---------------------------------------------------------------------------------------------------------------------