alepha 0.13.8 → 0.14.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.
- package/dist/api/audits/index.d.ts +2 -1
- package/dist/api/audits/index.d.ts.map +1 -0
- package/dist/api/files/index.d.ts +2 -1
- package/dist/api/files/index.d.ts.map +1 -0
- package/dist/api/jobs/index.d.ts +158 -157
- package/dist/api/jobs/index.d.ts.map +1 -0
- package/dist/api/notifications/index.d.ts.map +1 -0
- package/dist/api/parameters/index.d.ts +4 -4
- package/dist/api/parameters/index.d.ts.map +1 -0
- package/dist/api/users/index.d.ts +132 -131
- package/dist/api/users/index.d.ts.map +1 -0
- package/dist/api/verifications/index.d.ts.map +1 -0
- package/dist/batch/index.d.ts.map +1 -0
- package/dist/bucket/index.d.ts.map +1 -0
- package/dist/cache/core/index.d.ts.map +1 -0
- package/dist/cache/redis/index.d.ts.map +1 -0
- package/dist/cli/index.d.ts +44 -32
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +380 -109
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +11 -1
- package/dist/command/index.d.ts.map +1 -0
- package/dist/command/index.js +45 -6
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +1334 -1318
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +75 -71
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +1337 -1321
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +1337 -1321
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts.map +1 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/fake/index.d.ts.map +1 -0
- package/dist/file/index.d.ts.map +1 -0
- package/dist/lock/core/index.d.ts.map +1 -0
- package/dist/lock/redis/index.d.ts.map +1 -0
- package/dist/logger/index.d.ts +1 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +820 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +978 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/orm/index.d.ts +180 -107
- package/dist/orm/index.d.ts.map +1 -0
- package/dist/orm/index.js +260 -174
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +4 -4
- package/dist/queue/core/index.d.ts.map +1 -0
- package/dist/queue/redis/index.d.ts.map +1 -0
- package/dist/redis/index.d.ts.map +1 -0
- package/dist/retry/index.d.ts.map +1 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/server/auth/index.d.ts +155 -155
- package/dist/server/auth/index.d.ts.map +1 -0
- package/dist/server/cache/index.d.ts.map +1 -0
- package/dist/server/compress/index.d.ts.map +1 -0
- package/dist/server/cookies/index.d.ts.map +1 -0
- package/dist/server/core/index.d.ts.map +1 -0
- package/dist/server/cors/index.d.ts.map +1 -0
- package/dist/server/health/index.d.ts.map +1 -0
- package/dist/server/helmet/index.d.ts.map +1 -0
- package/dist/server/links/index.d.ts +33 -33
- package/dist/server/links/index.d.ts.map +1 -0
- package/dist/server/metrics/index.d.ts.map +1 -0
- package/dist/server/multipart/index.d.ts.map +1 -0
- package/dist/server/proxy/index.d.ts.map +1 -0
- package/dist/server/rate-limit/index.d.ts.map +1 -0
- package/dist/server/security/index.d.ts +9 -9
- package/dist/server/security/index.d.ts.map +1 -0
- package/dist/server/static/index.d.ts.map +1 -0
- package/dist/server/swagger/index.d.ts.map +1 -0
- package/dist/sms/index.d.ts.map +1 -0
- package/dist/thread/index.d.ts.map +1 -0
- package/dist/topic/core/index.d.ts.map +1 -0
- package/dist/topic/redis/index.d.ts.map +1 -0
- package/dist/vite/index.d.ts +10 -2
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +36 -14
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.d.ts.map +1 -0
- package/package.json +9 -4
- package/src/cli/apps/AlephaCli.ts +2 -0
- package/src/cli/apps/AlephaPackageBuilderCli.ts +12 -8
- package/src/cli/assets/mainTs.ts +9 -10
- package/src/cli/commands/ChangelogCommands.ts +389 -0
- package/src/cli/commands/DrizzleCommands.ts +204 -4
- package/src/cli/commands/ViteCommands.ts +26 -16
- package/src/cli/services/AlephaCliUtils.ts +23 -150
- package/src/command/providers/CliProvider.ts +76 -5
- package/src/core/providers/SchemaValidator.ts +23 -1
- package/src/mcp/errors/McpError.ts +72 -0
- package/src/mcp/helpers/jsonrpc.ts +163 -0
- package/src/mcp/index.ts +132 -0
- package/src/mcp/interfaces/McpTypes.ts +248 -0
- package/src/mcp/primitives/$prompt.ts +188 -0
- package/src/mcp/primitives/$resource.ts +171 -0
- package/src/mcp/primitives/$tool.ts +285 -0
- package/src/mcp/providers/McpServerProvider.ts +382 -0
- package/src/mcp/transports/SseMcpTransport.ts +172 -0
- package/src/mcp/transports/StdioMcpTransport.ts +126 -0
- package/src/orm/index.ts +12 -0
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +164 -0
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +3 -1
- package/src/vite/plugins/viteAlephaBuild.ts +8 -2
- package/src/vite/plugins/viteAlephaDev.ts +6 -2
- package/src/vite/tasks/buildServer.ts +1 -1
- package/src/vite/tasks/generateCloudflare.ts +43 -15
- package/src/vite/tasks/runAlepha.ts +1 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { t } from "alepha";
|
|
6
|
+
import { $command } from "alepha/command";
|
|
7
|
+
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
|
|
10
|
+
interface Commit {
|
|
11
|
+
hash: string;
|
|
12
|
+
type: string;
|
|
13
|
+
scope: string | null;
|
|
14
|
+
description: string;
|
|
15
|
+
breaking: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface ChangelogEntry {
|
|
19
|
+
version: string;
|
|
20
|
+
date: string;
|
|
21
|
+
features: Commit[];
|
|
22
|
+
fixes: Commit[];
|
|
23
|
+
docs: Commit[];
|
|
24
|
+
improvements: Commit[];
|
|
25
|
+
breaking: Commit[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface ChangelogConfig {
|
|
29
|
+
/**
|
|
30
|
+
* Commit prefixes to ignore (e.g., "project", "release", "chore")
|
|
31
|
+
*/
|
|
32
|
+
ignore?: string[];
|
|
33
|
+
/**
|
|
34
|
+
* Module scopes to recognize (e.g., "ui", "cli", "core")
|
|
35
|
+
* If not provided, all non-ignored scopes are included
|
|
36
|
+
*/
|
|
37
|
+
scopes?: string[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const DEFAULT_IGNORE = [
|
|
41
|
+
"project",
|
|
42
|
+
"release",
|
|
43
|
+
"starter",
|
|
44
|
+
"example",
|
|
45
|
+
"chore",
|
|
46
|
+
"ci",
|
|
47
|
+
"build",
|
|
48
|
+
"test",
|
|
49
|
+
"style",
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
function parseCommit(line: string, config: ChangelogConfig): Commit | null {
|
|
53
|
+
const match = line.match(/^([a-f0-9]+)\s+(.+)$/);
|
|
54
|
+
if (!match) return null;
|
|
55
|
+
|
|
56
|
+
const [, hash, message] = match;
|
|
57
|
+
const breaking =
|
|
58
|
+
message.includes("!:") || message.toLowerCase().includes("breaking");
|
|
59
|
+
const ignore = config.ignore ?? DEFAULT_IGNORE;
|
|
60
|
+
|
|
61
|
+
// Conventional commit: feat(scope): description or feat!: description
|
|
62
|
+
const conventionalMatch = message.match(
|
|
63
|
+
/^(feat|fix|docs|refactor|perf|revert)(?:\(([^)]+)\))?!?:\s*(.+)$/i,
|
|
64
|
+
);
|
|
65
|
+
if (conventionalMatch) {
|
|
66
|
+
const [, type, scope, description] = conventionalMatch;
|
|
67
|
+
|
|
68
|
+
// Skip if scope matches ignore list
|
|
69
|
+
if (scope) {
|
|
70
|
+
const baseScope = scope.split("/")[0];
|
|
71
|
+
if (ignore.includes(baseScope) || ignore.includes(scope)) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Skip if type (without scope) matches ignore list (e.g., "docs" in ignore)
|
|
77
|
+
if (!scope && ignore.includes(type.toLowerCase())) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
hash: hash.substring(0, 8),
|
|
83
|
+
type: type.toLowerCase(),
|
|
84
|
+
scope: scope || null,
|
|
85
|
+
description: description.trim(),
|
|
86
|
+
breaking,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Module-specific commit: module: description or module/submodule: description
|
|
91
|
+
const moduleMatch = message.match(
|
|
92
|
+
/^([a-z][a-z0-9-]*(?:\/[a-z][a-z0-9-]*)?):\s*(.+)$/i,
|
|
93
|
+
);
|
|
94
|
+
if (moduleMatch) {
|
|
95
|
+
const [, module, description] = moduleMatch;
|
|
96
|
+
const baseModule = module.split("/")[0];
|
|
97
|
+
|
|
98
|
+
// Skip ignored prefixes
|
|
99
|
+
if (ignore.includes(baseModule)) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// If scopes are defined, check if this is a known scope
|
|
104
|
+
if (config.scopes && !config.scopes.includes(baseModule)) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Determine type based on description keywords
|
|
109
|
+
const desc = description.toLowerCase();
|
|
110
|
+
let type = "improve";
|
|
111
|
+
if (
|
|
112
|
+
desc.includes("fix") ||
|
|
113
|
+
desc.includes("bug") ||
|
|
114
|
+
desc.includes("issue")
|
|
115
|
+
) {
|
|
116
|
+
type = "fix";
|
|
117
|
+
} else if (
|
|
118
|
+
desc.includes("add") ||
|
|
119
|
+
desc.includes("new") ||
|
|
120
|
+
desc.includes("implement")
|
|
121
|
+
) {
|
|
122
|
+
type = "feat";
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
hash: hash.substring(0, 8),
|
|
127
|
+
type,
|
|
128
|
+
scope: module,
|
|
129
|
+
description: description.trim(),
|
|
130
|
+
breaking,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function formatCommit(commit: Commit): string {
|
|
138
|
+
const scope = commit.scope ? `**${commit.scope}**: ` : "";
|
|
139
|
+
return `- ${scope}${commit.description} (\`${commit.hash}\`)`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function generateChangelog(entries: ChangelogEntry[]): string {
|
|
143
|
+
let output = "# Changelog\n\n";
|
|
144
|
+
output +=
|
|
145
|
+
"All notable changes to this project will be documented in this file.\n\n";
|
|
146
|
+
|
|
147
|
+
for (const entry of entries) {
|
|
148
|
+
output += `## [${entry.version}] - ${entry.date}\n\n`;
|
|
149
|
+
output += formatEntry(entry);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return output;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function formatEntry(entry: ChangelogEntry): string {
|
|
156
|
+
let output = "";
|
|
157
|
+
|
|
158
|
+
if (entry.breaking.length > 0) {
|
|
159
|
+
output += "### Breaking Changes\n\n";
|
|
160
|
+
for (const commit of entry.breaking) {
|
|
161
|
+
output += `${formatCommit(commit)}\n`;
|
|
162
|
+
}
|
|
163
|
+
output += "\n";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (entry.features.length > 0) {
|
|
167
|
+
output += "### Features\n\n";
|
|
168
|
+
for (const commit of entry.features) {
|
|
169
|
+
output += `${formatCommit(commit)}\n`;
|
|
170
|
+
}
|
|
171
|
+
output += "\n";
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (entry.fixes.length > 0) {
|
|
175
|
+
output += "### Bug Fixes\n\n";
|
|
176
|
+
for (const commit of entry.fixes) {
|
|
177
|
+
output += `${formatCommit(commit)}\n`;
|
|
178
|
+
}
|
|
179
|
+
output += "\n";
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return output;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function loadConfig(root: string): Promise<ChangelogConfig> {
|
|
186
|
+
try {
|
|
187
|
+
const pkgPath = join(root, "package.json");
|
|
188
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
|
|
189
|
+
return pkg.changelog ?? {};
|
|
190
|
+
} catch {
|
|
191
|
+
return {};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export class ChangelogCommands {
|
|
196
|
+
public readonly changelog = $command({
|
|
197
|
+
name: "changelog",
|
|
198
|
+
description: "Generate CHANGELOG.md from git commits",
|
|
199
|
+
flags: t.object({
|
|
200
|
+
release: t.optional(
|
|
201
|
+
t.boolean({
|
|
202
|
+
when: ["--release", "-r"],
|
|
203
|
+
description:
|
|
204
|
+
"Output release notes for the latest version only (for GitHub Release)",
|
|
205
|
+
}),
|
|
206
|
+
),
|
|
207
|
+
preview: t.optional(
|
|
208
|
+
t.boolean({
|
|
209
|
+
when: ["--preview", "-p"],
|
|
210
|
+
description: "Preview unreleased changes (commits since last tag)",
|
|
211
|
+
}),
|
|
212
|
+
),
|
|
213
|
+
output: t.optional(
|
|
214
|
+
t.string({
|
|
215
|
+
when: ["--output", "-o"],
|
|
216
|
+
description:
|
|
217
|
+
"Output file path (defaults to CHANGELOG.md, use - for stdout)",
|
|
218
|
+
}),
|
|
219
|
+
),
|
|
220
|
+
limit: t.optional(
|
|
221
|
+
t.number({
|
|
222
|
+
when: ["--limit", "-l"],
|
|
223
|
+
description: "Limit number of versions to include",
|
|
224
|
+
}),
|
|
225
|
+
),
|
|
226
|
+
}),
|
|
227
|
+
handler: async ({ flags, run, root }) => {
|
|
228
|
+
const config = await loadConfig(root);
|
|
229
|
+
|
|
230
|
+
const git = async (cmd: string) => {
|
|
231
|
+
const { stdout } = await execAsync(`git ${cmd}`, { cwd: root });
|
|
232
|
+
return stdout;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// Get all tags sorted by version
|
|
236
|
+
const tagsOutput = await git("tag --sort=-version:refname");
|
|
237
|
+
const tags = tagsOutput
|
|
238
|
+
.trim()
|
|
239
|
+
.split("\n")
|
|
240
|
+
.filter((tag) => tag.match(/^\d+\.\d+\.\d+$/));
|
|
241
|
+
|
|
242
|
+
if (tags.length === 0) {
|
|
243
|
+
throw new Error("No version tags found");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Preview mode: show unreleased changes
|
|
247
|
+
if (flags.preview) {
|
|
248
|
+
const latestTag = tags[0];
|
|
249
|
+
const commitsOutput = await git(`log ${latestTag}..HEAD --oneline`);
|
|
250
|
+
|
|
251
|
+
if (!commitsOutput.trim()) {
|
|
252
|
+
console.log("No unreleased changes since", latestTag);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const entry: ChangelogEntry = {
|
|
257
|
+
version: "Unreleased",
|
|
258
|
+
date: new Date().toISOString().split("T")[0],
|
|
259
|
+
features: [],
|
|
260
|
+
fixes: [],
|
|
261
|
+
docs: [],
|
|
262
|
+
improvements: [],
|
|
263
|
+
breaking: [],
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
for (const line of commitsOutput.trim().split("\n")) {
|
|
267
|
+
if (!line.trim()) continue;
|
|
268
|
+
const commit = parseCommit(line, config);
|
|
269
|
+
if (!commit) continue;
|
|
270
|
+
|
|
271
|
+
if (commit.breaking) entry.breaking.push(commit);
|
|
272
|
+
|
|
273
|
+
switch (commit.type) {
|
|
274
|
+
case "feat":
|
|
275
|
+
entry.features.push(commit);
|
|
276
|
+
break;
|
|
277
|
+
case "fix":
|
|
278
|
+
entry.fixes.push(commit);
|
|
279
|
+
break;
|
|
280
|
+
case "docs":
|
|
281
|
+
entry.docs.push(commit);
|
|
282
|
+
break;
|
|
283
|
+
case "refactor":
|
|
284
|
+
case "perf":
|
|
285
|
+
case "improve":
|
|
286
|
+
entry.improvements.push(commit);
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const hasCommits =
|
|
292
|
+
entry.features.length > 0 ||
|
|
293
|
+
entry.fixes.length > 0 ||
|
|
294
|
+
entry.breaking.length > 0;
|
|
295
|
+
|
|
296
|
+
if (!hasCommits) {
|
|
297
|
+
console.log("No public changes since", latestTag);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
console.log(`## [Unreleased] - since ${latestTag}\n`);
|
|
302
|
+
console.log(formatEntry(entry));
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const entries: ChangelogEntry[] = [];
|
|
307
|
+
const limit = flags.limit || (flags.release ? 1 : tags.length);
|
|
308
|
+
|
|
309
|
+
for (let i = 0; i < Math.min(limit, tags.length); i++) {
|
|
310
|
+
const tag = tags[i];
|
|
311
|
+
const prevTag = tags[i + 1];
|
|
312
|
+
|
|
313
|
+
// Get tag date
|
|
314
|
+
const dateOutput = await git(`log -1 --format=%ci ${tag}`);
|
|
315
|
+
const date = dateOutput.trim().split(" ")[0];
|
|
316
|
+
|
|
317
|
+
// Get commits between tags
|
|
318
|
+
const range = prevTag ? `${prevTag}..${tag}` : tag;
|
|
319
|
+
const commitsOutput = await git(`log ${range} --oneline`);
|
|
320
|
+
|
|
321
|
+
const entry: ChangelogEntry = {
|
|
322
|
+
version: tag,
|
|
323
|
+
date,
|
|
324
|
+
features: [],
|
|
325
|
+
fixes: [],
|
|
326
|
+
docs: [],
|
|
327
|
+
improvements: [],
|
|
328
|
+
breaking: [],
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
for (const line of commitsOutput.trim().split("\n")) {
|
|
332
|
+
if (!line.trim()) continue;
|
|
333
|
+
const commit = parseCommit(line, config);
|
|
334
|
+
if (!commit) continue;
|
|
335
|
+
|
|
336
|
+
if (commit.breaking) entry.breaking.push(commit);
|
|
337
|
+
|
|
338
|
+
switch (commit.type) {
|
|
339
|
+
case "feat":
|
|
340
|
+
entry.features.push(commit);
|
|
341
|
+
break;
|
|
342
|
+
case "fix":
|
|
343
|
+
entry.fixes.push(commit);
|
|
344
|
+
break;
|
|
345
|
+
case "docs":
|
|
346
|
+
entry.docs.push(commit);
|
|
347
|
+
break;
|
|
348
|
+
case "refactor":
|
|
349
|
+
case "perf":
|
|
350
|
+
case "improve":
|
|
351
|
+
entry.improvements.push(commit);
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Only add entry if it has any commits
|
|
357
|
+
const hasCommits =
|
|
358
|
+
entry.features.length > 0 ||
|
|
359
|
+
entry.fixes.length > 0 ||
|
|
360
|
+
entry.breaking.length > 0;
|
|
361
|
+
|
|
362
|
+
if (hasCommits) {
|
|
363
|
+
entries.push(entry);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (entries.length === 0) {
|
|
368
|
+
console.log("No public commits found");
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
let output: string;
|
|
373
|
+
if (flags.release) {
|
|
374
|
+
output = formatEntry(entries[0]);
|
|
375
|
+
} else {
|
|
376
|
+
output = generateChangelog(entries);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const outputPath = flags.output ?? "CHANGELOG.md";
|
|
380
|
+
if (outputPath === "-") {
|
|
381
|
+
console.log(output);
|
|
382
|
+
} else {
|
|
383
|
+
await run(`Writing ${outputPath}`, () =>
|
|
384
|
+
writeFile(join(root, outputPath), output, "utf8"),
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
});
|
|
389
|
+
}
|
|
@@ -13,6 +13,12 @@ const drizzleCommandFlags = t.object({
|
|
|
13
13
|
"Database provider name to target (e.g., 'postgres', 'sqlite')",
|
|
14
14
|
}),
|
|
15
15
|
),
|
|
16
|
+
mode: t.optional(
|
|
17
|
+
t.text({
|
|
18
|
+
description:
|
|
19
|
+
"Environment variable file(s) to load (e.g., 'production' to load .env.production) https://vite.dev/guide/env-and-mode",
|
|
20
|
+
}),
|
|
21
|
+
),
|
|
16
22
|
});
|
|
17
23
|
|
|
18
24
|
export class DrizzleCommands {
|
|
@@ -149,12 +155,14 @@ export class DrizzleCommands {
|
|
|
149
155
|
const commandFlags = flags.custom
|
|
150
156
|
? `--custom=${flags.custom}`
|
|
151
157
|
: undefined;
|
|
152
|
-
|
|
158
|
+
|
|
159
|
+
await this.runDrizzleKitCommand({
|
|
153
160
|
root,
|
|
154
161
|
args,
|
|
155
162
|
command: "generate",
|
|
156
163
|
commandFlags,
|
|
157
164
|
provider: flags.provider,
|
|
165
|
+
env: flags.mode,
|
|
158
166
|
logMessage: (providerName, dialect) =>
|
|
159
167
|
`Generate '${providerName}' migrations (${dialect}) ...`,
|
|
160
168
|
});
|
|
@@ -181,11 +189,12 @@ export class DrizzleCommands {
|
|
|
181
189
|
),
|
|
182
190
|
flags: drizzleCommandFlags,
|
|
183
191
|
handler: async ({ root, args, flags }) => {
|
|
184
|
-
await this.
|
|
192
|
+
await this.runDrizzleKitCommand({
|
|
185
193
|
root,
|
|
186
194
|
args,
|
|
187
195
|
command: "push",
|
|
188
196
|
provider: flags.provider,
|
|
197
|
+
env: flags.mode,
|
|
189
198
|
logMessage: (providerName, dialect) =>
|
|
190
199
|
`Push '${providerName}' schema (${dialect}) ...`,
|
|
191
200
|
});
|
|
@@ -212,11 +221,12 @@ export class DrizzleCommands {
|
|
|
212
221
|
),
|
|
213
222
|
flags: drizzleCommandFlags,
|
|
214
223
|
handler: async ({ root, args, flags }) => {
|
|
215
|
-
await this.
|
|
224
|
+
await this.runDrizzleKitCommand({
|
|
216
225
|
root,
|
|
217
226
|
args,
|
|
218
227
|
command: "migrate",
|
|
219
228
|
provider: flags.provider,
|
|
229
|
+
env: flags.mode,
|
|
220
230
|
logMessage: (providerName, dialect) =>
|
|
221
231
|
`Migrate '${providerName}' database (${dialect}) ...`,
|
|
222
232
|
});
|
|
@@ -243,17 +253,207 @@ export class DrizzleCommands {
|
|
|
243
253
|
),
|
|
244
254
|
flags: drizzleCommandFlags,
|
|
245
255
|
handler: async ({ root, args, flags }) => {
|
|
246
|
-
await this.
|
|
256
|
+
await this.runDrizzleKitCommand({
|
|
247
257
|
root,
|
|
248
258
|
args,
|
|
249
259
|
command: "studio",
|
|
250
260
|
provider: flags.provider,
|
|
261
|
+
env: flags.mode,
|
|
251
262
|
logMessage: (providerName, dialect) =>
|
|
252
263
|
`Launch Studio for '${providerName}' (${dialect}) ...`,
|
|
253
264
|
});
|
|
254
265
|
},
|
|
255
266
|
});
|
|
256
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Run a drizzle-kit command for all database providers in an Alepha instance.
|
|
270
|
+
*
|
|
271
|
+
* Iterates through all repository providers, prepares Drizzle config for each,
|
|
272
|
+
* and executes the specified drizzle-kit command.
|
|
273
|
+
*
|
|
274
|
+
* @param options - Configuration including command to run, flags, and logging
|
|
275
|
+
*/
|
|
276
|
+
public async runDrizzleKitCommand(options: {
|
|
277
|
+
root: string;
|
|
278
|
+
args?: string;
|
|
279
|
+
command: string;
|
|
280
|
+
commandFlags?: string;
|
|
281
|
+
provider?: string;
|
|
282
|
+
logMessage: (providerName: string, dialect: string) => string;
|
|
283
|
+
env?: string;
|
|
284
|
+
}): Promise<void> {
|
|
285
|
+
const rootDir = options.root;
|
|
286
|
+
|
|
287
|
+
const envFiles = [".env"];
|
|
288
|
+
if (options.env) {
|
|
289
|
+
envFiles.push(`.env.${options.env}`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
await this.utils.loadEnvFile(rootDir, envFiles);
|
|
293
|
+
|
|
294
|
+
this.log.debug(`Using project root: ${rootDir}`);
|
|
295
|
+
|
|
296
|
+
const { alepha, entry } = await this.utils.loadAlephaFromServerEntryFile(
|
|
297
|
+
rootDir,
|
|
298
|
+
options.args,
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
const drizzleKitProvider =
|
|
302
|
+
alepha.inject<DrizzleKitProvider>("DrizzleKitProvider");
|
|
303
|
+
const repositoryProvider =
|
|
304
|
+
alepha.inject<RepositoryProvider>("RepositoryProvider");
|
|
305
|
+
const accepted = new Set<string>([]);
|
|
306
|
+
|
|
307
|
+
for (const primitive of repositoryProvider.getRepositories()) {
|
|
308
|
+
const provider = primitive.provider;
|
|
309
|
+
const providerName = provider.name;
|
|
310
|
+
const dialect = provider.dialect;
|
|
311
|
+
|
|
312
|
+
if (accepted.has(providerName)) {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
accepted.add(providerName);
|
|
316
|
+
|
|
317
|
+
// Skip if provider filter is set and doesn't match
|
|
318
|
+
if (options.provider && options.provider !== providerName) {
|
|
319
|
+
this.log.debug(
|
|
320
|
+
`Skipping provider '${providerName}' (filter: ${options.provider})`,
|
|
321
|
+
);
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
this.log.info("");
|
|
326
|
+
this.log.info(options.logMessage(providerName, dialect));
|
|
327
|
+
|
|
328
|
+
const drizzleConfigJsPath = await this.prepareDrizzleConfig({
|
|
329
|
+
kit: drizzleKitProvider,
|
|
330
|
+
provider,
|
|
331
|
+
providerName,
|
|
332
|
+
providerUrl: provider.url,
|
|
333
|
+
dialect,
|
|
334
|
+
entry,
|
|
335
|
+
rootDir,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
const flags = options.commandFlags ? ` ${options.commandFlags}` : "";
|
|
339
|
+
await this.utils.exec(
|
|
340
|
+
`drizzle-kit ${options.command} --config=${drizzleConfigJsPath}${flags}`,
|
|
341
|
+
{
|
|
342
|
+
env: {
|
|
343
|
+
NODE_OPTIONS: "--import tsx",
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Prepare Drizzle configuration files for a database provider.
|
|
352
|
+
*
|
|
353
|
+
* Creates temporary entities.js and drizzle.config.js files needed
|
|
354
|
+
* for Drizzle Kit commands to run properly.
|
|
355
|
+
*
|
|
356
|
+
* @param options - Configuration options including kit, provider info, and paths
|
|
357
|
+
* @returns Path to the generated drizzle.config.js file
|
|
358
|
+
*/
|
|
359
|
+
public async prepareDrizzleConfig(options: {
|
|
360
|
+
kit: any;
|
|
361
|
+
provider: any;
|
|
362
|
+
providerName: string;
|
|
363
|
+
providerUrl: string;
|
|
364
|
+
dialect: string;
|
|
365
|
+
entry: string;
|
|
366
|
+
rootDir: string;
|
|
367
|
+
}): Promise<string> {
|
|
368
|
+
const models = Object.keys(options.kit.getModels(options.provider));
|
|
369
|
+
const entitiesJs = this.utils.generateEntitiesJs(
|
|
370
|
+
options.entry,
|
|
371
|
+
options.providerName,
|
|
372
|
+
models,
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
const entitiesJsPath = await this.utils.writeConfigFile(
|
|
376
|
+
"entities.js",
|
|
377
|
+
entitiesJs,
|
|
378
|
+
options.rootDir,
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
const config: Record<string, any> = {
|
|
382
|
+
schema: entitiesJsPath,
|
|
383
|
+
out: `./migrations/${options.providerName}`,
|
|
384
|
+
dialect: options.dialect,
|
|
385
|
+
dbCredentials: {
|
|
386
|
+
url: options.providerUrl,
|
|
387
|
+
},
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
if (options.providerName === "d1") {
|
|
391
|
+
config.driver = "d1-http";
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (options.providerName === "pglite") {
|
|
395
|
+
config.driver = "pglite";
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (options.dialect === "sqlite") {
|
|
399
|
+
if (options.providerName === "d1") {
|
|
400
|
+
const token = process.env.CLOUDFLARE_API_TOKEN;
|
|
401
|
+
if (!token) {
|
|
402
|
+
throw new AlephaError(
|
|
403
|
+
"CLOUDFLARE_API_TOKEN environment variable is not set. https://orm.drizzle.team/docs/guides/d1-http-with-drizzle-kit",
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const accountId = process.env.CLOUDFLARE_ACCOUNT_ID;
|
|
408
|
+
if (!accountId) {
|
|
409
|
+
throw new AlephaError(
|
|
410
|
+
"CLOUDFLARE_ACCOUNT_ID environment variable is not set. https://orm.drizzle.team/docs/guides/d1-http-with-drizzle-kit",
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const url = options.providerUrl;
|
|
415
|
+
if (!url.startsWith("cloudflare-d1://")) {
|
|
416
|
+
throw new AlephaError(
|
|
417
|
+
"D1 provider URL must start with 'cloudflare-d1://'.",
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const [, databaseId] = url
|
|
422
|
+
.replace("cloudflare-d1://", "")
|
|
423
|
+
.replace("cloudflare-d1:", "")
|
|
424
|
+
.split(":");
|
|
425
|
+
|
|
426
|
+
if (!databaseId) {
|
|
427
|
+
throw new AlephaError(
|
|
428
|
+
"Database ID is missing in the D1 provider URL. Cloudflare D1 URL format: cloudflare-d1://<database_name>:<database_id>",
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
config.dbCredentials = {
|
|
433
|
+
accountId,
|
|
434
|
+
databaseId,
|
|
435
|
+
token,
|
|
436
|
+
};
|
|
437
|
+
} else {
|
|
438
|
+
let url = options.providerUrl;
|
|
439
|
+
url = url.replace("sqlite://", "").replace("file://", "");
|
|
440
|
+
url = join(options.rootDir, url);
|
|
441
|
+
|
|
442
|
+
config.dbCredentials = {
|
|
443
|
+
url,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const drizzleConfigJs = `export default ${JSON.stringify(config, null, 2)}`;
|
|
449
|
+
|
|
450
|
+
return await this.utils.writeConfigFile(
|
|
451
|
+
"drizzle.config.js",
|
|
452
|
+
drizzleConfigJs,
|
|
453
|
+
options.rootDir,
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
|
|
257
457
|
// /**
|
|
258
458
|
// * Drop database schema (development only)
|
|
259
459
|
// *
|