alepha 0.14.0 → 0.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/README.md +3 -3
  2. package/dist/api/audits/index.d.ts +80 -1
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js.map +1 -1
  5. package/dist/api/files/index.d.ts +80 -1
  6. package/dist/api/files/index.d.ts.map +1 -1
  7. package/dist/api/files/index.js.map +1 -1
  8. package/dist/api/jobs/index.d.ts +236 -157
  9. package/dist/api/jobs/index.d.ts.map +1 -1
  10. package/dist/api/jobs/index.js.map +1 -1
  11. package/dist/api/notifications/index.d.ts +21 -1
  12. package/dist/api/notifications/index.d.ts.map +1 -1
  13. package/dist/api/parameters/index.d.ts +451 -4
  14. package/dist/api/parameters/index.d.ts.map +1 -1
  15. package/dist/api/parameters/index.js.map +1 -1
  16. package/dist/api/users/index.d.ts +252 -249
  17. package/dist/api/users/index.d.ts.map +1 -1
  18. package/dist/api/users/index.js +4 -0
  19. package/dist/api/users/index.js.map +1 -1
  20. package/dist/api/verifications/index.d.ts +128 -128
  21. package/dist/api/verifications/index.d.ts.map +1 -1
  22. package/dist/batch/index.js.map +1 -1
  23. package/dist/cache/core/index.js.map +1 -1
  24. package/dist/cli/index.d.ts +304 -115
  25. package/dist/cli/index.d.ts.map +1 -1
  26. package/dist/cli/index.js +650 -531
  27. package/dist/cli/index.js.map +1 -1
  28. package/dist/command/index.d.ts +210 -13
  29. package/dist/command/index.d.ts.map +1 -1
  30. package/dist/command/index.js +306 -69
  31. package/dist/command/index.js.map +1 -1
  32. package/dist/core/index.browser.js.map +1 -1
  33. package/dist/core/index.d.ts +1 -1
  34. package/dist/core/index.d.ts.map +1 -1
  35. package/dist/core/index.js +7 -6
  36. package/dist/core/index.js.map +1 -1
  37. package/dist/core/index.native.js +7 -6
  38. package/dist/core/index.native.js.map +1 -1
  39. package/dist/datetime/index.js.map +1 -1
  40. package/dist/fake/index.js.map +1 -1
  41. package/dist/file/index.d.ts.map +1 -1
  42. package/dist/file/index.js.map +1 -1
  43. package/dist/lock/redis/index.js.map +1 -1
  44. package/dist/logger/index.js.map +1 -1
  45. package/dist/mcp/index.js.map +1 -1
  46. package/dist/orm/index.browser.js +26 -5
  47. package/dist/orm/index.browser.js.map +1 -1
  48. package/dist/orm/index.d.ts +294 -215
  49. package/dist/orm/index.d.ts.map +1 -1
  50. package/dist/orm/index.js +522 -523
  51. package/dist/orm/index.js.map +1 -1
  52. package/dist/queue/redis/index.js +2 -4
  53. package/dist/queue/redis/index.js.map +1 -1
  54. package/dist/redis/index.d.ts +400 -29
  55. package/dist/redis/index.d.ts.map +1 -1
  56. package/dist/redis/index.js +412 -21
  57. package/dist/redis/index.js.map +1 -1
  58. package/dist/retry/index.js.map +1 -1
  59. package/dist/router/index.js.map +1 -1
  60. package/dist/scheduler/index.js.map +1 -1
  61. package/dist/security/index.d.ts.map +1 -1
  62. package/dist/security/index.js.map +1 -1
  63. package/dist/server/auth/index.d.ts +155 -155
  64. package/dist/server/auth/index.js.map +1 -1
  65. package/dist/server/cache/index.js.map +1 -1
  66. package/dist/server/cookies/index.browser.js.map +1 -1
  67. package/dist/server/cookies/index.js.map +1 -1
  68. package/dist/server/core/index.browser.js.map +1 -1
  69. package/dist/server/core/index.d.ts +0 -1
  70. package/dist/server/core/index.d.ts.map +1 -1
  71. package/dist/server/core/index.js.map +1 -1
  72. package/dist/server/helmet/index.d.ts +4 -1
  73. package/dist/server/helmet/index.d.ts.map +1 -1
  74. package/dist/server/helmet/index.js.map +1 -1
  75. package/dist/server/links/index.browser.js.map +1 -1
  76. package/dist/server/links/index.js.map +1 -1
  77. package/dist/server/multipart/index.d.ts.map +1 -1
  78. package/dist/server/multipart/index.js.map +1 -1
  79. package/dist/server/proxy/index.js.map +1 -1
  80. package/dist/server/rate-limit/index.js.map +1 -1
  81. package/dist/server/security/index.d.ts +9 -9
  82. package/dist/server/security/index.js.map +1 -1
  83. package/dist/server/swagger/index.js.map +1 -1
  84. package/dist/thread/index.js.map +1 -1
  85. package/dist/topic/core/index.js.map +1 -1
  86. package/dist/topic/redis/index.js +3 -3
  87. package/dist/topic/redis/index.js.map +1 -1
  88. package/dist/vite/index.js +9 -6
  89. package/dist/vite/index.js.map +1 -1
  90. package/dist/websocket/index.browser.js.map +1 -1
  91. package/dist/websocket/index.d.ts +7 -7
  92. package/dist/websocket/index.js.map +1 -1
  93. package/package.json +3 -3
  94. package/src/api/users/index.ts +4 -0
  95. package/src/cli/apps/AlephaCli.ts +36 -14
  96. package/src/cli/apps/AlephaPackageBuilderCli.ts +5 -1
  97. package/src/cli/assets/appRouterTs.ts +1 -1
  98. package/src/cli/atoms/changelogOptions.ts +45 -0
  99. package/src/cli/commands/{ViteCommands.ts → build.ts} +4 -93
  100. package/src/cli/commands/changelog.ts +244 -0
  101. package/src/cli/commands/clean.ts +14 -0
  102. package/src/cli/commands/{DrizzleCommands.ts → db.ts} +37 -124
  103. package/src/cli/commands/deploy.ts +118 -0
  104. package/src/cli/commands/dev.ts +57 -0
  105. package/src/cli/commands/format.ts +17 -0
  106. package/src/cli/commands/{CoreCommands.ts → init.ts} +2 -40
  107. package/src/cli/commands/lint.ts +17 -0
  108. package/src/cli/commands/root.ts +32 -0
  109. package/src/cli/commands/run.ts +24 -0
  110. package/src/cli/commands/test.ts +42 -0
  111. package/src/cli/commands/typecheck.ts +19 -0
  112. package/src/cli/commands/{VerifyCommands.ts → verify.ts} +1 -13
  113. package/src/cli/defineConfig.ts +24 -0
  114. package/src/cli/index.ts +17 -5
  115. package/src/cli/services/AlephaCliUtils.ts +4 -21
  116. package/src/cli/services/GitMessageParser.ts +77 -0
  117. package/src/command/helpers/EnvUtils.ts +37 -0
  118. package/src/command/index.ts +3 -1
  119. package/src/command/primitives/$command.ts +172 -6
  120. package/src/command/providers/CliProvider.ts +424 -91
  121. package/src/core/Alepha.ts +8 -5
  122. package/src/file/providers/NodeFileSystemProvider.ts +3 -1
  123. package/src/orm/index.browser.ts +1 -1
  124. package/src/orm/index.ts +18 -10
  125. package/src/orm/interfaces/PgQueryWhere.ts +1 -26
  126. package/src/orm/providers/{PostgresTypeProvider.ts → DatabaseTypeProvider.ts} +25 -3
  127. package/src/orm/providers/drivers/BunPostgresProvider.ts +225 -0
  128. package/src/orm/providers/drivers/BunSqliteProvider.ts +180 -0
  129. package/src/orm/providers/drivers/DatabaseProvider.ts +25 -0
  130. package/src/orm/providers/drivers/NodePostgresProvider.ts +0 -25
  131. package/src/orm/services/QueryManager.ts +10 -125
  132. package/src/queue/redis/providers/RedisQueueProvider.ts +2 -7
  133. package/src/redis/index.ts +65 -3
  134. package/src/redis/providers/BunRedisProvider.ts +304 -0
  135. package/src/redis/providers/BunRedisSubscriberProvider.ts +94 -0
  136. package/src/redis/providers/NodeRedisProvider.ts +280 -0
  137. package/src/redis/providers/NodeRedisSubscriberProvider.ts +94 -0
  138. package/src/redis/providers/RedisProvider.ts +134 -140
  139. package/src/redis/providers/RedisSubscriberProvider.ts +58 -49
  140. package/src/server/core/providers/BunHttpServerProvider.ts +0 -3
  141. package/src/server/core/providers/ServerBodyParserProvider.ts +3 -1
  142. package/src/server/core/providers/ServerProvider.ts +7 -4
  143. package/src/server/multipart/providers/ServerMultipartProvider.ts +3 -1
  144. package/src/server/proxy/providers/ServerProxyProvider.ts +1 -1
  145. package/src/topic/redis/providers/RedisTopicProvider.ts +3 -3
  146. package/src/vite/tasks/buildServer.ts +1 -0
  147. package/src/cli/commands/BiomeCommands.ts +0 -29
  148. package/src/cli/commands/ChangelogCommands.ts +0 -389
  149. package/src/orm/services/PgJsonQueryManager.ts +0 -511
