@elysiumjs/core 0.7.0 → 0.8.0

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.
@@ -2,21 +2,31 @@ import type { AnyElysia } from 'elysia';
2
2
  import type { Class } from 'type-fest';
3
3
  import type { Context } from './http';
4
4
  import type { LoggerInterface } from './logger';
5
+ import type { ModuleNode } from './module-graph';
5
6
  /**
6
7
  * Execute a chain of middlewares, stopping on the first return or throw.
7
8
  * @author Axel Nana <axel.nana@workbud.com>
8
9
  * @param middlewares The list of middleware instances to execute.
9
10
  * @param context The context to pass to each middleware.
10
11
  * @param method The method to call on each middleware.
12
+ * @param node The module the middlewares belong to. The chain runs in its context.
11
13
  */
12
- export declare const executeMiddlewareChain: (middlewares: Middleware[], context: any, method: Exclude<keyof Middleware, "guards">) => Promise<any>;
14
+ export declare const executeMiddlewareChain: (middlewares: Middleware[], context: any, method: Exclude<keyof Middleware, 'guards'>, node?: ModuleNode | null) => Promise<any>;
15
+ /**
16
+ * Builds middleware instances in the context of a module.
17
+ * @author Axel Nana <axel.nana@workbud.com>
18
+ * @param middlewares The middleware classes.
19
+ * @param node The owning module, or `null` to build them with global visibility only.
20
+ */
21
+ export declare const makeMiddlewares: (middlewares: Class<Middleware>[], node: ModuleNode | null) => Middleware[];
13
22
  /**
14
23
  * Apply a list of middlewares to an Elysia plugin.
15
24
  * @author Axel Nana <axel.nana@workbud.com>
16
25
  * @param middlewares The list of middlewares to apply.
17
26
  * @param plugin The Elysia plugin instance.
27
+ * @param node The module the middlewares belong to, or `null` for application-level middlewares.
18
28
  */
