alepha 0.13.8 → 0.14.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 (160) hide show
  1. package/dist/api/audits/index.d.ts +418 -338
  2. package/dist/api/audits/index.d.ts.map +1 -0
  3. package/dist/api/files/index.d.ts +81 -1
  4. package/dist/api/files/index.d.ts.map +1 -0
  5. package/dist/api/jobs/index.d.ts +107 -27
  6. package/dist/api/jobs/index.d.ts.map +1 -0
  7. package/dist/api/notifications/index.d.ts +21 -1
  8. package/dist/api/notifications/index.d.ts.map +1 -0
  9. package/dist/api/parameters/index.d.ts +455 -8
  10. package/dist/api/parameters/index.d.ts.map +1 -0
  11. package/dist/api/users/index.d.ts +844 -840
  12. package/dist/api/users/index.d.ts.map +1 -0
  13. package/dist/api/verifications/index.d.ts.map +1 -0
  14. package/dist/batch/index.d.ts.map +1 -0
  15. package/dist/bucket/index.d.ts.map +1 -0
  16. package/dist/cache/core/index.d.ts.map +1 -0
  17. package/dist/cache/redis/index.d.ts.map +1 -0
  18. package/dist/cli/index.d.ts +254 -59
  19. package/dist/cli/index.d.ts.map +1 -0
  20. package/dist/cli/index.js +499 -127
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/command/index.d.ts +217 -10
  23. package/dist/command/index.d.ts.map +1 -0
  24. package/dist/command/index.js +350 -74
  25. package/dist/command/index.js.map +1 -1
  26. package/dist/core/index.browser.js +1334 -1318
  27. package/dist/core/index.browser.js.map +1 -1
  28. package/dist/core/index.d.ts +76 -72
  29. package/dist/core/index.d.ts.map +1 -0
  30. package/dist/core/index.js +1337 -1321
  31. package/dist/core/index.js.map +1 -1
  32. package/dist/core/index.native.js +1337 -1321
  33. package/dist/core/index.native.js.map +1 -1
  34. package/dist/datetime/index.d.ts.map +1 -0
  35. package/dist/email/index.d.ts.map +1 -0
  36. package/dist/fake/index.d.ts.map +1 -0
  37. package/dist/file/index.d.ts.map +1 -0
  38. package/dist/file/index.js.map +1 -1
  39. package/dist/lock/core/index.d.ts.map +1 -0
  40. package/dist/lock/redis/index.d.ts.map +1 -0
  41. package/dist/logger/index.d.ts +1 -0
  42. package/dist/logger/index.d.ts.map +1 -0
  43. package/dist/mcp/index.d.ts +820 -0
  44. package/dist/mcp/index.d.ts.map +1 -0
  45. package/dist/mcp/index.js +978 -0
  46. package/dist/mcp/index.js.map +1 -0
  47. package/dist/orm/index.d.ts +234 -107
  48. package/dist/orm/index.d.ts.map +1 -0
  49. package/dist/orm/index.js +376 -316
  50. package/dist/orm/index.js.map +1 -1
  51. package/dist/queue/core/index.d.ts +4 -4
  52. package/dist/queue/core/index.d.ts.map +1 -0
  53. package/dist/queue/redis/index.d.ts.map +1 -0
  54. package/dist/queue/redis/index.js +2 -4
  55. package/dist/queue/redis/index.js.map +1 -1
  56. package/dist/redis/index.d.ts +400 -29
  57. package/dist/redis/index.d.ts.map +1 -0
  58. package/dist/redis/index.js +412 -21
  59. package/dist/redis/index.js.map +1 -1
  60. package/dist/retry/index.d.ts.map +1 -0
  61. package/dist/router/index.d.ts.map +1 -0
  62. package/dist/scheduler/index.d.ts +6 -6
  63. package/dist/scheduler/index.d.ts.map +1 -0
  64. package/dist/security/index.d.ts +28 -28
  65. package/dist/security/index.d.ts.map +1 -0
  66. package/dist/server/auth/index.d.ts +155 -155
  67. package/dist/server/auth/index.d.ts.map +1 -0
  68. package/dist/server/cache/index.d.ts.map +1 -0
  69. package/dist/server/compress/index.d.ts.map +1 -0
  70. package/dist/server/cookies/index.d.ts.map +1 -0
  71. package/dist/server/core/index.d.ts +0 -1
  72. package/dist/server/core/index.d.ts.map +1 -0
  73. package/dist/server/core/index.js.map +1 -1
  74. package/dist/server/cors/index.d.ts.map +1 -0
  75. package/dist/server/health/index.d.ts +17 -17
  76. package/dist/server/health/index.d.ts.map +1 -0
  77. package/dist/server/helmet/index.d.ts +4 -1
  78. package/dist/server/helmet/index.d.ts.map +1 -0
  79. package/dist/server/links/index.d.ts +33 -33
  80. package/dist/server/links/index.d.ts.map +1 -0
  81. package/dist/server/metrics/index.d.ts.map +1 -0
  82. package/dist/server/multipart/index.d.ts.map +1 -0
  83. package/dist/server/multipart/index.js.map +1 -1
  84. package/dist/server/proxy/index.d.ts.map +1 -0
  85. package/dist/server/proxy/index.js.map +1 -1
  86. package/dist/server/rate-limit/index.d.ts.map +1 -0
  87. package/dist/server/security/index.d.ts +9 -9
  88. package/dist/server/security/index.d.ts.map +1 -0
  89. package/dist/server/static/index.d.ts.map +1 -0
  90. package/dist/server/swagger/index.d.ts.map +1 -0
  91. package/dist/sms/index.d.ts.map +1 -0
  92. package/dist/thread/index.d.ts.map +1 -0
  93. package/dist/topic/core/index.d.ts.map +1 -0
  94. package/dist/topic/redis/index.d.ts.map +1 -0
  95. package/dist/topic/redis/index.js +3 -3
  96. package/dist/topic/redis/index.js.map +1 -1
  97. package/dist/vite/index.d.ts +10 -2
  98. package/dist/vite/index.d.ts.map +1 -0
  99. package/dist/vite/index.js +45 -20
  100. package/dist/vite/index.js.map +1 -1
  101. package/dist/websocket/index.d.ts.map +1 -0
  102. package/package.json +9 -4
  103. package/src/cli/apps/AlephaCli.ts +10 -3
  104. package/src/cli/apps/AlephaPackageBuilderCli.ts +15 -8
  105. package/src/cli/assets/mainTs.ts +9 -10
  106. package/src/cli/atoms/changelogOptions.ts +45 -0
  107. package/src/cli/commands/ChangelogCommands.ts +259 -0
  108. package/src/cli/commands/DeployCommands.ts +118 -0
  109. package/src/cli/commands/DrizzleCommands.ts +230 -10
  110. package/src/cli/commands/ViteCommands.ts +47 -23
  111. package/src/cli/defineConfig.ts +15 -0
  112. package/src/cli/index.ts +3 -0
  113. package/src/cli/services/AlephaCliUtils.ts +10 -154
  114. package/src/cli/services/GitMessageParser.ts +77 -0
  115. package/src/command/helpers/EnvUtils.ts +37 -0
  116. package/src/command/index.ts +3 -1
  117. package/src/command/primitives/$command.ts +172 -6
  118. package/src/command/providers/CliProvider.ts +499 -95
  119. package/src/core/Alepha.ts +1 -1
  120. package/src/core/providers/SchemaValidator.ts +23 -1
  121. package/src/file/providers/NodeFileSystemProvider.ts +3 -1
  122. package/src/mcp/errors/McpError.ts +72 -0
  123. package/src/mcp/helpers/jsonrpc.ts +163 -0
  124. package/src/mcp/index.ts +132 -0
  125. package/src/mcp/interfaces/McpTypes.ts +248 -0
  126. package/src/mcp/primitives/$prompt.ts +188 -0
  127. package/src/mcp/primitives/$resource.ts +171 -0
  128. package/src/mcp/primitives/$tool.ts +285 -0
  129. package/src/mcp/providers/McpServerProvider.ts +382 -0
  130. package/src/mcp/transports/SseMcpTransport.ts +172 -0
  131. package/src/mcp/transports/StdioMcpTransport.ts +126 -0
  132. package/src/orm/index.ts +20 -4
  133. package/src/orm/interfaces/PgQueryWhere.ts +1 -26
  134. package/src/orm/providers/drivers/BunPostgresProvider.ts +225 -0
  135. package/src/orm/providers/drivers/BunSqliteProvider.ts +180 -0
  136. package/src/orm/providers/drivers/CloudflareD1Provider.ts +164 -0
  137. package/src/orm/providers/drivers/DatabaseProvider.ts +25 -0
  138. package/src/orm/providers/drivers/NodePostgresProvider.ts +0 -25
  139. package/src/orm/providers/drivers/NodeSqliteProvider.ts +3 -1
  140. package/src/orm/services/QueryManager.ts +10 -125
  141. package/src/queue/redis/providers/RedisQueueProvider.ts +2 -7
  142. package/src/redis/index.ts +65 -3
  143. package/src/redis/providers/BunRedisProvider.ts +304 -0
  144. package/src/redis/providers/BunRedisSubscriberProvider.ts +94 -0
  145. package/src/redis/providers/NodeRedisProvider.ts +280 -0
  146. package/src/redis/providers/NodeRedisSubscriberProvider.ts +94 -0
  147. package/src/redis/providers/RedisProvider.ts +134 -140
  148. package/src/redis/providers/RedisSubscriberProvider.ts +58 -49
  149. package/src/server/core/providers/BunHttpServerProvider.ts +0 -3
  150. package/src/server/core/providers/ServerBodyParserProvider.ts +3 -1
  151. package/src/server/core/providers/ServerProvider.ts +7 -4
  152. package/src/server/multipart/providers/ServerMultipartProvider.ts +3 -1
  153. package/src/server/proxy/providers/ServerProxyProvider.ts +1 -1
  154. package/src/topic/redis/providers/RedisTopicProvider.ts +3 -3
  155. package/src/vite/plugins/viteAlephaBuild.ts +8 -2
  156. package/src/vite/plugins/viteAlephaDev.ts +6 -2
  157. package/src/vite/tasks/buildServer.ts +2 -1
  158. package/src/vite/tasks/generateCloudflare.ts +43 -15
  159. package/src/vite/tasks/runAlepha.ts +1 -0
  160. package/src/orm/services/PgJsonQueryManager.ts +0 -511
