@digital-alchemy/core 26.1.9 → 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 +66 -1
- package/dist/helpers/config-file-loader.mjs +82 -6
- 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 +29 -1
- package/dist/services/wiring.service.mjs +153 -20
- 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
package/dist/helpers/logger.mjs
CHANGED
|
@@ -1,23 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger interface and configuration — dual-arity log methods, color schemes,
|
|
3
|
+
* and a last-resort stderr writer.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* The logger supports six severity levels (trace, debug, info, warn, error, fatal)
|
|
7
|
+
* and dual-arity calls: `logger.info("message")` or `logger.info({ context, id }, "message")`.
|
|
8
|
+
* The logger service wraps this interface with chalk/stdout formatting, ALS
|
|
9
|
+
* integration, and LOG_LEVEL filtering. `fatalLog` is the last-resort writer for
|
|
10
|
+
* fatal conditions that cannot use the logger (e.g., during bootstrap failure).
|
|
11
|
+
*/
|
|
1
12
|
import fs from "node:fs";
|
|
2
13
|
import { is, SINGLE } from "../index.mjs";
|
|
14
|
+
/**
|
|
15
|
+
* Mapping from log level to console color for pretty-printed output.
|
|
16
|
+
*/
|
|
3
17
|
export const METHOD_COLORS = new Map([
|
|
4
18
|
["trace", "grey"],
|
|
5
19
|
["debug", "blue"],
|
|
20
|
+
["info", "green"],
|
|
6
21
|
["warn", "yellow"],
|
|
7
22
|
["error", "red"],
|
|
8
|
-
["info", "green"],
|
|
9
23
|
["fatal", "magenta"],
|
|
10
24
|
]);
|
|
25
|
+
/**
|
|
26
|
+
* Event name for log level configuration updates.
|
|
27
|
+
*
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
11
30
|
export const EVENT_UPDATE_LOG_LEVELS = "EVENT_UPDATE_LOG_LEVELS";
|
|
31
|
+
/**
|
|
32
|
+
* Last-resort logger for fatal conditions that cannot use the normal logger.
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* Writes directly to `stderr` bypassing the logger service. Use only when the
|
|
36
|
+
* logger is not available (e.g., during bootstrap failure before services are
|
|
37
|
+
* wired). If `data` is an Error, extracts and formats `name`, `cause`, `message`,
|
|
38
|
+
* and stack trace; otherwise, JSON-stringifies it. Always adds a trailing newline.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* try {
|
|
43
|
+
* await bootstrap();
|
|
44
|
+
* } catch (error) {
|
|
45
|
+
* fatalLog("bootstrap failed", error);
|
|
46
|
+
* process.exit(1);
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
12
50
|
export function fatalLog(message, data) {
|
|
13
51
|
if (data) {
|
|
14
52
|
if (data instanceof Error) {
|
|
53
|
+
// extract structured error fields with fallback defaults
|
|
15
54
|
const cause = is.string(data?.cause) && !is.empty(data?.cause) ? ` - ${data.cause}` : "";
|
|
16
55
|
const stack = data.stack ? `\n` + data.stack.split("\n").slice(SINGLE).join("\n") : ``;
|
|
17
56
|
const errorOutput = `${data.name || "Error"}${cause}: ${data.message || "Unknown error"}${stack}`;
|
|
18
57
|
message = [message, errorOutput].join("\n");
|
|
19
58
|
}
|
|
20
59
|
else {
|
|
60
|
+
// pretty-print non-error data
|
|
21
61
|
message = [message, JSON.stringify(data, undefined, " ")].join("\n");
|
|
22
62
|
}
|
|
23
63
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.mjs","sourceRoot":"","sources":["../../src/helpers/logger.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAIzB,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"logger.mjs","sourceRoot":"","sources":["../../src/helpers/logger.mts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AAIzB,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAqN1C;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAgC;IAClE,CAAC,OAAO,EAAE,MAAM,CAAC;IACjB,CAAC,OAAO,EAAE,MAAM,CAAC;IACjB,CAAC,MAAM,EAAE,OAAO,CAAC;IACjB,CAAC,MAAM,EAAE,QAAQ,CAAC;IAClB,CAAC,OAAO,EAAE,KAAK,CAAC;IAChB,CAAC,OAAO,EAAE,SAAS,CAAC;CACrB,CAAC,CAAC;AAOH;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,yBAAyB,CAAC;AAEjE;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,IAAc;IACtD,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,IAAI,YAAY,KAAK,EAAE,CAAC;YAC1B,yDAAyD;YACzD,MAAM,KAAK,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACvF,MAAM,WAAW,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,OAAO,GAAG,KAAK,KAAK,IAAI,CAAC,OAAO,IAAI,eAAe,GAAG,KAAK,EAAE,CAAC;YAClG,OAAO,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACN,8BAA8B;YAC9B,OAAO,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;IACD,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,OAAO,IAAI,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -2,13 +2,31 @@ import type { Except, Merge } from "type-fest";
|
|
|
2
2
|
import type { iTestRunner } from "../index.mts";
|
|
3
3
|
import type { OptionalModuleConfiguration } from "./config.mts";
|
|
4
4
|
import type { ApplicationDefinition, LibraryDefinition, ServiceFunction, ServiceMap, TLibrary } from "./wiring.mts";
|
|
5
|
+
/**
|
|
6
|
+
* Options that control how {@link DigitalAlchemyModule.extend} behaves when
|
|
7
|
+
* forking a module into a modified copy.
|
|
8
|
+
*/
|
|
5
9
|
export type ExtendOptions = {
|
|
10
|
+
/** Override the name given to the extended module. */
|
|
6
11
|
name?: string;
|
|
7
12
|
/**
|
|
8
13
|
* default: true
|
|
9
14
|
*/
|
|
10
15
|
keepConfiguration?: boolean;
|
|
11
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Intermediate representation of a Digital Alchemy module before it is
|
|
19
|
+
* committed to a concrete form (library, application, or test runner).
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* A `DigitalAlchemyModule` holds the full set of services and configuration
|
|
23
|
+
* that would be passed to `CreateLibrary` or `CreateApplication`, but defers
|
|
24
|
+
* that commitment so callers can compose and mutate before export.
|
|
25
|
+
*
|
|
26
|
+
* Call `.extend()` to obtain a {@link ModuleExtension} builder, then chain
|
|
27
|
+
* mutations before finalising with `.toLibrary()`, `.toApplication()`, or
|
|
28
|
+
* `.toTest()`.
|
|
29
|
+
*/
|
|
12
30
|
export type DigitalAlchemyModule<S extends ServiceMap, C extends OptionalModuleConfiguration> = {
|
|
13
31
|
services: S;
|
|
14
32
|
configuration: C;
|
|
@@ -18,6 +36,14 @@ export type DigitalAlchemyModule<S extends ServiceMap, C extends OptionalModuleC
|
|
|
18
36
|
priorityInit: string[];
|
|
19
37
|
extend: (options?: ExtendOptions) => ModuleExtension<S, C>;
|
|
20
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* Input shape required to construct a {@link DigitalAlchemyModule}.
|
|
41
|
+
*
|
|
42
|
+
* @remarks
|
|
43
|
+
* Mirrors `LibraryConfigurationOptions` but without the `type` discriminant or
|
|
44
|
+
* the runtime wiring symbol — those are added by `CreateLibrary` /
|
|
45
|
+
* `CreateApplication` at export time.
|
|
46
|
+
*/
|
|
21
47
|
export type CreateModuleOptions<S extends ServiceMap, C extends OptionalModuleConfiguration> = {
|
|
22
48
|
services: S;
|
|
23
49
|
configuration: C;
|
|
@@ -27,46 +53,130 @@ export type CreateModuleOptions<S extends ServiceMap, C extends OptionalModuleCo
|
|
|
27
53
|
priorityInit: string[];
|
|
28
54
|
};
|
|
29
55
|
/**
|
|
56
|
+
* Chainable builder returned by {@link DigitalAlchemyModule.extend}.
|
|
57
|
+
*
|
|
58
|
+
* @remarks
|
|
59
|
+
* Each mutation method returns the same `ModuleExtension` instance (or a
|
|
60
|
+
* narrowed variant of it), enabling fluent composition. The builder holds
|
|
61
|
+
* mutable references to the service map and dependency list; calling any
|
|
62
|
+
* terminal method (`.toLibrary()`, `.toApplication()`, `.toTest()`) reads the
|
|
63
|
+
* current state and produces an immutable definition.
|
|
64
|
+
*
|
|
65
|
+
* **Method categories:**
|
|
66
|
+
* - *Dependency mutations:* `appendLibrary`, `replaceLibrary`
|
|
67
|
+
* - *Service mutations:* `appendService`, `replaceService`, `pickService`,
|
|
68
|
+
* `omitService`, `rebuild`
|
|
69
|
+
* - *Terminal exports:* `toApplication`, `toLibrary`, `toTest`
|
|
70
|
+
*
|
|
30
71
|
* commands mutate module
|
|
31
72
|
*/
|
|
32
73
|
export type ModuleExtension<S extends ServiceMap, C extends OptionalModuleConfiguration> = {
|
|
74
|
+
/**
|
|
75
|
+
* Add a new library dependency not present in the base module.
|
|
76
|
+
*
|
|
77
|
+
* @remarks
|
|
78
|
+
* Throws if the library name is already appended or exists in the base
|
|
79
|
+
* `depends` list — use `replaceLibrary` in that case.
|
|
80
|
+
*/
|
|
33
81
|
appendLibrary: (library: TLibrary) => ModuleExtension<S, C>;
|
|
82
|
+
/**
|
|
83
|
+
* Inject an additional service that is not declared in the base module.
|
|
84
|
+
*
|
|
85
|
+
* @remarks
|
|
86
|
+
* Throws if a service with the same name already exists — use
|
|
87
|
+
* `replaceService` in that case.
|
|
88
|
+
*/
|
|
34
89
|
appendService: (name: string, target: ServiceFunction) => ModuleExtension<S, C>;
|
|
35
90
|
/**
|
|
91
|
+
* Swap a library dependency by name.
|
|
92
|
+
*
|
|
36
93
|
* name must match existing library
|
|
37
94
|
*/
|
|
38
95
|
replaceLibrary: (library: TLibrary) => ModuleExtension<S, C>;
|
|
39
96
|
/**
|
|
97
|
+
* Swap an existing service implementation while keeping the same key.
|
|
98
|
+
*
|
|
99
|
+
* @remarks
|
|
100
|
+
* The return type is narrowed to reflect the new function's signature.
|
|
101
|
+
* Throws if `name` does not exist in the current service map.
|
|
102
|
+
*
|
|
40
103
|
* name must match existing service
|
|
41
104
|
*/
|
|
42
105
|
replaceService: <TARGET extends keyof S, FN extends ServiceFunction>(name: TARGET, target: FN) => ModuleExtension<Merge<S, {
|
|
43
106
|
[key in TARGET]: FN;
|
|
44
107
|
}>, C>;
|
|
45
108
|
/**
|
|
109
|
+
* Reduce the service map to only the named services.
|
|
110
|
+
*
|
|
111
|
+
* @remarks
|
|
112
|
+
* Throws if any supplied name does not exist in the current service map.
|
|
113
|
+
*
|
|
46
114
|
* throws if any service does not exist
|
|
47
115
|
*/
|
|
48
116
|
pickService: <PICK extends keyof S>(...services: PICK[]) => ModuleExtension<Pick<S, PICK>, C>;
|
|
49
117
|
/**
|
|
118
|
+
* Remove specific services from the service map.
|
|
119
|
+
*
|
|
120
|
+
* @remarks
|
|
121
|
+
* Throws if any supplied name does not exist in the current service map.
|
|
122
|
+
*
|
|
50
123
|
* throws if any service does not exist
|
|
51
124
|
*/
|
|
52
125
|
omitService: <OMIT extends keyof S>(...services: OMIT[]) => ModuleExtension<Except<S, OMIT>, C>;
|
|
53
126
|
/**
|
|
127
|
+
* Replace the entire service map with a new set.
|
|
128
|
+
*
|
|
129
|
+
* @remarks
|
|
130
|
+
* `services` must extend `S` (i.e., it must be API-compatible with the
|
|
131
|
+
* original map). Use when rebuilding several services at once.
|
|
132
|
+
*
|
|
54
133
|
* build api compatible replacement (potentially adding)
|
|
55
134
|
*/
|
|
56
135
|
rebuild: <REPLACEMENTS extends S>(services: Partial<REPLACEMENTS>) => ModuleExtension<REPLACEMENTS, C>;
|
|
57
136
|
/**
|
|
137
|
+
* Finalise as an {@link ApplicationDefinition} for use with `bootstrap`.
|
|
138
|
+
*
|
|
58
139
|
* export as application
|
|
59
140
|
*/
|
|
60
141
|
toApplication: () => ApplicationDefinition<S, C>;
|
|
61
142
|
/**
|
|
143
|
+
* Finalise as a {@link LibraryDefinition} for use as a dependency.
|
|
144
|
+
*
|
|
62
145
|
* export as library
|
|
63
146
|
*/
|
|
64
147
|
toLibrary: () => LibraryDefinition<S, C>;
|
|
65
148
|
/**
|
|
149
|
+
* Finalise as an `iTestRunner` for use in vitest specs.
|
|
150
|
+
*
|
|
151
|
+
* @remarks
|
|
152
|
+
* Internally calls `.toApplication()` and wraps the result in a `TestRunner`.
|
|
153
|
+
* Prefer `.toTest()` in spec files over manually constructing both.
|
|
154
|
+
*
|
|
66
155
|
* export as test
|
|
67
156
|
*/
|
|
68
157
|
toTest: () => iTestRunner<S, C>;
|
|
69
158
|
};
|
|
159
|
+
/**
|
|
160
|
+
* Construct a new {@link DigitalAlchemyModule} from a raw options object.
|
|
161
|
+
*
|
|
162
|
+
* @remarks
|
|
163
|
+
* `createModule` is the entry point for the chainable module pattern. It
|
|
164
|
+
* captures the options into a working module object and exposes `.extend()` to
|
|
165
|
+
* begin the mutation chain. No library or application definition is produced
|
|
166
|
+
* until a terminal method is called on the resulting {@link ModuleExtension}.
|
|
167
|
+
*
|
|
168
|
+
* Use the static helpers `createModule.fromApplication` and
|
|
169
|
+
* `createModule.fromLibrary` to seed the builder from an existing definition
|
|
170
|
+
* instead of building from scratch.
|
|
171
|
+
*
|
|
172
|
+
* Typical call sequence:
|
|
173
|
+
* ```typescript
|
|
174
|
+
* createModule({ name: "my-lib", services, configuration, depends: [], priorityInit: [] })
|
|
175
|
+
* .extend()
|
|
176
|
+
* .appendLibrary(someLibrary)
|
|
177
|
+
* .toLibrary();
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
70
180
|
export declare function createModule<S extends ServiceMap, C extends OptionalModuleConfiguration>(options: CreateModuleOptions<S, C>): DigitalAlchemyModule<S, C>;
|
|
71
181
|
export declare namespace createModule {
|
|
72
182
|
var fromApplication: <S extends ServiceMap, C extends OptionalModuleConfiguration>(application: ApplicationDefinition<S, C>) => DigitalAlchemyModule<S, C>;
|
package/dist/helpers/module.mjs
CHANGED
|
@@ -1,7 +1,32 @@
|
|
|
1
1
|
import { CreateApplication, TestRunner } from "../index.mjs";
|
|
2
|
+
import { BootstrapException } from "./errors.mjs";
|
|
2
3
|
import { deepExtend } from "./extend.mjs";
|
|
3
4
|
import { CreateLibrary } from "./wiring.mjs";
|
|
4
|
-
//
|
|
5
|
+
// pure-helper file has no TServiceParams; tag bootstrap errors with a stable
|
|
6
|
+
// module-scoped context so consumers can identify the origin in stack traces
|
|
7
|
+
const MODULE_CONTEXT = "digital-alchemy:module";
|
|
8
|
+
// --- createModule factory -----------------------------------------------------
|
|
9
|
+
/**
|
|
10
|
+
* Construct a new {@link DigitalAlchemyModule} from a raw options object.
|
|
11
|
+
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* `createModule` is the entry point for the chainable module pattern. It
|
|
14
|
+
* captures the options into a working module object and exposes `.extend()` to
|
|
15
|
+
* begin the mutation chain. No library or application definition is produced
|
|
16
|
+
* until a terminal method is called on the resulting {@link ModuleExtension}.
|
|
17
|
+
*
|
|
18
|
+
* Use the static helpers `createModule.fromApplication` and
|
|
19
|
+
* `createModule.fromLibrary` to seed the builder from an existing definition
|
|
20
|
+
* instead of building from scratch.
|
|
21
|
+
*
|
|
22
|
+
* Typical call sequence:
|
|
23
|
+
* ```typescript
|
|
24
|
+
* createModule({ name: "my-lib", services, configuration, depends: [], priorityInit: [] })
|
|
25
|
+
* .extend()
|
|
26
|
+
* .appendLibrary(someLibrary)
|
|
27
|
+
* .toLibrary();
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
5
30
|
export function createModule(options) {
|
|
6
31
|
function extend(extendOptions) {
|
|
7
32
|
const appendLibrary = new Map();
|
|
@@ -10,18 +35,20 @@ export function createModule(options) {
|
|
|
10
35
|
appendLibrary: (library) => {
|
|
11
36
|
const name = library.name;
|
|
12
37
|
if (appendLibrary.has(name)) {
|
|
13
|
-
throw new
|
|
38
|
+
throw new BootstrapException(MODULE_CONTEXT, "LIBRARY_ALREADY_APPENDED", `${name} already is appended`);
|
|
14
39
|
}
|
|
15
40
|
const exists = workingModule.depends.some(i => i.name === name);
|
|
16
41
|
if (exists) {
|
|
17
|
-
|
|
42
|
+
// base depends list owns this name; callers must use replaceLibrary to swap it
|
|
43
|
+
throw new BootstrapException(MODULE_CONTEXT, "LIBRARY_USE_REPLACE", `${name} exists as a library in base, use replaceLibrary`);
|
|
18
44
|
}
|
|
19
45
|
appendLibrary.set(name, library);
|
|
20
46
|
return extend;
|
|
21
47
|
},
|
|
22
48
|
appendService: (name, service) => {
|
|
23
49
|
if (name in services) {
|
|
24
|
-
|
|
50
|
+
// service already registered; callers must explicitly opt into replacement
|
|
51
|
+
throw new BootstrapException(MODULE_CONTEXT, "SERVICE_USE_REPLACE", `${name} exists as a service in base, use replaceService`);
|
|
25
52
|
}
|
|
26
53
|
// @ts-expect-error the interface makes this type properly, idc
|
|
27
54
|
services[name] = service;
|
|
@@ -42,12 +69,14 @@ export function createModule(options) {
|
|
|
42
69
|
replaceLibrary: (library) => {
|
|
43
70
|
const name = library.name;
|
|
44
71
|
if (appendLibrary.has(name)) {
|
|
72
|
+
// already in the appended set — replace in-place
|
|
45
73
|
appendLibrary.set(name, library);
|
|
46
74
|
}
|
|
47
75
|
else {
|
|
48
76
|
const exists = workingModule.depends.some(i => i.name === name);
|
|
49
77
|
if (!exists) {
|
|
50
|
-
|
|
78
|
+
// neither appended nor in base depends; require explicit append first
|
|
79
|
+
throw new BootstrapException(MODULE_CONTEXT, "LIBRARY_NOT_FOUND", `${name} does not exist in module yet`);
|
|
51
80
|
}
|
|
52
81
|
appendLibrary.set(name, library);
|
|
53
82
|
}
|
|
@@ -56,13 +85,14 @@ export function createModule(options) {
|
|
|
56
85
|
// @ts-expect-error I don't care
|
|
57
86
|
replaceService: (name, target) => {
|
|
58
87
|
if (!(name in services)) {
|
|
59
|
-
throw new
|
|
88
|
+
throw new BootstrapException(MODULE_CONTEXT, "SERVICE_NOT_FOUND", `${String(name)} does not exist to replace`);
|
|
60
89
|
}
|
|
61
90
|
// @ts-expect-error I don't care
|
|
62
91
|
services[name] = target;
|
|
63
92
|
return extend;
|
|
64
93
|
},
|
|
65
94
|
toApplication: () => {
|
|
95
|
+
// merge base depends and appended libraries; appended wins on name collision
|
|
66
96
|
const depends = {};
|
|
67
97
|
workingModule.depends?.forEach(i => (depends[i.name] = i));
|
|
68
98
|
appendLibrary.forEach((value, key) => (depends[key] = value));
|
|
@@ -77,6 +107,7 @@ export function createModule(options) {
|
|
|
77
107
|
});
|
|
78
108
|
},
|
|
79
109
|
toLibrary: () => {
|
|
110
|
+
// same merge logic as toApplication; both export forms share the same dependency union
|
|
80
111
|
const depends = {};
|
|
81
112
|
workingModule.depends?.forEach(i => (depends[i.name] = i));
|
|
82
113
|
appendLibrary.forEach((value, key) => (depends[key] = value));
|
|
@@ -108,6 +139,16 @@ export function createModule(options) {
|
|
|
108
139
|
};
|
|
109
140
|
return workingModule;
|
|
110
141
|
}
|
|
142
|
+
// --- Static helpers on createModule -------------------------------------------
|
|
143
|
+
/**
|
|
144
|
+
* Seed a {@link DigitalAlchemyModule} from an existing
|
|
145
|
+
* {@link ApplicationDefinition}.
|
|
146
|
+
*
|
|
147
|
+
* @remarks
|
|
148
|
+
* Copies services, configuration, libraries, and `priorityInit` into the
|
|
149
|
+
* intermediate module shape so callers can fork and modify an application
|
|
150
|
+
* without reconstructing it from scratch.
|
|
151
|
+
*/
|
|
111
152
|
// #MARK: fromApplication
|
|
112
153
|
createModule.fromApplication = (application) => {
|
|
113
154
|
return createModule({
|
|
@@ -119,6 +160,14 @@ createModule.fromApplication = (application) => {
|
|
|
119
160
|
services: application.services,
|
|
120
161
|
});
|
|
121
162
|
};
|
|
163
|
+
/**
|
|
164
|
+
* Seed a {@link DigitalAlchemyModule} from an existing
|
|
165
|
+
* {@link LibraryDefinition}.
|
|
166
|
+
*
|
|
167
|
+
* @remarks
|
|
168
|
+
* Mirrors `fromApplication` but for library definitions, preserving
|
|
169
|
+
* `optionalDepends` which applications do not carry.
|
|
170
|
+
*/
|
|
122
171
|
// #MARK: fromLibrary
|
|
123
172
|
createModule.fromLibrary = (library) => {
|
|
124
173
|
return createModule({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.mjs","sourceRoot":"","sources":["../../src/helpers/module.mts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"module.mjs","sourceRoot":"","sources":["../../src/helpers/module.mts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal single-service bootstrap helper for scripts and one-off operations.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* `ServiceRunner` creates a complete DI graph for a single service, handling
|
|
6
|
+
* all wiring, lifecycle, and teardown. It is useful for scripts that need
|
|
7
|
+
* access to the full framework without building an application. The service
|
|
8
|
+
* function receives the same `TServiceParams` as a normal service, allowing
|
|
9
|
+
* access to config, logger, scheduler, and other boilerplate.
|
|
10
|
+
*/
|
|
1
11
|
import type { OptionalModuleConfiguration } from "./config.mts";
|
|
2
12
|
import type { ApplicationConfigurationOptions, BootstrapOptions, ConfigTypes, ServiceMap, TInjectedConfig, TServiceParams } from "./wiring.mts";
|
|
3
13
|
type ServiceRunnerConfiguration<C extends OptionalModuleConfiguration, NAME extends string> = Omit<ApplicationConfigurationOptions<ServiceMap, C>, "services" | "name" | "priorityInit"> & {
|
|
@@ -7,7 +17,23 @@ type LocalServiceParams<C extends OptionalModuleConfiguration, NAME extends stri
|
|
|
7
17
|
config: TInjectedConfig & Record<NAME, ConfigTypes<C>>;
|
|
8
18
|
};
|
|
9
19
|
/**
|
|
10
|
-
*
|
|
20
|
+
* Bootstrap and run a single typed service with full framework support.
|
|
21
|
+
*
|
|
22
|
+
* @remarks
|
|
23
|
+
* Creates an application with a single service, invokes its bootstrap lifecycle,
|
|
24
|
+
* and returns when the service completes. The service runs synchronously within
|
|
25
|
+
* the context of a fully-initialized DI graph, with access to all boilerplate
|
|
26
|
+
* services (logger, scheduler, lifecycle, config, etc.).
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* await ServiceRunner(
|
|
31
|
+
* { configuration: { myLib: {...} } },
|
|
32
|
+
* ({ logger, config }) => {
|
|
33
|
+
* logger.info("running with config:", config.myLib);
|
|
34
|
+
* }
|
|
35
|
+
* );
|
|
36
|
+
* ```
|
|
11
37
|
*/
|
|
12
38
|
export declare function ServiceRunner<C extends OptionalModuleConfiguration, NAME extends string = "dynamic">({ bootstrap, ...config }: ServiceRunnerConfiguration<C, NAME> & {
|
|
13
39
|
bootstrap?: BootstrapOptions;
|
|
@@ -1,6 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal single-service bootstrap helper for scripts and one-off operations.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* `ServiceRunner` creates a complete DI graph for a single service, handling
|
|
6
|
+
* all wiring, lifecycle, and teardown. It is useful for scripts that need
|
|
7
|
+
* access to the full framework without building an application. The service
|
|
8
|
+
* function receives the same `TServiceParams` as a normal service, allowing
|
|
9
|
+
* access to config, logger, scheduler, and other boilerplate.
|
|
10
|
+
*/
|
|
1
11
|
import { CreateApplication } from "../index.mjs";
|
|
2
12
|
/**
|
|
3
|
-
*
|
|
13
|
+
* Bootstrap and run a single typed service with full framework support.
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* Creates an application with a single service, invokes its bootstrap lifecycle,
|
|
17
|
+
* and returns when the service completes. The service runs synchronously within
|
|
18
|
+
* the context of a fully-initialized DI graph, with access to all boilerplate
|
|
19
|
+
* services (logger, scheduler, lifecycle, config, etc.).
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* await ServiceRunner(
|
|
24
|
+
* { configuration: { myLib: {...} } },
|
|
25
|
+
* ({ logger, config }) => {
|
|
26
|
+
* logger.info("running with config:", config.myLib);
|
|
27
|
+
* }
|
|
28
|
+
* );
|
|
29
|
+
* ```
|
|
4
30
|
*/
|
|
5
31
|
export async function ServiceRunner({ bootstrap, ...config }, service) {
|
|
6
32
|
await CreateApplication({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service-runner.mjs","sourceRoot":"","sources":["../../src/helpers/service-runner.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAwBjD
|
|
1
|
+
{"version":3,"file":"service-runner.mjs","sourceRoot":"","sources":["../../src/helpers/service-runner.mts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAwBjD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAIjC,EAAE,SAAS,EAAE,GAAG,MAAM,EAA0E,EAChG,OAAsE;IAEtE,MAAM,iBAAiB,CAAC;QACtB,0DAA0D;QAC1D,IAAI,EAAE,SAAS;QACf,oCAAoC;QACpC,GAAG,MAAM;QACT,QAAQ,EAAE;YACR,OAAO,EAAE,OAA0B;SACpC;KACF,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;AAC1B,CAAC"}
|