@c9up/ream 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -9
- package/dist/Application.d.ts +85 -5
- package/dist/Application.d.ts.map +1 -1
- package/dist/Application.js +171 -21
- package/dist/Application.js.map +1 -1
- package/dist/ErrorBoundary.d.ts +1 -7
- package/dist/ErrorBoundary.d.ts.map +1 -1
- package/dist/ErrorBoundary.js +31 -31
- package/dist/ErrorBoundary.js.map +1 -1
- package/dist/HttpKernel.js +2 -2
- package/dist/HttpKernel.js.map +1 -1
- package/dist/Ignitor.d.ts +18 -49
- package/dist/Ignitor.d.ts.map +1 -1
- package/dist/Ignitor.js +211 -173
- package/dist/Ignitor.js.map +1 -1
- package/dist/env/Env.d.ts +6 -0
- package/dist/env/Env.d.ts.map +1 -1
- package/dist/env/Env.js +6 -0
- package/dist/env/Env.js.map +1 -1
- package/dist/events/Emitter.d.ts +0 -6
- package/dist/events/Emitter.d.ts.map +1 -1
- package/dist/events/Emitter.js +42 -42
- package/dist/events/Emitter.js.map +1 -1
- package/dist/events/native.d.ts +13 -11
- package/dist/events/native.d.ts.map +1 -1
- package/dist/events/native.js.map +1 -1
- package/dist/helpers/compose.d.ts +30 -0
- package/dist/helpers/compose.d.ts.map +1 -0
- package/dist/helpers/compose.js +27 -0
- package/dist/helpers/compose.js.map +1 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.d.ts.map +1 -1
- package/dist/helpers/index.js +1 -0
- package/dist/helpers/index.js.map +1 -1
- package/dist/helpers/is.d.ts +69 -16
- package/dist/helpers/is.d.ts.map +1 -1
- package/dist/helpers/is.js +79 -16
- package/dist/helpers/is.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/middleware/Pipeline.d.ts +9 -3
- package/dist/middleware/Pipeline.d.ts.map +1 -1
- package/dist/middleware/Pipeline.js +10 -4
- package/dist/middleware/Pipeline.js.map +1 -1
- package/dist/native/generated.d.ts +243 -0
- package/dist/native/generated.d.ts.map +1 -0
- package/dist/native/generated.js +7 -0
- package/dist/native/generated.js.map +1 -0
- package/dist/rpc/RpcRouter.js +2 -2
- package/dist/rpc/RpcRouter.js.map +1 -1
- package/dist/services/app.d.ts +6 -0
- package/dist/services/app.d.ts.map +1 -1
- package/dist/services/app.js +8 -0
- package/dist/services/app.js.map +1 -1
- package/dist/services/config.d.ts +15 -0
- package/dist/services/config.d.ts.map +1 -0
- package/dist/services/config.js +18 -0
- package/dist/services/config.js.map +1 -0
- package/dist/services/encryption.d.ts +23 -0
- package/dist/services/encryption.d.ts.map +1 -0
- package/dist/services/encryption.js +33 -0
- package/dist/services/encryption.js.map +1 -0
- package/dist/services/urlBuilder.d.ts +21 -0
- package/dist/services/urlBuilder.d.ts.map +1 -0
- package/dist/services/urlBuilder.js +34 -0
- package/dist/services/urlBuilder.js.map +1 -0
- package/dist/session/drivers/CookieDriver.d.ts +1 -1
- package/dist/session/drivers/CookieDriver.d.ts.map +1 -1
- package/dist/session/drivers/CookieDriver.js +4 -4
- package/dist/session/drivers/CookieDriver.js.map +1 -1
- package/dist/testing/ApiResponse.d.ts +44 -44
- package/dist/testing/ApiResponse.d.ts.map +1 -1
- package/dist/testing/ApiResponse.js +48 -48
- package/dist/testing/ApiResponse.js.map +1 -1
- package/dist/testing/RequestBuilder.d.ts +39 -39
- package/dist/testing/RequestBuilder.d.ts.map +1 -1
- package/dist/testing/RequestBuilder.js +49 -49
- package/dist/testing/RequestBuilder.js.map +1 -1
- package/dist/testing/TestClient.d.ts +5 -5
- package/dist/testing/TestClient.d.ts.map +1 -1
- package/dist/testing/TestClient.js +7 -7
- package/dist/testing/TestClient.js.map +1 -1
- package/events.win32-x64-msvc.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +15 -2
- package/scheduler.win32-x64-msvc.node +0 -0
- package/scripts/build-napi-types.mjs +68 -0
- package/scripts/generate-napi-types.mjs +171 -0
- package/src/Application.ts +221 -21
- package/src/ErrorBoundary.ts +31 -31
- package/src/HttpKernel.ts +2 -2
- package/src/Ignitor.ts +228 -176
- package/src/env/Env.ts +6 -0
- package/src/events/Emitter.ts +42 -42
- package/src/events/native.ts +21 -14
- package/src/helpers/compose.ts +84 -0
- package/src/helpers/index.ts +1 -0
- package/src/helpers/is.ts +87 -16
- package/src/index.ts +3 -3
- package/src/middleware/Pipeline.ts +10 -4
- package/src/native/generated.ts +291 -0
- package/src/rpc/RpcRouter.ts +2 -2
- package/src/services/app.ts +9 -0
- package/src/services/config.ts +24 -0
- package/src/services/encryption.ts +42 -0
- package/src/services/urlBuilder.ts +41 -0
- package/src/session/drivers/CookieDriver.ts +4 -4
- package/src/testing/ApiResponse.ts +52 -52
- package/src/testing/RequestBuilder.ts +55 -55
- package/src/testing/TestClient.ts +8 -8
package/src/Ignitor.ts
CHANGED
|
@@ -41,12 +41,21 @@ import { CookieSigner } from './security/CookieSigner.js'
|
|
|
41
41
|
import { SignedUrl } from './security/SignedUrl.js'
|
|
42
42
|
import { Server } from './server/Server.js'
|
|
43
43
|
import { clearApp, setApp } from './services/app.js'
|
|
44
|
+
import { clearEncryption, setEncryption } from './services/encryption.js'
|
|
44
45
|
import { clearRouter, setRouter } from './services/router.js'
|
|
45
46
|
import { clearServer, setServer } from './services/server.js'
|
|
46
47
|
import { registerSessionTemplateTags } from './session/templateTags.js'
|
|
47
48
|
|
|
48
49
|
/** Application environment. */
|
|
49
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Re-exported from `Application`, which is where it is declared: the
|
|
52
|
+
* environment belongs to the application (a provider reads
|
|
53
|
+
* `app.getEnvironment()`), and the Ignitor is only where the entry point
|
|
54
|
+
* declares it. Two declarations would be two types that happen to look alike.
|
|
55
|
+
*/
|
|
56
|
+
export type { AppEnvironment } from './Application.js'
|
|
57
|
+
|
|
58
|
+
import type { AppEnvironment } from './Application.js'
|
|
50
59
|
|
|
51
60
|
/**
|
|
52
61
|
* Reamrc config — like AdonisJS adonisrc.ts with defineConfig().
|
|
@@ -94,7 +103,7 @@ export interface TestSuiteConfig {
|
|
|
94
103
|
name: string
|
|
95
104
|
/**
|
|
96
105
|
* The suite's files: a glob, several, or a callback returning their URLs
|
|
97
|
-
* (
|
|
106
|
+
* (helix `TestFiles`).
|
|
98
107
|
*/
|
|
99
108
|
files: string | string[] | (() => URL[] | Promise<URL[]>)
|
|
100
109
|
/** Per-test timeout for this suite, in ms. */
|
|
@@ -102,7 +111,7 @@ export interface TestSuiteConfig {
|
|
|
102
111
|
/** Extra attempts on failure for this suite. */
|
|
103
112
|
retries?: number
|
|
104
113
|
/**
|
|
105
|
-
* Configure the suite before it runs (
|
|
114
|
+
* Configure the suite before it runs (helix `TestSuite.configure`). Receives
|
|
106
115
|
* the same handle as the bootstrap's `configureSuite`, and runs after it.
|
|
107
116
|
*
|
|
108
117
|
* Costs an import of the rc file in every worker, since a function cannot
|
|
@@ -128,7 +137,7 @@ export interface TestSuiteHandle {
|
|
|
128
137
|
/**
|
|
129
138
|
* The `tests` block of the rc file — AdonisJS `adonisrc.ts` `tests`, field for
|
|
130
139
|
* field. The runner that consumes it is helix; ream reads the file, exactly as
|
|
131
|
-
* `@adonisjs/core` reads adonisrc and hands the suites to
|
|
140
|
+
* `@adonisjs/core` reads adonisrc and hands the suites to helix.
|
|
132
141
|
*/
|
|
133
142
|
export interface TestsConfig {
|
|
134
143
|
/** Named suites. `ream test` with no argument runs them all, in order. */
|
|
@@ -144,8 +153,34 @@ export interface TestsConfig {
|
|
|
144
153
|
bootstrap?: string
|
|
145
154
|
}
|
|
146
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Every option the `tests` block accepts.
|
|
158
|
+
*
|
|
159
|
+
* Kept beside the interface because it is the same list: TypeScript's
|
|
160
|
+
* excess-property check only fires on an object literal, so a key that reached
|
|
161
|
+
* `defineConfig` through a variable or a spread is invisible to it.
|
|
162
|
+
*/
|
|
163
|
+
const TESTS_OPTIONS = ['bootstrap', 'forceExit', 'suites', 'timeout'] as const
|
|
164
|
+
|
|
147
165
|
/** defineConfig helper — like AdonisJS defineConfig(). */
|
|
148
166
|
export function defineConfig(config: ReamrcConfig): ReamrcConfig {
|
|
167
|
+
// A key nobody reads is worse than a rejected one: an option that was
|
|
168
|
+
// renamed, removed between versions, or simply mistyped sits in the rc file
|
|
169
|
+
// doing nothing, and the run behaves as if it had never been written. Say so
|
|
170
|
+
// instead, and list what the block does accept — which is also the answer to
|
|
171
|
+
// "then where did my option go".
|
|
172
|
+
const unknown = Object.keys(config.tests ?? {}).filter(
|
|
173
|
+
(key) => !TESTS_OPTIONS.includes(key as (typeof TESTS_OPTIONS)[number]),
|
|
174
|
+
)
|
|
175
|
+
if (unknown.length > 0) {
|
|
176
|
+
throw new ReamError(
|
|
177
|
+
'E_REAMRC_UNKNOWN_OPTION',
|
|
178
|
+
`\`tests\` has no option named ${unknown.map((k) => `\`${k}\``).join(', ')}.`,
|
|
179
|
+
{
|
|
180
|
+
hint: `The \`tests\` block accepts: ${TESTS_OPTIONS.join(', ')}. An option removed between versions is gone from the rc file too — the runner is configured in the bootstrap module now.`,
|
|
181
|
+
},
|
|
182
|
+
)
|
|
183
|
+
}
|
|
149
184
|
return config
|
|
150
185
|
}
|
|
151
186
|
|
|
@@ -202,31 +237,36 @@ export interface IgnitorConfig {
|
|
|
202
237
|
* Lifecycle: register → boot → start → ready → shutdown
|
|
203
238
|
*/
|
|
204
239
|
export class Ignitor {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
240
|
+
#app: Application
|
|
241
|
+
#router: Router
|
|
242
|
+
#server: Server
|
|
243
|
+
#middleware: MiddlewareRegistry
|
|
209
244
|
/** Where each data package registers its migration runner. See `MigrationRegistry`. */
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
245
|
+
#migrations: MigrationRegistry
|
|
246
|
+
#errorBoundary: ErrorBoundary
|
|
247
|
+
/**
|
|
248
|
+
* The APP_KEY-backed signer, when there is one. Held so `stop()` can release
|
|
249
|
+
* the `services/encryption` locator against the exact instance it installed
|
|
250
|
+
* — an unguarded clear would tear down a newer Ignitor's binding.
|
|
251
|
+
*/
|
|
252
|
+
#signer?: CookieSigner
|
|
253
|
+
#_httpServer?: HyperServerLike
|
|
254
|
+
#config: IgnitorConfig
|
|
255
|
+
#appRoot?: URL
|
|
256
|
+
#reamrc?: ReamrcConfig
|
|
257
|
+
#providers: ProviderContract[] = []
|
|
258
|
+
#errorListeners: Array<(event: ErrorEvent) => void> = []
|
|
259
|
+
#phase: 'created' | 'registered' | 'booted' | 'started' | 'ready' | 'shutdown' = 'created'
|
|
260
|
+
#hotReloadCleanup?: () => void
|
|
221
261
|
#shutdownHandle?: ShutdownHandle
|
|
222
262
|
#host?: string
|
|
223
263
|
#console?: Console
|
|
224
264
|
|
|
225
265
|
// Inline configuration (for simple use or testing)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
266
|
+
#inlineRoutes?: (router: Router) => void
|
|
267
|
+
#inlineMiddleware: MiddlewareFunction[] = []
|
|
268
|
+
#inlineNamedMiddleware: Array<[string, MiddlewareFunction]> = []
|
|
269
|
+
#inlineProviderFactories: Array<(app: Application) => ProviderContract> = []
|
|
230
270
|
|
|
231
271
|
/**
|
|
232
272
|
* Create the Ignitor.
|
|
@@ -239,47 +279,47 @@ export class Ignitor {
|
|
|
239
279
|
*/
|
|
240
280
|
constructor(appRootOrConfig?: URL | IgnitorConfig, config?: IgnitorConfig) {
|
|
241
281
|
if (appRootOrConfig instanceof URL) {
|
|
242
|
-
this
|
|
243
|
-
this
|
|
282
|
+
this.#appRoot = appRootOrConfig
|
|
283
|
+
this.#config = config ?? {}
|
|
244
284
|
} else {
|
|
245
|
-
this
|
|
285
|
+
this.#config = appRootOrConfig ?? {}
|
|
246
286
|
}
|
|
247
287
|
|
|
248
|
-
this
|
|
249
|
-
this
|
|
250
|
-
this
|
|
251
|
-
this
|
|
252
|
-
this
|
|
253
|
-
this
|
|
288
|
+
this.#app = new Application()
|
|
289
|
+
this.#router = new Router()
|
|
290
|
+
this.#server = new Server(this.#router)
|
|
291
|
+
this.#middleware = new MiddlewareRegistry()
|
|
292
|
+
this.#migrations = new MigrationRegistry()
|
|
293
|
+
this.#errorBoundary = new ErrorBoundary((event) => this.#handleError(event), this.isDevMode())
|
|
254
294
|
|
|
255
295
|
// Register framework services in container
|
|
256
|
-
this
|
|
257
|
-
this
|
|
258
|
-
this
|
|
296
|
+
this.#app.container.singleton('router', () => this.#router)
|
|
297
|
+
this.#app.container.singleton('server', () => this.#server)
|
|
298
|
+
this.#app.container.singleton('middleware', () => this.#middleware)
|
|
259
299
|
// Always bound, even with no data package: an EMPTY registry is what lets
|
|
260
300
|
// the CLI tell "this app has no migrations" from "this app predates the
|
|
261
301
|
// registry" and say something useful in each case.
|
|
262
|
-
this
|
|
263
|
-
this
|
|
302
|
+
this.#app.container.singleton('migrations', () => this.#migrations)
|
|
303
|
+
this.#app.container.singleton('app', () => this.#app)
|
|
264
304
|
// AdonisJS binds the config store too, so code that only needs settings can
|
|
265
305
|
// resolve `config` instead of taking the whole application.
|
|
266
|
-
this
|
|
306
|
+
this.#app.container.singleton('config', () => this.#app.config)
|
|
267
307
|
// The HttpContext CLASS, so an integration provider can extend it the way
|
|
268
308
|
// AdonisJS does — `HttpContext.getter('view', …)` is how the view layer
|
|
269
309
|
// attaches `ctx.view`. Exported from the barrel too, but a provider that
|
|
270
310
|
// must stay agnostic reaches it through the container instead of importing
|
|
271
311
|
// ream at runtime.
|
|
272
|
-
this
|
|
312
|
+
this.#app.container.singleton('HttpContext', () => HttpContext)
|
|
273
313
|
// `appRoot` is the URL passed to `new Ignitor(new URL('../', import.meta.url))`.
|
|
274
314
|
// Providers resolve it through the container so they can interpret
|
|
275
315
|
// relative paths in config files (e.g. `pages.root: './resources/pages'`)
|
|
276
316
|
// against the project root — same convention `modules.path` uses.
|
|
277
|
-
if (this
|
|
278
|
-
const root = this
|
|
279
|
-
this
|
|
317
|
+
if (this.#appRoot) {
|
|
318
|
+
const root = this.#appRoot
|
|
319
|
+
this.#app.container.singleton('appRoot', () => root)
|
|
280
320
|
// Also expose it on the Application for the AdonisJS-style path helpers
|
|
281
321
|
// (`app.makePath`, `app.configPath`, `app.migrationsPath`, …).
|
|
282
|
-
this
|
|
322
|
+
this.#app.setAppRoot(root)
|
|
283
323
|
}
|
|
284
324
|
|
|
285
325
|
// Encryption / cookie-signing service (AdonisJS `APP_KEY` idiom). Registered
|
|
@@ -294,18 +334,20 @@ export class Ignitor {
|
|
|
294
334
|
)
|
|
295
335
|
}
|
|
296
336
|
const signer = new CookieSigner(appKey)
|
|
297
|
-
this
|
|
337
|
+
this.#app.container.singleton('encryption', () => signer)
|
|
338
|
+
this.#signer = signer
|
|
339
|
+
setEncryption(signer)
|
|
298
340
|
// Signed-URL helper (same APP_KEY): the router signs via makeSignedUrl,
|
|
299
341
|
// HttpContext hands it to the request so hasValidSignature() can verify.
|
|
300
342
|
const signedUrl = new SignedUrl({ secret: appKey })
|
|
301
|
-
this
|
|
302
|
-
this
|
|
343
|
+
this.#app.container.singleton('signedUrl', () => signedUrl)
|
|
344
|
+
this.#router.setSignedUrl(signedUrl)
|
|
303
345
|
}
|
|
304
346
|
|
|
305
347
|
// Set service singletons so route/kernel files can import them
|
|
306
|
-
setApp(this
|
|
307
|
-
setRouter(this
|
|
308
|
-
setServer(this
|
|
348
|
+
setApp(this.#app)
|
|
349
|
+
setRouter(this.#router)
|
|
350
|
+
setServer(this.#server)
|
|
309
351
|
}
|
|
310
352
|
|
|
311
353
|
// ─── Configuration ────────────────────────────────────────
|
|
@@ -315,28 +357,34 @@ export class Ignitor {
|
|
|
315
357
|
* Like AdonisJS: .tap((app) => { app.booting(...) })
|
|
316
358
|
*/
|
|
317
359
|
tap(callback: (app: Application) => void): this {
|
|
318
|
-
callback(this
|
|
360
|
+
callback(this.#app)
|
|
319
361
|
return this
|
|
320
362
|
}
|
|
321
363
|
|
|
322
|
-
/**
|
|
364
|
+
/**
|
|
365
|
+
* Set the application environment.
|
|
366
|
+
*
|
|
367
|
+
* Stored on the Application, which is where a provider reads it
|
|
368
|
+
* (`app.getEnvironment()`, the AdonisJS shape). The Ignitor keeps the setter
|
|
369
|
+
* because that is where the entry point declares it.
|
|
370
|
+
*/
|
|
323
371
|
setEnvironment(env: AppEnvironment): this {
|
|
324
|
-
this.
|
|
372
|
+
this.#app.setEnvironment(env)
|
|
325
373
|
return this
|
|
326
374
|
}
|
|
327
375
|
|
|
328
376
|
getEnvironment(): AppEnvironment {
|
|
329
|
-
return this.
|
|
377
|
+
return this.#app.getEnvironment()
|
|
330
378
|
}
|
|
331
379
|
|
|
332
380
|
/**
|
|
333
381
|
* Load the reamrc config (equivalent to adonisrc.ts).
|
|
334
382
|
*/
|
|
335
383
|
useRcFile(reamrc: ReamrcConfig): this {
|
|
336
|
-
this
|
|
384
|
+
this.#reamrc = reamrc
|
|
337
385
|
// Hand the app its directory overrides now rather than at boot: a path
|
|
338
386
|
// helper called from a provider's register() must already see them.
|
|
339
|
-
this
|
|
387
|
+
this.#app.rcContents(reamrc)
|
|
340
388
|
return this
|
|
341
389
|
}
|
|
342
390
|
|
|
@@ -344,37 +392,37 @@ export class Ignitor {
|
|
|
344
392
|
|
|
345
393
|
/** Define routes inline (simple mode). */
|
|
346
394
|
routes(callback: (router: Router) => void): this {
|
|
347
|
-
this
|
|
395
|
+
this.#inlineRoutes = callback
|
|
348
396
|
return this
|
|
349
397
|
}
|
|
350
398
|
|
|
351
399
|
/** Add global middleware inline. */
|
|
352
400
|
use(mw: MiddlewareFunction): this {
|
|
353
|
-
this
|
|
401
|
+
this.#inlineMiddleware.push(mw)
|
|
354
402
|
return this
|
|
355
403
|
}
|
|
356
404
|
|
|
357
405
|
/** Register a named middleware inline. */
|
|
358
406
|
named(name: string, mw: MiddlewareFunction): this {
|
|
359
|
-
this
|
|
407
|
+
this.#inlineNamedMiddleware.push([name, mw])
|
|
360
408
|
return this
|
|
361
409
|
}
|
|
362
410
|
|
|
363
411
|
/** Register a provider inline (for testing or simple apps). */
|
|
364
412
|
provider(factory: (app: Application) => ProviderContract): this {
|
|
365
|
-
this
|
|
413
|
+
this.#inlineProviderFactories.push(factory)
|
|
366
414
|
return this
|
|
367
415
|
}
|
|
368
416
|
|
|
369
417
|
/** Listen for error events. */
|
|
370
418
|
onError(listener: (event: ErrorEvent) => void): this {
|
|
371
|
-
this
|
|
419
|
+
this.#errorListeners.push(listener)
|
|
372
420
|
return this
|
|
373
421
|
}
|
|
374
422
|
|
|
375
423
|
/** Set a config value. */
|
|
376
424
|
configure(key: string, value: unknown): this {
|
|
377
|
-
this
|
|
425
|
+
this.#app.config.set(key, value)
|
|
378
426
|
return this
|
|
379
427
|
}
|
|
380
428
|
|
|
@@ -382,19 +430,19 @@ export class Ignitor {
|
|
|
382
430
|
|
|
383
431
|
/** Configure for HTTP server mode. */
|
|
384
432
|
httpServer(): this {
|
|
385
|
-
this.
|
|
433
|
+
this.#app.setEnvironment('web')
|
|
386
434
|
return this
|
|
387
435
|
}
|
|
388
436
|
|
|
389
437
|
/** Configure for CLI/console mode. Returns a ConsoleKernel for dispatching commands. */
|
|
390
438
|
console(): ConsoleKernel {
|
|
391
|
-
this.
|
|
439
|
+
this.#app.setEnvironment('console')
|
|
392
440
|
return new ConsoleKernel(this)
|
|
393
441
|
}
|
|
394
442
|
|
|
395
443
|
/** Configure for test mode. */
|
|
396
444
|
testMode(): this {
|
|
397
|
-
this.
|
|
445
|
+
this.#app.setEnvironment('test')
|
|
398
446
|
return this
|
|
399
447
|
}
|
|
400
448
|
|
|
@@ -403,25 +451,25 @@ export class Ignitor {
|
|
|
403
451
|
async start(): Promise<Ignitor> {
|
|
404
452
|
// Double-start guard: a second start() would re-run phaseRegister and
|
|
405
453
|
// instantiate + register every reamrc provider a second time.
|
|
406
|
-
if (this
|
|
454
|
+
if (this.#phase !== 'created') {
|
|
407
455
|
throw new ReamError(
|
|
408
456
|
'IGNITOR_ALREADY_STARTED',
|
|
409
|
-
`start() called while in phase '${this
|
|
457
|
+
`start() called while in phase '${this.#phase}' — an Ignitor boots once`,
|
|
410
458
|
{ hint: 'Create a new Ignitor instance instead of restarting this one.' },
|
|
411
459
|
)
|
|
412
460
|
}
|
|
413
461
|
this.#loadEnvironmentFiles()
|
|
414
462
|
try {
|
|
415
|
-
await this
|
|
416
|
-
await this
|
|
417
|
-
await this
|
|
463
|
+
await this.#phaseRegister()
|
|
464
|
+
await this.#phaseBoot()
|
|
465
|
+
await this.#phaseStart()
|
|
418
466
|
// Console documents its `consoleApp` service as available once the application has
|
|
419
467
|
// booted, so the locator is installed here rather than on first use —
|
|
420
468
|
// `import consoleApp from '@c9up/ream/services/console'` must not throw in a running
|
|
421
469
|
// app. Only the façade is built (three small modules); loading the
|
|
422
470
|
// commands themselves stays lazy, inside `consoleApp.boot()`.
|
|
423
471
|
await this.consoleApp()
|
|
424
|
-
await this
|
|
472
|
+
await this.#phaseReady()
|
|
425
473
|
} catch (err) {
|
|
426
474
|
// A throw mid-boot (e.g. a provider ready() failing AFTER the HTTP port
|
|
427
475
|
// is bound) must release everything the partial boot opened — port,
|
|
@@ -447,8 +495,8 @@ export class Ignitor {
|
|
|
447
495
|
* `process.env` win, so the shell / CI always overrides the files.
|
|
448
496
|
*/
|
|
449
497
|
#loadEnvironmentFiles(): void {
|
|
450
|
-
if (!this
|
|
451
|
-
loadEnvFiles(this
|
|
498
|
+
if (!this.#appRoot) return
|
|
499
|
+
loadEnvFiles(this.#appRoot, { skipEnvLocal: this.#app.getEnvironment() === 'test' })
|
|
452
500
|
}
|
|
453
501
|
|
|
454
502
|
/**
|
|
@@ -458,10 +506,10 @@ export class Ignitor {
|
|
|
458
506
|
* The engine is duck-typed out of the container so ream carries no dependency
|
|
459
507
|
* on it, and an app with no template layer simply skips this.
|
|
460
508
|
*/
|
|
461
|
-
|
|
509
|
+
async #registerSessionTemplateTags(): Promise<void> {
|
|
462
510
|
for (const token of ['inker', 'view']) {
|
|
463
|
-
if (!this
|
|
464
|
-
const binding: unknown = await this
|
|
511
|
+
if (!this.#app.container.has(token)) continue
|
|
512
|
+
const binding: unknown = await this.#app.container.resolve(token)
|
|
465
513
|
for (const candidate of [binding, Reflect.get(Object(binding), '_templates')]) {
|
|
466
514
|
const register = Reflect.get(Object(candidate), 'registerTag')
|
|
467
515
|
if (typeof register !== 'function') continue
|
|
@@ -475,106 +523,106 @@ export class Ignitor {
|
|
|
475
523
|
}
|
|
476
524
|
}
|
|
477
525
|
|
|
478
|
-
|
|
526
|
+
async #phaseRegister(): Promise<void> {
|
|
479
527
|
// Auto-load config/*.ts files into app.config
|
|
480
|
-
await this
|
|
528
|
+
await this.#autoloadConfig()
|
|
481
529
|
|
|
482
530
|
// Load providers from reamrc
|
|
483
|
-
if (this
|
|
484
|
-
for (const providerEntry of this
|
|
531
|
+
if (this.#reamrc?.providers) {
|
|
532
|
+
for (const providerEntry of this.#reamrc.providers) {
|
|
485
533
|
const providerImport =
|
|
486
534
|
typeof providerEntry === 'function' ? providerEntry : providerEntry.file
|
|
487
535
|
const env = typeof providerEntry === 'function' ? undefined : providerEntry.environment
|
|
488
536
|
|
|
489
537
|
// Skip providers not matching current environment
|
|
490
|
-
if (env && !env.includes(this.
|
|
538
|
+
if (env && !env.includes(this.#app.getEnvironment())) continue
|
|
491
539
|
|
|
492
540
|
const mod = await providerImport()
|
|
493
541
|
const ProviderClass = mod.default
|
|
494
|
-
const instance = new ProviderClass(this
|
|
495
|
-
this
|
|
496
|
-
this
|
|
542
|
+
const instance = new ProviderClass(this.#app)
|
|
543
|
+
this.#providers.push(instance)
|
|
544
|
+
this.#app.register(instance)
|
|
497
545
|
}
|
|
498
546
|
}
|
|
499
547
|
|
|
500
548
|
// Register inline providers
|
|
501
|
-
for (const factory of this
|
|
502
|
-
const instance = factory(this
|
|
503
|
-
this
|
|
504
|
-
this
|
|
549
|
+
for (const factory of this.#inlineProviderFactories) {
|
|
550
|
+
const instance = factory(this.#app)
|
|
551
|
+
this.#providers.push(instance)
|
|
552
|
+
this.#app.register(instance)
|
|
505
553
|
}
|
|
506
554
|
|
|
507
|
-
this
|
|
555
|
+
this.#phase = 'registered'
|
|
508
556
|
}
|
|
509
557
|
|
|
510
|
-
|
|
511
|
-
await this
|
|
512
|
-
this
|
|
558
|
+
async #phaseBoot(): Promise<void> {
|
|
559
|
+
await this.#app.boot()
|
|
560
|
+
this.#phase = 'booted'
|
|
513
561
|
}
|
|
514
562
|
|
|
515
|
-
|
|
563
|
+
async #phaseStart(): Promise<void> {
|
|
516
564
|
// Import preload files (routes.ts, kernel.ts, etc.)
|
|
517
|
-
if (this
|
|
518
|
-
for (const preloadEntry of this
|
|
565
|
+
if (this.#reamrc?.preloads) {
|
|
566
|
+
for (const preloadEntry of this.#reamrc.preloads) {
|
|
519
567
|
const preloadImport = typeof preloadEntry === 'function' ? preloadEntry : preloadEntry.file
|
|
520
568
|
const env =
|
|
521
569
|
typeof preloadEntry === 'function'
|
|
522
570
|
? undefined
|
|
523
571
|
: (preloadEntry as { environment?: string[] }).environment
|
|
524
572
|
|
|
525
|
-
if (env && !env.includes(this.
|
|
573
|
+
if (env && !env.includes(this.#app.getEnvironment())) continue
|
|
526
574
|
await preloadImport()
|
|
527
575
|
}
|
|
528
576
|
}
|
|
529
577
|
|
|
530
578
|
// Auto-load module files (routes.ts, etc.) from modules directory
|
|
531
|
-
await this
|
|
579
|
+
await this.#autoloadModules()
|
|
532
580
|
|
|
533
581
|
// Apply inline configuration
|
|
534
|
-
for (const mw of this
|
|
535
|
-
this
|
|
582
|
+
for (const mw of this.#inlineMiddleware) {
|
|
583
|
+
this.#middleware.use(mw)
|
|
536
584
|
}
|
|
537
|
-
for (const [name, mw] of this
|
|
538
|
-
this
|
|
585
|
+
for (const [name, mw] of this.#inlineNamedMiddleware) {
|
|
586
|
+
this.#middleware.register(name, mw)
|
|
539
587
|
}
|
|
540
|
-
if (this
|
|
541
|
-
this
|
|
588
|
+
if (this.#inlineRoutes) {
|
|
589
|
+
this.#inlineRoutes(this.#router)
|
|
542
590
|
}
|
|
543
591
|
|
|
544
592
|
// Call start() on providers
|
|
545
|
-
for (const provider of this
|
|
593
|
+
for (const provider of this.#providers) {
|
|
546
594
|
await callProviderPhase(provider, 'start')
|
|
547
595
|
}
|
|
548
596
|
|
|
549
|
-
this
|
|
597
|
+
this.#phase = 'started'
|
|
550
598
|
}
|
|
551
599
|
|
|
552
|
-
|
|
600
|
+
async #phaseReady(): Promise<void> {
|
|
553
601
|
// Boot the Server (resolves lazy error handler etc.)
|
|
554
|
-
await this
|
|
602
|
+
await this.#server.boot()
|
|
555
603
|
|
|
556
604
|
// Start HTTP server if in web mode
|
|
557
|
-
if (this.
|
|
605
|
+
if (this.#app.getEnvironment() === 'web' && this.#config.serverFactory) {
|
|
558
606
|
// Build the HttpKernel with server middleware + router middleware.
|
|
559
607
|
// The streaming backend is resolved lazily — the HyperServer is
|
|
560
608
|
// created a few lines down, after the kernel — so the kernel
|
|
561
|
-
// closes over the `this
|
|
609
|
+
// closes over the `this.#_httpServer` slot and reads it on every
|
|
562
610
|
// request through the factory.
|
|
563
611
|
const kernel = createHttpKernel({
|
|
564
|
-
router: this
|
|
565
|
-
middleware: this
|
|
566
|
-
container: this
|
|
612
|
+
router: this.#router,
|
|
613
|
+
middleware: this.#middleware,
|
|
614
|
+
container: this.#app.container,
|
|
567
615
|
exceptionHandler:
|
|
568
|
-
this
|
|
569
|
-
serverMiddleware: this
|
|
570
|
-
routerMiddleware: this
|
|
616
|
+
this.#server.getErrorHandler() ?? new ExceptionHandler(!this.#app.inProduction),
|
|
617
|
+
serverMiddleware: this.#server.getServerMiddleware(),
|
|
618
|
+
routerMiddleware: this.#router.getRouterMiddleware(),
|
|
571
619
|
onError: (error, ctx) => {
|
|
572
|
-
this
|
|
620
|
+
this.#errorBoundary.serviceError('HttpKernel', error, ctx.id)
|
|
573
621
|
},
|
|
574
622
|
// AdonisJS `config/app.ts` → `http: { allowMethodSpoofing: true }`.
|
|
575
|
-
allowMethodSpoofing: this
|
|
623
|
+
allowMethodSpoofing: this.#app.config.get<boolean>('app.http.allowMethodSpoofing') === true,
|
|
576
624
|
streamBackend: () => {
|
|
577
|
-
const server = this
|
|
625
|
+
const server = this.#_httpServer
|
|
578
626
|
if (
|
|
579
627
|
server &&
|
|
580
628
|
typeof server.registerStream === 'function' &&
|
|
@@ -596,28 +644,28 @@ export class Ignitor {
|
|
|
596
644
|
},
|
|
597
645
|
})
|
|
598
646
|
|
|
599
|
-
const desiredPort = this
|
|
647
|
+
const desiredPort = this.#config.port ?? 3000
|
|
600
648
|
// Port-scan fallback (+1..+19) is a DEV convenience only. In production
|
|
601
649
|
// a silent drift to :3001 while the LB targets :3000 is an outage —
|
|
602
650
|
// bind the configured port and let EADDRINUSE fail loudly. The probe
|
|
603
651
|
// also has an inherent TOCTOU; acceptable in dev, not in prod.
|
|
604
|
-
const availablePort = this
|
|
652
|
+
const availablePort = this.#app.inProduction
|
|
605
653
|
? desiredPort
|
|
606
654
|
: await findAvailablePort(desiredPort)
|
|
607
655
|
this.#host =
|
|
608
|
-
this
|
|
609
|
-
this
|
|
610
|
-
this
|
|
656
|
+
this.#config.host ?? process.env.HOST ?? (this.#app.inProduction ? '0.0.0.0' : 'localhost')
|
|
657
|
+
this.#_httpServer = this.#config.serverFactory(availablePort, this.#host)
|
|
658
|
+
this.#_httpServer.onRequest(kernel)
|
|
611
659
|
// Pre-resolve client IPs in Rust from the trusted-proxy CIDRs before
|
|
612
660
|
// listen. Security filtering itself lives in @c9up/blackhole.
|
|
613
|
-
const trustedProxies = this
|
|
661
|
+
const trustedProxies = this.#server.getTrustedProxies()
|
|
614
662
|
if (
|
|
615
663
|
trustedProxies.length > 0 &&
|
|
616
|
-
typeof this
|
|
664
|
+
typeof this.#_httpServer.configureTrustedProxies === 'function'
|
|
617
665
|
) {
|
|
618
|
-
this
|
|
666
|
+
this.#_httpServer.configureTrustedProxies([...trustedProxies])
|
|
619
667
|
}
|
|
620
|
-
await this
|
|
668
|
+
await this.#_httpServer.listen()
|
|
621
669
|
|
|
622
670
|
// Wire OS-signal graceful shutdown. Without it the process never closes
|
|
623
671
|
// the HTTP server on SIGTERM/SIGINT, so live keep-alive / SSE sockets keep
|
|
@@ -625,7 +673,7 @@ export class Ignitor {
|
|
|
625
673
|
// force-kill, and an orchestrator's rolling deploy drops in-flight work.
|
|
626
674
|
// onShutdown = this.stop(), which closes the port (aborting connections),
|
|
627
675
|
// shuts providers down (DB pools), and releases the locators.
|
|
628
|
-
if (this
|
|
676
|
+
if (this.#config.gracefulShutdown !== false) {
|
|
629
677
|
this.#shutdownHandle = installGracefulShutdown({
|
|
630
678
|
onShutdown: () => this.stop(),
|
|
631
679
|
logger: {
|
|
@@ -634,7 +682,7 @@ export class Ignitor {
|
|
|
634
682
|
},
|
|
635
683
|
})
|
|
636
684
|
}
|
|
637
|
-
} else if (this.
|
|
685
|
+
} else if (this.#app.getEnvironment() === 'web' && !this.#config.serverFactory) {
|
|
638
686
|
throw new ReamError(
|
|
639
687
|
'IGNITOR_NO_SERVER_FACTORY',
|
|
640
688
|
'httpServer() requires a serverFactory in config',
|
|
@@ -645,23 +693,23 @@ export class Ignitor {
|
|
|
645
693
|
}
|
|
646
694
|
|
|
647
695
|
// Install error boundary
|
|
648
|
-
this
|
|
696
|
+
this.#errorBoundary.install()
|
|
649
697
|
|
|
650
698
|
// Publish the flash-message tags to the template engine, if one is
|
|
651
699
|
// installed. After boot(), so the engine's own provider has bound it;
|
|
652
700
|
// before ready(), so a provider that pre-renders finds the tags in place.
|
|
653
|
-
await this
|
|
701
|
+
await this.#registerSessionTemplateTags()
|
|
654
702
|
|
|
655
703
|
// Call ready() on providers
|
|
656
|
-
for (const provider of this
|
|
704
|
+
for (const provider of this.#providers) {
|
|
657
705
|
await callProviderPhase(provider, 'ready')
|
|
658
706
|
}
|
|
659
707
|
|
|
660
708
|
// Core domain event: the app finished booting (all providers ready).
|
|
661
709
|
// Emitted once through the bus when events are wired — zero hot-path cost.
|
|
662
|
-
if (this
|
|
663
|
-
const bus = await this
|
|
664
|
-
bus.emit('app:ready', { environment: this.
|
|
710
|
+
if (this.#app.container.has('events')) {
|
|
711
|
+
const bus = await this.#app.container.resolve<Emitter>('events')
|
|
712
|
+
bus.emit('app:ready', { environment: this.#app.getEnvironment() })
|
|
665
713
|
}
|
|
666
714
|
|
|
667
715
|
// Dev-mode change watcher. IMPORTANT: this does NOT attempt an in-process
|
|
@@ -676,11 +724,11 @@ export class Ignitor {
|
|
|
676
724
|
// the Ignitor without a supervisor. A future true HMR needs a loader-hook
|
|
677
725
|
// (hot-hook style) that can invalidate the ESM cache — plug it in here.
|
|
678
726
|
if (this.isDevMode()) {
|
|
679
|
-
const watchDirs = this
|
|
680
|
-
this
|
|
727
|
+
const watchDirs = this.#config.watchDirs ?? ['app', 'start']
|
|
728
|
+
this.#hotReloadCleanup = startHotReload({
|
|
681
729
|
watchDirs,
|
|
682
730
|
onReload: () => {
|
|
683
|
-
this
|
|
731
|
+
this.#handleError({
|
|
684
732
|
type: 'system.info',
|
|
685
733
|
source: 'HotReload',
|
|
686
734
|
message:
|
|
@@ -691,7 +739,7 @@ export class Ignitor {
|
|
|
691
739
|
},
|
|
692
740
|
logger: {
|
|
693
741
|
info: (msg) =>
|
|
694
|
-
this
|
|
742
|
+
this.#handleError({
|
|
695
743
|
type: 'system.info',
|
|
696
744
|
source: 'HotReload',
|
|
697
745
|
message: msg,
|
|
@@ -702,7 +750,7 @@ export class Ignitor {
|
|
|
702
750
|
})
|
|
703
751
|
}
|
|
704
752
|
|
|
705
|
-
this
|
|
753
|
+
this.#phase = 'ready'
|
|
706
754
|
}
|
|
707
755
|
|
|
708
756
|
/**
|
|
@@ -710,13 +758,13 @@ export class Ignitor {
|
|
|
710
758
|
* Each file's default export is stored under its filename (without extension).
|
|
711
759
|
* e.g. config/database.ts → app.config.get('database')
|
|
712
760
|
*/
|
|
713
|
-
|
|
761
|
+
async #autoloadConfig(): Promise<void> {
|
|
714
762
|
const { readdirSync, existsSync } = await import('node:fs')
|
|
715
763
|
const { join, basename } = await import('node:path')
|
|
716
764
|
const { fileURLToPath, pathToFileURL } = await import('node:url')
|
|
717
765
|
|
|
718
|
-
const configDir = this
|
|
719
|
-
? join(fileURLToPath(this
|
|
766
|
+
const configDir = this.#appRoot
|
|
767
|
+
? join(fileURLToPath(this.#appRoot), 'config')
|
|
720
768
|
: join(process.cwd(), 'config')
|
|
721
769
|
|
|
722
770
|
if (!existsSync(configDir)) return
|
|
@@ -739,7 +787,7 @@ export class Ignitor {
|
|
|
739
787
|
// is what AdonisJS does (`fsImportAll`). Spread rather than stored as-is:
|
|
740
788
|
// an ESM namespace is sealed and null-prototype, so a later merge into
|
|
741
789
|
// this entry would fail on it.
|
|
742
|
-
this
|
|
790
|
+
this.#app.config.set(key, 'default' in mod ? mod.default : { ...mod })
|
|
743
791
|
}
|
|
744
792
|
}
|
|
745
793
|
|
|
@@ -747,8 +795,8 @@ export class Ignitor {
|
|
|
747
795
|
* Auto-load module files (routes.ts, etc.) from the modules directory.
|
|
748
796
|
* Scans modules.path for subdirectories and imports matching files.
|
|
749
797
|
*/
|
|
750
|
-
|
|
751
|
-
const modulesConfig = this
|
|
798
|
+
async #autoloadModules(): Promise<void> {
|
|
799
|
+
const modulesConfig = this.#reamrc?.modules
|
|
752
800
|
if (!modulesConfig?.path) return
|
|
753
801
|
|
|
754
802
|
const { readdirSync, existsSync } = await import('node:fs')
|
|
@@ -757,8 +805,8 @@ export class Ignitor {
|
|
|
757
805
|
const { pathToFileURL } = await import('node:url')
|
|
758
806
|
|
|
759
807
|
// Resolve modules path relative to app root or cwd
|
|
760
|
-
const basePath = this
|
|
761
|
-
? join(fileURLToPath(this
|
|
808
|
+
const basePath = this.#appRoot
|
|
809
|
+
? join(fileURLToPath(this.#appRoot), modulesConfig.path)
|
|
762
810
|
: resolve(modulesConfig.path)
|
|
763
811
|
|
|
764
812
|
if (!existsSync(basePath)) return
|
|
@@ -819,22 +867,26 @@ export class Ignitor {
|
|
|
819
867
|
this.#shutdownHandle = undefined
|
|
820
868
|
})
|
|
821
869
|
await attempt(() => {
|
|
822
|
-
if (this
|
|
870
|
+
if (this.#hotReloadCleanup) this.#hotReloadCleanup()
|
|
823
871
|
})
|
|
824
872
|
await attempt(async () => {
|
|
825
|
-
if (this
|
|
873
|
+
if (this.#_httpServer) await this.#_httpServer.close()
|
|
826
874
|
})
|
|
827
|
-
await attempt(() => this
|
|
828
|
-
await attempt(() => this
|
|
875
|
+
await attempt(() => this.#errorBoundary.uninstall())
|
|
876
|
+
await attempt(() => this.#app.shutdown())
|
|
829
877
|
|
|
830
878
|
// Release the process-wide service locators (ownership-guarded): a stopped
|
|
831
879
|
// app must not keep its container/router reachable through the module
|
|
832
880
|
// singletons, and must not clobber a newer Ignitor's bindings.
|
|
833
|
-
clearApp(this
|
|
834
|
-
clearRouter(this
|
|
835
|
-
clearServer(this
|
|
836
|
-
|
|
837
|
-
|
|
881
|
+
clearApp(this.#app)
|
|
882
|
+
clearRouter(this.#router)
|
|
883
|
+
clearServer(this.#server)
|
|
884
|
+
// `signedUrl` is what the router was handed; the signer behind
|
|
885
|
+
// `services/encryption` is the same object, so releasing it here keeps the
|
|
886
|
+
// locator from outliving the app that built it.
|
|
887
|
+
if (this.#signer) clearEncryption(this.#signer)
|
|
888
|
+
|
|
889
|
+
this.#phase = 'shutdown'
|
|
838
890
|
if (errors.length === 1) throw errors[0]
|
|
839
891
|
if (errors.length > 1) {
|
|
840
892
|
throw new AggregateError(errors, 'Ignitor.stop() completed with errors')
|
|
@@ -844,7 +896,7 @@ export class Ignitor {
|
|
|
844
896
|
// ─── Accessors ────────────────────────────────────────────
|
|
845
897
|
|
|
846
898
|
async port(): Promise<number> {
|
|
847
|
-
return this
|
|
899
|
+
return this.#_httpServer ? this.#_httpServer.port() : 0
|
|
848
900
|
}
|
|
849
901
|
|
|
850
902
|
/**
|
|
@@ -858,7 +910,7 @@ export class Ignitor {
|
|
|
858
910
|
}
|
|
859
911
|
|
|
860
912
|
getApp(): Application {
|
|
861
|
-
return this
|
|
913
|
+
return this.#app
|
|
862
914
|
}
|
|
863
915
|
|
|
864
916
|
/**
|
|
@@ -866,12 +918,12 @@ export class Ignitor {
|
|
|
866
918
|
* Undefined for inline/test ignitors created without one.
|
|
867
919
|
*/
|
|
868
920
|
getAppRoot(): URL | undefined {
|
|
869
|
-
return this
|
|
921
|
+
return this.#appRoot
|
|
870
922
|
}
|
|
871
923
|
|
|
872
924
|
/** Has the application already been started? */
|
|
873
925
|
isStarted(): boolean {
|
|
874
|
-
return this
|
|
926
|
+
return this.#phase === 'started' || this.#phase === 'ready'
|
|
875
927
|
}
|
|
876
928
|
|
|
877
929
|
/**
|
|
@@ -892,26 +944,26 @@ export class Ignitor {
|
|
|
892
944
|
|
|
893
945
|
this.#console = consoleApp
|
|
894
946
|
setConsole(consoleApp)
|
|
895
|
-
this
|
|
947
|
+
this.#app.container.singleton('console', () => consoleApp)
|
|
896
948
|
return consoleApp
|
|
897
949
|
}
|
|
898
950
|
|
|
899
951
|
/** The loaded rc file, if `useRcFile()` was called. */
|
|
900
952
|
getRcFile(): ReamrcConfig | undefined {
|
|
901
|
-
return this
|
|
953
|
+
return this.#reamrc
|
|
902
954
|
}
|
|
903
955
|
|
|
904
956
|
/** The custom module importer, when the app provided one. */
|
|
905
957
|
getImporter(): ((filePath: string) => Promise<unknown>) | undefined {
|
|
906
|
-
return this
|
|
958
|
+
return this.#config.importer
|
|
907
959
|
}
|
|
908
960
|
|
|
909
961
|
getRouter(): Router {
|
|
910
|
-
return this
|
|
962
|
+
return this.#router
|
|
911
963
|
}
|
|
912
964
|
|
|
913
965
|
getServer(): Server {
|
|
914
|
-
return this
|
|
966
|
+
return this.#server
|
|
915
967
|
}
|
|
916
968
|
|
|
917
969
|
/**
|
|
@@ -920,13 +972,13 @@ export class Ignitor {
|
|
|
920
972
|
*/
|
|
921
973
|
getKernel(): (request: HttpKernelRequest) => Promise<HttpKernelResponse> {
|
|
922
974
|
this.#kernel ??= createHttpKernel({
|
|
923
|
-
router: this
|
|
924
|
-
middleware: this
|
|
925
|
-
container: this
|
|
975
|
+
router: this.#router,
|
|
976
|
+
middleware: this.#middleware,
|
|
977
|
+
container: this.#app.container,
|
|
926
978
|
exceptionHandler:
|
|
927
|
-
this
|
|
928
|
-
serverMiddleware: this
|
|
929
|
-
routerMiddleware: this
|
|
979
|
+
this.#server.getErrorHandler() ?? new ExceptionHandler(!this.#app.inProduction),
|
|
980
|
+
serverMiddleware: this.#server.getServerMiddleware(),
|
|
981
|
+
routerMiddleware: this.#router.getRouterMiddleware(),
|
|
930
982
|
})
|
|
931
983
|
return this.#kernel
|
|
932
984
|
}
|
|
@@ -934,15 +986,15 @@ export class Ignitor {
|
|
|
934
986
|
#kernel?: (request: HttpKernelRequest) => Promise<HttpKernelResponse>
|
|
935
987
|
|
|
936
988
|
isDevMode(): boolean {
|
|
937
|
-
return this
|
|
989
|
+
return this.#app.inDev
|
|
938
990
|
}
|
|
939
991
|
|
|
940
992
|
getPhase(): string {
|
|
941
|
-
return this
|
|
993
|
+
return this.#phase
|
|
942
994
|
}
|
|
943
995
|
|
|
944
|
-
|
|
945
|
-
for (const listener of this
|
|
996
|
+
#handleError(event: ErrorEvent): void {
|
|
997
|
+
for (const listener of this.#errorListeners) {
|
|
946
998
|
try {
|
|
947
999
|
listener(event)
|
|
948
1000
|
} catch {
|
|
@@ -957,7 +1009,7 @@ export class Ignitor {
|
|
|
957
1009
|
* shutdown logger so drain progress / timeouts are observable.
|
|
958
1010
|
*/
|
|
959
1011
|
#emitSystemInfo(source: string, message: string, kind: 'info' | 'error'): void {
|
|
960
|
-
this
|
|
1012
|
+
this.#handleError({
|
|
961
1013
|
type: kind === 'error' ? 'system.error' : 'system.info',
|
|
962
1014
|
source,
|
|
963
1015
|
message,
|