@@ -2,7 +2,9 @@ import { join } from "node:path";
2
2
  import { $hook, $inject, $module, Alepha } from "alepha";
3
3
  import { FileSystemProvider } from "alepha/file";
4
4
  import { BiomeCommands } from "../commands/BiomeCommands.ts";
5
+ import { ChangelogCommands } from "../commands/ChangelogCommands.ts";
5
6
  import { CoreCommands } from "../commands/CoreCommands.ts";
7
+ import { DeployCommands } from "../commands/DeployCommands.ts";
6
8
  import { DrizzleCommands } from "../commands/DrizzleCommands.ts";
7
9
  import { VerifyCommands } from "../commands/VerifyCommands.ts";
8
10
  import { ViteCommands } from "../commands/ViteCommands.ts";
@@ -14,7 +16,8 @@ class AlephaCliExtension {
14
16
  protected readonly onConfigure = $hook({
15
17
  on: "configure",
16
18
  handler: async () => {
17
- const extensionPath = join(process.cwd(), "alepha.config.ts");
19
+ const root = process.cwd();
20
+ const extensionPath = join(root, "alepha.config.ts");
18
21
  const hasExtension = await this.fs.exists(extensionPath);
19
22
  if (!hasExtension) {
20
23
  return;
@@ -26,7 +29,9 @@ class AlephaCliExtension {
26
29
  return;
27
30
  }
28
31
 
29
- this.alepha.with(Extension);
32
+ this.alepha.inject(Extension, {
33
+ args: [this.alepha],
34
+ });
30
35
  },
31
36
  });
32
37
  }
@@ -35,10 +40,12 @@ export const AlephaCli = $module({
35
40
  name: "alepha.cli",
36
41
  services: [
37
42
  AlephaCliExtension,
43
+ BiomeCommands,
44
+ ChangelogCommands,
38
45
  CoreCommands,
46
+ DeployCommands,
39
47
  DrizzleCommands,
40
48
  VerifyCommands,
41
49
  ViteCommands,
42
- BiomeCommands,
43
50
  ],
44
51
  });
@@ -79,14 +79,17 @@ export class AlephaPackageBuilderCli {
79
79
  JSON.stringify(modules, null, 2),
80
80
  );
81
81
 
82
- const external = [
83
- "alepha",
84
- packageName,
85
- ...modules.map(
86
- (item) =>
87
- `${packageName}/${item.name.replace("-", "/").replace("/core", "")}`,
88
- ),
89
- ];
82
+ const tsconfig = await readFile(
83
+ join(root, "../../tsconfig.json"),
84
+ "utf-8",
85
+ );
86
+
87
+ const external: string[] = Object.keys(
88
+ JSON.parse(tsconfig).compilerOptions.paths,
89
+ );
90
+
91
+ external.push("bun");
92
+ external.push("bun:sqlite");
90
93
 
91
94
  await run.rm(this.dist);
92
95
 
@@ -103,6 +106,10 @@ export class AlephaPackageBuilderCli {
103
106
  fixedExtension: false,
104
107
  platform: "node", // TODO: node must be enabled only if index.node.ts exists
105
108
  external,
109
+ dts: {
110
+ sourcemap: true,
111
+ resolve: false,
112
+ },
106
113
  });
