alepha 0.13.4 → 0.13.5

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 (132) hide show
  1. package/dist/api-files/index.browser.js +5 -5
  2. package/dist/api-files/index.d.ts +191 -191
  3. package/dist/api-files/index.js +9 -9
  4. package/dist/api-jobs/index.browser.js +5 -5
  5. package/dist/api-jobs/index.d.ts +173 -173
  6. package/dist/api-jobs/index.js +9 -9
  7. package/dist/api-notifications/index.browser.js +11 -11
  8. package/dist/api-notifications/index.d.ts +11 -11
  9. package/dist/api-notifications/index.js +11 -11
  10. package/dist/api-parameters/index.browser.js +2 -2
  11. package/dist/api-parameters/index.d.ts +3 -3
  12. package/dist/api-parameters/index.js +3 -3
  13. package/dist/api-users/index.browser.js +6 -6
  14. package/dist/api-users/index.d.ts +55 -55
  15. package/dist/api-users/index.js +36 -36
  16. package/dist/api-verifications/index.browser.js +5 -5
  17. package/dist/api-verifications/index.d.ts +21 -21
  18. package/dist/api-verifications/index.js +10 -10
  19. package/dist/batch/index.d.ts +3 -3
  20. package/dist/batch/index.js +3 -3
  21. package/dist/bin/index.js +1 -1
  22. package/dist/bucket/index.d.ts +6 -6
  23. package/dist/bucket/index.js +7 -7
  24. package/dist/cache/index.d.ts +4 -4
  25. package/dist/cache/index.js +5 -5
  26. package/dist/cache-redis/index.d.ts +2 -2
  27. package/dist/cache-redis/index.js +2 -2
  28. package/dist/cli/{dist-lGnqsKpu.js → dist-BlfFtOk2.js} +16 -16
  29. package/dist/cli/{dist-lGnqsKpu.js.map → dist-BlfFtOk2.js.map} +1 -1
  30. package/dist/cli/index.d.ts +31 -75
  31. package/dist/cli/index.js +94 -171
  32. package/dist/cli/index.js.map +1 -1
  33. package/dist/command/index.d.ts +8 -8
  34. package/dist/command/index.js +11 -10
  35. package/dist/command/index.js.map +1 -1
  36. package/dist/core/index.browser.js +35 -35
  37. package/dist/core/index.d.ts +39 -39
  38. package/dist/core/index.js +35 -35
  39. package/dist/core/index.native.js +35 -35
  40. package/dist/datetime/index.d.ts +3 -3
  41. package/dist/datetime/index.js +3 -3
  42. package/dist/email/index.d.ts +11 -11
  43. package/dist/email/index.js +42 -42
  44. package/dist/fake/index.d.ts +2 -2
  45. package/dist/fake/index.js +5 -5
  46. package/dist/file/index.d.ts +4 -4
  47. package/dist/file/index.js +4 -4
  48. package/dist/lock/index.d.ts +5 -5
  49. package/dist/lock/index.js +5 -5
  50. package/dist/lock-redis/index.d.ts +2 -2
  51. package/dist/lock-redis/index.js +2 -2
  52. package/dist/logger/index.d.ts +11 -11
  53. package/dist/logger/index.js +12 -12
  54. package/dist/orm/index.browser.js +12 -12
  55. package/dist/orm/index.d.ts +52 -52
  56. package/dist/orm/index.js +32 -32
  57. package/dist/queue/index.d.ts +7 -7
  58. package/dist/queue/index.js +6 -6
  59. package/dist/queue-redis/index.d.ts +2 -2
  60. package/dist/queue-redis/index.js +2 -2
  61. package/dist/redis/index.d.ts +13 -13
  62. package/dist/redis/index.js +3 -3
  63. package/dist/retry/index.d.ts +5 -5
  64. package/dist/retry/index.js +5 -5
  65. package/dist/router/index.d.ts +1 -1
  66. package/dist/router/index.js +1 -1
  67. package/dist/scheduler/index.d.ts +4 -4
  68. package/dist/scheduler/index.js +7 -7
  69. package/dist/security/index.browser.js +7 -7
  70. package/dist/security/index.d.ts +15 -15
  71. package/dist/security/index.js +46 -46
  72. package/dist/server/index.browser.js +14 -14
  73. package/dist/server/index.d.ts +26 -26
  74. package/dist/server/index.js +26 -26
  75. package/dist/server-auth/index.browser.js +6 -6
  76. package/dist/server-auth/index.d.ts +163 -163
  77. package/dist/server-auth/index.js +13 -13
  78. package/dist/server-cache/index.d.ts +2 -2
  79. package/dist/server-cache/index.js +2 -2
  80. package/dist/server-compress/index.d.ts +2 -2
  81. package/dist/server-compress/index.js +2 -2
  82. package/dist/server-cookies/index.browser.js +3 -3
  83. package/dist/server-cookies/index.d.ts +4 -4
  84. package/dist/server-cookies/index.js +4 -4
  85. package/dist/server-cors/index.d.ts +3 -3
  86. package/dist/server-cors/index.js +3 -3
  87. package/dist/server-health/index.d.ts +3 -3
  88. package/dist/server-health/index.js +3 -3
  89. package/dist/server-helmet/index.d.ts +2 -2
  90. package/dist/server-helmet/index.js +2 -2
  91. package/dist/server-links/index.browser.js +5 -5
  92. package/dist/server-links/index.d.ts +40 -40
  93. package/dist/server-links/index.js +7 -7
  94. package/dist/server-metrics/index.d.ts +2 -2
  95. package/dist/server-metrics/index.js +86 -86
  96. package/dist/server-multipart/index.d.ts +2 -2
  97. package/dist/server-multipart/index.js +2 -2
  98. package/dist/server-proxy/index.d.ts +3 -3
  99. package/dist/server-proxy/index.js +3 -3
  100. package/dist/server-rate-limit/index.d.ts +3 -3
  101. package/dist/server-rate-limit/index.js +3 -3
  102. package/dist/server-security/index.browser.js +1 -1
  103. package/dist/server-security/index.d.ts +13 -13
  104. package/dist/server-security/index.js +4 -4
  105. package/dist/server-static/index.d.ts +3 -3
  106. package/dist/server-static/index.js +3 -3
  107. package/dist/server-swagger/index.d.ts +3 -3
  108. package/dist/server-swagger/index.js +3 -3
  109. package/dist/sms/index.d.ts +6 -6
  110. package/dist/sms/index.js +6 -6
  111. package/dist/thread/index.d.ts +3 -3
  112. package/dist/thread/index.js +3 -3
  113. package/dist/topic/index.d.ts +6 -6
  114. package/dist/topic/index.js +6 -6
  115. package/dist/topic-redis/index.d.ts +2 -2
  116. package/dist/topic-redis/index.js +2 -2
  117. package/dist/vite/index.d.ts +17 -17
  118. package/dist/vite/index.js +20 -20
  119. package/dist/websocket/index.browser.js +9 -9
  120. package/dist/websocket/index.d.ts +17 -17
  121. package/dist/websocket/index.js +10 -10
  122. package/package.json +5 -4
  123. package/src/cli/apps/AlephaCli.ts +1 -2
  124. package/src/cli/commands/BiomeCommands.ts +4 -6
  125. package/src/cli/commands/CoreCommands.ts +2 -2
  126. package/src/cli/commands/DrizzleCommands.ts +76 -78
  127. package/src/cli/commands/VerifyCommands.ts +3 -5
  128. package/src/cli/commands/ViteCommands.ts +6 -8
  129. package/src/cli/index.ts +1 -1
  130. package/src/cli/services/{ProjectUtils.ts → AlephaCliUtils.ts} +101 -16
  131. package/src/command/providers/CliProvider.ts +12 -4
  132. package/src/cli/services/ProcessRunner.ts +0 -89
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "alepha",
3
3
  "description": "Easy-to-use modern TypeScript framework for building many kind of applications.",
