@c9up/ream 0.1.26 → 0.1.27

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 (179) hide show
  1. package/README.md +65 -0
  2. package/dist/Ignitor.d.ts +115 -7
  3. package/dist/Ignitor.d.ts.map +1 -1
  4. package/dist/Ignitor.js +233 -37
  5. package/dist/Ignitor.js.map +1 -1
  6. package/dist/console/BaseCommand.d.ts +217 -0
  7. package/dist/console/BaseCommand.d.ts.map +1 -0
  8. package/dist/console/BaseCommand.js +340 -0
  9. package/dist/console/BaseCommand.js.map +1 -0
  10. package/dist/console/Console.d.ts +86 -0
  11. package/dist/console/Console.d.ts.map +1 -0
  12. package/dist/console/Console.js +134 -0
  13. package/dist/console/Console.js.map +1 -0
  14. package/dist/console/ExceptionHandler.d.ts +28 -0
  15. package/dist/console/ExceptionHandler.d.ts.map +1 -0
  16. package/dist/console/ExceptionHandler.js +76 -0
  17. package/dist/console/ExceptionHandler.js.map +1 -0
  18. package/dist/console/HelpCommand.d.ts +16 -0
  19. package/dist/console/HelpCommand.d.ts.map +1 -0
  20. package/dist/console/HelpCommand.js +40 -0
  21. package/dist/console/HelpCommand.js.map +1 -0
  22. package/dist/console/IndexGenerator.d.ts +24 -0
  23. package/dist/console/IndexGenerator.d.ts.map +1 -0
  24. package/dist/console/IndexGenerator.js +65 -0
  25. package/dist/console/IndexGenerator.js.map +1 -0
  26. package/dist/console/Kernel.d.ts +298 -0
  27. package/dist/console/Kernel.d.ts.map +1 -0
  28. package/dist/console/Kernel.js +1120 -0
  29. package/dist/console/Kernel.js.map +1 -0
  30. package/dist/console/ListCommand.d.ts +18 -0
  31. package/dist/console/ListCommand.d.ts.map +1 -0
  32. package/dist/console/ListCommand.js +60 -0
  33. package/dist/console/ListCommand.js.map +1 -0
  34. package/dist/console/assertions.d.ts +20 -0
  35. package/dist/console/assertions.d.ts.map +1 -0
  36. package/dist/console/assertions.js +84 -0
  37. package/dist/console/assertions.js.map +1 -0
  38. package/dist/console/cliui.d.ts +248 -0
  39. package/dist/console/cliui.d.ts.map +1 -0
  40. package/dist/console/cliui.js +587 -0
  41. package/dist/console/cliui.js.map +1 -0
  42. package/dist/console/decorators.d.ts +90 -0
  43. package/dist/console/decorators.d.ts.map +1 -0
  44. package/dist/console/decorators.js +142 -0
  45. package/dist/console/decorators.js.map +1 -0
  46. package/dist/console/index.d.ts +32 -0
  47. package/dist/console/index.d.ts.map +1 -0
  48. package/dist/console/index.js +24 -0
  49. package/dist/console/index.js.map +1 -0
  50. package/dist/console/loaders.d.ts +52 -0
  51. package/dist/console/loaders.d.ts.map +1 -0
  52. package/dist/console/loaders.js +131 -0
  53. package/dist/console/loaders.js.map +1 -0
  54. package/dist/console/parser.d.ts +86 -0
  55. package/dist/console/parser.d.ts.map +1 -0
  56. package/dist/console/parser.js +440 -0
  57. package/dist/console/parser.js.map +1 -0
  58. package/dist/console/prompts.d.ts +124 -0
  59. package/dist/console/prompts.d.ts.map +1 -0
  60. package/dist/console/prompts.js +356 -0
  61. package/dist/console/prompts.js.map +1 -0
  62. package/dist/console/tracing.d.ts +17 -0
  63. package/dist/console/tracing.d.ts.map +1 -0
  64. package/dist/console/tracing.js +11 -0
  65. package/dist/console/tracing.js.map +1 -0
  66. package/dist/console/types.d.ts +255 -0
  67. package/dist/console/types.d.ts.map +1 -0
  68. package/dist/console/types.js +25 -0
  69. package/dist/console/types.js.map +1 -0
  70. package/dist/console/ui.d.ts +20 -0
  71. package/dist/console/ui.d.ts.map +1 -0
  72. package/dist/console/ui.js +31 -0
  73. package/dist/console/ui.js.map +1 -0
  74. package/dist/console/utils.d.ts +27 -0
  75. package/dist/console/utils.d.ts.map +1 -0
  76. package/dist/console/utils.js +56 -0
  77. package/dist/console/utils.js.map +1 -0
  78. package/dist/container/Container.d.ts.map +1 -1
  79. package/dist/container/Container.js +40 -12
  80. package/dist/container/Container.js.map +1 -1
  81. package/dist/container/types.d.ts +14 -2
  82. package/dist/container/types.d.ts.map +1 -1
  83. package/dist/decorators/Service.d.ts +7 -0
  84. package/dist/decorators/Service.d.ts.map +1 -1
  85. package/dist/decorators/Service.js +23 -3
  86. package/dist/decorators/Service.js.map +1 -1
  87. package/dist/errors/ReamError.d.ts +25 -0
  88. package/dist/errors/ReamError.d.ts.map +1 -1
  89. package/dist/errors/ReamError.js +5 -0
  90. package/dist/errors/ReamError.js.map +1 -1
  91. package/dist/errors/prettyPrintError.d.ts +9 -0
  92. package/dist/errors/prettyPrintError.d.ts.map +1 -0
  93. package/dist/errors/prettyPrintError.js +22 -0
  94. package/dist/errors/prettyPrintError.js.map +1 -0
  95. package/dist/index.d.ts +14 -4
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +16 -2
  98. package/dist/index.js.map +1 -1
  99. package/dist/middleware/Pipeline.d.ts +29 -8
  100. package/dist/middleware/Pipeline.d.ts.map +1 -1
  101. package/dist/middleware/Pipeline.js +39 -1
  102. package/dist/middleware/Pipeline.js.map +1 -1
  103. package/dist/rpc/RpcRouter.d.ts.map +1 -1
  104. package/dist/rpc/RpcRouter.js +13 -2
  105. package/dist/rpc/RpcRouter.js.map +1 -1
  106. package/dist/services/app.d.ts +1 -1
  107. package/dist/services/app.js +1 -1
  108. package/dist/services/console.d.ts +21 -0
  109. package/dist/services/console.d.ts.map +1 -0
  110. package/dist/services/console.js +35 -0
  111. package/dist/services/console.js.map +1 -0
  112. package/dist/testing/ApiResponse.d.ts +218 -0
  113. package/dist/testing/ApiResponse.d.ts.map +1 -0
  114. package/dist/testing/ApiResponse.js +576 -0
  115. package/dist/testing/ApiResponse.js.map +1 -0
  116. package/dist/testing/RequestBuilder.d.ts +132 -72
  117. package/dist/testing/RequestBuilder.d.ts.map +1 -1
  118. package/dist/testing/RequestBuilder.js +389 -222
  119. package/dist/testing/RequestBuilder.js.map +1 -1
  120. package/dist/testing/TestClient.d.ts +34 -39
  121. package/dist/testing/TestClient.d.ts.map +1 -1
  122. package/dist/testing/TestClient.js +48 -56
  123. package/dist/testing/TestClient.js.map +1 -1
  124. package/dist/utils/Macroable.js +3 -3
  125. package/dist/utils/Macroable.js.map +1 -1
  126. package/events.darwin-arm64.node +0 -0
  127. package/events.darwin-x64.node +0 -0
  128. package/events.linux-arm64-gnu.node +0 -0
  129. package/events.linux-x64-gnu.node +0 -0
  130. package/events.win32-x64-msvc.node +0 -0
  131. package/index.darwin-arm64.node +0 -0
  132. package/index.darwin-x64.node +0 -0
  133. package/index.linux-arm64-gnu.node +0 -0
  134. package/index.linux-x64-gnu.node +0 -0
  135. package/index.win32-x64-msvc.node +0 -0
  136. package/package.json +15 -2
  137. package/scheduler.darwin-arm64.node +0 -0
  138. package/scheduler.darwin-x64.node +0 -0
  139. package/scheduler.linux-arm64-gnu.node +0 -0
  140. package/scheduler.linux-x64-gnu.node +0 -0
  141. package/scheduler.win32-x64-msvc.node +0 -0
  142. package/src/Ignitor.ts +340 -42
  143. package/src/console/BaseCommand.ts +467 -0
  144. package/src/console/Console.ts +174 -0
  145. package/src/console/ExceptionHandler.ts +91 -0
  146. package/src/console/HelpCommand.ts +37 -0
  147. package/src/console/IndexGenerator.ts +79 -0
  148. package/src/console/Kernel.ts +1382 -0
  149. package/src/console/ListCommand.ts +55 -0
  150. package/src/console/assertions.ts +114 -0
  151. package/src/console/cliui.ts +756 -0
  152. package/src/console/decorators.ts +199 -0
  153. package/src/console/index.ts +67 -0
  154. package/src/console/loaders.ts +145 -0
  155. package/src/console/parser.ts +598 -0
  156. package/src/console/prompts.ts +548 -0
  157. package/src/console/tracing.ts +22 -0
  158. package/src/console/types.ts +280 -0
  159. package/src/console/ui.ts +37 -0
  160. package/src/console/utils.ts +69 -0
  161. package/src/container/Container.ts +55 -13
  162. package/src/container/types.ts +15 -2
  163. package/src/decorators/Service.ts +28 -3
  164. package/src/errors/ReamError.ts +31 -0
  165. package/src/errors/prettyPrintError.ts +20 -0
  166. package/src/index.ts +30 -4
  167. package/src/middleware/Pipeline.ts +76 -5
  168. package/src/rpc/RpcRouter.ts +16 -2
  169. package/src/services/app.ts +1 -1
  170. package/src/services/console.ts +42 -0
  171. package/src/testing/ApiResponse.ts +691 -0
  172. package/src/testing/RequestBuilder.ts +498 -275
  173. package/src/testing/TestClient.ts +63 -86
  174. package/src/utils/Macroable.ts +3 -3
  175. package/dist/console/CommandRunner.d.ts +0 -18
  176. package/dist/console/CommandRunner.d.ts.map +0 -1
  177. package/dist/console/CommandRunner.js +0 -65
  178. package/dist/console/CommandRunner.js.map +0 -1
  179. package/src/console/CommandRunner.ts +0 -82
