@stitchem/core 0.0.3
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/CHANGELOG.md +6 -0
- package/LICENSE +21 -0
- package/README.md +815 -0
- package/dist/container/container.d.ts +79 -0
- package/dist/container/container.js +156 -0
- package/dist/container/module.map.d.ts +22 -0
- package/dist/container/module.map.js +40 -0
- package/dist/context/context.d.ts +181 -0
- package/dist/context/context.js +395 -0
- package/dist/context/scope.d.ts +30 -0
- package/dist/context/scope.js +42 -0
- package/dist/core/core.lifecycle.d.ts +41 -0
- package/dist/core/core.lifecycle.js +37 -0
- package/dist/core/core.lifetime.d.ts +21 -0
- package/dist/core/core.lifetime.js +22 -0
- package/dist/core/core.types.d.ts +2 -0
- package/dist/core/core.types.js +2 -0
- package/dist/core/core.utils.d.ts +8 -0
- package/dist/core/core.utils.js +13 -0
- package/dist/decorator/inject.decorator.d.ts +50 -0
- package/dist/decorator/inject.decorator.js +78 -0
- package/dist/decorator/injectable.decorator.d.ts +45 -0
- package/dist/decorator/injectable.decorator.js +46 -0
- package/dist/errors/core.error.d.ts +24 -0
- package/dist/errors/core.error.js +59 -0
- package/dist/errors/error.codes.d.ts +17 -0
- package/dist/errors/error.codes.js +21 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +23 -0
- package/dist/injector/injector.d.ts +78 -0
- package/dist/injector/injector.js +295 -0
- package/dist/instance-wrapper/instance-wrapper.d.ts +61 -0
- package/dist/instance-wrapper/instance-wrapper.js +142 -0
- package/dist/instance-wrapper/instance-wrapper.types.d.ts +18 -0
- package/dist/instance-wrapper/instance-wrapper.types.js +2 -0
- package/dist/logger/console.logger.d.ts +52 -0
- package/dist/logger/console.logger.js +90 -0
- package/dist/logger/logger.token.d.ts +23 -0
- package/dist/logger/logger.token.js +23 -0
- package/dist/logger/logger.types.d.ts +38 -0
- package/dist/logger/logger.types.js +12 -0
- package/dist/module/module.d.ts +104 -0
- package/dist/module/module.decorator.d.ts +28 -0
- package/dist/module/module.decorator.js +42 -0
- package/dist/module/module.graph.d.ts +52 -0
- package/dist/module/module.graph.js +263 -0
- package/dist/module/module.js +181 -0
- package/dist/module/module.ref.d.ts +81 -0
- package/dist/module/module.ref.js +123 -0
- package/dist/module/module.types.d.ts +80 -0
- package/dist/module/module.types.js +10 -0
- package/dist/provider/provider.guards.d.ts +46 -0
- package/dist/provider/provider.guards.js +62 -0
- package/dist/provider/provider.interface.d.ts +39 -0
- package/dist/provider/provider.interface.js +2 -0
- package/dist/test/test.d.ts +22 -0
- package/dist/test/test.js +23 -0
- package/dist/test/test.module-builder.d.ts +136 -0
- package/dist/test/test.module-builder.js +377 -0
- package/dist/test/test.module.d.ts +71 -0
- package/dist/test/test.module.js +151 -0
- package/dist/token/lazy.token.d.ts +44 -0
- package/dist/token/lazy.token.js +42 -0
- package/dist/token/token.types.d.ts +8 -0
- package/dist/token/token.types.js +2 -0
- package/dist/token/token.utils.d.ts +9 -0
- package/dist/token/token.utils.js +19 -0
- package/package.json +62 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose, inner;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
16
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
17
|
+
}
|
|
18
|
+
else if (async) {
|
|
19
|
+
env.stack.push({ async: true });
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
24
|
+
return function (env) {
|
|
25
|
+
function fail(e) {
|
|
26
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
27
|
+
env.hasError = true;
|
|
28
|
+
}
|
|
29
|
+
var r, s = 0;
|
|
30
|
+
function next() {
|
|
31
|
+
while (r = env.stack.pop()) {
|
|
32
|
+
try {
|
|
33
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
34
|
+
if (r.dispose) {
|
|
35
|
+
var result = r.dispose.call(r.value);
|
|
36
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
37
|
+
}
|
|
38
|
+
else s |= 1;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
fail(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
});
|
|
53
|
+
import { isDynamicModule } from '../module/module.types.js';
|
|
54
|
+
import { isModule } from '../module/module.decorator.js';
|
|
55
|
+
import { Container } from '../container/container.js';
|
|
56
|
+
import { ModuleGraph } from '../module/module.graph.js';
|
|
57
|
+
import { ModuleRef } from '../module/module.ref.js';
|
|
58
|
+
import { Injector } from '../injector/injector.js';
|
|
59
|
+
import { CoreError } from '../errors/core.error.js';
|
|
60
|
+
import { hasOnReady } from '../core/core.lifecycle.js';
|
|
61
|
+
import { ConsoleLogger, NoopLogger } from '../logger/console.logger.js';
|
|
62
|
+
import { LOGGER } from '../logger/logger.token.js';
|
|
63
|
+
import { Scope } from './scope.js';
|
|
64
|
+
/**
|
|
65
|
+
* The Context orchestrates the DI system.
|
|
66
|
+
* It compiles modules, manages their lifecycle, and provides dependency resolution.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* await using ctx = await Context.create(AppModule);
|
|
71
|
+
*
|
|
72
|
+
* const userService = await ctx.resolve(UserService);
|
|
73
|
+
*
|
|
74
|
+
* await ctx.withScope(async () => {
|
|
75
|
+
* const requestService = await ctx.resolve(RequestService);
|
|
76
|
+
* });
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export class Context {
|
|
80
|
+
container;
|
|
81
|
+
injector;
|
|
82
|
+
moduleGraph;
|
|
83
|
+
rootModule;
|
|
84
|
+
initialized = false;
|
|
85
|
+
// noinspection JSMismatchedCollectionQueryUpdate
|
|
86
|
+
componentKeys = [];
|
|
87
|
+
constructor() {
|
|
88
|
+
this.container = new Container();
|
|
89
|
+
this.injector = new Injector(this.container);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Creates and initializes a context from a root module.
|
|
93
|
+
*
|
|
94
|
+
* @throws CoreError (INVALID_MODULE) if the root is not decorated with @module()
|
|
95
|
+
*/
|
|
96
|
+
static async create(rootModule, options) {
|
|
97
|
+
const rootClass = isDynamicModule(rootModule)
|
|
98
|
+
? rootModule.module
|
|
99
|
+
: rootModule;
|
|
100
|
+
if (!isModule(rootClass)) {
|
|
101
|
+
throw CoreError.invalidModule(rootClass);
|
|
102
|
+
}
|
|
103
|
+
const context = new Context();
|
|
104
|
+
context.componentKeys = options?.componentKeys ?? [];
|
|
105
|
+
await context.init(rootModule, options);
|
|
106
|
+
return context;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Resolves a provider by token.
|
|
110
|
+
*
|
|
111
|
+
* By default, searches across ALL modules.
|
|
112
|
+
* Pass `strict: true` to scope resolution to root module visibility.
|
|
113
|
+
*
|
|
114
|
+
* Scope auto-detection: if called inside a {@link withScope} or
|
|
115
|
+
* `scope.run()` callback, the current scope is used automatically.
|
|
116
|
+
*/
|
|
117
|
+
async resolve(token, options) {
|
|
118
|
+
this.ensureInitialized();
|
|
119
|
+
const scope = Scope.current() ?? Scope.STATIC;
|
|
120
|
+
const { wrapper, module } = options?.strict
|
|
121
|
+
? this.container.getProviderByToken(token, this.rootModule)
|
|
122
|
+
: this.container.getProviderByTokenGlobal(token);
|
|
123
|
+
return this.injector.loadInstance(wrapper, module, scope);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Synchronously resolves a provider by token.
|
|
127
|
+
*/
|
|
128
|
+
resolveSync(token, options) {
|
|
129
|
+
this.ensureInitialized();
|
|
130
|
+
const scope = Scope.current() ?? Scope.STATIC;
|
|
131
|
+
const { wrapper, module } = options?.strict
|
|
132
|
+
? this.container.getProviderByToken(token, this.rootModule)
|
|
133
|
+
: this.container.getProviderByTokenGlobal(token);
|
|
134
|
+
return this.injector.loadInstanceSync(wrapper, module, scope);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Instantiates any class by resolving its dependencies.
|
|
138
|
+
* The class does not need to be registered as a provider.
|
|
139
|
+
*/
|
|
140
|
+
async constructClass(ctor, options) {
|
|
141
|
+
this.ensureInitialized();
|
|
142
|
+
const scope = Scope.current() ?? Scope.STATIC;
|
|
143
|
+
return options?.strict
|
|
144
|
+
? this.injector.instantiateClass(ctor, this.rootModule, scope)
|
|
145
|
+
: this.injector.instantiateClassGlobal(ctor, scope);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Resolves an injectable class with global dependency resolution.
|
|
149
|
+
*
|
|
150
|
+
* Use this for injectables that are not tied to a specific module
|
|
151
|
+
* (e.g. global guards, filters, middleware registered via `useGlobal`).
|
|
152
|
+
* Dependencies are always resolved globally; the instance is cached
|
|
153
|
+
* on the root module. The `strict` option is accepted for signature
|
|
154
|
+
* compatibility with {@link ModuleRef.resolveInjectable} but is ignored
|
|
155
|
+
* (Context always resolves globally).
|
|
156
|
+
*
|
|
157
|
+
* For module-scoped resolution, use {@link ModuleRef.resolveInjectable}.
|
|
158
|
+
*/
|
|
159
|
+
async resolveInjectable(ctor, _options) {
|
|
160
|
+
this.ensureInitialized();
|
|
161
|
+
const cached = this.rootModule.getInjectable(ctor);
|
|
162
|
+
if (cached)
|
|
163
|
+
return cached;
|
|
164
|
+
const scope = Scope.current() ?? Scope.STATIC;
|
|
165
|
+
const instance = await this.injector.instantiateClassGlobal(ctor, scope);
|
|
166
|
+
if (hasOnReady(instance)) {
|
|
167
|
+
await instance.onReady();
|
|
168
|
+
}
|
|
169
|
+
this.rootModule.setInjectable(ctor, instance);
|
|
170
|
+
return instance;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Runs a callback within a new disposable scope.
|
|
174
|
+
* The scope is automatically disposed when the callback completes.
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```ts
|
|
178
|
+
* await ctx.withScope(async () => {
|
|
179
|
+
* const svc = await ctx.resolve(RequestService);
|
|
180
|
+
* });
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
async withScope(fn) {
|
|
184
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
185
|
+
try {
|
|
186
|
+
this.ensureInitialized();
|
|
187
|
+
const scope = __addDisposableResource(env_1, Scope.create((s) => this.dispose(s)), true);
|
|
188
|
+
// noinspection ES6RedundantAwait
|
|
189
|
+
return await scope.run(fn);
|
|
190
|
+
}
|
|
191
|
+
catch (e_1) {
|
|
192
|
+
env_1.error = e_1;
|
|
193
|
+
env_1.hasError = true;
|
|
194
|
+
}
|
|
195
|
+
finally {
|
|
196
|
+
const result_1 = __disposeResources(env_1);
|
|
197
|
+
if (result_1)
|
|
198
|
+
await result_1;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Creates a new disposable scope for request-scoped dependencies.
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```ts
|
|
206
|
+
* await using scope = ctx.createScope();
|
|
207
|
+
* const svc = await scope.run(() => ctx.resolve(RequestService));
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
createScope() {
|
|
211
|
+
this.ensureInitialized();
|
|
212
|
+
return Scope.create((scope) => this.dispose(scope));
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Navigates to a specific module for resolution.
|
|
216
|
+
* Returns a ModuleRef bound to the target module.
|
|
217
|
+
*/
|
|
218
|
+
select(moduleClass) {
|
|
219
|
+
this.ensureInitialized();
|
|
220
|
+
const module = this.container.getModuleByClass(moduleClass);
|
|
221
|
+
return new ModuleRef(this.container, this.injector, module);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Returns all component instances for a given metadata key.
|
|
225
|
+
* Components are returned in module dependency order.
|
|
226
|
+
*/
|
|
227
|
+
getComponents(key) {
|
|
228
|
+
this.ensureInitialized();
|
|
229
|
+
const refs = [];
|
|
230
|
+
for (const id of this.moduleGraph.sortedIds) {
|
|
231
|
+
const module = this.container.getModule(id);
|
|
232
|
+
const moduleRef = module
|
|
233
|
+
.getProvider(ModuleRef)
|
|
234
|
+
?.getInstance(Scope.STATIC);
|
|
235
|
+
for (const entry of module.getComponents(key)) {
|
|
236
|
+
refs.push({
|
|
237
|
+
instance: entry.instance,
|
|
238
|
+
classRef: entry.classRef,
|
|
239
|
+
moduleRef,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return refs;
|
|
244
|
+
}
|
|
245
|
+
/** Disposes the context and all managed instances. */
|
|
246
|
+
async [Symbol.asyncDispose]() {
|
|
247
|
+
await this.dispose();
|
|
248
|
+
}
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
// Private
|
|
251
|
+
// ---------------------------------------------------------------------------
|
|
252
|
+
/**
|
|
253
|
+
* Disposes the context.
|
|
254
|
+
* With scope: only disposes scoped instances for that scope.
|
|
255
|
+
* Without scope: disposes injectables, components, then providers — shuts down.
|
|
256
|
+
*/
|
|
257
|
+
async dispose(scope) {
|
|
258
|
+
if (!this.initialized)
|
|
259
|
+
return;
|
|
260
|
+
if (!scope) {
|
|
261
|
+
await this.disposeInjectables();
|
|
262
|
+
await this.disposeComponents();
|
|
263
|
+
}
|
|
264
|
+
await this.container.dispose(scope);
|
|
265
|
+
if (!scope) {
|
|
266
|
+
this.initialized = false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
async init(rootModule, options) {
|
|
270
|
+
this.moduleGraph = new ModuleGraph(rootModule, this.container);
|
|
271
|
+
this.rootModule = this.container.getModule(this.moduleGraph.rootModuleId);
|
|
272
|
+
this.registerModuleRefs();
|
|
273
|
+
this.registerLogger(options?.logging);
|
|
274
|
+
this.container.buildGlobalExports();
|
|
275
|
+
await this.resolveSingletons();
|
|
276
|
+
await this.resolveComponents();
|
|
277
|
+
await this.fireOnReady();
|
|
278
|
+
this.initialized = true;
|
|
279
|
+
}
|
|
280
|
+
/** Registers a ModuleRef factory provider for each module. */
|
|
281
|
+
registerModuleRefs() {
|
|
282
|
+
for (const id of this.moduleGraph.sortedIds) {
|
|
283
|
+
const module = this.container.getModule(id);
|
|
284
|
+
module.addProvider({
|
|
285
|
+
provide: ModuleRef,
|
|
286
|
+
useFactory: () => new ModuleRef(this.container, this.injector, module),
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/** Registers the LOGGER provider in every module that doesn't already have one. */
|
|
291
|
+
registerLogger(logging) {
|
|
292
|
+
let instance;
|
|
293
|
+
if (logging === false) {
|
|
294
|
+
instance = new NoopLogger();
|
|
295
|
+
}
|
|
296
|
+
else if (logging?.logger) {
|
|
297
|
+
instance = logging.logger;
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
if (logging?.level !== undefined) {
|
|
301
|
+
ConsoleLogger.setLevel(logging.level);
|
|
302
|
+
}
|
|
303
|
+
instance = new ConsoleLogger();
|
|
304
|
+
}
|
|
305
|
+
for (const id of this.moduleGraph.sortedIds) {
|
|
306
|
+
const module = this.container.getModule(id);
|
|
307
|
+
if (!module.hasProvider(LOGGER)) {
|
|
308
|
+
module.addProvider({ provide: LOGGER, useValue: instance });
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/** Eagerly resolves all singleton providers in dependency order. */
|
|
313
|
+
async resolveSingletons() {
|
|
314
|
+
const instantiatedModules = new Map();
|
|
315
|
+
for (const id of this.moduleGraph.sortedIds) {
|
|
316
|
+
const module = this.container.getModule(id);
|
|
317
|
+
for (const [, wrapper] of module.providers) {
|
|
318
|
+
if (!wrapper.isSingleton || wrapper.isResolved())
|
|
319
|
+
continue;
|
|
320
|
+
await this.injector.loadInstance(wrapper, module, Scope.STATIC);
|
|
321
|
+
}
|
|
322
|
+
// Instantiate the module class (skip if classRef already instantiated).
|
|
323
|
+
const existing = instantiatedModules.get(module.classRef);
|
|
324
|
+
if (existing !== undefined) {
|
|
325
|
+
module.instance = existing;
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
const instance = await this.injector.instantiateClass(module.classRef, module);
|
|
329
|
+
module.instance = instance;
|
|
330
|
+
instantiatedModules.set(module.classRef, instance);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/** Instantiates component classes from registered component keys. */
|
|
334
|
+
async resolveComponents() {
|
|
335
|
+
for (const id of this.moduleGraph.sortedIds) {
|
|
336
|
+
const module = this.container.getModule(id);
|
|
337
|
+
const meta = module.metadata;
|
|
338
|
+
for (const key of this.componentKeys) {
|
|
339
|
+
const classes = meta[key];
|
|
340
|
+
if (!Array.isArray(classes))
|
|
341
|
+
continue;
|
|
342
|
+
for (const cls of classes) {
|
|
343
|
+
const instance = await this.injector.instantiateClass(cls, module);
|
|
344
|
+
module.addComponent(key, instance, cls);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/** Calls onReady() on all singleton providers, module classes, and components. */
|
|
350
|
+
async fireOnReady() {
|
|
351
|
+
for (const id of this.moduleGraph.sortedIds) {
|
|
352
|
+
const module = this.container.getModule(id);
|
|
353
|
+
for (const [, wrapper] of module.providers) {
|
|
354
|
+
if (!wrapper.isSingleton)
|
|
355
|
+
continue;
|
|
356
|
+
const instance = wrapper.getInstance(Scope.STATIC);
|
|
357
|
+
if (instance !== undefined && hasOnReady(instance)) {
|
|
358
|
+
await instance.onReady();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
if (module.instance !== undefined && hasOnReady(module.instance)) {
|
|
362
|
+
await module.instance.onReady();
|
|
363
|
+
}
|
|
364
|
+
for (const key of this.componentKeys) {
|
|
365
|
+
for (const entry of module.getComponents(key)) {
|
|
366
|
+
if (hasOnReady(entry.instance)) {
|
|
367
|
+
await entry.instance.onReady();
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
/** Disposes injectables in reverse module order, before component disposal. */
|
|
374
|
+
async disposeInjectables() {
|
|
375
|
+
const moduleIds = [...this.moduleGraph.sortedIds].reverse();
|
|
376
|
+
for (const id of moduleIds) {
|
|
377
|
+
const module = this.container.getModule(id);
|
|
378
|
+
await module.disposeInjectables();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/** Disposes components in reverse module order, before provider disposal. */
|
|
382
|
+
async disposeComponents() {
|
|
383
|
+
const moduleIds = [...this.moduleGraph.sortedIds].reverse();
|
|
384
|
+
for (const id of moduleIds) {
|
|
385
|
+
const module = this.container.getModule(id);
|
|
386
|
+
await module.disposeComponents();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
ensureInitialized() {
|
|
390
|
+
if (!this.initialized) {
|
|
391
|
+
throw new Error('Context is not initialized. Call Context.create() first.');
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type ScopeDisposer = (scope: Scope) => void | Promise<void>;
|
|
2
|
+
/**
|
|
3
|
+
* Represents a scoped resolution context.
|
|
4
|
+
* Manages scope propagation via AsyncLocalStorage and implements
|
|
5
|
+
* AsyncDisposable for automatic cleanup with `await using`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* await ctx.withScope(async () => {
|
|
10
|
+
* const svc = await ctx.resolve(RequestService);
|
|
11
|
+
* });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare class Scope implements AsyncDisposable {
|
|
15
|
+
/** Static scope for singleton instances. */
|
|
16
|
+
static readonly STATIC: Scope;
|
|
17
|
+
/** The unique identifier for this scope. */
|
|
18
|
+
readonly id: string;
|
|
19
|
+
private readonly _dispose?;
|
|
20
|
+
private constructor();
|
|
21
|
+
/** @internal Creates a new scope with a unique UUIDv7 identifier. */
|
|
22
|
+
static create(dispose?: ScopeDisposer): Scope;
|
|
23
|
+
/** Returns the currently active scope, or undefined if outside any scope. */
|
|
24
|
+
static current(): Scope | undefined;
|
|
25
|
+
/** Runs a callback within this scope context. */
|
|
26
|
+
run<T>(fn: () => T): T;
|
|
27
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { v7 as uuidv7 } from 'uuid';
|
|
2
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
3
|
+
const storage = new AsyncLocalStorage();
|
|
4
|
+
/**
|
|
5
|
+
* Represents a scoped resolution context.
|
|
6
|
+
* Manages scope propagation via AsyncLocalStorage and implements
|
|
7
|
+
* AsyncDisposable for automatic cleanup with `await using`.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* await ctx.withScope(async () => {
|
|
12
|
+
* const svc = await ctx.resolve(RequestService);
|
|
13
|
+
* });
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export class Scope {
|
|
17
|
+
/** Static scope for singleton instances. */
|
|
18
|
+
static STATIC = Object.freeze(new Scope('static'));
|
|
19
|
+
/** The unique identifier for this scope. */
|
|
20
|
+
id;
|
|
21
|
+
_dispose;
|
|
22
|
+
constructor(id, dispose) {
|
|
23
|
+
this.id = id;
|
|
24
|
+
this._dispose = dispose;
|
|
25
|
+
}
|
|
26
|
+
/** @internal Creates a new scope with a unique UUIDv7 identifier. */
|
|
27
|
+
static create(dispose) {
|
|
28
|
+
return new Scope(uuidv7(), dispose);
|
|
29
|
+
}
|
|
30
|
+
/** Returns the currently active scope, or undefined if outside any scope. */
|
|
31
|
+
static current() {
|
|
32
|
+
return storage.getStore();
|
|
33
|
+
}
|
|
34
|
+
/** Runs a callback within this scope context. */
|
|
35
|
+
run(fn) {
|
|
36
|
+
return storage.run(this, fn);
|
|
37
|
+
}
|
|
38
|
+
async [Symbol.asyncDispose]() {
|
|
39
|
+
await this._dispose?.(this);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for providers that need initialization after instantiation.
|
|
3
|
+
*/
|
|
4
|
+
export interface OnInit {
|
|
5
|
+
onInit(): void | Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Interface for providers that need cleanup before disposal.
|
|
9
|
+
*/
|
|
10
|
+
export interface OnDispose {
|
|
11
|
+
onDispose(): void | Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Interface for providers that need to run logic after all modules are initialized.
|
|
15
|
+
* Fires after every singleton provider has been created and onInit() has completed.
|
|
16
|
+
*/
|
|
17
|
+
export interface OnReady {
|
|
18
|
+
onReady(): void | Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Type guard for OnInit.
|
|
22
|
+
*
|
|
23
|
+
* @param instance - The value to check for OnInit conformance.
|
|
24
|
+
* @returns True if the instance implements the OnInit interface.
|
|
25
|
+
*/
|
|
26
|
+
export declare function hasOnInit(instance: unknown): instance is OnInit;
|
|
27
|
+
/**
|
|
28
|
+
* Type guard for OnDispose.
|
|
29
|
+
*
|
|
30
|
+
* @param instance - The value to check for OnDispose conformance.
|
|
31
|
+
* @returns True if the instance implements the OnDispose interface.
|
|
32
|
+
*/
|
|
33
|
+
export declare function hasOnDispose(instance: unknown): instance is OnDispose;
|
|
34
|
+
/**
|
|
35
|
+
* Type guard for OnReady.
|
|
36
|
+
*
|
|
37
|
+
* @param instance - The value to check for OnReady conformance.
|
|
38
|
+
* @returns True if the instance implements the OnReady interface.
|
|
39
|
+
*/
|
|
40
|
+
export declare function hasOnReady(instance: unknown): instance is OnReady;
|
|
41
|
+
//# sourceMappingURL=core.lifecycle.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guard for OnInit.
|
|
3
|
+
*
|
|
4
|
+
* @param instance - The value to check for OnInit conformance.
|
|
5
|
+
* @returns True if the instance implements the OnInit interface.
|
|
6
|
+
*/
|
|
7
|
+
export function hasOnInit(instance) {
|
|
8
|
+
return (typeof instance === 'object' &&
|
|
9
|
+
instance !== null &&
|
|
10
|
+
'onInit' in instance &&
|
|
11
|
+
typeof instance.onInit === 'function');
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Type guard for OnDispose.
|
|
15
|
+
*
|
|
16
|
+
* @param instance - The value to check for OnDispose conformance.
|
|
17
|
+
* @returns True if the instance implements the OnDispose interface.
|
|
18
|
+
*/
|
|
19
|
+
export function hasOnDispose(instance) {
|
|
20
|
+
return (typeof instance === 'object' &&
|
|
21
|
+
instance !== null &&
|
|
22
|
+
'onDispose' in instance &&
|
|
23
|
+
typeof instance.onDispose === 'function');
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Type guard for OnReady.
|
|
27
|
+
*
|
|
28
|
+
* @param instance - The value to check for OnReady conformance.
|
|
29
|
+
* @returns True if the instance implements the OnReady interface.
|
|
30
|
+
*/
|
|
31
|
+
export function hasOnReady(instance) {
|
|
32
|
+
return (typeof instance === 'object' &&
|
|
33
|
+
instance !== null &&
|
|
34
|
+
'onReady' in instance &&
|
|
35
|
+
typeof instance.onReady === 'function');
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=core.lifecycle.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines the lifetime/scope of a dependency instance.
|
|
3
|
+
*/
|
|
4
|
+
export declare enum Lifetime {
|
|
5
|
+
/**
|
|
6
|
+
* A single instance is created and shared across the entire container.
|
|
7
|
+
* This is the default lifetime.
|
|
8
|
+
*/
|
|
9
|
+
SINGLETON = "singleton",
|
|
10
|
+
/**
|
|
11
|
+
* A new instance is created every time the dependency is resolved.
|
|
12
|
+
*/
|
|
13
|
+
TRANSIENT = "transient",
|
|
14
|
+
/**
|
|
15
|
+
* A single instance is created per scope (identified by UUID).
|
|
16
|
+
* Scoped containers are created via `container.createScope()`.
|
|
17
|
+
* Useful for request-scoped dependencies in web frameworks.
|
|
18
|
+
*/
|
|
19
|
+
SCOPED = "scoped"
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=core.lifetime.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines the lifetime/scope of a dependency instance.
|
|
3
|
+
*/
|
|
4
|
+
export var Lifetime;
|
|
5
|
+
(function (Lifetime) {
|
|
6
|
+
/**
|
|
7
|
+
* A single instance is created and shared across the entire container.
|
|
8
|
+
* This is the default lifetime.
|
|
9
|
+
*/
|
|
10
|
+
Lifetime["SINGLETON"] = "singleton";
|
|
11
|
+
/**
|
|
12
|
+
* A new instance is created every time the dependency is resolved.
|
|
13
|
+
*/
|
|
14
|
+
Lifetime["TRANSIENT"] = "transient";
|
|
15
|
+
/**
|
|
16
|
+
* A single instance is created per scope (identified by UUID).
|
|
17
|
+
* Scoped containers are created via `container.createScope()`.
|
|
18
|
+
* Useful for request-scoped dependencies in web frameworks.
|
|
19
|
+
*/
|
|
20
|
+
Lifetime["SCOPED"] = "scoped";
|
|
21
|
+
})(Lifetime || (Lifetime = {}));
|
|
22
|
+
//# sourceMappingURL=core.lifetime.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Distinguishes class constructors from plain functions.
|
|
3
|
+
*
|
|
4
|
+
* A class has a prototype whose `constructor` property points back to
|
|
5
|
+
* itself and contains at least one additional own property (a method).
|
|
6
|
+
*/
|
|
7
|
+
export declare function isConstructor(fn: unknown): boolean;
|
|
8
|
+
//# sourceMappingURL=core.utils.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Distinguishes class constructors from plain functions.
|
|
3
|
+
*
|
|
4
|
+
* A class has a prototype whose `constructor` property points back to
|
|
5
|
+
* itself and contains at least one additional own property (a method).
|
|
6
|
+
*/
|
|
7
|
+
export function isConstructor(fn) {
|
|
8
|
+
const proto = fn.prototype;
|
|
9
|
+
return (proto !== undefined &&
|
|
10
|
+
proto.constructor === fn &&
|
|
11
|
+
Object.getOwnPropertyNames(proto).length > 1);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=core.utils.js.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Token } from '../token/token.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* TC39 accessor decorator for property injection.
|
|
4
|
+
*
|
|
5
|
+
* Use this when you need to inject a dependency as a class property
|
|
6
|
+
* rather than through the constructor.
|
|
7
|
+
*
|
|
8
|
+
* Must be used on a class also decorated with @injectable() or @module().
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* @injectable()
|
|
13
|
+
* class OrderService {
|
|
14
|
+
* @inject(LOGGER) accessor logger!: ILogger;
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function inject(token: Token): <This, Value>(_target: ClassAccessorDecoratorTarget<This, Value>, context: ClassAccessorDecoratorContext<This, Value>) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Flushes pending @inject() tokens accumulated during class definition
|
|
21
|
+
* into the registry, keyed by the given class constructor.
|
|
22
|
+
*
|
|
23
|
+
* Called by class decorators (@injectable, @module) which run after accessor decorators.
|
|
24
|
+
*
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
export declare function flushPendingInjectTokens(target: object): void;
|
|
28
|
+
/**
|
|
29
|
+
* Gets constructor injection tokens from a class's `static inject` array.
|
|
30
|
+
* These tokens are resolved in order and passed to the constructor.
|
|
31
|
+
*
|
|
32
|
+
* Returns an empty array if the class has no `static inject`.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* class UserService {
|
|
37
|
+
* static inject = [UserRepository, LOGGER] as const;
|
|
38
|
+
* constructor(readonly repo: UserRepository, readonly logger: ILogger) {}
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* getConstructorTokens(UserService); // [UserRepository, LOGGER]
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function getConstructorTokens(target: object): readonly Token[];
|
|
45
|
+
/**
|
|
46
|
+
* Gets all accessor-injected property tokens for a class.
|
|
47
|
+
* Returns a Map of property name → injection token, or undefined if none.
|
|
48
|
+
*/
|
|
49
|
+
export declare function getInjectTokens(target: object): ReadonlyMap<string | symbol, Token> | undefined;
|
|
50
|
+
//# sourceMappingURL=inject.decorator.d.ts.map
|