4
4
  "author": "Nicolas Foures",
5
- "version": "0.13.4",
5
+ "version": "0.13.5",
6
6
  "type": "module",
7
7
  "engines": {
8
8
  "node": ">=22.0.0"
@@ -20,14 +20,14 @@
20
20
  "@biomejs/biome": "^2.3.8",
21
21
  "@redis/client": "^5.10.0",
22
22
  "dayjs": "^1.11.19",
23
- "drizzle-kit": "^0.31.7",
24
- "drizzle-orm": "^0.44.7",
23
+ "drizzle-kit": "^0.31.8",
24
+ "drizzle-orm": "^0.45.0",
25
25
  "postgres": "^3.4.7",
26
26
  "tsx": "^4.21.0",
27
27
  "typebox": "^1.0.61",
28
28
  "typescript": "^5.9.3",
29
29
  "vite": "^7.2.6",
30
- "vite-bundle-analyzer": "^1.2.3",
30
+ "vite-bundle-analyzer": "^1.3.0",
31
31
  "ws": "^8.18.3"
32
32
  },
33
33
  "devDependencies": {
@@ -42,6 +42,7 @@
42
42
  "openid-client": "^6.8.1",
43
43
  "prom-client": "^15.1.3",
44
44
  "swagger-ui-dist": "^5.30.3",
45
+ "tsdown": "^0.17.0",
45
46
  "vitest": "^4.0.15"
46
47
  },
