@elysiumjs/core 0.10.0 → 0.11.1

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/dist/kernel.d.ts CHANGED
@@ -18,12 +18,19 @@ export declare class Kernel {
18
18
  private readonly initOrder;
19
19
  private readonly inFlight;
20
20
  private readonly failures;
21
+ private readonly rejected;
21
22
  private readonly ownClasses;
22
23
  private readonly wired;
23
24
  private shutdownPromise;
24
25
  constructor(moduleGraph: ModuleGraph);
25
26
  /** The module graph this kernel runs. */
26
27
  get graph(): ModuleGraph;
28
+ /**
29
+ * The service classes a module registers, from its `services` (factory definitions left out).
30
+ * Empty until {@link register} has run.
31
+ * @param node The module.
32
+ */
33
+ serviceClasses(node: ModuleNode): readonly Class<any>[];
27
34
  /**
28
35
  * Phase 2: resolves each module's config, instantiates the module and registers its services.
29
36
  * @throws {ConflictingModuleConfigError | MissingModuleConfigError}
@@ -34,7 +41,12 @@ export declare class Kernel {
34
41
  * and a failure is remembered and rethrown.
35
42
  */
36
43
  initModule(node: ModuleNode): Promise<void>;
37
- /** Initialises every module in graph order, then constructs every non-factory service. */
44
+ /**
45
+ * Forgets the modules whose initialisation failed, so that the next {@link initModule}
46
+ * runs their `onInit` again. Initialisations still in flight are kept.
47
+ */
48
+ resetFailures(): void;
49
+ /** Initialises every module in graph order, then constructs every singleton service. */
38
50
  bootEager(): Promise<void>;
39
51
  /**
40
52
  * Lazily builds a class owned by `owner`, such as a command: initialises the owner,
@@ -63,6 +75,8 @@ export declare class Kernel {
63
75
  * @throws The error of a failing synchronous `onInit`, which is remembered.
64
76
  */
65
77
  private initSync;
78
+ /** Removes event listeners wired by {@link initOwn}. */
79
+ private unwire;
66
80
  private runShutdown;
67
81
  /** Evaluates the config that applies to `node`, checking function-form ties. */
68
82
  private resolveNodeConfig;
@@ -1,5 +1,5 @@
1
1
  import type { Class } from 'type-fest';
2
- import type { EventData } from './event';
2
+ import type { LifecycleEvent } from './lifecycle';
3
3
  import type { ModuleNode } from './module-graph';
4
4
  /**
5
5
  * A listener the kernel added to the event bus.
@@ -7,7 +7,7 @@ import type { ModuleNode } from './module-graph';
7
7
  */
8
8
  export type WiredListener = {
9
9
  readonly event: string;
10
- readonly wrapper: (data: EventData<any>) => Promise<void>;
10
+ readonly wrapper: (data: LifecycleEvent<any>) => Promise<void>;
11
11
  };
12
12
  /**
13
13
  * Registers the decorated event listeners of `owned` classes on the event bus, each running
@@ -17,7 +17,7 @@ export type WiredListener = {
17
17
  * @param owned The module's own classes (services and controllers).
18
18
  * @param services The subset of `owned` registered as services in the module.
19
19
  * @returns The listeners added, so they can be removed on shutdown.
20
- * @throws {EventListenerNotSupportedError} For an instance listener on a non-service class.
20
+ * @throws {LifecycleListenerNotSupportedError} For an instance listener on a non-service class.
21
21
  * @author Axel Nana <axel.nana@workbud.com>
22
22
  */
23
- export declare function wireEventListeners(node: ModuleNode, owned: readonly Class<any>[], services: ReadonlySet<Class<any>>): WiredListener[];
23
+ export declare function wireLifecycleListeners(node: ModuleNode, owned: readonly Class<any>[], services: ReadonlySet<Class<any>>): WiredListener[];
@@ -4,8 +4,8 @@ import { EventEmitter } from 'node:events';
4
4
  * @internal
5
5
  * @author Axel Nana <axel.nana@workbud.com>
6
6
  */
7
- export declare class EventBus extends EventEmitter {
8
- static readonly instance: EventBus;
7
+ export declare class LifecycleBus extends EventEmitter {
8
+ static readonly instance: LifecycleBus;
9
9
  private constructor();
10
10
  emitError(error: unknown): void;
11
11
  emitEvent(event: string, data: unknown, source?: unknown): void;
@@ -14,7 +14,7 @@ export declare class EventBus extends EventEmitter {
14
14
  * Stores the event data with the source.
15
15
  * @author Axel Nana <axel.nana@workbud.com>
16
16
  */
17
- export type EventData<TData, TSource = null> = {
17
+ export type LifecycleEvent<TData, TSource = null> = {
18
18
  /**
19
19
  * The emitted data.
20
20
  */
@@ -25,10 +25,10 @@ export type EventData<TData, TSource = null> = {
25
25
  source: TSource | null;
26
26
  };
27
27
  /**
28
- * Event handler function.
28
+ * Lifecycle handler function.
29
29
  * @author Axel Nana <axel.nana@workbud.com>
30
30
  */
31
- export type EventHandler<TData, TSource = any> = (event: EventData<TData, TSource>) => void | Promise<void>;
31
+ export type LifecycleHandler<TData, TSource = any> = (event: LifecycleEvent<TData, TSource>) => void | Promise<void>;
32
32
  /**
33
33
  * Properties required when declaring a method as an event listener.
34
34
  * @author Axel Nana <axel.nana@workbud.com>
@@ -52,10 +52,10 @@ export type ListenProps = {
52
52
  prepend?: boolean;
53
53
  };
54
54
  /**
55
- * A listener recorded on a class by `@Event.on` / `@Event.once`.
55
+ * A listener recorded on a class by `@Lifecycle.on` / `@Lifecycle.once`.
56
56
  * @author Axel Nana <axel.nana@workbud.com>
57
57
  */
58
- export type EventListenerMeta = {
58
+ export type LifecycleListenerMeta = {
59
59
  readonly event: string;
60
60
  readonly method: string | symbol;
61
61
  readonly mode: 'on' | 'once';
@@ -63,12 +63,12 @@ export type EventListenerMeta = {
63
63
  readonly static: boolean;
64
64
  };
65
65
  /**
66
- * Gets the listeners recorded on a class by the `@Event.on` / `@Event.once` decorators.
66
+ * Gets the listeners recorded on a class by the `@Lifecycle.on` / `@Lifecycle.once` decorators.
67
67
  * @author Axel Nana <axel.nana@workbud.com>
68
68
  * @param cls The class to inspect.
69
69
  */
70
- export declare function getEventListeners(cls: object): readonly EventListenerMeta[];
71
- export declare namespace Event {
70
+ export declare function getLifecycleListeners(cls: object): readonly LifecycleListenerMeta[];
71
+ export declare namespace Lifecycle {
72
72
  /**
73
73
  * Emits an event with the provided data and source.
74
74
  * @author Axel Nana <axel.nana@workbud.com>
@@ -87,30 +87,37 @@ export declare namespace Event {
87
87
  * Static methods are called on the class. Instance methods require a class registered as a
88
88
  * service: they run on the instance the container resolves.
89
89
  * @author Axel Nana <axel.nana@workbud.com>
90
- * @param options The decorator options.
90
+ * @param event The event name.
91
+ * @param options Whether the listener goes first (`prepend`).
92
+ * @example
93
+ * ```ts
94
+ * @Lifecycle.on('elysium:error')
95
+ * public static report(e: LifecycleEvent<Error>) {}
96
+ * ```
91
97
  */
92
- function on(options: Omit<ListenProps, 'mode'>): MethodDecorator;
98
+ function on(event: string, options?: Omit<ListenProps, 'mode' | 'event'>): MethodDecorator;
93
99
  /**
94
100
  * Registers an event listener for the specified event.
95
101
  * @author Axel Nana <axel.nana@workbud.com>
96
102
  * @param event The event name.
97
103
  * @param handler The event handler function.
98
104
  */
99
- function on<TData, TSource = any>(event: string, handler: EventHandler<TData, TSource>): void;
105
+ function on<TData, TSource = any>(event: string, handler: LifecycleHandler<TData, TSource>): void;
100
106
  /**
101
107
  * Marks a method as an event listener that listens for the event only once.
102
- * Like {@link Event.on}, the decorator is metadata-only and the kernel wires it per module.
108
+ * Like {@link Lifecycle.on}, the decorator is metadata-only and the kernel wires it per module.
103
109
  * @author Axel Nana <axel.nana@workbud.com>
104
- * @param options The decorator options.
110
+ * @param event The event name.
111
+ * @param options Whether the listener goes first (`prepend`).
105
112
  */
106
- function once(options: Omit<ListenProps, 'mode'>): MethodDecorator;
113
+ function once(event: string, options?: Omit<ListenProps, 'mode' | 'event'>): MethodDecorator;
107
114
  /**
108
115
  * Registers an event listener for the specified event that listens for the event only once.
109
116
  * @author Axel Nana <axel.nana@workbud.com>
110
117
  * @param event The event name.
111
118
  * @param handler The event handler function.
112
119
  */
113
- function once<TData, TSource = any>(event: string, handler: EventHandler<TData, TSource>): void;
120
+ function once<TData, TSource = any>(event: string, handler: LifecycleHandler<TData, TSource>): void;
114
121
  /**
115
122
  * Removes all event listeners for the specified event.
116
123
  * @author Axel Nana <axel.nana@workbud.com>
@@ -127,5 +134,5 @@ export declare namespace Event {
127
134
  * @param options The decorator options.
128
135
  * @throws {ElysiumError} If `options.mode` is neither `on` nor `once`.
129
136
  */
130
- const listen: (options: ListenProps) => MethodDecorator;
137
+ const listen: ({ mode, event, ...options }: ListenProps) => MethodDecorator;
131
138
  }
@@ -12,6 +12,11 @@ import type { ModuleNode } from './module-graph';
12
12
  * @param node The module the middlewares belong to. The chain runs in its context.
13
13
  */
14
14
  export declare const executeMiddlewareChain: (middlewares: Middleware[], context: any, method: Exclude<keyof Middleware, 'guards'>, node?: ModuleNode | null) => Promise<any>;
15
+ /**
16
+ * The status responses thrown by middlewares to stop a request. They are responses, not errors.
17
+ * @author Axel Nana <axel.nana@workbud.com>
18
+ */
19
+ export declare const middlewareStops: WeakSet<object>;
15
20
  /**
16
21
  * Builds middleware instances in the context of a module.
17
22
  * @author Axel Nana <axel.nana@workbud.com>
@@ -20,19 +25,24 @@ export declare const executeMiddlewareChain: (middlewares: Middleware[], context
20
25
  */
21
26
  export declare const makeMiddlewares: (middlewares: Class<Middleware>[], node: ModuleNode | null) => Middleware[];
22
27
  /**
23
- * Apply a list of middlewares to an Elysia plugin.
28
+ * Remembers the middlewares a request went through, so that their onError runs if it fails.
24
29
  * @author Axel Nana <axel.nana@workbud.com>
25
- * @param middlewares The list of middlewares to apply.
26
- * @param plugin The Elysia plugin instance.
27
- * @param node The module the middlewares belong to, or `null` for application-level middlewares.
28
30
  */
31
+ export declare const enlist: (context: any, middlewares: Middleware[], node: ModuleNode | null) => void;
32
+ /**
33
+ * Runs the `onError` hook of the middlewares a failed request went through, innermost first.
34
+ * Called by the error handler before it builds the response; a failing hook is logged.
35
+ * @author Axel Nana <axel.nana@workbud.com>
36
+ * @param context The context of the failed request, with its `error`.
37
+ */
38
+ export declare const runErrorMiddlewares: (context: any) => Promise<void>;
29
39
  export declare const applyMiddlewares: (middlewares: Class<Middleware>[], plugin: AnyElysia, node: ModuleNode | null) => void;
30
40
  /**
31
41
  * Base class for middleware.
32
42
  * @author Axel Nana <axel.nana@workbud.com>
33
43
  * @template TGuards The list of guards for the middleware.
34
44
  */
35
- export declare abstract class Middleware<TGuards extends string[] = []> {
45
+ export declare abstract class Middleware<TGuards extends string[] = string[]> {
36
46
  /**
37
47
  * Registers a list of middlewares to be applied on a controller or endpoint.
38
48
  * @author Axel Nana <axel.nana@workbud.com>
@@ -81,6 +91,15 @@ export declare abstract class Middleware<TGuards extends string[] = []> {
81
91
  * @param _ctx The request context.
82
92
  */
83
93
  onBeforeHandle(_ctx: Context): any;
94
+ /**
95
+ * Called when the request fails, before the error response is built, such as to save what the
96
+ * request changed or to set headers on the error response. Its context carries the `error`.
97
+ * It cannot replace the response: exception filters do that.
98
+ * @param _ctx The request context.
99
+ */
100
+ onError(_ctx: Context & {
101
+ error: unknown;
102
+ }): any;
84
103
  /**
85
104
  * Called after the response has been sent.
86
105
  * @param _ctx The request context.
@@ -13,6 +13,12 @@ export declare namespace ModuleContext {
13
13
  * @returns The value returned by `fn`.
14
14
  */
15
15
  function run<T>(node: ModuleNode, fn: () => T): T;
16
+ /**
17
+ * Runs `fn` outside of any module, so that service lookups only see the global scope.
18
+ * @param fn The function to run.
19
+ * @returns The value returned by `fn`.
20
+ */
21
+ function outside<T>(fn: () => T): T;
16
22
  /**
17
23
  * Gets the module current in this async context, if any.
18
24
  */
@@ -1,5 +1,6 @@
1
1
  import type { Class } from 'type-fest';
2
2
  import type { ConfiguredModule, ModuleClass, ModuleProps } from './module';
3
+ import type { ServiceToken } from './token';
3
4
  /**
4
5
  * A configuration given to a module from one import site.
5
6
  * @author Axel Nana <axel.nana@workbud.com>
@@ -36,8 +37,8 @@ export type ModuleGraph = {
36
37
  node(cls: ModuleClass): ModuleNode | undefined;
37
38
  /** The direct imports of `node`, plus the modules they re-export. */
38
39
  visibleOwners(node: ModuleNode): readonly ModuleNode[];
39
- /** The service names `node` exports, or `true` when it exports everything. */
40
- exportsOf(node: ModuleNode): readonly string[] | true;
40
+ /** The service tokens `node` exports, or `true` when it exports everything. */
41
+ exportsOf(node: ModuleNode): readonly ServiceToken[] | true;
41
42
  /** Gathers controllers, commands or jobs across modules, with their owner. */
42
43
  collect<K extends Collectable>(key: K): readonly {
43
44
  item: Class<any>;
package/dist/module.d.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  import type { Elysia } from 'elysia';
2
2
  import type { Class } from 'type-fest';
3
+ import type { TSchema } from 'typebox';
3
4
  import type { CommandClass } from './command';
5
+ import type { FilterClass } from './filter';
4
6
  import type { Route } from './http';
7
+ import type { InterceptorClass } from './interceptor';
5
8
  import type { ServiceDefinition } from './service';
6
9
  import { Symbols } from './utils';
7
10
  /**
@@ -39,6 +42,21 @@ export type ModuleProps<TConfig = unknown> = {
39
42
  prefix?: Route;
40
43
  /** Environments (`NODE_ENV` values) in which the module is active. */
41
44
  env?: string[];
45
+ /**
46
+ * The environment variables the module needs, as a TypeBox object. Merged with the schemas of
47
+ * the app and the other active modules, and validated at boot.
48
+ */
49
+ envSchema?: TSchema;
50
+ /**
51
+ * Exception filters for the routes of this module's controllers. They run after the filters
52
+ * of the route and the controller, and before those of the app.
53
+ */
54
+ filters?: FilterClass[];
55
+ /**
56
+ * Interceptors around the handlers of this module's controllers, inside those of the app and
57
+ * outside those of the controller.
58
+ */
59
+ interceptors?: InterceptorClass[];
42
60
  };
43
61
  /**
44
62
  * Checks whether a value is a configured module.