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.
- package/README.md +3 -3
- package/dist/api/audits/index.d.ts +80 -1
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.d.ts +80 -1
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +236 -157
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/notifications/index.d.ts +21 -1
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/parameters/index.d.ts +451 -4
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/users/index.d.ts +252 -249
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +4 -0
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +128 -128
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cli/index.d.ts +304 -115
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +650 -531
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +210 -13
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +306 -69
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +7 -6
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +7 -6
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.js.map +1 -1
- package/dist/fake/index.js.map +1 -1
- package/dist/file/index.d.ts.map +1 -1
- package/dist/file/index.js.map +1 -1
- package/dist/lock/redis/index.js.map +1 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/index.browser.js +26 -5
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.d.ts +294 -215
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +522 -523
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/redis/index.js +2 -4
- package/dist/queue/redis/index.js.map +1 -1
- package/dist/redis/index.d.ts +400 -29
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js +412 -21
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.js.map +1 -1
- package/dist/router/index.js.map +1 -1
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +155 -155
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.js.map +1 -1
- package/dist/server/cookies/index.browser.js.map +1 -1
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.browser.js.map +1 -1
- package/dist/server/core/index.d.ts +0 -1
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/helmet/index.d.ts +4 -1
- package/dist/server/helmet/index.d.ts.map +1 -1
- package/dist/server/helmet/index.js.map +1 -1
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/multipart/index.d.ts.map +1 -1
- package/dist/server/multipart/index.js.map +1 -1
- package/dist/server/proxy/index.js.map +1 -1
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/security/index.d.ts +9 -9
- package/dist/server/security/index.js.map +1 -1
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/core/index.js.map +1 -1
- package/dist/topic/redis/index.js +3 -3
- package/dist/topic/redis/index.js.map +1 -1
- package/dist/vite/index.js +9 -6
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.d.ts +7 -7
- package/dist/websocket/index.js.map +1 -1
- package/package.json +3 -3
- package/src/api/users/index.ts +4 -0
- package/src/cli/apps/AlephaCli.ts +36 -14
- package/src/cli/apps/AlephaPackageBuilderCli.ts +5 -1
- package/src/cli/assets/appRouterTs.ts +1 -1
- package/src/cli/atoms/changelogOptions.ts +45 -0
- package/src/cli/commands/{ViteCommands.ts → build.ts} +4 -93
- package/src/cli/commands/changelog.ts +244 -0
- package/src/cli/commands/clean.ts +14 -0
- package/src/cli/commands/{DrizzleCommands.ts → db.ts} +37 -124
- package/src/cli/commands/deploy.ts +118 -0
- package/src/cli/commands/dev.ts +57 -0
- package/src/cli/commands/format.ts +17 -0
- package/src/cli/commands/{CoreCommands.ts → init.ts} +2 -40
- package/src/cli/commands/lint.ts +17 -0
- package/src/cli/commands/root.ts +32 -0
- package/src/cli/commands/run.ts +24 -0
- package/src/cli/commands/test.ts +42 -0
- package/src/cli/commands/typecheck.ts +19 -0
- package/src/cli/commands/{VerifyCommands.ts → verify.ts} +1 -13
- package/src/cli/defineConfig.ts +24 -0
- package/src/cli/index.ts +17 -5
- package/src/cli/services/AlephaCliUtils.ts +4 -21
- package/src/cli/services/GitMessageParser.ts +77 -0
- package/src/command/helpers/EnvUtils.ts +37 -0
- package/src/command/index.ts +3 -1
- package/src/command/primitives/$command.ts +172 -6
- package/src/command/providers/CliProvider.ts +424 -91
- package/src/core/Alepha.ts +8 -5
- package/src/file/providers/NodeFileSystemProvider.ts +3 -1
- package/src/orm/index.browser.ts +1 -1
- package/src/orm/index.ts +18 -10
- package/src/orm/interfaces/PgQueryWhere.ts +1 -26
- package/src/orm/providers/{PostgresTypeProvider.ts → DatabaseTypeProvider.ts} +25 -3
- package/src/orm/providers/drivers/BunPostgresProvider.ts +225 -0
- package/src/orm/providers/drivers/BunSqliteProvider.ts +180 -0
- package/src/orm/providers/drivers/DatabaseProvider.ts +25 -0
- package/src/orm/providers/drivers/NodePostgresProvider.ts +0 -25
- package/src/orm/services/QueryManager.ts +10 -125
- package/src/queue/redis/providers/RedisQueueProvider.ts +2 -7
- package/src/redis/index.ts +65 -3
- package/src/redis/providers/BunRedisProvider.ts +304 -0
- package/src/redis/providers/BunRedisSubscriberProvider.ts +94 -0
- package/src/redis/providers/NodeRedisProvider.ts +280 -0
- package/src/redis/providers/NodeRedisSubscriberProvider.ts +94 -0
- package/src/redis/providers/RedisProvider.ts +134 -140
- package/src/redis/providers/RedisSubscriberProvider.ts +58 -49
- package/src/server/core/providers/BunHttpServerProvider.ts +0 -3
- package/src/server/core/providers/ServerBodyParserProvider.ts +3 -1
- package/src/server/core/providers/ServerProvider.ts +7 -4
- package/src/server/multipart/providers/ServerMultipartProvider.ts +3 -1
- package/src/server/proxy/providers/ServerProxyProvider.ts +1 -1
- package/src/topic/redis/providers/RedisTopicProvider.ts +3 -3
- package/src/vite/tasks/buildServer.ts +1 -0
- package/src/cli/commands/BiomeCommands.ts +0 -29
- package/src/cli/commands/ChangelogCommands.ts +0 -389
- 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
|
|
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
|
|
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
|
|
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 "./
|
|
4
|
-
export * from "./commands/
|
|
5
|
-
export * from "./commands/
|
|
6
|
-
export * from "./commands/
|
|
7
|
-
export * from "./commands/
|
|
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
|
|
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
|
|
503
|
+
public async loadEnv(
|
|
503
504
|
root: string,
|
|
504
505
|
files: string[] = [".env"],
|
|
505
506
|
): Promise<void> {
|
|
506
|
-
|
|
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
|
+
}
|
package/src/command/index.ts
CHANGED
|
@@ -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
|
// ---------------------------------------------------------------------------------------------------------------------
|