package/README.md CHANGED
@@ -35,6 +35,71 @@ const app = new Ignitor({ port: 3000 })
35
35
  await app.start()
36
36
  ```
37
37
 
38
+ ## Testing
39
+
40
+ Declare your suites in `reamrc.ts`, the way AdonisJS declares them in
41
+ `adonisrc.ts`, and `ream test` runs them:
42
+
43
+ ```ts
44
+ // reamrc.ts
45
+ export default defineConfig({
46
+ tests: {
47
+ timeout: 2_000,
48
+ forceExit: false,
49
+ suites: [
50
+ { name: 'unit', files: ['tests/unit/**/*.spec.(js|ts)'] },
51
+ {
52
+ name: 'functional',
53
+ files: ['tests/functional/**/*.spec.ts'],
54
+ timeout: 30_000,
55
+ // Japa's per-suite `configure`. Costs an import of this file in every
56
+ // worker — `configureSuite` in tests/bootstrap.ts does the same for free.
57
+ configure: (suite) => suite.setup(() => startHttpServer()),
58
+ },
59
+ ],
60
+ },
61
+ })
62
+ ```
63
+
64
+ ```sh
65
+ ream test # every suite, in order
66
+ ream test functional # one suite
67
+ ream test --bail --threads=4
68
+ ```
69
+
70
+ `ream test` sets `NODE_ENV=test` and loads the `.env` files itself, before
71
+ spawning anything: `.env.test` wins over `.env`, `.env.local` is skipped so
72
+ a developer's local overrides never decide what CI runs, and the shell keeps
73
+ the last word. The app writes no hook for this — the workers inherit the
74
+ environment of the process that spawned them.
75
+
76
+ `japaPlugins: true` lets official Japa plugins (`@japa/assert`, …) run: it
77
+ points `@japa/runner/core` at helix's shim in every worker, so a plugin
78
+ instruments the runner that is actually running. Off by default —
79
+ redirecting a package specifier is not something to do behind your back.
80
+
81
+ `forceExit: true` makes the run call `process.exit()` once it ends instead
82
+ of waiting for the event loop to drain — the same thing Japa does with it,
83
+ and the answer to a pool or a server the app left open. Without it the
84
+ process exits on its own, so a leaked handle surfaces as a diagnosable
85
+ hang rather than being swallowed.
86
+
87
+ The stratification is AdonisJS's: ream reads its rc file and hands the
88
+ suites to the runner ([`@c9up/helix`](../helix)), exactly as
89
+ `@adonisjs/core` reads `adonisrc.ts` and hands them to Japa. helix knows
90
+ nothing about ream, and ream owns no test execution. `tests/bootstrap.ts`
91
+ — plugins, `runnerHooks`, `configureSuite` — is helix's, unchanged.
92
+
93
+ Driving it yourself (a `bin/test.ts`, a console command) is the same call:
94
+
95
+ ```ts
96
+ import { runTestsFromRcFile } from '@c9up/ream/test-runner'
97
+
98
+ process.exitCode = await runTestsFromRcFile('./reamrc.ts', {
99
+ suites: process.argv.slice(2),
100
+ })
101
+ ```
102
+
38
103
  ## Ecosystem
39
104
 
40
105
  Every package is standalone and publishable on its own; they consume the Ream
package/dist/Ignitor.d.ts CHANGED
@@ -13,7 +13,10 @@
13
13
  * @implements FR17, FR20, FR23
14
14
  */
15
15
  import { Application } from './Application.js';
16
+ import type { Console } from './console/Console.js';
17
+ import type { Kernel as ConsoleKernelInstance } from './console/Kernel.js';
16
18
  import type { ErrorEvent } from './ErrorBoundary.js';
19
+ import { prettyPrintError } from './errors/prettyPrintError.js';
17
20
  import type { HttpKernelRequest, HttpKernelResponse } from './HttpKernel.js';
18
21
  import type { MiddlewareFunction } from './middleware/Pipeline.js';
19
22
  import type { AppContext, ProviderContract } from './Provider.js';
@@ -38,12 +41,81 @@ export interface ReamrcConfig {
38
41
  environment?: string[];
39
42
  }>;
40
43
  commands?: Array<() => Promise<unknown>>;
44
+ /**
45
+ * Command shorthands — Console's `commandsAliases`. The value is the command the
46
+ * alias stands for, flags included:
47
+ * `{ resource: 'make:controller --resource' }`
48
+ */
49
+ commandsAliases?: Record<string, string>;
41
50
  modules?: {
42
51
  /** Path to the modules directory (relative to app root). Default: './app/modules' */
43
52
  path?: string;
44
53
  /** Auto-loaded files in each module directory. Default: ['routes'] */
45
54
  autoload?: string[];
46
55
  };
56
+ /** Test suites and runner settings — the `tests` block of adonisrc.ts. */
57
+ tests?: TestsConfig;
58
+ }
59
+ /** One test suite, as declared in the rc file (AdonisJS `tests.suites[]`). */
60
+ export interface TestSuiteConfig {
61
+ /** Suite name — what `ream test <name>` selects. */
62
+ name: string;
63
+ /**
64
+ * The suite's files: a glob, several, or a callback returning their URLs
65
+ * (Japa `TestFiles`).
66
+ */
67
+ files: string | string[] | (() => URL[] | Promise<URL[]>);
68
+ /** Per-test timeout for this suite, in ms. */
69
+ timeout?: number;
70
+ /** Extra attempts on failure for this suite. */
71
+ retries?: number;
72
+ /**
73
+ * Configure the suite before it runs (Japa `TestSuite.configure`). Receives
74
+ * the same handle as the bootstrap's `configureSuite`, and runs after it.
75
+ *
76
+ * Costs an import of the rc file in every worker, since a function cannot
77
+ * cross a process boundary — `configureSuite` in `tests/bootstrap.ts` does
78
+ * the same job for free, and is what AdonisJS itself uses.
79
+ */
80
+ configure?: (suite: TestSuiteHandle) => void;
81
+ }
82
+ /**
83
+ * What a suite's `configure` receives — helix's `SuiteHandle`, re-declared here
84
+ * so the rc file types without ream depending on the runner at type level.
85
+ */
86
+ export interface TestSuiteHandle {
87
+ readonly name: string;
88
+ setup(fn: () => void | Promise<void>): TestSuiteHandle;
89
+ teardown(fn: () => void | Promise<void>): TestSuiteHandle;
90
+ onTest(callback: (test: unknown) => void): TestSuiteHandle;
91
+ onGroup(callback: (group: unknown) => void): TestSuiteHandle;
92
+ bail(toggle?: boolean): TestSuiteHandle;
93
+ }
94
+ /**
95
+ * The `tests` block of the rc file — AdonisJS `adonisrc.ts` `tests`, field for
96
+ * field. The runner that consumes it is helix; ream reads the file, exactly as
97
+ * `@adonisjs/core` reads adonisrc and hands the suites to Japa.
98
+ */
99
+ export interface TestsConfig {
100
+ /** Named suites. `ream test` with no argument runs them all, in order. */
101
+ suites?: TestSuiteConfig[];
102
+ /** Default per-test timeout, in ms. */
103
+ timeout?: number;
104
+ /** `process.exit()` once the run ends instead of draining the event loop. */
105
+ forceExit?: boolean;
106
+ /**
107
+ * Ream particularity: the bootstrap module's path. AdonisJS hardcodes
108
+ * `tests/bootstrap.ts`; that stays the default here, and this overrides it.
109
+ */
110
+ bootstrap?: string;
111
+ /**
112
+ * Point `@japa/runner/core` at helix's shim in every worker, so official Japa
113
+ * plugins (`@japa/assert`, …) instrument the runner that is actually running.
114
+ *
115
+ * Off by default: redirecting a package specifier is not something to do
116
+ * behind a user's back, and a project with no Japa plugin gains nothing.
117
+ */
118
+ japaPlugins?: boolean;
47
119
  }
48
120
  /** defineConfig helper — like AdonisJS defineConfig(). */
49
121
  export declare function defineConfig(config: ReamrcConfig): ReamrcConfig;
@@ -179,6 +251,24 @@ export declare class Ignitor {
179
251
  stop(): Promise<void>;
180
252
  port(): Promise<number>;
181
253
  getApp(): Application;
254
+ /**
255
+ * The application root passed to `new Ignitor(new URL('../', import.meta.url))`.
256
+ * Undefined for inline/test ignitors created without one.
257
+ */
258
+ getAppRoot(): URL | undefined;
259
+ /** Has the application already been started? */
260
+ isStarted(): boolean;
261
+ /**
262
+ * The `consoleApp` façade — the programmatic console.
263
+ *
264
+ * Built on demand: the console modules are only imported when something
265
+ * actually reaches for them, so an HTTP-only boot does not pay for the CLI.
266
+ */
267
+ consoleApp(): Promise<Console>;
268
+ /** The loaded rc file, if `useRcFile()` was called. */
269
+ getRcFile(): ReamrcConfig | undefined;
270
+ /** The custom module importer, when the app provided one. */
271
+ getImporter(): ((filePath: string) => Promise<unknown>) | undefined;
182
272
  getRouter(): Router;
183
273
  getServer(): Server;
184
274
  /**
@@ -190,17 +280,35 @@ export declare class Ignitor {
190
280
  getPhase(): string;
191
281
  private handleError;
192
282
  }
283
+ export { prettyPrintError };
193
284
  /**
194
- * Pretty-print an error (like AdonisJS prettyPrintError).
195
- */
196
- export declare function prettyPrintError(error: unknown): void;
197
- /**
198
- * ConsoleKernel — handles CLI command dispatching.
199
- * Like AdonisJS: new Ignitor(...).console().handle(process.argv.splice(2))
285
+ * ConsoleKernel — boots the app in console mode and dispatches a command.
286
+ *
287
+ * Console parity: `new Ignitor(...).console().handle(process.argv.slice(2))`.
288
+ *
289
+ * Commands come from two places, as in Console:
290
+ * 1. the app's own `commands/` directory, discovered automatically;
291
+ * 2. `reamrc.commands[]`, which registers commands shipped by packages.
292
+ *
293
+ * The application is booted lazily — only for a command declaring
294
+ * `options.startApp`. A generator has no reason to open a database
295
+ * connection, and paying the boot cost on every `--help` is worse than
296
+ * pointless: it fails on a machine where the DB is simply not running.
200
297
  */
201
298
  export declare class ConsoleKernel {
202
- private ignitor;
299
+ #private;
203
300
  constructor(ignitor: Ignitor);
301
+ /**
302
+ * Build the console kernel and the function that fills it.
303
+ *
304
+ * Shared by `handle()` (the CLI) and the `consoleApp` façade (programmatic use), so
305
+ * both see exactly the same commands — discovery plus `reamrc.commands` —
306
+ * instead of two loaders drifting apart.
307
+ */
308
+ build(): Promise<{
309
+ kernel: ConsoleKernelInstance;
310
+ load: () => Promise<void>;
311
+ }>;
204
312
  handle(argv: string[]): Promise<void>;
205
313
  }
206
314
  //# sourceMappingURL=Ignitor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Ignitor.d.ts","sourceRoot":"","sources":["../src/Ignitor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Ignitor.d.ts","sourceRoot":"","sources":["../src/Ignitor.ts"],"names":[],"mappings":"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"}
package/dist/Ignitor.js CHANGED
@@ -13,8 +13,10 @@
13
13
  * @implements FR17, FR20, FR23
14
14
  */
15
15
  import { Application } from './Application.js';
16
+ import { isCommandClass } from './console/types.js';
16
17
  import { ErrorBoundary } from './ErrorBoundary.js';
17
18
  import { loadEnvFiles } from './env/loadEnvFiles.js';
19
+ import { prettyPrintError } from './errors/prettyPrintError.js';
18
20
  import { ReamError } from './errors/ReamError.js';
19
21
  import { installGracefulShutdown } from './GracefulShutdown.js';
20
22
  import { startHotReload } from './HotReload.js';
@@ -54,6 +56,7 @@ export class Ignitor {
54
56
  phase = 'created';
55
57
  hotReloadCleanup;
56
58
  #shutdownHandle;
59
+ #console;
57
60
  // Inline configuration (for simple use or testing)
58
61
  inlineRoutes;
59
62
  inlineMiddleware = [];
@@ -202,6 +205,12 @@ export class Ignitor {
202
205
  await this.phaseRegister();
203
206
  await this.phaseBoot();
204
207
  await this.phaseStart();
208
+ // Console documents its `consoleApp` service as available once the application has
209
+ // booted, so the locator is installed here rather than on first use —
210
+ // `import consoleApp from '@c9up/ream/services/console'` must not throw in a running
211
+ // app. Only the façade is built (three small modules); loading the
212
+ // commands themselves stays lazy, inside `consoleApp.boot()`.
213
+ await this.consoleApp();
205
214
  await this.phaseReady();
206
215
  }
207
216
  catch (err) {
@@ -221,7 +230,7 @@ export class Ignitor {
221
230
  }
222
231
  /**
223
232
  * Load `.env` files into `process.env` before config and providers boot —
224
- * mirroring AdonisJS, which loads env in BOTH the HTTP and the ace (console)
233
+ * mirroring AdonisJS, which loads env in BOTH the HTTP and the consoleApp (console)
225
234
  * flows. Without this, `ream migrate` and other console commands booted in a
226
235
  * clean subprocess never saw the `.env` the web server got from the shell.
227
236
  *
@@ -487,6 +496,19 @@ export class Ignitor {
487
496
  */
488
497
  async stop() {
489
498
  const errors = [];
499
+ // Release the consoleApp locator first — ownership-guarded, so a second Ignitor
500
+ // having rebound it is left alone.
501
+ if (this.#console !== undefined) {
502
+ const consoleApp = this.#console;
503
+ this.#console = undefined;
504
+ try {
505
+ const { clearConsole } = await import('./services/console.js');
506
+ clearConsole(consoleApp);
507
+ }
508
+ catch {
509
+ /* the module never loaded — nothing to clear */
510
+ }
511
+ }
490
512
  const attempt = async (step) => {
491
513
  try {
492
514
  await step();
@@ -532,6 +554,45 @@ export class Ignitor {
532
554
  getApp() {
533
555
  return this.app;
534
556
  }
557
+ /**
558
+ * The application root passed to `new Ignitor(new URL('../', import.meta.url))`.
559
+ * Undefined for inline/test ignitors created without one.
560
+ */
561
+ getAppRoot() {
562
+ return this.appRoot;
563
+ }
564
+ /** Has the application already been started? */
565
+ isStarted() {
566
+ return this.phase === 'started' || this.phase === 'ready';
567
+ }
568
+ /**
569
+ * The `consoleApp` façade — the programmatic console.
570
+ *
571
+ * Built on demand: the console modules are only imported when something
572
+ * actually reaches for them, so an HTTP-only boot does not pay for the CLI.
573
+ */
574
+ async consoleApp() {
575
+ if (this.#console !== undefined)
576
+ return this.#console;
577
+ const [{ Console }, { setConsole }] = await Promise.all([
578
+ import('./console/Console.js'),
579
+ import('./services/console.js'),
580
+ ]);
581
+ const { kernel, load } = await new ConsoleKernel(this).build();
582
+ const consoleApp = new Console({ kernel, load });
583
+ this.#console = consoleApp;
584
+ setConsole(consoleApp);
585
+ this.app.container.singleton('console', () => consoleApp);
586
+ return consoleApp;
587
+ }
588
+ /** The loaded rc file, if `useRcFile()` was called. */
589
+ getRcFile() {
590
+ return this.reamrc;
591
+ }
592
+ /** The custom module importer, when the app provided one. */
593
+ getImporter() {
594
+ return this.config.importer;
595
+ }
535
596
  getRouter() {
536
597
  return this.router;
537
598
  }
@@ -585,56 +646,191 @@ export class Ignitor {
585
646
  });
586
647
  }
587
648
  }
649
+ export { prettyPrintError };
588
650
  /**
589
- * Pretty-print an error (like AdonisJS prettyPrintError).
590
- */
591
- export function prettyPrintError(error) {
592
- if (error instanceof ReamError) {
593
- console.error(error.toDevString());
594
- }
595
- else if (error instanceof Error) {
596
- console.error(`\n ${error.message}\n`);
597
- if (error.stack)
598
- console.error(error.stack);
599
- }
600
- else {
601
- console.error(error);
602
- }
603
- }
604
- /**
605
- * ConsoleKernel — handles CLI command dispatching.
606
- * Like AdonisJS: new Ignitor(...).console().handle(process.argv.splice(2))
651
+ * ConsoleKernel — boots the app in console mode and dispatches a command.
652
+ *
653
+ * Console parity: `new Ignitor(...).console().handle(process.argv.slice(2))`.
654
+ *
655
+ * Commands come from two places, as in Console:
656
+ * 1. the app's own `commands/` directory, discovered automatically;
657
+ * 2. `reamrc.commands[]`, which registers commands shipped by packages.
658
+ *
659
+ * The application is booted lazily — only for a command declaring
660
+ * `options.startApp`. A generator has no reason to open a database
661
+ * connection, and paying the boot cost on every `--help` is worse than
662
+ * pointless: it fails on a machine where the DB is simply not running.
607
663
  */
608
664
  export class ConsoleKernel {
609
- ignitor;
665
+ #ignitor;
666
+ #started = false;
610
667
  constructor(ignitor) {
611
- this.ignitor = ignitor;
668
+ this.#ignitor = ignitor;
612
669
  }
613
- async handle(argv) {
614
- await this.ignitor.start();
615
- const { CommandRunner } = await import('./console/CommandRunner.js');
616
- const runner = new CommandRunner();
617
- // Auto-register commands from reamrc
618
- const reamrc = this.ignitor.reamrc;
619
- if (reamrc?.commands) {
620
- for (const commandImport of reamrc.commands) {
621
- const mod = await commandImport();
622
- const cmd = mod.default;
623
- if (cmd?.name && typeof cmd?.run === 'function') {
624
- runner.register(cmd);
670
+ /**
671
+ * Build the console kernel and the function that fills it.
672
+ *
673
+ * Shared by `handle()` (the CLI) and the `consoleApp` façade (programmatic use), so
674
+ * both see exactly the same commands — discovery plus `reamrc.commands` —
675
+ * instead of two loaders drifting apart.
676
+ */
677
+ async build() {
678
+ const { Kernel } = await import('./console/Kernel.js');
679
+ const kernel = new Kernel({
680
+ binaryName: 'ream',
681
+ // A `staysAlive` command ends itself through `this.terminate()`; the
682
+ // kernel deliberately does not tear the app down for those.
683
+ onTerminate: async () => {
684
+ if (this.#started)
685
+ await this.#ignitor.stop();
686
+ },
687
+ startApp: async () => {
688
+ // The façade can be used from an already-running application; starting
689
+ // it a second time would re-register providers.
690
+ if (!this.#ignitor.isStarted()) {
691
+ await this.#ignitor.start();
692
+ this.#started = true;
693
+ }
694
+ const app = this.#ignitor.getApp();
695
+ app.container.singleton('consoleKernel', () => kernel);
696
+ return app;
697
+ },
698
+ });
699
+ // Discovery is registered as a LOADER, not run beside the kernel: `boot()`
700
+ // is then the single moment commands come in, whether they were found on
701
+ // disk, declared in the rc file, or added by an application's own loader.
702
+ kernel.addLoader(this.#commandLoader(kernel));
703
+ const load = async () => {
704
+ const aliases = this.#ignitor.getRcFile()?.commandsAliases;
705
+ if (aliases !== undefined) {
706
+ for (const [alias, expansion] of Object.entries(aliases)) {
707
+ kernel.addAlias(alias, expansion);
625
708
  }
626
709
  }
627
- }
628
- this.ignitor.getApp().container.singleton('console', () => runner);
710
+ await kernel.boot();
711
+ };
712
+ return { kernel, load };
713
+ }
714
+ async handle(argv) {
715
+ const { kernel, load } = await this.build();
716
+ await load();
717
+ let staysAlive = false;
629
718
  try {
630
- await runner.handle(argv);
719
+ // `process.execArgv` IS what node was started with — Console's `nodeArgs`,
720
+ // which a command reads to know how the process was launched.
721
+ const result = await kernel.handle(argv, process.execArgv);
722
+ staysAlive = result.staysAlive;
631
723
  }
632
724
  finally {
633
725
  // A throwing command must still release the Ignitor's resources
634
726
  // (scheduler tickers, watchers) — otherwise the CLI process hangs.
635
- await this.ignitor.stop();
727
+ // A `staysAlive` command keeps them on purpose.
728
+ if (this.#started && !staysAlive)
729
+ await this.#ignitor.stop();
730
+ }
731
+ }
732
+ /**
733
+ * The application's commands, as a loader the kernel consumes at boot.
734
+ *
735
+ * Two sources, one collection: the `commands/` directory and the rc file's
736
+ * `commands[]`. Collecting them together is what lets a command listed in
737
+ * both be reported as the duplicate it is.
738
+ */
739
+ #commandLoader(kernel) {
740
+ const found = new Map();
741
+ return {
742
+ getMetaData: async () => {
743
+ await this.#discoverAppCommands(kernel, found);
744
+ await this.#loadPackageCommands(found);
745
+ // Imported here, not at the top of the file: the console stack is only
746
+ // loaded for a console dispatch, and a static import would pull it into
747
+ // every HTTP boot.
748
+ const { serializeCommand } = await import('./console/Kernel.js');
749
+ return [...found.values()].map(serializeCommand);
750
+ },
751
+ getCommand: async (metadata) => found.get(metadata.commandName) ?? null,
752
+ };
753
+ }
754
+ /**
755
+ * Load every command in the app's `commands/` directory.
756
+ *
757
+ * Tolerant by design: a module that does not default-export a command is
758
+ * reported and skipped rather than aborting the whole CLI. Discovery scans a
759
+ * conventional directory, so a stray helper file there must not make
760
+ * `ream list` unusable. Explicitly declared entries (`reamrc.commands`) are
761
+ * held to a stricter standard — see {@link #loadPackageCommands}.
762
+ */
763
+ async #discoverAppCommands(kernel, found) {
764
+ const root = this.#ignitor.getAppRoot();
765
+ if (root === undefined)
766
+ return;
767
+ // The framework's own `FsLoader`, with the application's importer plugged
768
+ // in: scanning a directory for commands is written once, and an app that
769
+ // compiles its sources differently only replaces the import.
770
+ const { FsLoader } = await import('./console/loaders.js');
771
+ const importModule = (file) => this.#importModule(file);
772
+ const loader = new (class extends FsLoader {
773
+ import(file) {
774
+ return importModule(file);
775
+ }
776
+ })(new URL('commands/', root)).onSkipped((fileName) => {
777
+ // Tolerant by design: discovery scans a conventional directory, so a
778
+ // stray helper there must not make `ream list` unusable.
779
+ kernel.logger.warning(`Skipped ${fileName} in commands/ — no default-exported command class.`);
780
+ });
781
+ for (const metadata of await loader.getMetaData()) {
782
+ const command = await loader.getCommand(metadata);
783
+ if (command !== null)
784
+ found.set(command.commandName, command);
785
+ }
786
+ }
787
+ /**
788
+ * Register commands declared in `reamrc.commands[]` — the channel for
789
+ * commands shipped by packages, which discovery cannot see.
790
+ *
791
+ * Strict: an entry listed by hand that does not resolve to a command is a
792
+ * configuration error, and silently ignoring it is how a command "disappears"
793
+ * with no explanation.
794
+ */
795
+ async #loadPackageCommands(found) {
796
+ const declared = this.#ignitor.getRcFile()?.commands;
797
+ if (!declared)
798
+ return;
799
+ for (const entry of declared) {
800
+ const command = commandOf(await entry());
801
+ if (command === undefined) {
802
+ throw new ReamError('E_CONSOLE_INVALID_COMMAND', 'An entry of reamrc.ts `commands` does not default-export a command class.', {
803
+ hint: 'Expected `export default class X { static commandName = "..."; static description = "..."; run() {} }`.',
804
+ });
805
+ }
806
+ // The most likely duplicate after the move to auto-discovery: a command
807
+ // living in `commands/` AND still listed by hand. Say so, rather than
808
+ // leaving the user to guess which two registrations collided.
809
+ const existing = found.get(command.commandName);
810
+ if (existing !== undefined && existing !== command) {
811
+ throw new ReamError('E_CONSOLE_DUPLICATE_COMMAND', `Two commands claim the name "${command.commandName}".`, {
812
+ hint: 'Commands in commands/ are discovered automatically — remove this entry from reamrc.ts `commands`, which is only for commands shipped by packages.',
813
+ });
814
+ }
815
+ found.set(command.commandName, command);
636
816
  }
637
817
  }
818
+ /** Honour the app's custom importer when it provided one. */
819
+ async #importModule(file) {
820
+ const importer = this.#ignitor.getImporter();
821
+ if (importer === undefined)
822
+ return import(file.href);
823
+ const root = this.#ignitor.getAppRoot();
824
+ const relative = root === undefined ? file.href : `./${file.href.slice(root.href.length)}`;
825
+ return importer(relative);
826
+ }
827
+ }
828
+ /** The command a module default-exports, or undefined when it exports none. */
829
+ function commandOf(mod) {
830
+ if (typeof mod !== 'object' || mod === null)
831
+ return undefined;
832
+ const value = Reflect.get(mod, 'default');
833
+ return isCommandClass(value) ? value : undefined;
638
834
  }
639
835
  async function findAvailablePort(desired) {
640
836
  const net = await import('node:net');