@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.
Files changed (112) hide show
  1. package/README.md +3 -9
  2. package/dist/Application.d.ts +85 -5
  3. package/dist/Application.d.ts.map +1 -1
  4. package/dist/Application.js +171 -21
  5. package/dist/Application.js.map +1 -1
  6. package/dist/ErrorBoundary.d.ts +1 -7
  7. package/dist/ErrorBoundary.d.ts.map +1 -1
  8. package/dist/ErrorBoundary.js +31 -31
  9. package/dist/ErrorBoundary.js.map +1 -1
  10. package/dist/HttpKernel.js +2 -2
  11. package/dist/HttpKernel.js.map +1 -1
  12. package/dist/Ignitor.d.ts +18 -49
  13. package/dist/Ignitor.d.ts.map +1 -1
  14. package/dist/Ignitor.js +211 -173
  15. package/dist/Ignitor.js.map +1 -1
  16. package/dist/env/Env.d.ts +6 -0
  17. package/dist/env/Env.d.ts.map +1 -1
  18. package/dist/env/Env.js +6 -0
  19. package/dist/env/Env.js.map +1 -1
  20. package/dist/events/Emitter.d.ts +0 -6
  21. package/dist/events/Emitter.d.ts.map +1 -1
  22. package/dist/events/Emitter.js +42 -42
  23. package/dist/events/Emitter.js.map +1 -1
  24. package/dist/events/native.d.ts +13 -11
  25. package/dist/events/native.d.ts.map +1 -1
  26. package/dist/events/native.js.map +1 -1
  27. package/dist/helpers/compose.d.ts +30 -0
  28. package/dist/helpers/compose.d.ts.map +1 -0
  29. package/dist/helpers/compose.js +27 -0
  30. package/dist/helpers/compose.js.map +1 -0
  31. package/dist/helpers/index.d.ts +1 -0
  32. package/dist/helpers/index.d.ts.map +1 -1
  33. package/dist/helpers/index.js +1 -0
  34. package/dist/helpers/index.js.map +1 -1
  35. package/dist/helpers/is.d.ts +69 -16
  36. package/dist/helpers/is.d.ts.map +1 -1
  37. package/dist/helpers/is.js +79 -16
  38. package/dist/helpers/is.js.map +1 -1
  39. package/dist/index.d.ts +4 -3
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +2 -3
  42. package/dist/index.js.map +1 -1
  43. package/dist/middleware/Pipeline.d.ts +9 -3
  44. package/dist/middleware/Pipeline.d.ts.map +1 -1
  45. package/dist/middleware/Pipeline.js +10 -4
  46. package/dist/middleware/Pipeline.js.map +1 -1
  47. package/dist/native/generated.d.ts +243 -0
  48. package/dist/native/generated.d.ts.map +1 -0
  49. package/dist/native/generated.js +7 -0
  50. package/dist/native/generated.js.map +1 -0
  51. package/dist/rpc/RpcRouter.js +2 -2
  52. package/dist/rpc/RpcRouter.js.map +1 -1
  53. package/dist/services/app.d.ts +6 -0
  54. package/dist/services/app.d.ts.map +1 -1
  55. package/dist/services/app.js +8 -0
  56. package/dist/services/app.js.map +1 -1
  57. package/dist/services/config.d.ts +15 -0
  58. package/dist/services/config.d.ts.map +1 -0
  59. package/dist/services/config.js +18 -0
  60. package/dist/services/config.js.map +1 -0
  61. package/dist/services/encryption.d.ts +23 -0
  62. package/dist/services/encryption.d.ts.map +1 -0
  63. package/dist/services/encryption.js +33 -0
  64. package/dist/services/encryption.js.map +1 -0
  65. package/dist/services/urlBuilder.d.ts +21 -0
  66. package/dist/services/urlBuilder.d.ts.map +1 -0
  67. package/dist/services/urlBuilder.js +34 -0
  68. package/dist/services/urlBuilder.js.map +1 -0
  69. package/dist/session/drivers/CookieDriver.d.ts +1 -1
  70. package/dist/session/drivers/CookieDriver.d.ts.map +1 -1
  71. package/dist/session/drivers/CookieDriver.js +4 -4
  72. package/dist/session/drivers/CookieDriver.js.map +1 -1
  73. package/dist/testing/ApiResponse.d.ts +44 -44
  74. package/dist/testing/ApiResponse.d.ts.map +1 -1
  75. package/dist/testing/ApiResponse.js +48 -48
  76. package/dist/testing/ApiResponse.js.map +1 -1
  77. package/dist/testing/RequestBuilder.d.ts +39 -39
  78. package/dist/testing/RequestBuilder.d.ts.map +1 -1
  79. package/dist/testing/RequestBuilder.js +49 -49
  80. package/dist/testing/RequestBuilder.js.map +1 -1
  81. package/dist/testing/TestClient.d.ts +5 -5
  82. package/dist/testing/TestClient.d.ts.map +1 -1
  83. package/dist/testing/TestClient.js +7 -7
  84. package/dist/testing/TestClient.js.map +1 -1
  85. package/events.win32-x64-msvc.node +0 -0
  86. package/index.win32-x64-msvc.node +0 -0
  87. package/package.json +15 -2
  88. package/scheduler.win32-x64-msvc.node +0 -0
  89. package/scripts/build-napi-types.mjs +68 -0
  90. package/scripts/generate-napi-types.mjs +171 -0
  91. package/src/Application.ts +221 -21
  92. package/src/ErrorBoundary.ts +31 -31
  93. package/src/HttpKernel.ts +2 -2
  94. package/src/Ignitor.ts +228 -176
  95. package/src/env/Env.ts +6 -0
  96. package/src/events/Emitter.ts +42 -42
  97. package/src/events/native.ts +21 -14
  98. package/src/helpers/compose.ts +84 -0
  99. package/src/helpers/index.ts +1 -0
  100. package/src/helpers/is.ts +87 -16
  101. package/src/index.ts +3 -3
  102. package/src/middleware/Pipeline.ts +10 -4
  103. package/src/native/generated.ts +291 -0
  104. package/src/rpc/RpcRouter.ts +2 -2
  105. package/src/services/app.ts +9 -0
  106. package/src/services/config.ts +24 -0
  107. package/src/services/encryption.ts +42 -0
  108. package/src/services/urlBuilder.ts +41 -0
  109. package/src/session/drivers/CookieDriver.ts +4 -4
  110. package/src/testing/ApiResponse.ts +52 -52
  111. package/src/testing/RequestBuilder.ts +55 -55
  112. package/src/testing/TestClient.ts +8 -8