107
114
 
108
115
  if (item.native) {
@@ -1,14 +1,13 @@
1
1
  export const mainTs = () => `
2
- import { Alepha, run } from "alepha";
3
- import { $logger } from "alepha/logger";
2
+ import { run } from "alepha";
3
+ import { $route } from "alepha/server";
4
4
 
5
- const alepha = Alepha.create();
5
+ class App {
6
+ root = $route({
7
+ path: "/",
8
+ handler: () => "Hello, Alepha!",
9
+ });
10
+ }
6
11
 
7
- alepha.with(() => {
8
- const log = $logger();
9
-
10
- log.info("Hello from Alepha!");
11
- });
12
-
13
- run(alepha);
12
+ run(App);
14
13
  `.trim();
@@ -0,0 +1,45 @@
1
+ import { $atom, type Static, t } from "alepha";
2
+
3
+ /**
4
+ * Default scopes to ignore in changelog generation.
5
+ * Commits with these scopes won't appear in release notes.
6
+ */
7
+ export const DEFAULT_IGNORE = [
8
+ "project",
9
+ "release",
10
+ "starter",
11
+ "example",
12
+ "chore",
13
+ "ci",
14
+ "build",
15
+ "test",
16
+ "style",
17
+ ];
18
+
19
+ /**
20
+ * Changelog configuration atom.
21
+ *
22
+ * Configure in `alepha.config.ts`:
23
+ * ```ts
24
+ * import { changelogOptions } from "alepha/cli";
25
+ *
26
+ * alepha.set(changelogOptions, {
27
+ * ignore: ["project", "release", "chore", "docs"],
28
+ * });
29
+ * ```
30
+ */
31
+ export const changelogOptions = $atom({
32
+ name: "alepha.changelog",
33
+ schema: t.object({
34
+ /**
35
+ * Scopes to ignore (e.g., "project", "release", "chore").
36
+ * Commits like `feat(chore): ...` will be excluded from changelog.
37
+ */
38
+ ignore: t.optional(t.array(t.string())),
39
+ }),
40
+ default: {
41
+ ignore: DEFAULT_IGNORE,
42
+ },
43
+ });
44
+
45
+ export type ChangelogOptions = Static<typeof changelogOptions.schema>;
@@ -0,0 +1,259 @@
1
+ import { exec } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import { $inject, $use, t } from "alepha";
4
+ import { $command } from "alepha/command";
5
+ import { $logger } from "alepha/logger";
6
+ import { changelogOptions } from "../atoms/changelogOptions.ts";
7
+ import { GitMessageParser } from "../services/GitMessageParser.ts";
8
+
9
+ export {
10
+ type ChangelogOptions,
11
+ changelogOptions,
12
+ DEFAULT_IGNORE,
13
+ } from "../atoms/changelogOptions.ts";
14
+ export { GitMessageParser } from "../services/GitMessageParser.ts";
15
+
16
+ const execAsync = promisify(exec);
17
+
18
+ // =============================================================================
19
+ // GIT PROVIDER
20
+ // =============================================================================
21
+
22
+ /**
23
+ * Git provider for executing git commands.
24
+ * Can be substituted in tests with a mock implementation.
25
+ */
26
+ export class GitProvider {
27
+ async exec(cmd: string, cwd: string): Promise<string> {
28
+ const { stdout } = await execAsync(`git ${cmd}`, { cwd });
29
+ return stdout;
30
+ }
31
+ }
32
+
33
+ // =============================================================================
34
+ // TYPES
35
+ // =============================================================================
36
+
37
+ export interface Commit {
38
+ hash: string;
39
+ type: string;
40
+ scope: string | null;
41
+ description: string;
42
+ breaking: boolean;
43
+ }
44
+
45
+ interface ChangelogEntry {
46
+ features: Commit[];
47
+ fixes: Commit[];
48
+ breaking: Commit[];
49
+ }
50
+
51
+ // =============================================================================
52
+ // CHANGELOG COMMANDS
53
+ // =============================================================================
54
+
55
+ /**
56
+ * Changelog command for generating release notes from git commits.
57
+ *
58
+ * Usage:
59
+ * - `alepha changelog` - Show unreleased changes since latest tag to HEAD
60
+ * - `alepha changelog --from=1.0.0` - Show changes from version to HEAD
61
+ * - `alepha changelog --from=1.0.0 --to=1.1.0` - Show changes between two refs
62
+ * - `alepha changelog | tee -a CHANGELOG.md` - Append to file
63
+ */
64
+ export class ChangelogCommands {
65
+ protected readonly log = $logger();
66
+ protected readonly git = $inject(GitProvider);
67
+ protected readonly parser = $inject(GitMessageParser);
68
+ protected readonly config = $use(changelogOptions);
69
+
70
+ // ---------------------------------------------------------------------------
71
+ // FORMATTING
72
+ // ---------------------------------------------------------------------------
73
+
74
+ /**
75
+ * Format a single commit line.
76
+ * Example: `- **cli**: add new command (\`abc1234\`)`
77
+ */
78
+ protected formatCommit(commit: Commit): string {
79
+ return `- **${commit.scope}**: ${commit.description} (\`${commit.hash}\`)`;
80
+ }
81
+
82
+ /**
83
+ * Format the changelog entry with sections.
84
+ */
85
+ protected formatEntry(entry: ChangelogEntry): string {
86
+ const sections: string[] = [];
87
+
88
+ if (entry.breaking.length > 0) {
89
+ sections.push("### Breaking Changes\n");
90
+ for (const commit of entry.breaking) {
91
+ sections.push(this.formatCommit(commit));
92
+ }
93
+ sections.push("");
94
+ }
95
+
96
+ if (entry.features.length > 0) {
97
+ sections.push("### Features\n");
98
+ for (const commit of entry.features) {
99
+ sections.push(this.formatCommit(commit));
100
+ }
101
+ sections.push("");
102
+ }
103
+
104
+ if (entry.fixes.length > 0) {
105
+ sections.push("### Bug Fixes\n");
106
+ for (const commit of entry.fixes) {
107
+ sections.push(this.formatCommit(commit));
108
+ }
109
+ sections.push("");
110
+ }
111
+
112
+ return sections.join("\n");
113
+ }
114
+
115
+ // ---------------------------------------------------------------------------
116
+ // PARSING
117
+ // ---------------------------------------------------------------------------
118
+
119
+ /**
120
+ * Parse git log output into a changelog entry.
121
+ */
122
+ protected parseCommits(commitsOutput: string): ChangelogEntry {
123
+ const entry: ChangelogEntry = {
124
+ features: [],
125
+ fixes: [],
126
+ breaking: [],
127
+ };
128
+
129
+ for (const line of commitsOutput.trim().split("\n")) {
130
+ if (!line.trim()) continue;
131
+
132
+ const commit = this.parser.parseCommit(line, this.config);
133
+ if (!commit) {
134
+ this.log.trace("Skipping commit", { line });
135
+ continue;
136
+ }
137
+
138
+ this.log.trace("Parsed commit", { commit });
139
+
140
+ // Categorize commit
141
+ if (commit.breaking) {
142
+ entry.breaking.push(commit);
143
+ }
144
+ if (commit.type === "feat") {
145
+ entry.features.push(commit);
146
+ } else if (commit.type === "fix") {
147
+ entry.fixes.push(commit);
148
+ }
149
+ }
150
+
151
+ return entry;
152
+ }
153
+
154
+ /**
155
+ * Check if entry has any public commits.
156
+ */
157
+ protected hasChanges(entry: ChangelogEntry): boolean {
158
+ return (
159
+ entry.features.length > 0 ||
160
+ entry.fixes.length > 0 ||
161
+ entry.breaking.length > 0
162
+ );
163
+ }
164
+
165
+ /**
166
+ * Get the latest version tag.
167
+ */
168
+ protected async getLatestTag(
169
+ git: (cmd: string) => Promise<string>,
170
+ ): Promise<string | null> {
171
+ const tagsOutput = await git("tag --sort=-version:refname");
172
+ const tags = tagsOutput
173
+ .trim()
174
+ .split("\n")
175
+ .filter((tag) => tag.match(/^\d+\.\d+\.\d+$/));
176
+
177
+ return tags[0] || null;
178
+ }
179
+
180
+ // ---------------------------------------------------------------------------
181
+ // COMMAND
182
+ // ---------------------------------------------------------------------------
183
+
184
+ public readonly changelog = $command({
185
+ name: "changelog",
186
+ description:
187
+ "Generate changelog from conventional commits (outputs to stdout)",
188
+ flags: t.object({
189
+ /**
190
+ * Show changes from this ref (tag, commit, branch).
191
+ * Defaults to the latest version tag.
192
+ * Example: --from=1.0.0
193
+ */
194
+ from: t.optional(
195
+ t.string({
196
+ aliases: ["f"],
197
+ description: "Starting ref (default: latest tag)",
198
+ }),
199
+ ),
200
+ /**
201
+ * Show changes up to this ref (tag, commit, branch).
202
+ * Defaults to HEAD.
203
+ * Example: --to=main
204
+ */
205
+ to: t.optional(
206
+ t.string({
207
+ aliases: ["t"],
208
+ description: "Ending ref (default: HEAD)",
209
+ }),
210
+ ),
211
+ }),
212
+ handler: async ({ flags, root }) => {
213
+ const git = (cmd: string) => this.git.exec(cmd, root);
214
+
215
+ // Determine the starting point
216
+ let fromRef: string;
217
+
218
+ if (flags.from) {
219
+ // User specified a ref
220
+ fromRef = flags.from;
221
+ this.log.debug("Using specified from ref", { from: fromRef });
222
+ } else {
223
+ // Use latest tag
224
+ const latestTag = await this.getLatestTag(git);
225
+ if (!latestTag) {
226
+ process.stdout.write("No version tags found in repository\n");
227
+ return;
228
+ }
229
+ fromRef = latestTag;
230
+ this.log.debug("Using latest tag", { from: fromRef });
231
+ }
232
+
233
+ // Determine the ending point
234
+ const toRef = flags.to || "HEAD";
235
+ this.log.debug("Using to ref", { to: toRef });
236
+
237
+ // Get commits in range
238
+ const commitsOutput = await git(`log ${fromRef}..${toRef} --oneline`);
239
+
240
+ if (!commitsOutput.trim()) {
241
+ process.stdout.write(`No changes in range ${fromRef}..${toRef}\n`);
242
+ return;
243
+ }
244
+
245
+ // Parse and format
246
+ const entry = this.parseCommits(commitsOutput);
247
+
248
+ if (!this.hasChanges(entry)) {
249
+ process.stdout.write(
250
+ `No public changes in range ${fromRef}..${toRef}\n`,
251
+ );
252
+ return;
253
+ }
254
+
255
+ // Output the formatted changelog (no header - caller adds it if needed)
256
+ process.stdout.write(this.formatEntry(entry));
257
+ },
258
+ });
259
+ }
@@ -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 DeployCommands {
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
+ }