@@ -16,6 +16,7 @@ import {
16
16
  import { $logger } from "alepha/logger";
17
17
  import { CommandError } from "../errors/CommandError.ts";
18
18
  import { Asker } from "../helpers/Asker.ts";
19
+ import { EnvUtils } from "../helpers/EnvUtils.ts";
19
20
  import { Runner } from "../helpers/Runner.ts";
20
21
  import {
21
22
  $command,
@@ -81,6 +82,7 @@ export class CliProvider {
81
82
  protected readonly log = $logger();
82
83
  protected readonly runner = $inject(Runner);
83
84
  protected readonly asker = $inject(Asker);
85
+ protected readonly envUtils = $inject(EnvUtils);
84
86
 
85
87
  protected readonly options = $use(cliOptions);
86
88
 
@@ -111,8 +113,12 @@ export class CliProvider {
111
113
  on: "ready",
112
114
  handler: async () => {
113
115
  const argv = [...this.argv];
114
- const commandName = argv.find((arg) => !arg.startsWith("-")) ?? "";
115
- let command = this.findCommand(commandName);
116
+
117
+ // Extract positional arguments (potential command path)
118
+ const positionalArgs = argv.filter((arg) => !arg.startsWith("-"));
119
+
120
+ // Resolve command using the new resolution logic
121
+ const { command, consumedArgs } = this.resolveCommand(positionalArgs);
116
122
 
117
123
  const globalFlags = this.parseFlags(
118
124
  argv,
@@ -128,74 +134,199 @@ export class CliProvider {
128
134
  }
129
135
 
130
136
  if (!command) {
131
- // check if one command is the root command (name === "") and has 'args'
137
+ // Check if there's a root command (name === "")
132
138
  const rootCommand = this.findCommand("");
133
- if (rootCommand?.options.args) {
134
- command = rootCommand;
135
- } else {
136
- if (commandName !== "") {
137
- this.log.error(`Unknown command: '${commandName}'`);
138
- this.printHelp();
139
- }
139
+
140
+ // If we have positional args but no matching command, show error
141
+ const commandName = positionalArgs[0] ?? "";
142
+ if (commandName !== "" && !rootCommand?.options.args) {
143
+ this.log.error(`Unknown command: '${commandName}'`);
144
+ this.printHelp();
140
145
  return;
141
146
  }
142
- }
143
147
 
144
- const commandFlags = this.parseCommandFlags(argv, command.flags);
145
- const commandArgs = this.parseCommandArgs(
146
- argv,
147
- command.options.args,
148
- command.name === "",
149
- command.flags,
150
- );
151
-
152
- await this.alepha.context.run(async () => {
153
- this.log.debug(`Executing command '${command.name}'...`, {
154
- flags: commandFlags,
155
- args: commandArgs,
156
- });
157
-
158
- const runner = this.runner;
159
-
160
- // Start command session for pretty print
161
- runner.startCommand(this.name, command.name);
162
-
163
- const args = {
164
- flags: commandFlags,
165
- args: commandArgs,
166
- run: runner.run,
167
- ask: this.asker.ask,
168
- fs,
169
- glob,
170
- root: process.cwd(),
171
- };
172
-
173
- // Execute pre-hooks
174
- const preHooks = this.findPreHooks(command.name);
175
- for (const hook of preHooks) {
176
- this.log.debug(`Executing pre-hook for '${command.name}'...`);
177
- await hook.options.handler(args as CommandHandlerArgs<TObject>);
148
+ // Execute root command if it exists
149
+ if (rootCommand) {
150
+ await this.executeCommand(rootCommand, argv, true);
151
+ return;
178
152
  }
179
153
 
180
- // Execute main command
181
- await command.options.handler(args as CommandHandlerArgs<TObject>);
182
-
183
- // Execute post-hooks
184
- const postHooks = this.findPostHooks(command.name);
185
- for (const hook of postHooks) {
186
- this.log.debug(`Executing post-hook for '${command.name}'...`);
187
- await hook.options.handler(args as CommandHandlerArgs<TObject>);
188
- }
154
+ // No command found and no root command
155
+ return;
156
+ }
189
157
 
190
- if (command.options.summary !== false) {
191
- runner.summary();
192
- }
158
+ // Remove consumed command path args from argv for argument parsing
159
+ const remainingArgv = this.removeConsumedArgs(argv, consumedArgs);
193
160
 
194
- this.log.debug(`Command '${command.name}' executed successfully.`);
195
- });
161
+ // Since we've removed the command path, treat it like a root command for parsing
162
+ await this.executeCommand(command, remainingArgv, true);
196
163
  },
197
164
  });
198
165
 
166
+ /**
167
+ * Execute a command with the given argv.
168
+ */
169
+ protected async executeCommand(
170
+ command: CommandPrimitive<TObject>,
171
+ argv: string[],
172
+ isRootCommand: boolean,
173
+ ): Promise<void> {
174
+ const root = process.cwd();
175
+
176
+ // Handle --mode flag if command has mode option enabled
177
+ let modeValue: string | undefined;
178
+ if (command.options.mode) {
179
+ modeValue = this.parseModeFlag(argv);
180
+ // Use default mode if not provided and mode is a string
181
+ if (modeValue === undefined && typeof command.options.mode === "string") {
182
+ modeValue = command.options.mode;
183
+ }
184
+ await this.loadModeEnv(root, modeValue);
185
+ }
186
+
187
+ const commandFlags = this.parseCommandFlags(argv, command.flags);
188
+ const commandArgs = this.parseCommandArgs(
189
+ argv,
190
+ command.options.args,
191
+ isRootCommand,
192
+ command.flags,
193
+ );
194
+ const commandEnv = this.parseCommandEnv(command.env, command.name);
195
+
196
+ await this.alepha.context.run(async () => {
197
+ this.log.debug(`Executing command '${command.name}'...`, {
198
+ flags: commandFlags,
199
+ args: commandArgs,
200
+ mode: modeValue,
201
+ });
202
+
203
+ const runner = this.runner;
204
+
205
+ // Start command session for pretty print
206
+ runner.startCommand(this.name, command.name);
207
+
208
+ const args = {
209
+ flags: commandFlags,
210
+ args: commandArgs,
211
+ env: commandEnv,
212
+ run: runner.run,
213
+ ask: this.asker.ask,
214
+ fs,
215
+ glob,
216
+ root,
217
+ help: () => this.printHelp(command),
218
+ mode: modeValue,
219
+ };
220
+
221
+ // Execute pre-hooks
222
+ const preHooks = this.findPreHooks(command.name);
223
+ for (const hook of preHooks) {
224
+ this.log.debug(`Executing pre-hook for '${command.name}'...`);
225
+ await hook.options.handler(args as CommandHandlerArgs<TObject>);
226
+ }
227
+
228
+ // Execute main command
229
+ await command.options.handler(args as CommandHandlerArgs<TObject>);
230
+
231
+ // Execute post-hooks
232
+ const postHooks = this.findPostHooks(command.name);
233
+ for (const hook of postHooks) {
234
+ this.log.debug(`Executing post-hook for '${command.name}'...`);
235
+ await hook.options.handler(args as CommandHandlerArgs<TObject>);
236
+ }
237
+
238
+ if (command.options.summary !== false) {
239
+ runner.summary();
240
+ }
241
+
242
+ this.log.debug(`Command '${command.name}' executed successfully.`);
243
+ });
244
+ }
245
+
246
+ /**
247
+ * Remove consumed command path arguments from argv.
248
+ */
249
+ protected removeConsumedArgs(
250
+ argv: string[],
251
+ consumedArgs: string[],
252
+ ): string[] {
253
+ const result: string[] = [];
254
+ let consumedIndex = 0;
255
+
256
+ for (const arg of argv) {
257
+ if (arg.startsWith("-")) {
258
+ result.push(arg);
259
+ } else if (
260
+ consumedIndex < consumedArgs.length &&
261
+ arg === consumedArgs[consumedIndex]
262
+ ) {
263
+ consumedIndex++;
264
+ // Skip this arg, it's part of the command path
265
+ } else {
266
+ result.push(arg);
267
+ }
268
+ }
269
+
270
+ return result;
271
+ }
272
+
273
+ /**
274
+ * Resolve a command from positional arguments.
275
+ *
276
+ * Supports:
277
+ * 1. Space-separated subcommands: `deploy vercel` -> finds deploy command, then vercel child
278
+ * 2. Colon notation (backwards compat): `deploy:vercel` -> finds command with name "deploy:vercel"
279
+ * 3. Simple commands: `build` -> finds command with name "build"
280
+ */
281
+ protected resolveCommand(positionalArgs: string[]): {
282
+ command: CommandPrimitive<TObject> | undefined;
283
+ consumedArgs: string[];
284
+ } {
285
+ if (positionalArgs.length === 0) {
286
+ return { command: undefined, consumedArgs: [] };
287
+ }
288
+
289
+ const firstArg = positionalArgs[0];
290
+
291
+ // First, try colon notation for backwards compatibility (e.g., "deploy:vercel")
292
+ if (firstArg.includes(":")) {
293
+ const command = this.findCommand(firstArg);
294
+ if (command) {
295
+ return { command, consumedArgs: [firstArg] };
296
+ }
297
+ }
298
+
299
+ // Try to find command with space-separated subcommand path
300
+ let currentCommand = this.findCommand(firstArg);
301
+ const consumedArgs: string[] = [];
302
+
303
+ if (!currentCommand) {
304
+ return { command: undefined, consumedArgs: [] };
305
+ }
306
+
307
+ consumedArgs.push(firstArg);
308
+
309
+ // Walk through remaining args to find nested subcommands
310
+ for (let i = 1; i < positionalArgs.length; i++) {
311
+ const arg = positionalArgs[i];
312
+
313
+ if (!currentCommand.hasChildren) {
314
+ break;
315
+ }
316
+
317
+ const childCommand = currentCommand.findChild(arg);
318
+ if (childCommand) {
319
+ currentCommand = childCommand;
320
+ consumedArgs.push(arg);
321
+ } else {
322
+ // No matching child, stop here
323
+ break;
324
+ }
325
+ }
326
+
327
+ return { command: currentCommand, consumedArgs };
328
+ }
329
+
199
330
  public get commands(): CommandPrimitive<any>[] {
200
331
  return this.alepha.primitives($command);
201
332
  }
@@ -221,35 +352,13 @@ export class CliProvider {
221
352
  }
222
353
 
223
354
  /**
224
- * Get all global flags including those from the root command (name === "")
355
+ * Get global flags (help only, root command flags are NOT global).
225
356
  */
226
357
  protected getAllGlobalFlags(): Record<
227
358
  string,
228
359
  { aliases: string[]; description?: string; schema: TSchema }
229
360
  > {
230
- const rootCommand = this.commands.find((cmd) => cmd.name === "");
231
- const allGlobalFlags: Record<
232
- string,
233
- { aliases: string[]; description?: string; schema: TSchema }
234
- > = { ...this.globalFlags };
235
-
236
- if (rootCommand) {
237
- // Add root command flags to global flags
238
- for (const [key, value] of Object.entries(rootCommand.flags.properties)) {
239
- allGlobalFlags[key] = {
240
- aliases: [
241
- key,
242
- ...((value as any).aliases ??
243
- ((value as any).alias ? [(value as any).alias] : undefined) ??
244
- []),
245
- ],
246
- description: (value as any).description,
247
- schema: value as TSchema,
248
- };
249
- }
250
- }
251
-
252
- return allGlobalFlags;
361
+ return { ...this.globalFlags };
253
362
  }
254
363
 
255
364
  protected parseCommandFlags(
@@ -292,6 +401,87 @@ export class CliProvider {
292
401
  }
293
402
  }
294
403
 
404
+ protected parseCommandEnv(
405
+ schema: TObject,
406
+ commandName: string,
407
+ ): Record<string, any> {
408
+ const result: Record<string, any> = {};
409
+ const missing: string[] = [];
410
+
411
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
412
+ const value = process.env[key];
413
+
414
+ if (value !== undefined) {
415
+ result[key] = value;
416
+ } else if (t.schema.isOptional(propSchema)) {
417
+ // Check for default value
418
+ if ("default" in propSchema) {
419
+ result[key] = propSchema.default;
420
+ }
421
+ } else {
422
+ missing.push(key);
423
+ }
424
+ }
425
+
426
+ if (missing.length > 0) {
427
+ const vars = missing.join(", ");
428
+ throw new CommandError(
429
+ `Missing required environment variable${missing.length > 1 ? "s" : ""}: ${vars}`,
430
+ );
431
+ }
432
+
433
+ try {
434
+ return this.alepha.codec.decode(schema, result);
435
+ } catch (error) {
436
+ if (error instanceof TypeBoxError) {
437
+ throw new CommandError(
438
+ `Invalid environment variable: ${error.cause.instancePath || "env"} ${error.cause.message}`,
439
+ );
440
+ }
441
+ throw error;
442
+ }
443
+ }
444
+
445
+ /**
446
+ * Parse --mode or -m flag from argv.
447
+ */
448
+ protected parseModeFlag(argv: string[]): string | undefined {
449
+ for (let i = 0; i < argv.length; i++) {
450
+ const arg = argv[i];
451
+
452
+ // Handle --mode=value or -m=value
453
+ if (arg.startsWith("--mode=") || arg.startsWith("-m=")) {
454
+ return arg.split("=")[1];
455
+ }
456
+
457
+ // Handle --mode value or -m value
458
+ if (arg === "--mode" || arg === "-m") {
459
+ const nextArg = argv[i + 1];
460
+ if (nextArg && !nextArg.startsWith("-")) {
461
+ return nextArg;
462
+ }
463
+ throw new CommandError("Flag --mode requires a value.");
464
+ }
465
+ }
466
+
467
+ return undefined;
468
+ }
469
+
470
+ /**
471
+ * Load environment files based on mode.
472
+ */
473
+ protected async loadModeEnv(
474
+ root: string,
475
+ mode: string | undefined,
476
+ ): Promise<void> {
477
+ const envFiles = [".env"];
478
+ if (mode) {
479
+ envFiles.push(`.env.${mode}`);
480
+ }
481
+ this.log.debug(`Loading env files: ${envFiles.join(", ")}`);
482
+ await this.envUtils.loadEnv(root, envFiles);
483
+ }
484
+
295
485
  protected parseFlags(
296
486
  argv: string[],
297
487
  flagDefs: { key: string; aliases: string[]; schema: TSchema }[],
@@ -522,8 +712,12 @@ export class CliProvider {
522
712
 
523
713
  if (command?.name) {
524
714
  // Command-specific help
525
- const argsUsage = this.generateArgsUsage(command.options.args);
526
- const usage = `${cliName} ${command.name}${argsUsage}`.trim();
715
+ const hasChildren = command.hasChildren;
716
+ const argsUsage = hasChildren
717
+ ? " <command>"
718
+ : this.generateArgsUsage(command.options.args);
719
+ const commandPath = this.getCommandPath(command);
720
+ const usage = `${cliName} ${commandPath}${argsUsage}`.trim();
527
721
  this.log.info(`Usage: \`${usage}\``);
528
722
 
529
723
  if (command.options.description) {
@@ -531,6 +725,25 @@ export class CliProvider {
531
725
  this.log.info(`\t${command.options.description}`);
532
726
  }
533
727
 
728
+ // Show subcommands if this is a parent command
729
+ if (hasChildren) {
730
+ this.log.info("");
731
+ this.log.info("Commands:");
732
+ const maxSubCmdLength = this.getMaxChildCmdLength(command.children);
733
+
734
+ for (const child of command.children) {
735
+ if (child.options.hide) {
736
+ continue;
737
+ }
738
+ const childArgsUsage = this.generateArgsUsage(child.options.args);
739
+ const cmdStr = [child.name, ...child.aliases].join(", ");
740
+ const fullCmdStr = `${cmdStr}${childArgsUsage}`;
741
+ this.log.info(
742
+ ` ${cliName} ${command.name} ${fullCmdStr.padEnd(maxSubCmdLength)} # ${child.options.description ?? ""}`,
743
+ );
744
+ }
745
+ }
746
+
534
747
  this.log.info("");
535
748
  this.log.info("Flags:");
536
749
 
@@ -541,6 +754,19 @@ export class CliProvider {
541
754
  aliases: (value as any).alias ?? [key],
542
755
  description: (value as any).description,
543
756
  })),
757
+ // Add --mode flag if command has mode option enabled
758
+ ...(command.options.mode
759
+ ? [
760
+ {
761
+ key: "mode",
762
+ aliases: ["m", "mode"],
763
+ description:
764
+ typeof command.options.mode === "string"
765
+ ? `Environment mode - loads .env.{mode} (default: ${command.options.mode})`
766
+ : "Environment mode (e.g., production, staging) - loads .env.{mode}",
767
+ },
768
+ ]
769
+ : []),
544
770
  ...Object.entries(this.getAllGlobalFlags()).map(([key, value]) => ({
545
771
  key,
546
772
  ...value,
@@ -556,21 +782,42 @@ export class CliProvider {
556
782
  ` ${flagStr.padEnd(maxFlagLength)} # ${description ?? ""}`,
557
783
  );
558
784
  }
785
+
786
+ // Show environment variables if defined
787
+ const envVars = Object.entries(command.env.properties);
788
+ if (envVars.length > 0) {
789
+ this.log.info("");
790
+ this.log.info("Env:");
791
+ const maxEnvLength = Math.max(...envVars.map(([key]) => key.length));
792
+ for (const [key, schema] of envVars) {
793
+ const isOptional = t.schema.isOptional(schema as TSchema);
794
+ const description = (schema as any).description ?? "";
795
+ const optionalStr = isOptional ? " (optional)" : "";
796
+ this.log.info(
797
+ ` ${key.padEnd(maxEnvLength)} # ${description}${optionalStr}`,
798
+ );
799
+ }
800
+ }
559
801
  } else {
560
802
  // general help
561
803
  this.log.info(this.description || "Available commands:");
562
804
  this.log.info("");
563
805
  this.log.info("Commands:");
564
- const maxCmdLength = this.getMaxCmdLength(this.commands);
565
806
 
566
- for (const command of this.commands) {
807
+ // Get top-level commands (commands that are not children of other commands)
808
+ const topLevelCommands = this.getTopLevelCommands();
809
+ const maxCmdLength = this.getMaxCmdLength(topLevelCommands);
810
+
811
+ for (const command of topLevelCommands) {
567
812
  // skip root command and hooks in list
568
813
  if (command.name === "" || command.options.hide) {
569
814
  continue;
570
815
  }
571
816
 
572
817
  const cmdStr = [command.name, ...command.aliases].join(", ");
573
- const argsUsage = this.generateArgsUsage(command.options.args);
818
+ const argsUsage = command.hasChildren
819
+ ? " <command>"
820
+ : this.generateArgsUsage(command.options.args);
574
821
  const fullCmdStr = `${cmdStr}${argsUsage}`;
575
822
  this.log.info(
576
823
  ` ${cliName} ${fullCmdStr.padEnd(maxCmdLength)} # ${command.options.description ?? ""}`,
@@ -579,7 +826,26 @@ export class CliProvider {
579
826
 
580
827
  this.log.info("");
581
828
  this.log.info("Flags:");
582
- const globalFlags = Object.values(this.getAllGlobalFlags());
829
+
830
+ // In general help, also show root command flags
831
+ const rootCommand = this.commands.find((cmd) => cmd.name === "");
832
+ const rootFlags = rootCommand
833
+ ? Object.entries(rootCommand.flags.properties).map(([key, value]) => ({
834
+ key,
835
+ aliases: [
836
+ key,
837
+ ...((value as any).aliases ??
838
+ ((value as any).alias ? [(value as any).alias] : undefined) ??
839
+ []),
840
+ ],
841
+ description: (value as any).description,
842
+ }))
843
+ : [];
844
+
845
+ const globalFlags = [
846
+ ...rootFlags,
847
+ ...Object.values(this.getAllGlobalFlags()),
848
+ ];
583
849
  const maxFlagLength = this.getMaxFlagLength(globalFlags);
584
850
  for (const { aliases, description } of globalFlags) {
585
851
  const flagStr = aliases
@@ -593,13 +859,80 @@ export class CliProvider {
593
859
  this.log.info(""); // Newline
594
860
  }
595
861
 
596
- private getMaxCmdLength(commands: CommandPrimitive[]): number {
862
+ /**
863
+ * Get the full command path (e.g., "deploy vercel" for a child command).
864
+ */
865
+ protected getCommandPath(command: CommandPrimitive<any>): string {
866
+ const path: string[] = [command.name];
867
+ let current = command;
868
+
869
+ // Walk up the tree to find parents
870
+ while (true) {
871
+ const parent = this.findParentCommand(current);
872
+ if (!parent) break;
873
+ path.unshift(parent.name);
874
+ current = parent;
875
+ }
876
+
877
+ return path.join(" ");
878
+ }
879
+
880
+ /**
881
+ * Find the parent command of a given command.
882
+ */
883
+ protected findParentCommand(
884
+ command: CommandPrimitive<any>,
885
+ ): CommandPrimitive<any> | undefined {
886
+ for (const cmd of this.commands) {
887
+ if (cmd.children.includes(command)) {
888
+ return cmd;
889
+ }
890
+ }
891
+ return undefined;
892
+ }
893
+
894
+ /**
895
+ * Get top-level commands (commands that are not children of other commands).
896
+ */
897
+ protected getTopLevelCommands(): CommandPrimitive<any>[] {
898
+ const allChildren = new Set<CommandPrimitive<any>>();
899
+
900
+ // Collect all children
901
+ for (const command of this.commands) {
902
+ for (const child of command.children) {
903
+ allChildren.add(child);
904
+ }
905
+ }
906
+
907
+ // Return commands that are not children
908
+ return this.commands.filter((cmd) => !allChildren.has(cmd));
909
+ }
910
+
911
+ /**
912
+ * Get max length for child command display.
913
+ */
914
+ protected getMaxChildCmdLength(children: CommandPrimitive<any>[]): number {
915
+ return Math.max(
916
+ ...children
917
+ .filter((c) => !c.options.hide)
918
+ .map((c) => {
919
+ const cmdStr = [c.name, ...c.aliases].join(", ");
920
+ const argsUsage = this.generateArgsUsage(c.options.args);
921
+ return `${cmdStr}${argsUsage}`.length;
922
+ }),
923
+ 0,
924
+ );
925
+ }
926
+
927
+ protected getMaxCmdLength(commands: CommandPrimitive[]): number {
597
928
  return Math.max(
598
929
  ...commands
599
930
  .filter((c) => !c.options.hide && c.name !== "")
600
931
  .map((c) => {
601
932
  const cmdStr = [c.name, ...c.aliases].join(", ");
602
- const argsUsage = this.generateArgsUsage(c.options.args);
933
+ const argsUsage = c.hasChildren
934
+ ? " <command>"
935
+ : this.generateArgsUsage(c.options.args);
603
936
  return `${cmdStr}${argsUsage}`.length;
604
937
  }),
605
938
  );
@@ -165,11 +165,14 @@ export class Alepha {
165
165
  ...state.env,
166
166
  ...process.env,
167
167
  };
168
+ }
168
169
 
169
- // force production mode when building with vite
170
- if (process.env.NODE_ENV === "production") {
171
- (state.env as Record<string, string>).NODE_ENV ??= "production";
172
- }
170
+ // force production mode when building with vite
171
+ if (process.env.NODE_ENV === "production") {
172
+ state.env ??= {};
173
+ Object.assign(state.env, {
174
+ NODE_ENV: "production",
175
+ });
173
176
  }
174
177
 
175
178
  const alepha = new Alepha(state);
@@ -1016,7 +1019,7 @@ export interface Env {
1016
1019
  /**
1017
1020
  * Optional environment variable that indicates the current environment.
1018
1021
  */
1019
- NODE_ENV?: "dev" | "test" | "production";
1022
+ NODE_ENV?: string;
1020
1023
 
1021
1024
  /**
1022
1025
  * Optional name of the application.
@@ -548,7 +548,9 @@ export class NodeFileSystemProvider implements FileSystemProvider {
548
548
  const stream = new PassThrough();
549
549
 
550
550
  fetch(url)
551
- .then((res) => Readable.fromWeb(res.body as NodeWebStream).pipe(stream))
551
+ .then((res) =>
552
+ Readable.fromWeb(res.body as unknown as NodeWebStream).pipe(stream),
553
+ )
552
554
  .catch((err) => stream.destroy(err));
553
555
 
554
556
  return stream;
@@ -16,7 +16,7 @@ export * from "./interfaces/FilterOperators.ts";
16
16
  export * from "./interfaces/PgQuery.ts";
17
17
  export * from "./interfaces/PgQueryWhere.ts";
18
18
  export * from "./primitives/$entity.ts";
19
- export * from "./providers/PostgresTypeProvider.ts";
19
+ export * from "./providers/DatabaseTypeProvider.ts";
20
20
  export * from "./schemas/legacyIdSchema.ts";
21
21
 
22
22
  // ---------------------------------------------------------------------------------------------------------------------