@@ -13,6 +13,7 @@
13
13
  * @implements FR17, FR20
14
14
  */
15
15
 
16
+ import { readFileSync } from 'node:fs'
16
17
  import { join, relative } from 'node:path'
17
18
  import { fileURLToPath } from 'node:url'
18
19
  import { ConfigStore } from './ConfigLoader.js'
@@ -29,17 +30,45 @@ import { callProviderPhase } from './Provider.js'
29
30
  */
30
31
  export type ApplicationMode = 'run' | 'warmup'
31
32
 
33
+ /**
34
+ * What the process is running as (AdonisJS `AppEnvironments`).
35
+ *
36
+ * Providers and preloads are filtered on it — a console-only provider must not
37
+ * boot inside an HTTP request. `repl` is absent because ream has no REPL; it
38
+ * belongs here the day one exists, not before.
39
+ */
40
+ export type AppEnvironment = 'web' | 'console' | 'test' | 'unknown'
41
+
42
+ /**
43
+ * Where the application is in its lifecycle (AdonisJS `ApplicationStates`).
44
+ *
45
+ * Read it rather than inferring from a boolean: "not booted" covers both
46
+ * "starting up" and "already shut down", and code that guards on the wrong one
47
+ * runs at exactly the wrong moment.
48
+ */
49
+ export type ApplicationState =
50
+ | 'created'
51
+ | 'initiated'
52
+ | 'booted'
53
+ | 'ready'
54
+ | 'terminating'
55
+ | 'terminated'
56
+
32
57
  export class Application implements AppContext {
33
58
  readonly container: Container
34
59
  readonly config: ConfigStore
35
60
  #appRoot?: URL
36
61
  #directories: DirectoriesNode = { ...defaultDirectories }
37
- private providers: ProviderContract[] = []
38
- private _booted = false
62
+ #providers: ProviderContract[] = []
63
+ #booted = false
64
+ #environment: AppEnvironment = 'unknown'
65
+ #state: ApplicationState = 'created'
66
+ #packageJsonRead = false
67
+ #packageJsonCache: { name?: string; version?: string } | undefined
39
68
  #mode: ApplicationMode = 'run'
40
- private _bootingHooks: Array<() => Promise<void> | void> = []
41
- private _bootedHooks: Array<() => Promise<void> | void> = []
42
- private _shutdownHooks: Array<() => Promise<void> | void> = []
69
+ #bootingHooks: Array<() => Promise<void> | void> = []
70
+ #bootedHooks: Array<() => Promise<void> | void> = []
71
+ #shutdownHooks: Array<() => Promise<void> | void> = []
43
72
 
44
73
  constructor() {
45
74
  this.container = new Container()
@@ -69,7 +98,7 @@ export class Application implements AppContext {
69
98
  * otherwise would leave the app half-started.
70
99
  */
71
100
  setMode(mode: ApplicationMode): this {
72
- if (this._booted) {
101
+ if (this.#booted) {
73
102
  throw new Error(`Cannot switch to '${mode}' mode: the application is already booted.`)
74
103
  }
75
104
  this.#mode = mode
@@ -313,7 +342,7 @@ export class Application implements AppContext {
313
342
  * Like AdonisJS app.booting().
314
343
  */
315
344
  booting(callback: () => Promise<void> | void): void {
316
- this._bootingHooks.push(callback)
345
+ this.#bootingHooks.push(callback)
317
346
  }
318
347
 
319
348
  /**
@@ -321,7 +350,7 @@ export class Application implements AppContext {
321
350
  * Like AdonisJS app.booted().
322
351
  */
323
352
  booted(callback: () => Promise<void> | void): void {
324
- if (this._booted) {
353
+ if (this.#booted) {
325
354
  // Already booted — run immediately. Invoke callback() INSIDE the
326
355
  // promise chain (not as `Promise.resolve(callback())`, which
327
356
  // evaluates callback() eagerly and lets a synchronous throw
@@ -334,7 +363,7 @@ export class Application implements AppContext {
334
363
  })
335
364
  return
336
365
  }
337
- this._bootedHooks.push(callback)
366
+ this.#bootedHooks.push(callback)
338
367
  }
339
368
 
340
369
  // ─── Signal handling ──────────────────────────────────────
@@ -354,6 +383,166 @@ export class Application implements AppContext {
354
383
  process.on(signal, callback)
355
384
  }
356
385
 
386
+ /**
387
+ * What the process is running as (AdonisJS `getEnvironment`).
388
+ *
389
+ * Lives on the application, not on the Ignitor, because that is where a
390
+ * provider reads it: `if (app.getEnvironment() !== 'web') return`.
391
+ */
392
+ getEnvironment(): AppEnvironment {
393
+ return this.#environment
394
+ }
395
+
396
+ /**
397
+ * Declare what the process is running as (AdonisJS `setEnvironment`).
398
+ *
399
+ * Refused after boot: providers and preloads were already filtered on the
400
+ * old value, so changing it afterwards would describe an application that
401
+ * was never assembled.
402
+ */
403
+ setEnvironment(environment: AppEnvironment): this {
404
+ if (this.#booted) {
405
+ throw new Error(
406
+ `Cannot switch to the '${environment}' environment: the application is already booted.`,
407
+ )
408
+ }
409
+ this.#environment = environment
410
+ return this
411
+ }
412
+
413
+ /** `process.env.NODE_ENV`, or `'unknown'` (AdonisJS `nodeEnvironment`). */
414
+ get nodeEnvironment(): string {
415
+ return process.env.NODE_ENV ?? 'unknown'
416
+ }
417
+
418
+ /** Where the application is in its lifecycle (AdonisJS `getState`). */
419
+ getState(): ApplicationState {
420
+ return this.#state
421
+ }
422
+
423
+ /** True once boot and every ready hook have completed (AdonisJS `isReady`). */
424
+ get isReady(): boolean {
425
+ return this.#state === 'ready'
426
+ }
427
+
428
+ /** True while shutdown hooks are running (AdonisJS `isTerminating`). */
429
+ get isTerminating(): boolean {
430
+ return this.#state === 'terminating'
431
+ }
432
+
433
+ /** True once shutdown has completed (AdonisJS `isTerminated`). */
434
+ get isTerminated(): boolean {
435
+ return this.#state === 'terminated'
436
+ }
437
+
438
+ /** The name from the app's `package.json`, when it could be read. */
439
+ get appName(): string | undefined {
440
+ return this.#packageJson()?.name
441
+ }
442
+
443
+ /** The version from the app's `package.json`, when it could be read. */
444
+ get version(): string | undefined {
445
+ return this.#packageJson()?.version
446
+ }
447
+
448
+ /**
449
+ * A snapshot of the application, for a health endpoint or a bug report
450
+ * (AdonisJS `toJSON`).
451
+ */
452
+ toJSON(): {
453
+ appName: string | undefined
454
+ version: string | undefined
455
+ environment: AppEnvironment
456
+ nodeEnvironment: string
457
+ state: ApplicationState
458
+ isReady: boolean
459
+ isTerminating: boolean
460
+ } {
461
+ return {
462
+ appName: this.appName,
463
+ version: this.version,
464
+ environment: this.#environment,
465
+ nodeEnvironment: this.nodeEnvironment,
466
+ state: this.#state,
467
+ isReady: this.isReady,
468
+ isTerminating: this.isTerminating,
469
+ }
470
+ }
471
+
472
+ /**
473
+ * Listen for a signal, then stop listening (AdonisJS `listenOnce`).
474
+ *
475
+ * Registered through {@link listen} so the handler is still removed by
476
+ * `terminate()` when the signal never arrives — otherwise a stopped app
477
+ * would leave a listener behind, and the next real signal would run the
478
+ * handlers of every app instance that ever existed.
479
+ */
480
+ listenOnce(signal: NodeJS.Signals, callback: () => void): void {
481
+ const once = (): void => {
482
+ process.removeListener(signal, once)
483
+ callback()
484
+ }
485
+ this.listen(signal, once)
486
+ }
487
+
488
+ /** {@link listenOnce}, only when `condition` holds (AdonisJS `listenOnceIf`). */
489
+ listenOnceIf(condition: boolean, signal: NodeJS.Signals, callback: () => void): void {
490
+ if (condition) this.listenOnce(signal, callback)
491
+ }
492
+
493
+ /**
494
+ * Tell the process supervisor the app reached a state (AdonisJS `notify`).
495
+ *
496
+ * A no-op unless something is listening — systemd's `NOTIFY_SOCKET`, or a
497
+ * parent process. Silent by design: an app should not fail to start because
498
+ * nothing was there to hear it.
499
+ */
500
+ notify(message: string): void {
501
+ process.send?.({ type: 'ream:notify', message })
502
+ }
503
+
504
+ /**
505
+ * Import a module and return its default export (AdonisJS `importDefault`).
506
+ *
507
+ * Throws when there is none, naming the file: a preload or provider entry
508
+ * without a default export otherwise fails later, as `undefined is not a
509
+ * constructor`, far from the file at fault.
510
+ */
511
+ async importDefault<T = unknown>(importFn: () => Promise<unknown>, label?: string): Promise<T> {
512
+ const module = await importFn()
513
+ if (module === null || typeof module !== 'object' || !('default' in module)) {
514
+ throw new Error(
515
+ `Missing default export${label ? ` in "${label}"` : ''}. ` +
516
+ 'Export the value as `export default`, or import the named export directly.',
517
+ )
518
+ }
519
+ // The caller declares what it expects to find, the same contract as
520
+ // `container.make<T>()` over a dynamic import.
521
+ return module.default as T
522
+ }
523
+
524
+ /** The app's `package.json`, read once and cached; `undefined` when absent. */
525
+ #packageJson(): { name?: string; version?: string } | undefined {
526
+ if (this.#packageJsonRead) return this.#packageJsonCache
527
+ this.#packageJsonRead = true
528
+ try {
529
+ const path = this.makePath('package.json')
530
+ const raw = readFileSync(path, 'utf8')
531
+ const parsed: unknown = JSON.parse(raw)
532
+ if (parsed !== null && typeof parsed === 'object') {
533
+ const name = 'name' in parsed && typeof parsed.name === 'string' ? parsed.name : undefined
534
+ const version =
535
+ 'version' in parsed && typeof parsed.version === 'string' ? parsed.version : undefined
536
+ this.#packageJsonCache = { name, version }
537
+ }
538
+ } catch {
539
+ // No app root, no package.json, unreadable, or malformed — every one of
540
+ // those means "we do not know the name", not "fail to boot".
541
+ this.#packageJsonCache = undefined
542
+ }
543
+ return this.#packageJsonCache
544
+ }
545
+
357
546
  /**
358
547
  * Conditionally listen for a process signal.
359
548
  * Like AdonisJS app.listenIf(app.managedByPm2, 'SIGINT', ...).
@@ -377,7 +566,7 @@ export class Application implements AppContext {
377
566
 
378
567
  /** Register a provider instance. */
379
568
  register(provider: ProviderContract): void {
380
- this.providers.push(provider)
569
+ this.#providers.push(provider)
381
570
  if (typeof provider.register === 'function') {
382
571
  provider.register()
383
572
  }
@@ -385,26 +574,32 @@ export class Application implements AppContext {
385
574
 
386
575
  /** Boot all registered providers. Runs booting/booted hooks. */
387
576
  async boot(): Promise<void> {
388
- if (this._booted) return
577
+ if (this.#booted) return
389
578
 
390
579
  // Run booting hooks
391
- for (const hook of this._bootingHooks) {
580
+ for (const hook of this.#bootingHooks) {
392
581
  await hook()
393
582
  }
394
583
 
395
584
  // Boot providers. `callProviderPhase` skips providers that don't
396
585
  // declare the phase — duck-typed packages (spectrum, warden, etc.)
397
586
  // implement only what they need.
398
- for (const provider of this.providers) {
587
+ for (const provider of this.#providers) {
399
588
  await callProviderPhase(provider, 'boot')
400
589
  }
401
590
 
402
- this._booted = true
591
+ this.#booted = true
592
+ this.#state = 'booted'
403
593
 
404
594
  // Run booted hooks
405
- for (const hook of this._bootedHooks) {
595
+ for (const hook of this.#bootedHooks) {
406
596
  await hook()
407
597
  }
598
+
599
+ // Ready only once the booted hooks have run: they are where an app wires
600
+ // the things a request will need, so reporting ready before them would
601
+ // green a health check on an application that cannot serve yet.
602
+ this.#state = 'ready'
408
603
  }
409
604
 
410
605
  /**
@@ -414,8 +609,9 @@ export class Application implements AppContext {
414
609
  */
415
610
  async shutdown(): Promise<void> {
416
611
  const errors: unknown[] = []
612
+ this.#state = 'terminating'
417
613
 
418
- for (const hook of this._shutdownHooks) {
614
+ for (const hook of this.#shutdownHooks) {
419
615
  try {
420
616
  await hook()
421
617
  } catch (err) {
@@ -423,7 +619,7 @@ export class Application implements AppContext {
423
619
  }
424
620
  }
425
621
 
426
- for (const provider of [...this.providers].reverse()) {
622
+ for (const provider of [...this.#providers].reverse()) {
427
623
  try {
428
624
  await callProviderPhase(provider, 'shutdown')
429
625
  } catch (err) {
@@ -437,7 +633,11 @@ export class Application implements AppContext {
437
633
  }
438
634
  this.#signalHandlers = []
439
635
 
440
- this._booted = false
636
+ this.#booted = false
637
+ // Terminated even when a hook threw: the hooks and providers have all been
638
+ // given their turn, so the application IS down. Reporting anything else
639
+ // would leave a supervisor waiting on a shutdown that already happened.
640
+ this.#state = 'terminated'
441
641
  if (errors.length === 1) throw errors[0]
442
642
  if (errors.length > 1) {
443
643
  throw new AggregateError(errors, 'Application.shutdown() completed with errors')
@@ -446,14 +646,14 @@ export class Application implements AppContext {
446
646
 
447
647
  /** Register a shutdown hook. */
448
648
  onShutdown(callback: () => Promise<void> | void): void {
449
- this._shutdownHooks.push(callback)
649
+ this.#shutdownHooks.push(callback)
450
650
  }
451
651
 
452
652
  get isBooted(): boolean {
453
- return this._booted
653
+ return this.#booted
454
654
  }
455
655
 
456
656
  get providerCount(): number {
457
- return this.providers.length
657
+ return this.#providers.length
458
658
  }
459
659
  }
@@ -33,20 +33,20 @@ export type ErrorEmitter = (event: ErrorEvent) => void
33
33
  * - In dev mode, also logs to console
34
34
  */
35
35
  export class ErrorBoundary {
36
- private emitter: ErrorEmitter
37
- private devMode: boolean
38
- private installed = false
39
- private rejectionHandler?: (reason: unknown) => void
40
- private exceptionHandler?: (error: Error) => void
36
+ #emitter: ErrorEmitter
37
+ #devMode: boolean
38
+ #installed = false
39
+ #rejectionHandler?: (reason: unknown) => void
40
+ #exceptionHandler?: (error: Error) => void
41
41
 
42
42
  constructor(emitter: ErrorEmitter, devMode = false) {
43
- this.emitter = emitter
44
- this.devMode = devMode
43
+ this.#emitter = emitter
44
+ this.#devMode = devMode
45
45
  }
46
46
 
47
47
  /** Install global error handlers. */
48
48
  install(): void {
49
- if (this.installed) return
49
+ if (this.#installed) return
50
50
 
51
51
  // Skip global install under vitest — each test instantiates its own
52
52
  // Ignitor and rarely calls `.stop()`, so per-test listeners pile up on
@@ -55,42 +55,42 @@ export class ErrorBoundary {
55
55
  // `handleError`/`serviceError` calls; only the Node-level signal
56
56
  // bridging is suppressed.
57
57
  if (process.env.VITEST === 'true' || process.env.NODE_ENV === 'test') {
58
- this.installed = true
58
+ this.#installed = true
59
59
  return
60
60
  }
61
61
 
62
- this.rejectionHandler = (reason) => {
63
- this.handleError('system.fatal', 'UnhandledRejection', reason)
62
+ this.#rejectionHandler = (reason) => {
63
+ this.#handleError('system.fatal', 'UnhandledRejection', reason)
64
64
  }
65
65
 
66
- this.exceptionHandler = (error) => {
67
- this.handleError('system.fatal', 'UncaughtException', error)
66
+ this.#exceptionHandler = (error) => {
67
+ this.#handleError('system.fatal', 'UncaughtException', error)
68
68
  // Node.js is in undefined state after uncaughtException — must exit
69
69
  process.exit(1)
70
70
  }
71
71
 
72
- process.on('unhandledRejection', this.rejectionHandler)
73
- process.on('uncaughtException', this.exceptionHandler)
72
+ process.on('unhandledRejection', this.#rejectionHandler)
73
+ process.on('uncaughtException', this.#exceptionHandler)
74
74
 
75
- this.installed = true
75
+ this.#installed = true
76
76
  }
77
77
 
78
78
  /** Uninstall global error handlers (for testing). Only removes OUR handlers. */
79
79
  uninstall(): void {
80
- if (this.rejectionHandler) {
81
- process.removeListener('unhandledRejection', this.rejectionHandler)
80
+ if (this.#rejectionHandler) {
81
+ process.removeListener('unhandledRejection', this.#rejectionHandler)
82
82
  }
83
- if (this.exceptionHandler) {
84
- process.removeListener('uncaughtException', this.exceptionHandler)
83
+ if (this.#exceptionHandler) {
84
+ process.removeListener('uncaughtException', this.#exceptionHandler)
85
85
  }
86
- this.rejectionHandler = undefined
87
- this.exceptionHandler = undefined
88
- this.installed = false
86
+ this.#rejectionHandler = undefined
87
+ this.#exceptionHandler = undefined
88
+ this.#installed = false
89
89
  }
90
90
 
91
91
  /** Emit a service error (handler failure). */
92
92
  serviceError(source: string, error: unknown, correlationId?: string): void {
93
- this.handleError('service.error', source, error, correlationId)
93
+ this.#handleError('service.error', source, error, correlationId)
94
94
  }
95
95
 
96
96
  /** Emit a security rejection. */
@@ -103,15 +103,15 @@ export class ErrorBoundary {
103
103
  correlationId,
104
104
  timestamp: new Date().toISOString(),
105
105
  }
106
- this.emit(event)
106
+ this.#emit(event)
107
107
  }
108
108
 
109
109
  /** Emit a system error (infrastructure failure). */
110
110
  systemError(source: string, error: unknown, correlationId?: string): void {
111
- this.handleError('system.error', source, error, correlationId)
111
+ this.#handleError('system.error', source, error, correlationId)
112
112
  }
113
113
 
114
- private handleError(
114
+ #handleError(
115
115
  type: ErrorEvent['type'],
116
116
  source: string,
117
117
  error: unknown,
@@ -131,12 +131,12 @@ export class ErrorBoundary {
131
131
  timestamp: new Date().toISOString(),
132
132
  }
133
133
 
134
- this.emit(event)
134
+ this.#emit(event)
135
135
  }
136
136
 
137
- private emit(event: ErrorEvent): void {
137
+ #emit(event: ErrorEvent): void {
138
138
  try {
139
- this.emitter(event)
139
+ this.#emitter(event)
140
140
  } catch {
141
141
  // If the emitter itself fails, log to stderr as last resort
142
142
  process.stderr.write(`[ErrorBoundary] Failed to emit: ${JSON.stringify(event)}\n`)
@@ -150,7 +150,7 @@ export class ErrorBoundary {
150
150
  // boundary would turn each forgotten `await` from a loud crash into
151
151
  // nothing at all, which is the opposite of what it is for. A duplicated
152
152
  // line when a listener also logs it is a much smaller price.
153
- if (this.devMode || event.type === 'system.fatal') {
153
+ if (this.#devMode || event.type === 'system.fatal') {
154
154
  const prefix =
155
155
  event.type === 'system.fatal'
156
156
  ? '✗ FATAL'
package/src/HttpKernel.ts CHANGED
@@ -19,7 +19,7 @@ import type {
19
19
  MiddlewareRegistry,
20
20
  RuntimeValidator,
21
21
  } from './middleware/Pipeline.js'
22
- import { compose } from './middleware/Pipeline.js'
22
+ import { composeMiddleware } from './middleware/Pipeline.js'
23
23
  import type { AnyConstructor, RouteDefinition, Router } from './router/Router.js'
24
24
  import type { CookieSigner } from './security/CookieSigner.js'
25
25
  import type { SignedUrl } from './security/SignedUrl.js'
@@ -318,7 +318,7 @@ export function createHttpKernel(
318
318
  // HttpContext ALS so HttpContext.get()/getOrFail() reach this request
319
319
  // anywhere down the call stack (AdonisJS parity) — transparent to the
320
320
  // pipeline itself.
321
- const fullPipeline = compose([...serverMw, coreHandler])
321
+ const fullPipeline = composeMiddleware([...serverMw, coreHandler])
322
322
  await HttpContext.run(ctx, () => fullPipeline(ctx, async () => {}))
323
323
 
324
324
  ctx.events?.emit('http:response', {