@digital-alchemy/core 26.2.17 → 26.5.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/CLAUDE.md +302 -0
- package/README.md +19 -3
- package/dist/helpers/async.d.mts +37 -0
- package/dist/helpers/async.mjs +50 -15
- package/dist/helpers/async.mjs.map +1 -1
- package/dist/helpers/config-environment-loader.d.mts +39 -0
- package/dist/helpers/config-environment-loader.mjs +51 -11
- package/dist/helpers/config-environment-loader.mjs.map +1 -1
- package/dist/helpers/config-file-loader.d.mts +65 -0
- package/dist/helpers/config-file-loader.mjs +80 -4
- package/dist/helpers/config-file-loader.mjs.map +1 -1
- package/dist/helpers/config.d.mts +202 -5
- package/dist/helpers/config.mjs +60 -0
- package/dist/helpers/config.mjs.map +1 -1
- package/dist/helpers/context.d.mts +12 -1
- package/dist/helpers/cron.d.mts +154 -7
- package/dist/helpers/cron.mjs +47 -4
- package/dist/helpers/cron.mjs.map +1 -1
- package/dist/helpers/errors.d.mts +45 -0
- package/dist/helpers/errors.mjs +45 -0
- package/dist/helpers/errors.mjs.map +1 -1
- package/dist/helpers/events.d.mts +23 -0
- package/dist/helpers/events.mjs +23 -0
- package/dist/helpers/events.mjs.map +1 -1
- package/dist/helpers/extend.d.mts +50 -0
- package/dist/helpers/extend.mjs +63 -0
- package/dist/helpers/extend.mjs.map +1 -1
- package/dist/helpers/index.d.mts +9 -0
- package/dist/helpers/index.mjs +9 -0
- package/dist/helpers/index.mjs.map +1 -1
- package/dist/helpers/lifecycle.d.mts +102 -16
- package/dist/helpers/lifecycle.mjs +19 -1
- package/dist/helpers/lifecycle.mjs.map +1 -1
- package/dist/helpers/logger.d.mts +178 -17
- package/dist/helpers/logger.mjs +41 -1
- package/dist/helpers/logger.mjs.map +1 -1
- package/dist/helpers/module.d.mts +110 -0
- package/dist/helpers/module.mjs +55 -6
- package/dist/helpers/module.mjs.map +1 -1
- package/dist/helpers/service-runner.d.mts +27 -1
- package/dist/helpers/service-runner.mjs +27 -1
- package/dist/helpers/service-runner.mjs.map +1 -1
- package/dist/helpers/utilities.d.mts +123 -3
- package/dist/helpers/utilities.mjs +110 -3
- package/dist/helpers/utilities.mjs.map +1 -1
- package/dist/helpers/wiring.d.mts +385 -0
- package/dist/helpers/wiring.mjs +120 -0
- package/dist/helpers/wiring.mjs.map +1 -1
- package/dist/services/als.service.d.mts +10 -0
- package/dist/services/als.service.mjs +49 -0
- package/dist/services/als.service.mjs.map +1 -1
- package/dist/services/configuration.service.d.mts +22 -0
- package/dist/services/configuration.service.mjs +140 -12
- package/dist/services/configuration.service.mjs.map +1 -1
- package/dist/services/index.d.mts +8 -0
- package/dist/services/index.mjs +8 -0
- package/dist/services/index.mjs.map +1 -1
- package/dist/services/internal.service.d.mts +98 -19
- package/dist/services/internal.service.mjs +91 -9
- package/dist/services/internal.service.mjs.map +1 -1
- package/dist/services/is.service.d.mts +64 -4
- package/dist/services/is.service.mjs +67 -4
- package/dist/services/is.service.mjs.map +1 -1
- package/dist/services/lifecycle.service.d.mts +26 -0
- package/dist/services/lifecycle.service.mjs +67 -9
- package/dist/services/lifecycle.service.mjs.map +1 -1
- package/dist/services/logger.service.d.mts +27 -0
- package/dist/services/logger.service.mjs +133 -9
- package/dist/services/logger.service.mjs.map +1 -1
- package/dist/services/scheduler.service.d.mts +19 -0
- package/dist/services/scheduler.service.mjs +87 -4
- package/dist/services/scheduler.service.mjs.map +1 -1
- package/dist/services/wiring.service.d.mts +28 -0
- package/dist/services/wiring.service.mjs +152 -19
- package/dist/services/wiring.service.mjs.map +1 -1
- package/dist/testing/index.d.mts +4 -0
- package/dist/testing/index.mjs +4 -0
- package/dist/testing/index.mjs.map +1 -1
- package/dist/testing/mock-logger.d.mts +8 -0
- package/dist/testing/mock-logger.mjs +9 -0
- package/dist/testing/mock-logger.mjs.map +1 -1
- package/dist/testing/test-module.d.mts +107 -27
- package/dist/testing/test-module.mjs +58 -1
- package/dist/testing/test-module.mjs.map +1 -1
- package/package.json +33 -31
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file helpers/wiring.mts — types and pure functions for the DI wiring engine.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* This file is the **type backbone** of `@digital-alchemy/core`. It owns
|
|
6
|
+
* `TServiceParams`, `CreateLibrary`, `buildSortOrder`, `wireOrder`, and the
|
|
7
|
+
* two wiring-boundary symbols (`COERCE_CONTEXT`, `WIRE_PROJECT`). Every
|
|
8
|
+
* downstream `@digital-alchemy` library depends on these types.
|
|
9
|
+
*
|
|
10
|
+
* **Split rationale.** This file was deliberately separated from
|
|
11
|
+
* `services/wiring.service.mts` to break a circular module reference that
|
|
12
|
+
* would form if types and the runtime bootstrap logic lived together. Do not
|
|
13
|
+
* merge them. Runtime orchestration (`CreateApplication`, `bootstrap`,
|
|
14
|
+
* `wireService`, `teardown`) lives in `services/wiring.service.mts`; pure
|
|
15
|
+
* types and pure functions live here.
|
|
16
|
+
*
|
|
17
|
+
* **Downstream impact.** Changes to `TServiceParams` shape or to any exported
|
|
18
|
+
* type here ripple into every library. Treat all public exports as breaking
|
|
19
|
+
* surface.
|
|
20
|
+
*/
|
|
1
21
|
import type { AsyncLocalStorage } from "node:async_hooks";
|
|
2
22
|
import type { EventEmitter } from "node:events";
|
|
3
23
|
import type { Dayjs } from "dayjs";
|
|
@@ -8,9 +28,37 @@ import type { CronExpression, TOffset } from "./cron.mts";
|
|
|
8
28
|
import type { TLifecycleBase } from "./lifecycle.mts";
|
|
9
29
|
import type { GetLogger, TConfigLogLevel } from "./logger.mts";
|
|
10
30
|
import type { TBlackHole } from "./utilities.mts";
|
|
31
|
+
/**
|
|
32
|
+
* The value a service factory may return.
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* A service that returns `void` is wired for its side effects only (e.g.
|
|
36
|
+
* attaching lifecycle hooks). A service that returns an `OBJECT` exposes that
|
|
37
|
+
* object as its public API, accessible to other services via the module's
|
|
38
|
+
* namespace on `TServiceParams`.
|
|
39
|
+
*/
|
|
11
40
|
export type TServiceReturn<OBJECT extends object = object> = void | OBJECT;
|
|
41
|
+
/**
|
|
42
|
+
* Unresolved mapping of service name → factory function within a module.
|
|
43
|
+
*
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
12
46
|
export type TModuleMappings = Record<string, ServiceFunction>;
|
|
47
|
+
/**
|
|
48
|
+
* Resolved mapping of service name → return value after the factory has run.
|
|
49
|
+
*
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
13
52
|
export type TResolvedModuleMappings = Record<string, TServiceReturn>;
|
|
53
|
+
/**
|
|
54
|
+
* Shape of the options object passed to `CreateApplication`.
|
|
55
|
+
*
|
|
56
|
+
* @remarks
|
|
57
|
+
* `name` must be declared in `LoadedModules` via declaration merging so the
|
|
58
|
+
* type system can track which modules are loaded. `services` is the record of
|
|
59
|
+
* service factories that make up the application. `priorityInit` lets callers
|
|
60
|
+
* force specific services to wire before the rest.
|
|
61
|
+
*/
|
|
14
62
|
export interface ApplicationConfigurationOptions<S extends ServiceMap, C extends OptionalModuleConfiguration> {
|
|
15
63
|
name: keyof LoadedModules;
|
|
16
64
|
services: S;
|
|
@@ -21,33 +69,78 @@ export interface ApplicationConfigurationOptions<S extends ServiceMap, C extends
|
|
|
21
69
|
*/
|
|
22
70
|
priorityInit?: Extract<keyof S, string>[];
|
|
23
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Union of all module definition shapes — either a library or an application.
|
|
74
|
+
*
|
|
75
|
+
* @remarks
|
|
76
|
+
* Used as a generic bound wherever code needs to accept either kind without
|
|
77
|
+
* caring which one it is.
|
|
78
|
+
*/
|
|
24
79
|
export type TConfigurable<S extends ServiceMap = ServiceMap, C extends OptionalModuleConfiguration = OptionalModuleConfiguration> = LibraryDefinition<S, C> | ApplicationDefinition<S, C>;
|
|
80
|
+
/**
|
|
81
|
+
* Type-safe config accessor bound to a specific configurable module.
|
|
82
|
+
*
|
|
83
|
+
* @remarks
|
|
84
|
+
* `K` is constrained to the keys of the config block extracted from `PARENT`,
|
|
85
|
+
* and the return type is the concrete resolved type for that key.
|
|
86
|
+
*/
|
|
25
87
|
export type TGetConfig<PARENT extends TConfigurable = TConfigurable> = <K extends keyof ExtractConfig<PARENT>>(key: K) => CastConfigResult<ExtractConfig<PARENT>[K]>;
|
|
88
|
+
/**
|
|
89
|
+
* Maps a `ServiceMap` to the resolved return types of each factory.
|
|
90
|
+
*
|
|
91
|
+
* @remarks
|
|
92
|
+
* Async factories are unwrapped — `Promise<T>` becomes `T`. This is the shape
|
|
93
|
+
* of the API object exposed on `TServiceParams` for a loaded module.
|
|
94
|
+
*/
|
|
26
95
|
export type GetApisResult<S extends ServiceMap> = {
|
|
27
96
|
[K in keyof S]: ReturnType<S[K]> extends Promise<infer AsyncResult> ? AsyncResult : ReturnType<S[K]>;
|
|
28
97
|
};
|
|
98
|
+
/** @internal — extracts the config block from a library or application definition. */
|
|
29
99
|
type ExtractConfig<T> = T extends LibraryDefinition<ServiceMap, infer C> ? C : T extends ApplicationDefinition<ServiceMap, infer C> ? C : never;
|
|
100
|
+
/** A cron expression string or a `CronExpression` enum member. */
|
|
30
101
|
export type Schedule = string | CronExpression;
|
|
102
|
+
/** A runnable item that can be started and stopped. */
|
|
31
103
|
export type ScheduleItem = {
|
|
32
104
|
start: () => void;
|
|
33
105
|
stop: () => void;
|
|
34
106
|
};
|
|
107
|
+
/** Base options required by every scheduler method. */
|
|
35
108
|
export type SchedulerOptions = {
|
|
36
109
|
exec: () => TBlackHole;
|
|
37
110
|
};
|
|
38
111
|
/**
|
|
112
|
+
* General code scheduling functions.
|
|
113
|
+
*
|
|
114
|
+
* @remarks
|
|
115
|
+
* Injected as `scheduler` into every `TServiceParams`. The scheduler is
|
|
116
|
+
* lifecycle-aware — scheduled callbacks do not begin firing until the
|
|
117
|
+
* `onReady` lifecycle stage. Each method returns a `RemoveCallback` so
|
|
118
|
+
* temporary schedules can be torn down without waiting for full shutdown.
|
|
119
|
+
*
|
|
39
120
|
* General code scheduling functions
|
|
40
121
|
*
|
|
41
122
|
* Each method returns a stop function, for temporary scheduling items
|
|
42
123
|
*/
|
|
43
124
|
export type TScheduler = {
|
|
44
125
|
/**
|
|
126
|
+
* Run code on a cron schedule.
|
|
127
|
+
*
|
|
128
|
+
* @remarks
|
|
129
|
+
* Accepts a single schedule string or an array of schedules to fire `exec`
|
|
130
|
+
* on multiple cron patterns simultaneously.
|
|
131
|
+
*
|
|
45
132
|
* Run code on a cron schedule
|
|
46
133
|
*/
|
|
47
134
|
cron: (options: SchedulerOptions & {
|
|
48
135
|
schedule: Schedule | Schedule[];
|
|
49
136
|
}) => RemoveCallback;
|
|
50
137
|
/**
|
|
138
|
+
* Run code at a dynamically computed time each period.
|
|
139
|
+
*
|
|
140
|
+
* @remarks
|
|
141
|
+
* Calls `next` at startup and again after each `reset` schedule fires.
|
|
142
|
+
* `next` returns the absolute `Dayjs` of the next desired execution.
|
|
143
|
+
*
|
|
51
144
|
* Run code at a different time every {period}
|
|
52
145
|
*
|
|
53
146
|
* Calls `next` at start, and as determined by `reset`.
|
|
@@ -59,6 +152,8 @@ export type TScheduler = {
|
|
|
59
152
|
next: () => Dayjs;
|
|
60
153
|
}) => RemoveCallback;
|
|
61
154
|
/**
|
|
155
|
+
* Lifecycle-aware `setInterval` replacement.
|
|
156
|
+
*
|
|
62
157
|
* Same as setInterval but:
|
|
63
158
|
*
|
|
64
159
|
* - handles shutdown events properly
|
|
@@ -66,6 +161,8 @@ export type TScheduler = {
|
|
|
66
161
|
*/
|
|
67
162
|
setInterval: (callback: () => TBlackHole, target: TOffset) => RemoveCallback;
|
|
68
163
|
/**
|
|
164
|
+
* Lifecycle-aware `setTimeout` replacement.
|
|
165
|
+
*
|
|
69
166
|
* Same as setTimeout but:
|
|
70
167
|
*
|
|
71
168
|
* - handles shutdown events properly
|
|
@@ -73,13 +170,49 @@ export type TScheduler = {
|
|
|
73
170
|
*/
|
|
74
171
|
setTimeout: (callback: () => TBlackHole, target: TOffset) => RemoveCallback;
|
|
75
172
|
};
|
|
173
|
+
/**
|
|
174
|
+
* Registry of all loaded module definitions.
|
|
175
|
+
*
|
|
176
|
+
* @remarks
|
|
177
|
+
* Downstream libraries extend this interface via declaration merging so that
|
|
178
|
+
* `TServiceParams` can expose their APIs under the correct key. The
|
|
179
|
+
* `boilerplate` entry is always present; every other entry is added by a
|
|
180
|
+
* library's declaration merge.
|
|
181
|
+
*
|
|
182
|
+
* @example Declaration merge in a library
|
|
183
|
+
* ```typescript
|
|
184
|
+
* declare module "@digital-alchemy/core" {
|
|
185
|
+
* export interface LoadedModules {
|
|
186
|
+
* my_lib: typeof MY_LIB;
|
|
187
|
+
* }
|
|
188
|
+
* }
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
76
191
|
export interface LoadedModules {
|
|
77
192
|
boilerplate: typeof LIB_BOILERPLATE;
|
|
78
193
|
}
|
|
194
|
+
/** @internal — maps a config definition type to its concrete injected value. */
|
|
79
195
|
type CastConfigResult<T extends AnyConfig> = T extends StringConfig<infer STRING> ? STRING : T extends BooleanConfig ? boolean : T extends NumberConfig ? number : T extends StringArrayConfig ? string[] : T extends InternalConfig<infer VALUE> ? VALUE : never;
|
|
196
|
+
/**
|
|
197
|
+
* The fully-typed config object injected as `config` into `TServiceParams`.
|
|
198
|
+
*
|
|
199
|
+
* @remarks
|
|
200
|
+
* Shaped as `{ [ModuleName]: { [ConfigKey]: ResolvedType } }`. Populated at
|
|
201
|
+
* bootstrap time by the configuration service and kept in sync via the
|
|
202
|
+
* `configSources` loaders.
|
|
203
|
+
*/
|
|
80
204
|
export type TInjectedConfig = {
|
|
81
205
|
[ModuleName in keyof ModuleConfigs]: ConfigTypes<ModuleConfigs[ModuleName]>;
|
|
82
206
|
};
|
|
207
|
+
/**
|
|
208
|
+
* Per-request data that can be attached to a log entry via ALS.
|
|
209
|
+
*
|
|
210
|
+
* @remarks
|
|
211
|
+
* Extend this interface via declaration merging to attach application-specific
|
|
212
|
+
* fields to every log line emitted within an ALS context.
|
|
213
|
+
*
|
|
214
|
+
* SEE DOCS http://docs.digital-alchemy.app/docs/core/declaration-merging
|
|
215
|
+
*/
|
|
83
216
|
export interface AsyncLogData {
|
|
84
217
|
/**
|
|
85
218
|
* return ms since entry, precision is on you
|
|
@@ -90,9 +223,24 @@ export interface AsyncLogData {
|
|
|
90
223
|
*/
|
|
91
224
|
logger?: GetLogger;
|
|
92
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Shape of the data stored inside the `AsyncLocalStorage` instance managed by
|
|
228
|
+
* the ALS service.
|
|
229
|
+
*
|
|
230
|
+
* @remarks
|
|
231
|
+
* Extend via declaration merging to carry additional request-scoped fields
|
|
232
|
+
* alongside `logs`.
|
|
233
|
+
*/
|
|
93
234
|
export interface AsyncLocalData {
|
|
94
235
|
logs: AsyncLogData;
|
|
95
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Public API of the ALS service as injected into `TServiceParams`.
|
|
239
|
+
*
|
|
240
|
+
* @remarks
|
|
241
|
+
* Wraps `AsyncLocalStorage<AsyncLocalData>` with helpers for entering a
|
|
242
|
+
* context, reading the store, and merging per-request log data.
|
|
243
|
+
*/
|
|
96
244
|
export type AlsExtension = {
|
|
97
245
|
asyncStorage: () => AsyncLocalStorage<AsyncLocalData>;
|
|
98
246
|
getStore: () => AsyncLocalData;
|
|
@@ -100,7 +248,41 @@ export type AlsExtension = {
|
|
|
100
248
|
enterWith(data: AsyncLocalData): void;
|
|
101
249
|
getLogData: () => AsyncLogData;
|
|
102
250
|
};
|
|
251
|
+
/** A function that returns additional data to merge into ALS log context. */
|
|
103
252
|
export type AlsHook = () => object;
|
|
253
|
+
/**
|
|
254
|
+
* The single parameter received by every service factory in the DI graph.
|
|
255
|
+
*
|
|
256
|
+
* @remarks
|
|
257
|
+
* `TServiceParams` is the core contract of `@digital-alchemy/core`. There is
|
|
258
|
+
* no class hierarchy, no decorators, no reflection — services are plain
|
|
259
|
+
* functions that destructure the params they need:
|
|
260
|
+
*
|
|
261
|
+
* ```typescript
|
|
262
|
+
* export function MyService({ logger, lifecycle, config }: TServiceParams) {
|
|
263
|
+
* lifecycle.onReady(() => logger.info("ready"));
|
|
264
|
+
* }
|
|
265
|
+
* ```
|
|
266
|
+
*
|
|
267
|
+
* **Well-known injections:**
|
|
268
|
+
* - `als` — `AsyncLocalStorage` wrapper for per-request context
|
|
269
|
+
* - `context` — branded string describing this service's wiring path
|
|
270
|
+
* - `event` — application-global `EventEmitter`
|
|
271
|
+
* - `internal` — utility methods and boot-state introspection
|
|
272
|
+
* - `lifecycle` — hooks into the seven boot/shutdown stages
|
|
273
|
+
* - `logger` — context-aware logger bound to this service
|
|
274
|
+
* - `scheduler` — cron/interval/sliding scheduling; respects lifecycle
|
|
275
|
+
* - `config` — typed config object; safe to read after `onPostConfig`
|
|
276
|
+
* - `params` — self-reference, useful when passing the full block deeper
|
|
277
|
+
*
|
|
278
|
+
* **Module APIs** are added as additional properties via declaration merging on
|
|
279
|
+
* {@link LoadedModules}. Each loaded library exposes its return value under the
|
|
280
|
+
* library's declared name.
|
|
281
|
+
*
|
|
282
|
+
* **Note:** `TServiceParams` lives in `helpers/wiring.mts`, not in
|
|
283
|
+
* `services/wiring.service.mts`. The split breaks a circular module reference
|
|
284
|
+
* that would form if types and the runtime bootstrap lived together.
|
|
285
|
+
*/
|
|
104
286
|
export type TServiceParams = {
|
|
105
287
|
/**
|
|
106
288
|
* hooks for AsyncLocalStorage
|
|
@@ -150,24 +332,80 @@ export type TServiceParams = {
|
|
|
150
332
|
} & {
|
|
151
333
|
[K in ExternalLoadedModules]: GetApis<LoadedModules[K]>;
|
|
152
334
|
};
|
|
335
|
+
/** Names of all modules registered in `LoadedModules`. */
|
|
153
336
|
export type LoadedModuleNames = Extract<keyof LoadedModules, string>;
|
|
337
|
+
/** `LoadedModuleNames` minus `"boilerplate"` — the user-land module names. */
|
|
154
338
|
type ExternalLoadedModules = Exclude<LoadedModuleNames, "boilerplate">;
|
|
339
|
+
/**
|
|
340
|
+
* Maps each loaded module name to its declared configuration block.
|
|
341
|
+
*
|
|
342
|
+
* @internal
|
|
343
|
+
*/
|
|
155
344
|
type ModuleConfigs = {
|
|
156
345
|
[K in LoadedModuleNames]: LoadedModules[K] extends LibraryDefinition<ServiceMap, infer Config> ? Config : LoadedModules[K] extends ApplicationDefinition<ServiceMap, infer Config> ? Config : never;
|
|
157
346
|
};
|
|
347
|
+
/**
|
|
348
|
+
* Resolves each key of a config block to its concrete injected type.
|
|
349
|
+
*
|
|
350
|
+
* Now, map these configurations to their respective types using CastConfigResult for each property in the configs
|
|
351
|
+
*/
|
|
158
352
|
export type ConfigTypes<Config> = {
|
|
159
353
|
[Key in keyof Config]: Config[Key] extends AnyConfig ? CastConfigResult<Config[Key]> : never;
|
|
160
354
|
};
|
|
355
|
+
/**
|
|
356
|
+
* Maps each loaded module name to its flattened `"module.service"` strings.
|
|
357
|
+
*
|
|
358
|
+
* @remarks
|
|
359
|
+
* Used to build the `levelOverrides` key set in `LoggerOptions` so callers can
|
|
360
|
+
* address per-service log levels with full type safety.
|
|
361
|
+
*/
|
|
161
362
|
export type ServiceNames<T extends Extract<LoadedModuleNames, string> = Extract<LoadedModuleNames, string>> = {
|
|
162
363
|
[key in T]: LoadedModules[key] extends LibraryDefinition<infer S, OptionalModuleConfiguration> ? `${key}.${Extract<keyof S, string>}` : LoadedModules[key] extends ApplicationDefinition<infer S, OptionalModuleConfiguration> ? `${key}.${Extract<keyof S, string>}` : never;
|
|
163
364
|
};
|
|
365
|
+
/**
|
|
366
|
+
* Union of all `"module.service"` strings across every loaded module.
|
|
367
|
+
*
|
|
368
|
+
* @remarks
|
|
369
|
+
* Flattened variant of {@link ServiceNames} — the distributed union rather
|
|
370
|
+
* than the mapped object form.
|
|
371
|
+
*/
|
|
164
372
|
export type FlatServiceNames<T extends Extract<LoadedModuleNames, string> = Extract<LoadedModuleNames, string>> = {
|
|
165
373
|
[key in T]: LoadedModules[key] extends LibraryDefinition<infer S, OptionalModuleConfiguration> ? `${key}.${Extract<keyof S, string>}` : LoadedModules[key] extends ApplicationDefinition<infer S, OptionalModuleConfiguration> ? `${key}.${Extract<keyof S, string>}` : never;
|
|
166
374
|
}[T];
|
|
375
|
+
/**
|
|
376
|
+
* Extracts the resolved API object type for a loaded module definition.
|
|
377
|
+
*
|
|
378
|
+
* @remarks
|
|
379
|
+
* Used internally to type the per-module properties on `TServiceParams`.
|
|
380
|
+
*/
|
|
167
381
|
export type GetApis<T> = T extends LibraryDefinition<infer S, OptionalModuleConfiguration> ? GetApisResult<S> : T extends ApplicationDefinition<infer S, OptionalModuleConfiguration> ? GetApisResult<S> : never;
|
|
382
|
+
/**
|
|
383
|
+
* A function that accepts a service name and returns the resolved API for that
|
|
384
|
+
* service, handling async factories transparently.
|
|
385
|
+
*
|
|
386
|
+
* @internal
|
|
387
|
+
*/
|
|
168
388
|
export type Loader<PARENT extends TConfigurable> = <K extends keyof PARENT["services"]>(serviceName: K) => ReturnType<PARENT["services"][K]> extends Promise<infer AsyncResult> ? AsyncResult : ReturnType<PARENT["services"][K]>;
|
|
389
|
+
/**
|
|
390
|
+
* The function signature every service factory must satisfy.
|
|
391
|
+
*
|
|
392
|
+
* @remarks
|
|
393
|
+
* A service receives `TServiceParams` and returns either a value or a
|
|
394
|
+
* `Promise` of a value. The wiring engine awaits async factories before
|
|
395
|
+
* exposing the result to other services.
|
|
396
|
+
*/
|
|
169
397
|
export type ServiceFunction<R = unknown> = (params: TServiceParams) => R | Promise<R>;
|
|
398
|
+
/** A record of service-name → service factory. */
|
|
170
399
|
export type ServiceMap = Record<string, ServiceFunction>;
|
|
400
|
+
/**
|
|
401
|
+
* Shape of the options object passed to `CreateLibrary`.
|
|
402
|
+
*
|
|
403
|
+
* @remarks
|
|
404
|
+
* `name` must match the key declared in `LoadedModules`. `depends` lists
|
|
405
|
+
* libraries that must be wired before this one; `optionalDepends` lists
|
|
406
|
+
* libraries that should be wired first *if* they are present in the
|
|
407
|
+
* application but do not cause a boot failure when absent.
|
|
408
|
+
*/
|
|
171
409
|
export interface LibraryConfigurationOptions<S extends ServiceMap, C extends OptionalModuleConfiguration> {
|
|
172
410
|
name: keyof LoadedModules;
|
|
173
411
|
services: S;
|
|
@@ -192,9 +430,29 @@ export interface LibraryConfigurationOptions<S extends ServiceMap, C extends Opt
|
|
|
192
430
|
*/
|
|
193
431
|
priorityInit?: Extract<keyof S, string>[];
|
|
194
432
|
}
|
|
433
|
+
/**
|
|
434
|
+
* Partial snapshot of the full application configuration hierarchy.
|
|
435
|
+
*
|
|
436
|
+
* @remarks
|
|
437
|
+
* Used by `BootstrapOptions.configuration` to supply override values before
|
|
438
|
+
* env/argv/file loaders run, and by the `merge` method on
|
|
439
|
+
* `DigitalAlchemyConfiguration`.
|
|
440
|
+
*/
|
|
195
441
|
export type PartialConfiguration = Partial<{
|
|
196
442
|
[ModuleName in keyof ModuleConfigs]: Partial<ConfigTypes<ModuleConfigs[ModuleName]>>;
|
|
197
443
|
}>;
|
|
444
|
+
/**
|
|
445
|
+
* Options passed to `ApplicationDefinition.bootstrap()` to customise the boot
|
|
446
|
+
* sequence.
|
|
447
|
+
*
|
|
448
|
+
* @remarks
|
|
449
|
+
* Most fields have safe defaults and are optional. The most commonly used are:
|
|
450
|
+
* - `configuration` — seed values applied before loaders run
|
|
451
|
+
* - `appendLibrary` / `appendService` — inject test doubles without modifying
|
|
452
|
+
* the application definition
|
|
453
|
+
* - `configSources` — opt individual loader channels in/out
|
|
454
|
+
* - `loggerOptions` — tune the built-in logger's output format
|
|
455
|
+
*/
|
|
198
456
|
export type BootstrapOptions = {
|
|
199
457
|
/**
|
|
200
458
|
* An extra library to load after the application is constructed.
|
|
@@ -250,6 +508,14 @@ export type BootstrapOptions = {
|
|
|
250
508
|
*/
|
|
251
509
|
configSources?: Partial<Record<DataTypes, boolean>>;
|
|
252
510
|
};
|
|
511
|
+
/**
|
|
512
|
+
* Fine-grained controls for the built-in chalk/stdout logger.
|
|
513
|
+
*
|
|
514
|
+
* @remarks
|
|
515
|
+
* Passed as `loggerOptions` inside `BootstrapOptions`. Overrides here only
|
|
516
|
+
* affect the built-in logger; they are ignored when `customLogger` is
|
|
517
|
+
* provided.
|
|
518
|
+
*/
|
|
253
519
|
export type LoggerOptions = {
|
|
254
520
|
/**
|
|
255
521
|
* Generic data to include as data payload for all logs
|
|
@@ -299,7 +565,26 @@ export type LoggerOptions = {
|
|
|
299
565
|
*/
|
|
300
566
|
stdOut?: boolean;
|
|
301
567
|
};
|
|
568
|
+
/**
|
|
569
|
+
* Symbol used as the key for the `[WIRE_PROJECT]` method on library and
|
|
570
|
+
* application definitions.
|
|
571
|
+
*
|
|
572
|
+
* @remarks
|
|
573
|
+
* This symbol is the wiring boundary between a module definition and the
|
|
574
|
+
* bootstrap engine. `wiring.service.mts` calls `[WIRE_PROJECT]` on each
|
|
575
|
+
* module to initialise its lifecycle, load its configuration, and wire each
|
|
576
|
+
* service in priority order. Do not call `[WIRE_PROJECT]` outside of the
|
|
577
|
+
* bootstrap process.
|
|
578
|
+
*
|
|
579
|
+
* @internal
|
|
580
|
+
*/
|
|
302
581
|
export declare const WIRE_PROJECT: unique symbol;
|
|
582
|
+
/**
|
|
583
|
+
* Internal wiring method present on every `LibraryDefinition` and
|
|
584
|
+
* `ApplicationDefinition`.
|
|
585
|
+
*
|
|
586
|
+
* @internal
|
|
587
|
+
*/
|
|
303
588
|
type Wire = {
|
|
304
589
|
/**
|
|
305
590
|
* Internal method used in bootstrapping, do not call elsewhere
|
|
@@ -309,9 +594,26 @@ type Wire = {
|
|
|
309
594
|
*/
|
|
310
595
|
[WIRE_PROJECT]: (internal: InternalDefinition, WireService: (project: string, service: string, definition: ServiceFunction, lifecycle: TLifecycleBase, internal: InternalDefinition) => Promise<TServiceReturn<object>>) => Promise<TLifecycleBase>;
|
|
311
596
|
};
|
|
597
|
+
/**
|
|
598
|
+
* A fully-constructed library module, ready to be listed in
|
|
599
|
+
* `ApplicationConfigurationOptions.libraries`.
|
|
600
|
+
*
|
|
601
|
+
* @remarks
|
|
602
|
+
* Created by `CreateLibrary`. The `[WIRE_PROJECT]` symbol key is the bootstrap
|
|
603
|
+
* engine's entry point into this module; every other field is readable for
|
|
604
|
+
* introspection but should not be mutated after construction.
|
|
605
|
+
*/
|
|
312
606
|
export type LibraryDefinition<S extends ServiceMap, C extends OptionalModuleConfiguration> = LibraryConfigurationOptions<S, C> & Wire & {
|
|
313
607
|
type: "library";
|
|
314
608
|
};
|
|
609
|
+
/**
|
|
610
|
+
* A fully-constructed application module, ready to be bootstrapped.
|
|
611
|
+
*
|
|
612
|
+
* @remarks
|
|
613
|
+
* Created by `CreateApplication` in `services/wiring.service.mts`. Extends the
|
|
614
|
+
* library configuration shape with the `bootstrap` and `teardown` methods, a
|
|
615
|
+
* `booted` flag, and a `logger` pre-bound to the application context.
|
|
616
|
+
*/
|
|
315
617
|
export type ApplicationDefinition<S extends ServiceMap, C extends OptionalModuleConfiguration> = ApplicationConfigurationOptions<S, C> & Wire & {
|
|
316
618
|
logger: GetLogger;
|
|
317
619
|
type: "application";
|
|
@@ -319,11 +621,94 @@ export type ApplicationDefinition<S extends ServiceMap, C extends OptionalModule
|
|
|
319
621
|
bootstrap: (options?: BootstrapOptions) => Promise<TServiceParams>;
|
|
320
622
|
teardown: () => Promise<void>;
|
|
321
623
|
};
|
|
624
|
+
/** Convenience alias for a library definition with unknown service/config types. */
|
|
322
625
|
export type TLibrary = LibraryDefinition<ServiceMap, OptionalModuleConfiguration>;
|
|
626
|
+
/**
|
|
627
|
+
* Topologically sort the libraries declared by an application so that each
|
|
628
|
+
* library's dependencies are wired before it.
|
|
629
|
+
*
|
|
630
|
+
* @remarks
|
|
631
|
+
* Uses a simple iterative approach: on each pass, pick the first library whose
|
|
632
|
+
* required dependencies have all already been placed in `out`, then remove it
|
|
633
|
+
* from the working set. Optional dependencies that are not present in the
|
|
634
|
+
* application's library list are skipped rather than causing a failure.
|
|
635
|
+
*
|
|
636
|
+
* Version mismatches (same name, different object reference) emit a warning
|
|
637
|
+
* but do not block boot — the application-declared version wins.
|
|
638
|
+
*
|
|
639
|
+
* @throws {BootstrapException} `MISSING_DEPENDENCY` when a required dependency
|
|
640
|
+
* is not listed in the application's `libraries` array.
|
|
641
|
+
* @throws {BootstrapException} `BAD_SORT` when no progress can be made —
|
|
642
|
+
* usually indicates a circular dependency.
|
|
643
|
+
*/
|
|
323
644
|
export declare function buildSortOrder<S extends ServiceMap, C extends OptionalModuleConfiguration>(app: ApplicationDefinition<S, C>, logger: GetLogger): TLibrary[];
|
|
645
|
+
/**
|
|
646
|
+
* Cast a plain string to the branded `TContext` type.
|
|
647
|
+
*
|
|
648
|
+
* @remarks
|
|
649
|
+
* Used at the wiring boundary to produce context strings without importing the
|
|
650
|
+
* `TContext` brand into every caller. Prefer descriptive names like
|
|
651
|
+
* `"boilerplate:wiring"` over opaque identifiers so logs are readable.
|
|
652
|
+
*
|
|
653
|
+
* @internal
|
|
654
|
+
*/
|
|
324
655
|
export declare const COERCE_CONTEXT: (context: string) => TContext;
|
|
656
|
+
/**
|
|
657
|
+
* Fixed context string used for all errors and log messages emitted during the
|
|
658
|
+
* wiring phase itself.
|
|
659
|
+
*
|
|
660
|
+
* @internal
|
|
661
|
+
*/
|
|
325
662
|
export declare const WIRING_CONTEXT: TContext;
|
|
663
|
+
/**
|
|
664
|
+
* Assert that a library name and service map meet the minimum requirements to
|
|
665
|
+
* be wired.
|
|
666
|
+
*
|
|
667
|
+
* @remarks
|
|
668
|
+
* Called by `CreateLibrary` before any other work. Throws immediately if the
|
|
669
|
+
* name is empty or if any entry in `serviceList` is not a function, so
|
|
670
|
+
* misconfiguration surfaces at library-construction time rather than at wire
|
|
671
|
+
* time.
|
|
672
|
+
*
|
|
673
|
+
* @throws {BootstrapException} `MISSING_LIBRARY_NAME` when `project` is empty.
|
|
674
|
+
* @throws {BootstrapException} `INVALID_SERVICE_DEFINITION` when any service is not a function.
|
|
675
|
+
*
|
|
676
|
+
* @internal
|
|
677
|
+
*/
|
|
326
678
|
export declare function validateLibrary<S extends ServiceMap>(project: string, serviceList: S): void | never;
|
|
679
|
+
/**
|
|
680
|
+
* Produce a wiring order for a service list, hoisting `priority` items to the
|
|
681
|
+
* front and appending the remainder in their original order.
|
|
682
|
+
*
|
|
683
|
+
* @remarks
|
|
684
|
+
* Validates that `priority` contains no duplicates before merging, since a
|
|
685
|
+
* service wired twice would produce subtle bugs that are hard to diagnose at
|
|
686
|
+
* runtime.
|
|
687
|
+
*
|
|
688
|
+
* @throws {BootstrapException} `DOUBLE_PRIORITY` when `priority` contains
|
|
689
|
+
* duplicate service names.
|
|
690
|
+
*/
|
|
327
691
|
export declare function wireOrder<T extends string>(priority: T[], list: T[]): T[];
|
|
692
|
+
/**
|
|
693
|
+
* Construct a `LibraryDefinition` from a configuration options object.
|
|
694
|
+
*
|
|
695
|
+
* @remarks
|
|
696
|
+
* `CreateLibrary` is the public factory for all non-application modules. It
|
|
697
|
+
* validates the name and service map, then builds the `[WIRE_PROJECT]`
|
|
698
|
+
* implementation that the bootstrap engine calls to wire each service in order.
|
|
699
|
+
*
|
|
700
|
+
* **Lifecycle hook timing.** Services should attach all lifecycle hooks at the
|
|
701
|
+
* top level of their factory function. Hooks attached after the
|
|
702
|
+
* `[WIRE_PROJECT]` call completes will be silently lost (worst case) or cause
|
|
703
|
+
* a fatal error (best case).
|
|
704
|
+
*
|
|
705
|
+
* **Priority init.** `priorityInit` services are wired before all others in
|
|
706
|
+
* declaration order. The remaining services wire in no guaranteed order.
|
|
707
|
+
*
|
|
708
|
+
* @throws {BootstrapException} `MISSING_LIBRARY_NAME` — via `validateLibrary`.
|
|
709
|
+
* @throws {BootstrapException} `INVALID_SERVICE_DEFINITION` — via `validateLibrary`.
|
|
710
|
+
* @throws {BootstrapException} `MISSING_PRIORITY_SERVICE` when a name listed in
|
|
711
|
+
* `priorityInit` does not correspond to a service in the `services` map.
|
|
712
|
+
*/
|
|
328
713
|
export declare function CreateLibrary<S extends ServiceMap, C extends OptionalModuleConfiguration>({ name: libraryName, configuration, priorityInit, services, depends, optionalDepends, ...extra }: LibraryConfigurationOptions<S, C>): LibraryDefinition<S, C>;
|
|
329
714
|
export {};
|