@c9up/ream 0.1.26 → 0.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -0
- package/dist/Ignitor.d.ts +115 -7
- package/dist/Ignitor.d.ts.map +1 -1
- package/dist/Ignitor.js +233 -37
- package/dist/Ignitor.js.map +1 -1
- package/dist/console/BaseCommand.d.ts +217 -0
- package/dist/console/BaseCommand.d.ts.map +1 -0
- package/dist/console/BaseCommand.js +340 -0
- package/dist/console/BaseCommand.js.map +1 -0
- package/dist/console/Console.d.ts +86 -0
- package/dist/console/Console.d.ts.map +1 -0
- package/dist/console/Console.js +134 -0
- package/dist/console/Console.js.map +1 -0
- package/dist/console/ExceptionHandler.d.ts +28 -0
- package/dist/console/ExceptionHandler.d.ts.map +1 -0
- package/dist/console/ExceptionHandler.js +76 -0
- package/dist/console/ExceptionHandler.js.map +1 -0
- package/dist/console/HelpCommand.d.ts +16 -0
- package/dist/console/HelpCommand.d.ts.map +1 -0
- package/dist/console/HelpCommand.js +40 -0
- package/dist/console/HelpCommand.js.map +1 -0
- package/dist/console/IndexGenerator.d.ts +24 -0
- package/dist/console/IndexGenerator.d.ts.map +1 -0
- package/dist/console/IndexGenerator.js +65 -0
- package/dist/console/IndexGenerator.js.map +1 -0
- package/dist/console/Kernel.d.ts +298 -0
- package/dist/console/Kernel.d.ts.map +1 -0
- package/dist/console/Kernel.js +1120 -0
- package/dist/console/Kernel.js.map +1 -0
- package/dist/console/ListCommand.d.ts +18 -0
- package/dist/console/ListCommand.d.ts.map +1 -0
- package/dist/console/ListCommand.js +60 -0
- package/dist/console/ListCommand.js.map +1 -0
- package/dist/console/assertions.d.ts +20 -0
- package/dist/console/assertions.d.ts.map +1 -0
- package/dist/console/assertions.js +84 -0
- package/dist/console/assertions.js.map +1 -0
- package/dist/console/cliui.d.ts +248 -0
- package/dist/console/cliui.d.ts.map +1 -0
- package/dist/console/cliui.js +587 -0
- package/dist/console/cliui.js.map +1 -0
- package/dist/console/decorators.d.ts +90 -0
- package/dist/console/decorators.d.ts.map +1 -0
- package/dist/console/decorators.js +142 -0
- package/dist/console/decorators.js.map +1 -0
- package/dist/console/index.d.ts +32 -0
- package/dist/console/index.d.ts.map +1 -0
- package/dist/console/index.js +24 -0
- package/dist/console/index.js.map +1 -0
- package/dist/console/loaders.d.ts +52 -0
- package/dist/console/loaders.d.ts.map +1 -0
- package/dist/console/loaders.js +131 -0
- package/dist/console/loaders.js.map +1 -0
- package/dist/console/parser.d.ts +86 -0
- package/dist/console/parser.d.ts.map +1 -0
- package/dist/console/parser.js +440 -0
- package/dist/console/parser.js.map +1 -0
- package/dist/console/prompts.d.ts +124 -0
- package/dist/console/prompts.d.ts.map +1 -0
- package/dist/console/prompts.js +364 -0
- package/dist/console/prompts.js.map +1 -0
- package/dist/console/tracing.d.ts +17 -0
- package/dist/console/tracing.d.ts.map +1 -0
- package/dist/console/tracing.js +11 -0
- package/dist/console/tracing.js.map +1 -0
- package/dist/console/types.d.ts +255 -0
- package/dist/console/types.d.ts.map +1 -0
- package/dist/console/types.js +25 -0
- package/dist/console/types.js.map +1 -0
- package/dist/console/ui.d.ts +20 -0
- package/dist/console/ui.d.ts.map +1 -0
- package/dist/console/ui.js +31 -0
- package/dist/console/ui.js.map +1 -0
- package/dist/console/utils.d.ts +27 -0
- package/dist/console/utils.d.ts.map +1 -0
- package/dist/console/utils.js +56 -0
- package/dist/console/utils.js.map +1 -0
- package/dist/container/Container.d.ts.map +1 -1
- package/dist/container/Container.js +40 -12
- package/dist/container/Container.js.map +1 -1
- package/dist/container/types.d.ts +14 -2
- package/dist/container/types.d.ts.map +1 -1
- package/dist/decorators/Service.d.ts +7 -0
- package/dist/decorators/Service.d.ts.map +1 -1
- package/dist/decorators/Service.js +23 -3
- package/dist/decorators/Service.js.map +1 -1
- package/dist/errors/ReamError.d.ts +25 -0
- package/dist/errors/ReamError.d.ts.map +1 -1
- package/dist/errors/ReamError.js +5 -0
- package/dist/errors/ReamError.js.map +1 -1
- package/dist/errors/prettyPrintError.d.ts +9 -0
- package/dist/errors/prettyPrintError.d.ts.map +1 -0
- package/dist/errors/prettyPrintError.js +22 -0
- package/dist/errors/prettyPrintError.js.map +1 -0
- package/dist/index.d.ts +14 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -2
- package/dist/index.js.map +1 -1
- package/dist/middleware/Pipeline.d.ts +29 -8
- package/dist/middleware/Pipeline.d.ts.map +1 -1
- package/dist/middleware/Pipeline.js +39 -1
- package/dist/middleware/Pipeline.js.map +1 -1
- package/dist/rpc/RpcRouter.d.ts.map +1 -1
- package/dist/rpc/RpcRouter.js +13 -2
- package/dist/rpc/RpcRouter.js.map +1 -1
- package/dist/services/app.d.ts +1 -1
- package/dist/services/app.js +1 -1
- package/dist/services/console.d.ts +21 -0
- package/dist/services/console.d.ts.map +1 -0
- package/dist/services/console.js +35 -0
- package/dist/services/console.js.map +1 -0
- package/dist/testing/ApiResponse.d.ts +218 -0
- package/dist/testing/ApiResponse.d.ts.map +1 -0
- package/dist/testing/ApiResponse.js +576 -0
- package/dist/testing/ApiResponse.js.map +1 -0
- package/dist/testing/RequestBuilder.d.ts +132 -72
- package/dist/testing/RequestBuilder.d.ts.map +1 -1
- package/dist/testing/RequestBuilder.js +389 -222
- package/dist/testing/RequestBuilder.js.map +1 -1
- package/dist/testing/TestClient.d.ts +34 -39
- package/dist/testing/TestClient.d.ts.map +1 -1
- package/dist/testing/TestClient.js +48 -56
- package/dist/testing/TestClient.js.map +1 -1
- package/dist/utils/Macroable.js +3 -3
- package/dist/utils/Macroable.js.map +1 -1
- package/events.darwin-arm64.node +0 -0
- package/events.darwin-x64.node +0 -0
- package/events.linux-arm64-gnu.node +0 -0
- package/events.linux-x64-gnu.node +0 -0
- package/events.win32-x64-msvc.node +0 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +15 -2
- package/scheduler.darwin-arm64.node +0 -0
- package/scheduler.darwin-x64.node +0 -0
- package/scheduler.linux-arm64-gnu.node +0 -0
- package/scheduler.linux-x64-gnu.node +0 -0
- package/scheduler.win32-x64-msvc.node +0 -0
- package/src/Ignitor.ts +340 -42
- package/src/console/BaseCommand.ts +467 -0
- package/src/console/Console.ts +174 -0
- package/src/console/ExceptionHandler.ts +91 -0
- package/src/console/HelpCommand.ts +37 -0
- package/src/console/IndexGenerator.ts +79 -0
- package/src/console/Kernel.ts +1382 -0
- package/src/console/ListCommand.ts +55 -0
- package/src/console/assertions.ts +114 -0
- package/src/console/cliui.ts +756 -0
- package/src/console/decorators.ts +199 -0
- package/src/console/index.ts +67 -0
- package/src/console/loaders.ts +145 -0
- package/src/console/parser.ts +598 -0
- package/src/console/prompts.ts +556 -0
- package/src/console/tracing.ts +22 -0
- package/src/console/types.ts +280 -0
- package/src/console/ui.ts +37 -0
- package/src/console/utils.ts +69 -0
- package/src/container/Container.ts +55 -13
- package/src/container/types.ts +15 -2
- package/src/decorators/Service.ts +28 -3
- package/src/errors/ReamError.ts +31 -0
- package/src/errors/prettyPrintError.ts +20 -0
- package/src/index.ts +30 -4
- package/src/middleware/Pipeline.ts +76 -5
- package/src/rpc/RpcRouter.ts +16 -2
- package/src/services/app.ts +1 -1
- package/src/services/console.ts +42 -0
- package/src/testing/ApiResponse.ts +691 -0
- package/src/testing/RequestBuilder.ts +498 -275
- package/src/testing/TestClient.ts +63 -86
- package/src/utils/Macroable.ts +3 -3
- package/dist/console/CommandRunner.d.ts +0 -18
- package/dist/console/CommandRunner.d.ts.map +0 -1
- package/dist/console/CommandRunner.js +0 -65
- package/dist/console/CommandRunner.js.map +0 -1
- 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
|
-
*
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
*
|
|
199
|
-
*
|
|
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
|
|
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
|
package/dist/Ignitor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Ignitor.d.ts","sourceRoot":"","sources":["../src/Ignitor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;
|
|
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
|
|
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
|
-
*
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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
|
|
668
|
+
this.#ignitor = ignitor;
|
|
612
669
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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');
|