19
- export declare const applyMiddlewares: (middlewares: Class<Middleware>[], plugin: AnyElysia) => void;
29
+ export declare const applyMiddlewares: (middlewares: Class<Middleware>[], plugin: AnyElysia, node: ModuleNode | null) => void;
20
30
  /**
21
31
  * Base class for middleware.
22
32
  * @author Axel Nana <axel.nana@workbud.com>
@@ -0,0 +1,20 @@
1
+ import type { ModuleNode } from './module-graph';
2
+ /**
3
+ * Tracks which module is currently being booted or is serving a request,
4
+ * so that service lookups can enforce module visibility.
5
+ *
6
+ * @author Axel Nana <axel.nana@workbud.com>
7
+ */
8
+ export declare namespace ModuleContext {
9
+ /**
10
+ * Runs `fn` with `node` as the current module. The context follows async continuations.
11
+ * @param node The module to make current.
12
+ * @param fn The function to run.
13
+ * @returns The value returned by `fn`.
14
+ */
15
+ function run<T>(node: ModuleNode, fn: () => T): T;
16
+ /**
17
+ * Gets the module current in this async context, if any.
18
+ */
19
+ function current(): ModuleNode | undefined;
20
+ }
@@ -0,0 +1,68 @@
1
+ import type { Class } from 'type-fest';
2
+ import type { ConfiguredModule, ModuleClass, ModuleProps } from './module';
3
+ /**
4
+ * A configuration given to a module from one import site.
5
+ * @author Axel Nana <axel.nana@workbud.com>
6
+ */
7
+ export type ConfigSource = {
8
+ readonly site: string;
9
+ readonly depth: number;
10
+ readonly config: ConfiguredModule<unknown>['config'];
11
+ };
12
+ /**
13
+ * A module in the graph.
14
+ * @author Axel Nana <axel.nana@workbud.com>
15
+ */
16
+ export type ModuleNode = {
17
+ readonly id: string;
18
+ readonly moduleClass: ModuleClass;
19
+ readonly props: ModuleProps;
20
+ readonly imports: readonly ModuleNode[];
21
+ readonly depth: number;
22
+ readonly configSources: readonly ConfigSource[];
23
+ };
24
+ type Collectable = 'controllers' | 'commands' | 'jobs';
25
+ /**
26
+ * The resolved module graph.
27
+ * @author Axel Nana <axel.nana@workbud.com>
28
+ */
29
+ export type ModuleGraph = {
30
+ /** Active modules, imports first. */
31
+ readonly nodes: readonly ModuleNode[];
32
+ /** Modules excluded by their `env`. */
33
+ readonly skipped: readonly ModuleClass[];
34
+ /** The environment the graph was built for. */
35
+ readonly environment: string;
36
+ node(cls: ModuleClass): ModuleNode | undefined;
37
+ /** The direct imports of `node`, plus the modules they re-export. */
38
+ visibleOwners(node: ModuleNode): readonly ModuleNode[];
39
+ /** The service names `node` exports, or `true` when it exports everything. */
40
+ exportsOf(node: ModuleNode): readonly string[] | true;
41
+ /** Gathers controllers, commands or jobs across modules, with their owner. */
42
+ collect<K extends Collectable>(key: K): readonly {
43
+ item: Class<any>;
44
+ module: ModuleNode;
45
+ }[];
46
+ };
47
+ type Entry = ModuleClass | ConfiguredModule<any>;
48
+ /**
49
+ * Builds the module graph from decorator metadata. Pure: constructs nothing.
50
+ *
51
+ * @param rootModules The modules declared by the application.
52
+ * @param environment The active environment.
53
+ * @throws {ModuleCycleError} On an import cycle.
54
+ * @throws {DuplicateModuleNameError} When two different module classes share a name.
55
+ * @throws {DuplicateCommandError} When two modules register the same command.
56
+ * @author Axel Nana <axel.nana@workbud.com>
57
+ */
58
+ export declare function buildModuleGraph(rootModules: Entry[], environment?: string): ModuleGraph;
59
+ /**
60
+ * Picks the configuration that applies to a module: the one from the lowest depth.
61
+ * Equal object-form configs at the same depth are accepted; different ones conflict.
62
+ * Function-form configs are not compared here.
63
+ *
64
+ * @throws {ConflictingModuleConfigError} When two different object configs tie.
65
+ * @author Axel Nana <axel.nana@workbud.com>
66
+ */
67
+ export declare function selectConfig(node: ModuleNode): ConfigSource | undefined;
68
+ export {};
package/dist/module.d.ts CHANGED
@@ -1,48 +1,106 @@
1
+ import type { Elysia } from 'elysia';
1
2
  import type { Class } from 'type-fest';
3
+ import type { CommandClass } from './command';
2
4
  import type { Route } from './http';
5
+ import type { ServiceDefinition } from './service';
6
+ import { Symbols } from './utils';
3
7
  /**
4
- * Properties required when declaring a module using the `@module()` decorator.
8
+ * Type for a module class.
5
9
  * @author Axel Nana <axel.nana@workbud.com>
6
10
  */