47
48
  "scarfSettings": {
@@ -6,11 +6,11 @@ import { CoreCommands } from "../commands/CoreCommands.ts";
6
6
  import { DrizzleCommands } from "../commands/DrizzleCommands.ts";
7
7
  import { VerifyCommands } from "../commands/VerifyCommands.ts";
8
8
  import { ViteCommands } from "../commands/ViteCommands.ts";
9
- import { ProcessRunner } from "../services/ProcessRunner.ts";
10
9
 
11
10
  class AlephaCliExtension {
12
11
  protected readonly alepha = $inject(Alepha);
13
12
  protected readonly fs = $inject(FileSystemProvider);
13
+
14
14
  protected readonly onConfigure = $hook({
15
15
  on: "configure",
16
16
  handler: async () => {
@@ -35,7 +35,6 @@ export const AlephaCli = $module({
35
35
  name: "alepha.cli",
36
36
  services: [
37
37
  AlephaCliExtension,
38
- ProcessRunner,
39
38
  CoreCommands,
40
39
  DrizzleCommands,
41
40
  VerifyCommands,
@@ -1,20 +1,18 @@
1
1
  import { $inject } from "alepha";
2
2
  import { $command } from "alepha/command";
3
3
  import { $logger } from "alepha/logger";
4
- import { ProcessRunner } from "../services/ProcessRunner.ts";
5
- import { ProjectUtils } from "../services/ProjectUtils.ts";
4
+ import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
6
5
 
7
6
  export class BiomeCommands {
8
7
  protected readonly log = $logger();
9
- protected readonly runner = $inject(ProcessRunner);
10
- protected readonly utils = $inject(ProjectUtils);
8
+ protected readonly utils = $inject(AlephaCliUtils);
11
9
 
12
10
  public readonly format = $command({
13
11
  name: "format",
14
12
  description: "Format the codebase using Biome",
15
13
  handler: async ({ root }) => {
16
14
  await this.utils.ensureConfig(root, { biomeJson: true });
17
- await this.runner.exec(`biome format --fix`);
15
+ await this.utils.exec(`biome format --fix`);
18
16
  },
19
17
  });
20
18
 
@@ -23,7 +21,7 @@ export class BiomeCommands {
23
21
  description: "Run linter across the codebase using Biome",
24
22
  handler: async ({ root }) => {
25
23
  await this.utils.ensureConfig(root, { biomeJson: true });
26
- await this.runner.exec(`biome check --formatter-enabled=false --fix`);
24
+ await this.utils.exec(`biome check --formatter-enabled=false --fix`);
27
25
  },
28
26
  });
29
27
  }
@@ -1,13 +1,13 @@
1
1
  import { $inject, t } from "alepha";
2
2
  import { $command, CliProvider } from "alepha/command";
3
3
  import { $logger } from "alepha/logger";
4
- import { ProjectUtils } from "../services/ProjectUtils.ts";
4
+ import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
5
5
  import { version } from "../version.ts";
6
6
 
7
7
  export class CoreCommands {
8
8
  protected readonly log = $logger();
9
9
  protected readonly cli = $inject(CliProvider);
10
- protected readonly utils = $inject(ProjectUtils);
10
+ protected readonly utils = $inject(AlephaCliUtils);
11
11
 
12
12
  /**
13
13
  * Called when no command is provided
@@ -4,8 +4,7 @@ import { $inject, AlephaError, t } from "alepha";
4
4
  import { $command } from "alepha/command";
5
5
  import { $logger } from "alepha/logger";
6
6
  import type { DrizzleKitProvider, RepositoryProvider } from "alepha/orm";
7
- import { ProcessRunner } from "../services/ProcessRunner.ts";
8
- import { ProjectUtils } from "../services/ProjectUtils.ts";
7
+ import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
9
8
 
10
9
  const drizzleCommandFlags = t.object({
11
10
  provider: t.optional(
@@ -18,8 +17,7 @@ const drizzleCommandFlags = t.object({
18
17
 
19
18
  export class DrizzleCommands {
20
19
  log = $logger();
21
- runner = $inject(ProcessRunner);
22
- utils = $inject(ProjectUtils);
20
+ utils = $inject(AlephaCliUtils);
23
21
 
24
22
  /**
25
23
  * Check if database migrations are up to date.
@@ -256,78 +254,78 @@ export class DrizzleCommands {
256
254
  },
257
255
  });
258
256
 
259
- /**
260
- * Drop database schema (development only)
261
- *
262
- * @experimental
263
- */
264
- drop = $command({
265
- name: "db:drop",
266
- description: "Drop database schema (development only)",
267
- summary: false,
268
- args: t.optional(
269
- t.text({
270
- title: "path",
271
- description: "Path to the Alepha server entry file",
272
- }),
273
- ),
274
- flags: drizzleCommandFlags,
275
- handler: async ({ flags }) => {
276
- // TODO: Implement db:drop
277
- this.log.warn("db:drop is not yet implemented");
278
- if (flags.provider) {
279
- this.log.info(`Provider filter: ${flags.provider}`);
280
- }
281
- },
282
- });
283
-
284
- /**
285
- * Seed database with initial data
286
- *
287
- * @experimental
288
- */
289
- seed = $command({
290
- name: "db:seed",
291
- description: "Seed database with initial data",
292
- summary: false,
293
- args: t.optional(
294
- t.text({
295
- title: "path",
296
- description: "Path to the Alepha server entry file",
297
- }),
298
- ),
299
- flags: drizzleCommandFlags,
300
- handler: async ({ flags }) => {
301
- // TODO: Implement db:seed
302
- this.log.warn("db:seed is not yet implemented");
303
- if (flags.provider) {
304
- this.log.info(`Provider filter: ${flags.provider}`);
305
- }
306
- },
307
- });
308
-
309
- /**
310
- * Show pending database migrations status
311
- *
312
- * @experimental
313
- */
314
- status = $command({
315
- name: "db:status",
316
- description: "Show pending database migrations status",
317
- summary: false,
318
- args: t.optional(
319
- t.text({
320
- title: "path",
321
- description: "Path to the Alepha server entry file",
322
- }),
323
- ),
324
- flags: drizzleCommandFlags,
325
- handler: async ({ flags }) => {
326
- // TODO: Implement db:status
327
- this.log.warn("db:status is not yet implemented");
328
- if (flags.provider) {
329
- this.log.info(`Provider filter: ${flags.provider}`);
330
- }
331
- },
332
- });
257
+ // /**
258
+ // * Drop database schema (development only)
259
+ // *
260
+ // * @experimental
261
+ // */
262
+ // drop = $command({
263
+ // name: "db:drop",
264
+ // description: "Drop database schema (development only)",
265
+ // summary: false,
266
+ // args: t.optional(
267
+ // t.text({
268
+ // title: "path",
269
+ // description: "Path to the Alepha server entry file",
270
+ // }),
271
+ // ),
272
+ // flags: drizzleCommandFlags,
273
+ // handler: async ({ flags }) => {
274
+ // // TODO: Implement db:drop
275
+ // this.log.warn("db:drop is not yet implemented");
276
+ // if (flags.provider) {
277
+ // this.log.info(`Provider filter: ${flags.provider}`);
278
+ // }
279
+ // },
280
+ // });
281
+ //
282
+ // /**
283
+ // * Seed database with initial data
284
+ // *
285
+ // * @experimental
286
+ // */
287
+ // seed = $command({
288
+ // name: "db:seed",
289
+ // description: "Seed database with initial data",
290
+ // summary: false,
291
+ // args: t.optional(
292
+ // t.text({
293
+ // title: "path",
294
+ // description: "Path to the Alepha server entry file",
295
+ // }),
296
+ // ),
297
+ // flags: drizzleCommandFlags,
298
+ // handler: async ({ flags }) => {
299
+ // // TODO: Implement db:seed
300
+ // this.log.warn("db:seed is not yet implemented");
301
+ // if (flags.provider) {
302
+ // this.log.info(`Provider filter: ${flags.provider}`);
303
+ // }
304
+ // },
305
+ // });
306
+ //
307
+ // /**
308
+ // * Show pending database migrations status
309
+ // *
310
+ // * @experimental
311
+ // */
312
+ // status = $command({
313
+ // name: "db:status",
314
+ // description: "Show pending database migrations status",
315
+ // summary: false,
316
+ // args: t.optional(
317
+ // t.text({
318
+ // title: "path",
319
+ // description: "Path to the Alepha server entry file",
320
+ // }),
321
+ // ),
322
+ // flags: drizzleCommandFlags,
323
+ // handler: async ({ flags }) => {
324
+ // // TODO: Implement db:status
325
+ // this.log.warn("db:status is not yet implemented");
326
+ // if (flags.provider) {
327
+ // this.log.info(`Provider filter: ${flags.provider}`);
328
+ // }
329
+ // },
330
+ // });
333
331
  }
@@ -1,11 +1,9 @@
1
1
  import { $inject } from "alepha";
2
2
  import { $command } from "alepha/command";
3
- import { ProcessRunner } from "../services/ProcessRunner.ts";
4
- import { ProjectUtils } from "../services/ProjectUtils.ts";
3
+ import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
5
4
 
6
5
  export class VerifyCommands {
7
- protected readonly processRunner = $inject(ProcessRunner);
8
- protected readonly utils = $inject(ProjectUtils);
6
+ protected readonly utils = $inject(AlephaCliUtils);
9
7
 
10
8
  /**
11
9
  * Run a series of verification commands to ensure code quality and correctness.
@@ -51,7 +49,7 @@ export class VerifyCommands {
51
49
  name: "typecheck",
52
50
  description: "Check TypeScript types across the codebase",
53
51
  handler: async () => {
54
- await this.processRunner.exec("tsc --noEmit");
52
+ await this.utils.exec("tsc --noEmit");
55
53
  },
56
54
  });
57
55
  }
@@ -15,13 +15,11 @@ import {
15
15
  prerenderPages,
16
16
  type ViteAlephaBuildOptions,
17
17
  } from "alepha/vite";
18
- import { ProcessRunner } from "../services/ProcessRunner.ts";
19
- import { ProjectUtils } from "../services/ProjectUtils.ts";
18
+ import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
20
19
 
21
20
  export class ViteCommands {
22
21
  protected readonly log = $logger();
23
- protected readonly runner = $inject(ProcessRunner);
24
- protected readonly utils = $inject(ProjectUtils);
22
+ protected readonly utils = $inject(AlephaCliUtils);
25
23
 
26
24
  protected readonly env = $env(
27
25
  t.object({
@@ -41,7 +39,7 @@ export class ViteCommands {
41
39
  args: t.text({ title: "path", description: "Filepath to run" }),
42
40
  handler: async ({ args, flags, root }) => {
43
41
  await this.utils.ensureTsConfig(root);
44
- await this.runner.exec(`tsx ${flags.watch ? "watch " : ""}${args}`);
42
+ await this.utils.exec(`tsx ${flags.watch ? "watch " : ""}${args}`);
45
43
  },
46
44
  });
47
45
 
@@ -73,11 +71,11 @@ export class ViteCommands {
73
71
  cmd += ` --env-file=./.env`;
74
72
  }
75
73
  cmd += ` ${entry}`;
76
- await this.runner.exec(cmd);
74
+ await this.utils.exec(cmd);
77
75
  return;
78
76
  }
79
77
 
80
- await this.runner.exec(`vite`);
78
+ await this.utils.exec(`vite`);
81
79
  },
82
80
  });
83
81
 
@@ -313,7 +311,7 @@ export class ViteCommands {
313
311
  process.exit(1);
314
312
  }
315
313
 
316
- await this.runner.exec(`vitest run ${this.env.VITEST_ARGS}`);
314
+ await this.utils.exec(`vitest run ${this.env.VITEST_ARGS}`);
317
315
  },
318
316
  });
319
317
  }
package/src/cli/index.ts CHANGED
@@ -5,5 +5,5 @@ export * from "./commands/CoreCommands.ts";
5
5
  export * from "./commands/DrizzleCommands.ts";
6
6
  export * from "./commands/VerifyCommands.ts";
7
7
  export * from "./commands/ViteCommands.ts";
8
- export * from "./services/ProjectUtils.ts";
8
+ export * from "./services/AlephaCliUtils.ts";
9
9
  export * from "./version.ts";
@@ -1,4 +1,5 @@
1
- import { access, readFile, writeFile } from "node:fs/promises";
1
+ import { spawn } from "node:child_process";
2
+ import { access, mkdir, readFile, writeFile } from "node:fs/promises";
2
3
  import { join } from "node:path";
3
4
  import { $inject, Alepha, AlephaError } from "alepha";
4
5
  import { FileSystemProvider } from "alepha/file";
@@ -13,7 +14,6 @@ import { mainBrowserTs } from "../assets/mainBrowserTs.ts";
13
14
  import { tsconfigJson } from "../assets/tsconfigJson.ts";
14
15
  import { viteConfigTs } from "../assets/viteConfigTs.ts";
15
16
  import { version } from "../version.ts";
16
- import { ProcessRunner } from "./ProcessRunner.ts";
17
17
 
18
18
  /**
19
19
  * Utility service for common project operations used by CLI commands.
@@ -25,11 +25,96 @@ import { ProcessRunner } from "./ProcessRunner.ts";
25
25
  * - Drizzle ORM/Kit utilities
26
26
  * - Alepha instance loading
27
27
  */
28
- export class ProjectUtils {
28
+ export class AlephaCliUtils {
29
29
  protected readonly log = $logger();
30
- protected readonly runner = $inject(ProcessRunner);
31
30
  protected readonly fs = $inject(FileSystemProvider);
32
31
 
32
+ /**
33
+ * Execute a command using npx with inherited stdio.
34
+ *
35
+ * @param command - The command to execute (will be passed to npx)
36
+ * @param env - Optional environment variables to set for the command
37
+ * @returns Promise that resolves when the process exits
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * const runner = alepha.inject(ProcessRunner);
42
+ * await runner.exec("tsx watch src/index.ts");
43
+ * ```
44
+ */
45
+ public async exec(
46
+ command: string,
47
+ env: Record<string, string> = {},
48
+ ): Promise<void> {
49
+ const root = process.cwd();
50
+ this.log.debug(`Executing command: ${command}`, { cwd: root });
51
+
52
+ const suffix = process.platform === "win32" ? ".cmd" : "";
53
+ const [app, ...args] = command.split(" ");
54
+ const execPath = await this.checkFileExists(
55
+ root,
56
+ `node_modules/.bin/${app}${suffix}`,
57
+ true,
58
+ );
59
+
60
+ if (!execPath) {
61
+ throw new AlephaError(
62
+ `Could not find executable for command '${app}'. Make sure the package is installed.`,
63
+ );
64
+ }
65
+
66
+ const prog = spawn(execPath, args, {
67
+ stdio: "inherit",
68
+ cwd: root,
69
+ env: {
70
+ ...process.env,
71
+ ...env,
72
+ // NODE_OPTIONS: "--import tsx",
73
+ },
74
+ });
75
+
76
+ await new Promise<void>((resolve) =>
77
+ prog.on("exit", () => {
78
+ resolve();
79
+ }),
80
+ );
81
+ }
82
+
83
+ /**
84
+ * Write a configuration file to node_modules/.alepha directory.
85
+ *
86
+ * Creates the .alepha directory if it doesn't exist and writes the file with the given content.
87
+ *
88
+ * @param name - The name of the config file to create
89
+ * @param content - The content to write to the file
90
+ * @param root - The root directory (defaults to process.cwd())
91
+ * @returns The absolute path to the created file
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * const runner = alepha.inject(ProcessRunner);
96
+ * const configPath = await runner.writeConfigFile("biome.json", biomeConfig);
97
+ * ```
98
+ */
99
+ public async writeConfigFile(
100
+ name: string,
101
+ content: string,
102
+ root = process.cwd(),
103
+ ): Promise<string> {
104
+ const dir = join(root, "node_modules", ".alepha");
105
+
106
+ await mkdir(dir, {
107
+ recursive: true,
108
+ }).catch(() => null);
109
+
110
+ const path = join(dir, name);
111
+ await writeFile(path, content);
112
+
113
+ this.log.debug(`Config file written: ${path}`);
114
+
115
+ return path;
116
+ }
117
+
33
118
  // ===================================================================================================================
34
119
  // Package Manager & Project Setup
35
120
  // ===================================================================================================================
@@ -207,27 +292,26 @@ export class ProjectUtils {
207
292
  root: string,
208
293
  name: string,
209
294
  checkParentDirectories: boolean = false,
210
- ): Promise<boolean> {
295
+ ): Promise<string | undefined> {
211
296
  const configPath = join(root, name);
212
297
  if (!checkParentDirectories) {
213
298
  try {
214
299
  await access(configPath);
215
- return true;
300
+ return configPath;
216
301
  } catch {
217
- return false;
302
+ return;
218
303
  }
219
304
  }
220
305
 
221
- let found = false;
222
306
  let currentDir = root;
223
307
  const maxIterations = 10; // safety to prevent infinite loops
224
308
  let level = 0;
225
309
 
226
310
  while (level < maxIterations) {
227
311
  try {
228
- await access(join(currentDir, name));
229
- found = true;
230
- break;
312
+ const maybe = join(currentDir, name);
313
+ await access(maybe);
314
+ return maybe;
231
315
  } catch {
232
316
  const parentDir = join(currentDir, "..");
233
317
  if (parentDir === currentDir) {
@@ -237,8 +321,6 @@ export class ProjectUtils {
237
321
  }
238
322
  level += 1;
239
323
  }
240
-
241
- return found;
242
324
  }
243
325
 
244
326
  protected async ensureFileExists(
@@ -379,7 +461,7 @@ ${models.map((it: string) => `export const ${it} = models["${it}"];`).join("\n")
379
461
  models,
380
462
  );
381
463
 
382
- const entitiesJsPath = await this.runner.writeConfigFile(
464
+ const entitiesJsPath = await this.writeConfigFile(
383
465
  "entities.js",
384
466
  entitiesJs,
385
467
  options.rootDir,
@@ -410,7 +492,7 @@ ${models.map((it: string) => `export const ${it} = models["${it}"];`).join("\n")
410
492
 
411
493
  const drizzleConfigJs = `export default ${JSON.stringify(config, null, 2)}`;
412
494
 
413
- return await this.runner.writeConfigFile(
495
+ return await this.writeConfigFile(
414
496
  "drizzle.config.js",
415
497
  drizzleConfigJs,
416
498
  options.rootDir,
@@ -500,8 +582,11 @@ ${models.map((it: string) => `export const ${it} = models["${it}"];`).join("\n")
500
582
  });
501
583
 
502
584
  const flags = options.commandFlags ? ` ${options.commandFlags}` : "";
503
- await this.runner.exec(
585
+ await this.exec(
504
586
  `drizzle-kit ${options.command} --config=${drizzleConfigJsPath}${flags}`,
587
+ {
588
+ NODE_OPTIONS: "--import tsx",
589
+ },
505
590
  );
506
591
  }
507
592
  }
@@ -142,7 +142,11 @@ export class CliProvider {
142
142
  }
143
143
 
144
144
  const commandFlags = this.parseCommandFlags(argv, command.flags);
145
- const commandArgs = this.parseCommandArgs(argv, command.options.args);
145
+ const commandArgs = this.parseCommandArgs(
146
+ argv,
147
+ command.options.args,
148
+ command.name === "",
149
+ );
146
150
 
147
151
  await this.alepha.context.run(async () => {
148
152
  this.log.debug(`Executing command '${command.name}'...`, {
@@ -291,15 +295,19 @@ export class CliProvider {
291
295
  return result;
292
296
  }
293
297
 
294
- protected parseCommandArgs(argv: string[], schema?: TSchema): any {
298
+ protected parseCommandArgs(
299
+ argv: string[],
300
+ schema?: TSchema,
301
+ isRootCommand = false,
302
+ ): any {
295
303
  if (!schema) {
296
304
  return undefined;
297
305
  }
298
306
 
299
307
  // Extract positional arguments (non-flag arguments)
300
308
  const positionalArgs = argv.filter((arg) => !arg.startsWith("-"));
301
- // Remove the command name from the positional args
302
- const argsOnly = positionalArgs.slice(1);
309
+ // For root commands, there's no command name to remove; otherwise slice off the command name
310
+ const argsOnly = isRootCommand ? positionalArgs : positionalArgs.slice(1);
303
311
 
304
312
  try {
305
313
  if (t.schema.isOptional(schema)) {
@@ -1,89 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { mkdir, writeFile } from "node:fs/promises";
3
- import { join } from "node:path";
4
- import { $logger } from "alepha/logger";
5
-
6
- /**
7
- * Service for running external processes with logging support.
8
- *
9
- * This service wraps Node.js child_process functionality and provides:
10
- * - Automatic logging of executed commands
11
- * - Promise-based execution
12
- * - Inherited stdio for seamless output
13
- * - Working directory context
14
- * - Config file management in node_modules/.alepha
15
- */
16
- export class ProcessRunner {
17
- protected readonly log = $logger();
18
-
19
- /**
20
- * Execute a command using npx with inherited stdio.
21
- *
22
- * @param command - The command to execute (will be passed to npx)
23
- * @param env - Optional environment variables to set for the command
24
- * @returns Promise that resolves when the process exits
25
- *
26
- * @example
27
- * ```ts
28
- * const runner = alepha.inject(ProcessRunner);
29
- * await runner.exec("tsx watch src/index.ts");
30
- * ```
31
- */
32
- public async exec(
33
- command: string,
34
- env: Record<string, string> = {},
35
- ): Promise<void> {
36
- this.log.debug(`Executing command: npx ${command}`, { cwd: process.cwd() });
37
-
38
- const prog = spawn("npx", command.split(" "), {
39
- stdio: "inherit",
40
- cwd: process.cwd(),
41
- env: {
42
- ...process.env,
43
- ...env,
44
- NODE_OPTIONS: "--import tsx",
45
- },
46
- });
47
-
48
- await new Promise<void>((resolve) =>
49
- prog.on("exit", () => {
50
- resolve();
51
- }),
52
- );
53
- }
54
-
55
- /**
56
- * Write a configuration file to node_modules/.alepha directory.
57
- *
58
- * Creates the .alepha directory if it doesn't exist and writes the file with the given content.
59
- *
60
- * @param name - The name of the config file to create
61
- * @param content - The content to write to the file
62
- * @param root - The root directory (defaults to process.cwd())
63
- * @returns The absolute path to the created file
64
- *
65
- * @example
66
- * ```ts
67
- * const runner = alepha.inject(ProcessRunner);
68
- * const configPath = await runner.writeConfigFile("biome.json", biomeConfig);
69
- * ```
70
- */
71
- public async writeConfigFile(
72
- name: string,
73
- content: string,
74
- root = process.cwd(),
75
- ): Promise<string> {
76
- const dir = join(root, "node_modules", ".alepha");
77
-
78
- await mkdir(dir, {
79
- recursive: true,
80
- }).catch(() => null);
81
-
82
- const path = join(dir, name);
83
- await writeFile(path, content);
84
-
85
- this.log.debug(`Config file written: ${path}`);
86
-
87
- return path;
88
- }
89
- }