7
- export type ModuleProps = {
8
- /**
9
- * The list of controllers provided by the module.
10
- */
11
+ export type ModuleClass = Class<Module>;
12
+ /**
13
+ * A module bound to a configuration, as returned by `Module.with()`.
14
+ * @author Axel Nana <axel.nana@workbud.com>
15
+ */
16
+ export type ConfiguredModule<TConfig> = {
17
+ readonly [Symbols.configuredModule]: true;
18
+ readonly module: ModuleClass;
19
+ readonly config: Partial<TConfig> | (() => Partial<TConfig> | Promise<Partial<TConfig>>);
20
+ };
21
+ /**
22
+ * Properties used when declaring a module with `@Module.register()`.
23
+ * @author Axel Nana <axel.nana@workbud.com>
24
+ */
25
+ export type ModuleProps<TConfig = unknown> = {
26
+ /** Modules (or configured modules) whose exports this module can use. */
27
+ imports?: (ModuleClass | ConfiguredModule<any>)[];
28
+ /** The services this module provides, optionally derived from its resolved config. */
29
+ services?: ServiceDefinition[] | ((config: TConfig) => ServiceDefinition[]);
30
+ /** What this module exposes to importers, or `true` to expose everything. */
31
+ exports?: (Class<any> | string | ModuleClass)[] | true;
32
+ /** The controllers provided by the module. */
11
33
  controllers?: Class<any>[];
12
- /**
13
- * The list of jobs provided by the module.
14
- */
34
+ /** The commands provided by the module. */
35
+ commands?: CommandClass[];
36
+ /** The jobs provided by the module. */
15
37
  jobs?: Class<any>[];
16
- /**
17
- * The base path for all the controllers registered by this module.
18
- * This value will be prefixed to each controllers path.
19
- */
38
+ /** The base path prefixed to every controller path of this module. */
20
39
  prefix?: Route;
40
+ /** Environments (`NODE_ENV` values) in which the module is active. */
41
+ env?: string[];
21
42
  };
22
43
  /**
23
- * Type for a module class.
44
+ * Checks whether a value is a configured module.
24
45
  * @author Axel Nana <axel.nana@workbud.com>
25
46
  */
26
- export type ModuleClass = Class<Module>;
47
+ export declare function isConfiguredModule(value: unknown): value is ConfiguredModule<unknown>;
48
+ /**
49
+ * Reads the props a module class was declared with.
50
+ * @throws {InvalidModuleError} When the class is not decorated with `@Module.register()`.
51
+ * @author Axel Nana <axel.nana@workbud.com>
52
+ */
53
+ export declare function getModuleProps(cls: ModuleClass): ModuleProps;
27
54
  /**
28
55
  * Base class for all modules.
29
56
  * @author Axel Nana <axel.nana@workbud.com>
30
- *
31
- * This class provides base features for modules, such as hooks and lifecycle methods.
32
57
  */
33
58
  export declare abstract class Module {
34
59
  /**
35
- * Marks a class as a module.
60
+ * Marks a class as a module. Only stores metadata.
36
61
  * @author Axel Nana <axel.nana@workbud.com>
37
- * @param options The decorator options.
38
62
  */
39
- static register(options?: ModuleProps): (target: ModuleClass) => void;
63
+ static register<TConfig = any>(props?: ModuleProps<TConfig>): (target: ModuleClass) => void;
40
64
  /**
41
- * Hooks that are executed before the module is registered.
42
- */
43
- beforeRegister(): Promise<void>;
44
- /**
45
- * Hooks that are executed after the module is registered.
65
+ * Creates a base class for modules that accept configuration.
66
+ * @author Axel Nana <axel.nana@workbud.com>
46
67
  */
47
- afterRegister(): Promise<void>;
68
+ static configurable<TConfig extends object>(options?: {
69
+ defaults?: Partial<TConfig>;
70
+ required?: (keyof TConfig)[];
71
+ }): (abstract new () => {
72
+ /** The resolved configuration, set by the kernel. */
73
+ get config(): TConfig;
74
+ /** Called once the module's services are ready. */
75
+ onInit(): Promise<void> | void;
76
+ /** Called when the application shuts down. */
77
+ onDestroy(): Promise<void> | void;
78
+ /** Called with the Elysia app before it starts listening. */
79
+ onServe(_app: Elysia<any>): Promise<void> | void;
80
+ }) & {
81
+ /** Injection token of the resolved config of this module. */
82
+ get config(): string;
83
+ /** Binds a configuration to this module. */
84
+ with(config: Partial<TConfig> | (() => Partial<TConfig> | Promise<Partial<TConfig>>)): ConfiguredModule<TConfig>;
85
+ /**
86
+ * Merges the defaults with the input and checks required keys. Array defaults are
87
+ * concatenated with the user's arrays (primitives are de-duplicated), not replaced.
88
+ */
89
+ resolveConfig(input?: Partial<TConfig>): TConfig;
90
+ /**
91
+ * Marks a class as a module. Only stores metadata.
92
+ * @author Axel Nana <axel.nana@workbud.com>
93
+ */
94
+ register<TConfig_1 = any>(props?: ModuleProps<TConfig_1>): (target: ModuleClass) => void;
95
+ configurable<TConfig extends object>(options?: {
96
+ defaults?: Partial<TConfig>;
97
+ required?: (keyof TConfig)[];
98
+ }): /*elided*/ any;
99
+ };
100
+ /** Called once the module's services are ready. */
101
+ onInit(): Promise<void> | void;
102
+ /** Called when the application shuts down. */
103
+ onDestroy(): Promise<void> | void;
104
+ /** Called with the Elysia app before it starts listening. */
105
+ onServe(_app: Elysia<any>): Promise<void> | void;
48
106
  }
package/dist/redis.d.ts CHANGED
@@ -5,27 +5,32 @@
5
5
  export type RedisConnectionProps = Bun.RedisOptions & {
6
6
  url: string;
7
7
  };
8
+ /**
9
+ * Configuration of the {@link RedisModule}.
10
+ * @author Axel Nana <axel.nana@workbud.com>
11
+ */
12
+ export type RedisModuleConfig = {
13
+ /** The name of the connection that `default` resolves to. */
14
+ default: string;
15
+ /** The connections to declare, by name. */
16
+ connections: Record<string, RedisConnectionProps>;
17
+ };
8
18
  export declare namespace Redis {
9
19
  /**
10
- * Retrieves the client for a redis connection.
20
+ * Creates a service name for a redis connection.
11
21
  * @author Axel Nana <axel.nana@workbud.com>
12
22
  * @param name The name of the connection.
13
- * @returns The Bun.RedisClient for the connection with the given name.
23
+ * @returns A service name for the connection.
14
24
  */
15
- const getConnection: (name: string) => Bun.RedisClient;
25
+ const getConnectionName: (name: string) => string;
16
26
  /**
17
- * Creates and registers a new redis connection.
18
- *
19
- * This will make the registered connection available for dependency injection with
20
- * the key `redis.connection.{name}`, where `{name}` is replaced with the given name.
21
- *
27
+ * Retrieves the client for a redis connection declared by the {@link RedisModule}.
22
28
  * @author Axel Nana <axel.nana@workbud.com>
23
- *
24
29
  * @param name The name of the connection.
25
- * @param config The configuration for the redis connection.
26
- * @returns The newly created and registered redis connection.
30
+ * @returns The Bun.RedisClient for the connection with the given name.
31
+ * @throws {ServiceNotFoundError} If no connection with that name is declared.
27
32
  */
28
- const registerConnection: (name: string, config: RedisConnectionProps) => Bun.RedisClient;
33
+ const getConnection: (name?: string) => Bun.RedisClient;
29
34
  /**
30
35
  * Checks if a redis connection with the given name exists.
31
36
  * @author Axel Nana <axel.nana@workbud.com>
@@ -39,11 +44,43 @@ export declare namespace Redis {
39
44
  * @returns The default redis connection.
40
45
  */
41
46
  const getDefaultConnection: () => Bun.RedisClient;
42
- /**
43
- * Sets the default redis connection.
44
- * @author Axel Nana <axel.nana@workbud.com>
45
- * @param name The name of the connection to set as default.
46
- * @returns The default redis connection.
47
- */
48
- const setDefaultConnection: (name: string) => Bun.RedisClient;
49
47
  }
48
+ declare const RedisModule_base: (abstract new () => {
49
+ get config(): RedisModuleConfig;
50
+ onInit(): Promise<void> | void;
51
+ onDestroy(): Promise<void> | void;
52
+ onServe(_app: import("elysia").default<any>): Promise<void> | void;
53
+ }) & {
54
+ get config(): string;
55
+ with(config: Partial<RedisModuleConfig> | (() => Partial<RedisModuleConfig> | Promise<Partial<RedisModuleConfig>>)): import("./module").ConfiguredModule<RedisModuleConfig>;
56
+ resolveConfig(input?: Partial<RedisModuleConfig>): RedisModuleConfig;
57
+ register<TConfig = any>(props?: import("./module").ModuleProps<TConfig>): (target: import("./module").ModuleClass) => void;
58
+ configurable<TConfig extends object>(options?: {
59
+ defaults?: Partial<TConfig>;
60
+ required?: (keyof TConfig)[];
61
+ }): (abstract new () => {
62
+ get config(): TConfig;
63
+ onInit(): Promise<void> | void;
64
+ onDestroy(): Promise<void> | void;
65
+ onServe(_app: import("elysia").default<any>): Promise<void> | void;
66
+ }) & {
67
+ get config(): string;
68
+ with(config: Partial<TConfig> | (() => Partial<TConfig> | Promise<Partial<TConfig>>)): import("./module").ConfiguredModule<TConfig>;
69
+ resolveConfig(input?: Partial<TConfig>): TConfig;
70
+ register<TConfig_1 = any>(props?: import("./module").ModuleProps<TConfig_1>): (target: import("./module").ModuleClass) => void;
71
+ configurable<TConfig extends object>(options?: {
72
+ defaults?: Partial<TConfig>;
73
+ required?: (keyof TConfig)[];
74
+ }): /*elided*/ any;
75
+ };
76
+ };
77
+ /**
78
+ * Declares redis connections as global services named `redis.connection.{name}`, plus
79
+ * `redis.connection.default` aliasing the configured default. Clients are created on first use.
80
+ * @author Axel Nana <axel.nana@workbud.com>
81
+ */
82
+ export declare class RedisModule extends RedisModule_base {
83
+ /** Closes the clients that were created. */
84
+ onDestroy(): Promise<void>;
85
+ }
86
+ export {};
package/dist/service.d.ts CHANGED
@@ -49,14 +49,38 @@ export type ServiceProps = Partial<{
49
49
  * @see ServiceScope
50
50
  */
51
51
  scope: ServiceScope;
52
+ /**
53
+ * Whether the service is visible from every module without being exported.
54
+ */
55
+ global: boolean;
52
56
  }>;
57
+ /**
58
+ * Describes a service built by a factory function instead of a class.
59
+ * @author Axel Nana <axel.nana@workbud.com>
60
+ */
61
+ export type ServiceFactoryDefinition = {
62
+ /** The name of the service. */
63
+ name: string;
64
+ /** Builds the service from the owning module's config and a resolver for visible services. */
65
+ factory: (config: any, get: <T>(name: string | Class<T>) => T) => unknown;
66
+ /** The service's scope. Defaults to `ServiceScope.SINGLETON`. */
67
+ scope?: ServiceScope;
68
+ /** Whether the service is visible from every module. */
69
+ global?: boolean;
70
+ };
71
+ /**
72
+ * Anything a module can list in its `services`.
73
+ * @author Axel Nana <axel.nana@workbud.com>
74
+ */
75
+ export type ServiceDefinition = Class<any> | ServiceFactoryDefinition;
53
76
  export declare namespace Service {
54
77
  /**
55
- * Marks a class as a service
78
+ * Marks a class as a service. This only records metadata: the service is registered
79
+ * when the module that lists it is loaded.
56
80
  * @author Axel Nana <axel.nana@workbud.com>
57
- * @param options The decorator options.
81
+ * @param props The service options.
58
82
  */
59
- const register: (options?: ServiceProps) => (target: ServiceClass) => void;
83
+ const register: (props?: ServiceProps) => (target: ServiceClass) => void;
60
84
  /**
61
85
  * Resolves a registered service and set it as a parameter value.
62
86
  * @author Axel Nana <axel.nana@workbud.com>
@@ -64,22 +88,31 @@ export declare namespace Service {
64
88
  */
65
89
  const inject: (service?: string | ServiceClass) => ParameterDecorator;
66
90
  /**
67
- * Retrieves a registered service's instance from the container.
68
- * Resolves lazy services on first access and caches the result.
91
+ * Retrieves a service visible from the current module context (global services only
92
+ * when there is none). Lazy services are built on first access.
69
93
  * @author Axel Nana <axel.nana@workbud.com>
70
94
  * @param service The name of the service, or its class.
71
- * @returns An instance of the registered service, or `null` if no service with that name/type was registered.
95
+ * @returns The service, or `null` if none with that name/type is visible.
96
+ * @throws {ServiceNotReadyError} If the service, or its module, is still initialising.
72
97
  */
73
98
  const get: <T>(service: string | TypedServiceClass<T>) => T | null;
74
99
  /**
75
- * Registers a lazy service that is only instantiated on first access.
100
+ * Like `get`, but waits for the `onInit` of the service and of its owning module.
101
+ * @author Axel Nana <axel.nana@workbud.com>
102
+ * @param service The name of the service, or its class.
103
+ * @throws {ServiceNotFoundError | ServiceNotVisibleError | EnvironmentSkippedModuleError}
104
+ */
105
+ const resolve: <T>(service: string | TypedServiceClass<T>) => Promise<T>;
106
+ /**
107
+ * Registers a global lazy service that is only instantiated on first access.
76
108
  * @author Axel Nana <axel.nana@workbud.com>
77
109
  * @param name The name of the service.
78
110
  * @param resolver A factory function called on first access.
111
+ * @throws {DuplicateGlobalServiceError} If a global service with that name exists.
79
112
  */
80
113
  const registerLazy: <T>(name: string, resolver: () => T) => void;
81
114
  /**
82
- * Instantiates a service with its dependencies.
115
+ * Instantiates a service with its dependencies, resolved from the current module context.
83
116
  *
84
117
  * This function always create a new instance of the service, even if the service's scope is `ServiceScope.SINGLETON`
85
118
  * and it has already been registered in the container.
@@ -87,56 +120,67 @@ export declare namespace Service {
87
120
  * @author Axel Nana <axel.nana@workbud.com>
88
121
  *
89
122
  * @param service The service class to instantiate.
123
+ * @throws {ServiceNotFoundError | ServiceNotVisibleError | EnvironmentSkippedModuleError}
90
124
  */
91
125
  const make: <T>(service: TypedServiceClass<T>) => T;
92
126
  /**
93
- * Binds a service to the container and sets it as a singleton.
127
+ * Binds a service to the global scope and sets it as a singleton.
94
128
  * @author Axel Nana <axel.nana@workbud.com>
95
129
  * @param service The service class to bind.
96
130
  * @param name An optional name for the service. If not set, the name of the service class is used instead.
97
131
  */
98
132
  const bind: <T>(service: TypedServiceClass<T>, name?: string) => T;
99
133
  /**
100
- * Binds a service to the container and sets it as a factory.
134
+ * Binds a service to the global scope and sets it as a factory.
101
135
  * @author Axel Nana <axel.nana@workbud.com>
102
136
  * @param name A name for the service.
103
137
  * @param factory The factory function used to instantiate the service.
104
138
  */
105
139
  function factory<T>(name: string, factory: FactoryFn<T>): T;
106
140
  /**
107
- * Binds a service to the container and sets it as a factory.
141
+ * Binds a service to the global scope and sets it as a factory.
108
142
  * @author Axel Nana <axel.nana@workbud.com>
109
143
  * @param service The service class to bind.
110
144
  * @param name An optional name for the service. If not set, the name of the service class is used instead.
111
145
  */
112
146
  function factory<T>(service: TypedServiceClass<T>, name?: string): T;
113
147
  /**
114
- * Binds a service's instance to the container.
148
+ * Binds a service's instance to the global scope.
115
149
  * @author Axel Nana <axel.nana@workbud.com>
116
150
  * @param service The service class or name to bind.
117
151
  * @param instance The instance to bind.
152
+ * @throws {DuplicateGlobalServiceError} If a global service with that name exists.
118
153
  */
119
154
  const instance: <T>(service: string | TypedServiceClass<T>, instance: T) => T;
120
155
  /**
121
- * Removes a service from the container.
156
+ * Removes a service from the global scope.
122
157
  * @author Axel Nana <axel.nana@workbud.com>
123
158
  * @param service The service class or name to remove.
124
159
  */
125
160
  const remove: (service: string | TypedServiceClass<any>) => void;
126
161
  /**
127
- * Checks if a service exists in the container.
162
+ * Checks if a service exists in the global scope.
128
163
  * @author Axel Nana <axel.nana@workbud.com>
129
164
  * @param service The service class or name to check.
130
165
  * @returns `true` if the service exists, `false` otherwise.
131
166
  */
132
167
  const exists: (service: string | TypedServiceClass<any>) => boolean;
133
168
  /**
134
- * Clears the container.
169
+ * Checks if a service has already been built, without building it.
170
+ * Matches by name in any scope (global or module), not only the global one.
171
+ * Lets modules release only the resources they actually created.
172
+ * @author Axel Nana <axel.nana@workbud.com>
173
+ * @param name The name of the service to check.
174
+ * @returns `true` if the service has a live instance, `false` otherwise.
175
+ */
176
+ const isConstructed: (name: string) => boolean;
177
+ /**
178
+ * Clears the global scope.
135
179
  * @author Axel Nana <axel.nana@workbud.com>
136
180
  */
137
181
  const clear: () => void;
138
182
  /**
139
- * Returns an array of registered service names matching the given pattern.
183
+ * Returns an array of global service names matching the given pattern.
140
184
  * Pattern supports '*' (any sequence) and '?' (any single character), similarly to Redis KEYS.
141
185
  * @author Axel Nana <axel.nana@workbud.com>
142
186
  * @param pattern The glob-like pattern to match service names.
@@ -0,0 +1,39 @@
1
+ import type { Class } from 'type-fest';
2
+ import type { ConfiguredModule, ModuleClass } from './module';
3
+ import { Kernel } from './kernel';
4
+ /**
5
+ * Options of {@link Application.testing}.
6
+ * @author Axel Nana <axel.nana@workbud.com>
7
+ */
8
+ export type TestingOptions = {
9
+ /** The modules to boot. */
10
+ modules: (ModuleClass | ConfiguredModule<any>)[];
11
+ /** Services registered as globals; they win over module services of the same name. */
12
+ overrides?: Record<string, unknown>;
13
+ /** The environment to build the graph for. Defaults to `test`. */
14
+ environment?: string;
15
+ /** `eager` initialises every module and builds every service. Defaults to `lazy`. */
16
+ boot?: 'lazy' | 'eager';
17
+ };
18
+ /**
19
+ * A booted module graph, for tests.
20
+ * @author Axel Nana <axel.nana@workbud.com>
21
+ */
22
+ export type TestingApp = {
23
+ /** The kernel running the graph. */
24
+ kernel: Kernel;
25
+ /**
26
+ * Resolves a service, optionally from inside a module, so that visibility applies.
27
+ * @param name The service name or class.
28
+ * @param from The module to resolve from. Defaults to global lookups only.
29
+ */
30
+ get<T>(name: string | Class<T>, from?: ModuleClass): Promise<T>;
31
+ /** Destroys the services and modules, and resets the container. */
32
+ shutdown(): Promise<void>;
33
+ };
34
+ /**
35
+ * Boots a module graph without a CLI or an HTTP server.
36
+ * @param options See {@link TestingOptions}.
37
+ * @author Axel Nana <axel.nana@workbud.com>
38
+ */
39
+ export declare function createTestingApp(options: TestingOptions): Promise<TestingApp>;
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Elysia } from 'elysia';
2
+ import type { ModuleNode } from './module-graph';
2
3
  export declare namespace Symbols {
3
4
  const app: unique symbol;
4
5
  const controller: unique symbol;
@@ -7,19 +8,35 @@ export declare namespace Symbols {
7
8
  const websocket: unique symbol;
8
9
  const wamp: unique symbol;
9
10
  const http: unique symbol;
10
- const job: unique symbol;
11
11
  const arg: unique symbol;
12
12
  const elysiaPlugin: unique symbol;
13
13
  const middlewareGuards: unique symbol;
14
+ const eventListeners: unique symbol;
15
+ const serviceOptions: unique symbol;
16
+ const module: unique symbol;
17
+ const configuredModule: unique symbol;
18
+ const moduleConfig: unique symbol;
19
+ const command: unique symbol;
14
20
  }
21
+ /**
22
+ * Gets the current environment name.
23
+ * @returns `NODE_ENV`, or `development` when unset.
24
+ *
25
+ * @author Axel Nana <axel.nana@workbud.com>
26
+ */
27
+ export declare function currentEnvironment(): string;
15
28
  export declare const nextTick: () => Promise<unknown>;
16
- export type ElysiaPlugin = () => Promise<Elysia>;
29
+ /** The factory stored on a controller class; builds its Elysia plugin in a module's context. */
30
+ export type ElysiaPlugin = (node: ModuleNode) => Promise<Elysia>;
17
31
  /**
18
- * Deeply merges objects from right to left
19
- * Arrays are also merged if they are found in child objects
32
+ * Deeply merges objects from right to left. The target's values win over the sources'.
33
+ * Arrays are also merged if they are found in child objects.
34
+ *
35
+ * Neither `target` nor any source is mutated, and the result shares no plain object or
36
+ * array with them. Class instances and functions are kept by reference.
20
37
  *
21
- * @param target The target object to merge into
22
- * @param sources One or more source objects to merge from
23
- * @returns The merged object (same reference as target)
38
+ * @param target The base object
39
+ * @param sources One or more source objects providing the missing values
40
+ * @returns A new merged object
24
41
  */
25
42
  export declare function deepMerge<T extends Record<string, any | any[]>>(target: T, ...sources: T[]): T;