@lppedd/di-wise-neo 0.3.0 → 0.3.2
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/README.md +64 -8
- package/dist/cjs/index.d.ts +30 -30
- package/dist/cjs/index.js +10 -10
- package/dist/cjs/index.js.map +1 -1
- package/dist/es/index.d.mts +30 -30
- package/dist/es/index.mjs +9 -9
- package/dist/es/index.mjs.map +1 -1
- package/package.json +3 -5
- package/src/container.ts +0 -292
- package/src/decorators/autoRegister.ts +0 -24
- package/src/decorators/decorators.ts +0 -47
- package/src/decorators/index.ts +0 -7
- package/src/decorators/inject.ts +0 -42
- package/src/decorators/injectAll.ts +0 -45
- package/src/decorators/injectable.ts +0 -61
- package/src/decorators/optional.ts +0 -39
- package/src/decorators/optionalAll.ts +0 -42
- package/src/decorators/scoped.ts +0 -32
- package/src/defaultContainer.ts +0 -519
- package/src/errors.ts +0 -47
- package/src/index.ts +0 -16
- package/src/inject.ts +0 -88
- package/src/injectAll.ts +0 -21
- package/src/injectionContext.ts +0 -46
- package/src/injector.ts +0 -117
- package/src/metadata.ts +0 -41
- package/src/middleware.ts +0 -85
- package/src/optional.ts +0 -65
- package/src/optionalAll.ts +0 -19
- package/src/provider.ts +0 -61
- package/src/scope.ts +0 -8
- package/src/token.ts +0 -84
- package/src/tokenRegistry.ts +0 -165
- package/src/tokensRef.ts +0 -46
- package/src/utils/context.ts +0 -19
- package/src/utils/disposable.ts +0 -10
- package/src/utils/invariant.ts +0 -6
- package/src/utils/keyedStack.ts +0 -26
- package/src/utils/typeName.ts +0 -28
- package/src/utils/weakRefMap.ts +0 -28
- package/src/valueRef.ts +0 -3
package/src/defaultContainer.ts
DELETED
@@ -1,519 +0,0 @@
|
|
1
|
-
import type { Container, ContainerOptions } from "./container";
|
2
|
-
import { assert, expectNever, throwExistingUnregisteredError, throwUnregisteredError } from "./errors";
|
3
|
-
import { injectBy } from "./inject";
|
4
|
-
import { injectAll } from "./injectAll";
|
5
|
-
import { createResolution, provideInjectionContext, useInjectionContext } from "./injectionContext";
|
6
|
-
import { getMetadata } from "./metadata";
|
7
|
-
import { optionalBy } from "./optional";
|
8
|
-
import { optionalAll } from "./optionalAll";
|
9
|
-
import {
|
10
|
-
isClassProvider,
|
11
|
-
isExistingProvider,
|
12
|
-
isFactoryProvider,
|
13
|
-
isValueProvider,
|
14
|
-
type Provider,
|
15
|
-
} from "./provider";
|
16
|
-
import { Scope } from "./scope";
|
17
|
-
import { type Constructor, isConstructor, type Token } from "./token";
|
18
|
-
import { isBuilder, type Registration, type RegistrationOptions, TokenRegistry } from "./tokenRegistry";
|
19
|
-
import { isDisposable } from "./utils/disposable";
|
20
|
-
|
21
|
-
/**
|
22
|
-
* The default implementation of a di-wise-neo {@link Container}.
|
23
|
-
*/
|
24
|
-
export class DefaultContainer implements Container {
|
25
|
-
// eslint-disable-next-line no-use-before-define
|
26
|
-
private readonly myChildren: Set<DefaultContainer> = new Set();
|
27
|
-
private readonly myOptions: ContainerOptions;
|
28
|
-
private readonly myTokenRegistry: TokenRegistry;
|
29
|
-
private myDisposed: boolean = false;
|
30
|
-
|
31
|
-
constructor(
|
32
|
-
private readonly myParent: DefaultContainer | undefined,
|
33
|
-
options: Partial<ContainerOptions>,
|
34
|
-
) {
|
35
|
-
this.myOptions = {
|
36
|
-
autoRegister: false,
|
37
|
-
defaultScope: Scope.Inherited,
|
38
|
-
...options,
|
39
|
-
};
|
40
|
-
|
41
|
-
this.myTokenRegistry = new TokenRegistry(this.myParent?.myTokenRegistry);
|
42
|
-
}
|
43
|
-
|
44
|
-
get registry(): TokenRegistry {
|
45
|
-
return this.myTokenRegistry;
|
46
|
-
}
|
47
|
-
|
48
|
-
get options(): ContainerOptions {
|
49
|
-
return {
|
50
|
-
...this.myOptions,
|
51
|
-
};
|
52
|
-
}
|
53
|
-
|
54
|
-
get parent(): Container | undefined {
|
55
|
-
return this.myParent;
|
56
|
-
}
|
57
|
-
|
58
|
-
get isDisposed(): boolean {
|
59
|
-
return this.myDisposed;
|
60
|
-
}
|
61
|
-
|
62
|
-
createChild(options?: Partial<ContainerOptions>): Container {
|
63
|
-
this.checkDisposed();
|
64
|
-
const container = new DefaultContainer(this, {
|
65
|
-
...this.myOptions,
|
66
|
-
...options,
|
67
|
-
});
|
68
|
-
|
69
|
-
this.myChildren.add(container);
|
70
|
-
return container;
|
71
|
-
}
|
72
|
-
|
73
|
-
clearCache(): unknown[] {
|
74
|
-
this.checkDisposed();
|
75
|
-
return this.myTokenRegistry.clearRegistrations();
|
76
|
-
}
|
77
|
-
|
78
|
-
getCached<T>(token: Token<T>): T | undefined {
|
79
|
-
this.checkDisposed();
|
80
|
-
const registration = this.myTokenRegistry.get(token);
|
81
|
-
return registration?.value?.current;
|
82
|
-
}
|
83
|
-
|
84
|
-
getAllCached<T>(token: Token<T>): T[] {
|
85
|
-
this.checkDisposed();
|
86
|
-
const registrations = this.myTokenRegistry.getAll(token);
|
87
|
-
|
88
|
-
if (!registrations) {
|
89
|
-
return [];
|
90
|
-
}
|
91
|
-
|
92
|
-
const values = new Set<T>();
|
93
|
-
|
94
|
-
for (const registration of registrations) {
|
95
|
-
const value = registration.value;
|
96
|
-
|
97
|
-
if (value) {
|
98
|
-
values.add(value.current);
|
99
|
-
}
|
100
|
-
}
|
101
|
-
|
102
|
-
return Array.from(values);
|
103
|
-
}
|
104
|
-
|
105
|
-
resetRegistry(): unknown[] {
|
106
|
-
this.checkDisposed();
|
107
|
-
const [, registrations] = this.myTokenRegistry.deleteAll();
|
108
|
-
const values = new Set<unknown>();
|
109
|
-
|
110
|
-
for (const registration of registrations) {
|
111
|
-
const value = registration.value;
|
112
|
-
|
113
|
-
if (value) {
|
114
|
-
values.add(value.current);
|
115
|
-
}
|
116
|
-
}
|
117
|
-
|
118
|
-
return Array.from(values);
|
119
|
-
}
|
120
|
-
|
121
|
-
isRegistered(token: Token): boolean {
|
122
|
-
this.checkDisposed();
|
123
|
-
return this.myTokenRegistry.get(token) !== undefined;
|
124
|
-
}
|
125
|
-
|
126
|
-
register<T>(...args: [Constructor<T & object>] | [Token<T>, Provider<T>, RegistrationOptions?]): this {
|
127
|
-
this.checkDisposed();
|
128
|
-
|
129
|
-
if (args.length == 1) {
|
130
|
-
const Class = args[0];
|
131
|
-
const metadata = getMetadata(Class);
|
132
|
-
|
133
|
-
// Register the class itself
|
134
|
-
this.myTokenRegistry.set(Class, {
|
135
|
-
// The provider is of type ClassProvider, initialized by getMetadata
|
136
|
-
provider: metadata.provider,
|
137
|
-
options: {
|
138
|
-
scope: metadata.scope,
|
139
|
-
},
|
140
|
-
dependencies: metadata.dependencies,
|
141
|
-
});
|
142
|
-
|
143
|
-
// Register the additional tokens specified via class decorators.
|
144
|
-
// These tokens will point to the original Class token and will have the same scope.
|
145
|
-
for (const token of metadata.tokensRef.getRefTokens()) {
|
146
|
-
this.myTokenRegistry.set(token, {
|
147
|
-
provider: {
|
148
|
-
useExisting: Class,
|
149
|
-
},
|
150
|
-
});
|
151
|
-
}
|
152
|
-
} else {
|
153
|
-
const [token, provider, options] = args;
|
154
|
-
|
155
|
-
if (isClassProvider(provider)) {
|
156
|
-
const Class = provider.useClass;
|
157
|
-
const metadata = getMetadata(Class);
|
158
|
-
this.myTokenRegistry.set(token, {
|
159
|
-
provider: metadata.provider,
|
160
|
-
options: {
|
161
|
-
// The explicit registration options override what is specified
|
162
|
-
// via class decorators (e.g., @Scoped)
|
163
|
-
scope: metadata.scope,
|
164
|
-
...options,
|
165
|
-
},
|
166
|
-
dependencies: metadata.dependencies,
|
167
|
-
});
|
168
|
-
} else {
|
169
|
-
if (isExistingProvider(provider)) {
|
170
|
-
assert(
|
171
|
-
token !== provider.useExisting,
|
172
|
-
`the useExisting token ${token.name} cannot be the same as the token being registered`,
|
173
|
-
);
|
174
|
-
}
|
175
|
-
|
176
|
-
this.myTokenRegistry.set(token, {
|
177
|
-
provider: provider,
|
178
|
-
options: options,
|
179
|
-
});
|
180
|
-
}
|
181
|
-
}
|
182
|
-
|
183
|
-
return this;
|
184
|
-
}
|
185
|
-
|
186
|
-
unregister<T>(token: Token<T>): T[] {
|
187
|
-
this.checkDisposed();
|
188
|
-
const registrations = this.myTokenRegistry.delete(token);
|
189
|
-
|
190
|
-
if (!registrations) {
|
191
|
-
return [];
|
192
|
-
}
|
193
|
-
|
194
|
-
const values = new Set<T>();
|
195
|
-
|
196
|
-
for (const registration of registrations) {
|
197
|
-
const value = registration.value;
|
198
|
-
|
199
|
-
if (value) {
|
200
|
-
values.add(value.current);
|
201
|
-
}
|
202
|
-
}
|
203
|
-
|
204
|
-
return Array.from(values);
|
205
|
-
}
|
206
|
-
|
207
|
-
resolve<T>(token: Token<T>, optional?: boolean): T | undefined {
|
208
|
-
this.checkDisposed();
|
209
|
-
const registration = this.myTokenRegistry.get(token);
|
210
|
-
|
211
|
-
if (registration) {
|
212
|
-
return this.resolveRegistration(token, registration);
|
213
|
-
}
|
214
|
-
|
215
|
-
if (isConstructor(token)) {
|
216
|
-
return this.instantiateClass(token, optional);
|
217
|
-
}
|
218
|
-
|
219
|
-
return optional ? undefined : throwUnregisteredError(token);
|
220
|
-
}
|
221
|
-
|
222
|
-
resolveAll<T>(token: Token<T>, optional?: boolean): NonNullable<T>[] {
|
223
|
-
this.checkDisposed();
|
224
|
-
const registrations = this.myTokenRegistry.getAll(token);
|
225
|
-
|
226
|
-
if (registrations) {
|
227
|
-
return registrations
|
228
|
-
.map((registration) => this.resolveRegistration(token, registration))
|
229
|
-
.filter((value) => value != null);
|
230
|
-
}
|
231
|
-
|
232
|
-
if (isConstructor(token)) {
|
233
|
-
const instance = this.instantiateClass(token, optional);
|
234
|
-
return instance === undefined // = could not resolve, but since it is optional
|
235
|
-
? []
|
236
|
-
: [instance];
|
237
|
-
}
|
238
|
-
|
239
|
-
return optional ? [] : throwUnregisteredError(token);
|
240
|
-
}
|
241
|
-
|
242
|
-
dispose(): void {
|
243
|
-
if (this.myDisposed) {
|
244
|
-
return;
|
245
|
-
}
|
246
|
-
|
247
|
-
// Dispose children containers first
|
248
|
-
for (const child of this.myChildren) {
|
249
|
-
child.dispose();
|
250
|
-
}
|
251
|
-
|
252
|
-
this.myChildren.clear();
|
253
|
-
|
254
|
-
// Remove ourselves from our parent container
|
255
|
-
this.myParent?.myChildren?.delete(this);
|
256
|
-
this.myDisposed = true;
|
257
|
-
|
258
|
-
const [, registrations] = this.myTokenRegistry.deleteAll();
|
259
|
-
const disposedRefs = new Set<any>();
|
260
|
-
|
261
|
-
// Dispose all resolved (aka instantiated) tokens that implement the Disposable interface
|
262
|
-
for (const registration of registrations) {
|
263
|
-
const value = registration.value?.current;
|
264
|
-
|
265
|
-
if (isDisposable(value) && !disposedRefs.has(value)) {
|
266
|
-
disposedRefs.add(value);
|
267
|
-
value.dispose();
|
268
|
-
}
|
269
|
-
}
|
270
|
-
|
271
|
-
// Allow values to be GCed
|
272
|
-
disposedRefs.clear();
|
273
|
-
}
|
274
|
-
|
275
|
-
private resolveRegistration<T>(token: Token<T>, registration: Registration<T>): T {
|
276
|
-
let currRegistration: Registration<T> | undefined = registration;
|
277
|
-
let currProvider = currRegistration.provider;
|
278
|
-
|
279
|
-
while (isExistingProvider(currProvider)) {
|
280
|
-
const targetToken = currProvider.useExisting;
|
281
|
-
currRegistration = this.myTokenRegistry.get(targetToken);
|
282
|
-
|
283
|
-
if (!currRegistration) {
|
284
|
-
throwExistingUnregisteredError(token, targetToken);
|
285
|
-
}
|
286
|
-
|
287
|
-
currProvider = currRegistration.provider;
|
288
|
-
}
|
289
|
-
|
290
|
-
try {
|
291
|
-
return this.resolveProviderValue(currRegistration, currProvider);
|
292
|
-
} catch (e) {
|
293
|
-
// If we were trying to resolve a token registered via ExistingProvider,
|
294
|
-
// we must add the cause of the error to the message
|
295
|
-
if (isExistingProvider(registration.provider)) {
|
296
|
-
throwExistingUnregisteredError(token, e as Error);
|
297
|
-
}
|
298
|
-
|
299
|
-
throw e;
|
300
|
-
}
|
301
|
-
}
|
302
|
-
|
303
|
-
private instantiateClass<T extends object>(Class: Constructor<T>, optional?: boolean): T | undefined {
|
304
|
-
const metadata = getMetadata(Class);
|
305
|
-
|
306
|
-
if (metadata.autoRegister ?? this.myOptions.autoRegister) {
|
307
|
-
this.register(Class);
|
308
|
-
return (this as Container).resolve(Class);
|
309
|
-
}
|
310
|
-
|
311
|
-
const scope = this.resolveScope(metadata.scope);
|
312
|
-
|
313
|
-
if (optional && scope === Scope.Container) {
|
314
|
-
// It would not be possible to resolve the class in container scope,
|
315
|
-
// as that would require prior registration.
|
316
|
-
// However, since resolution is marked optional, we simply return undefined.
|
317
|
-
return undefined;
|
318
|
-
}
|
319
|
-
|
320
|
-
assert(
|
321
|
-
scope !== Scope.Container,
|
322
|
-
`unregistered class ${Class.name} cannot be resolved in container scope`,
|
323
|
-
);
|
324
|
-
|
325
|
-
const registration: Registration<T> = {
|
326
|
-
provider: metadata.provider,
|
327
|
-
options: {
|
328
|
-
scope: scope,
|
329
|
-
},
|
330
|
-
dependencies: metadata.dependencies,
|
331
|
-
};
|
332
|
-
|
333
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
334
|
-
return this.resolveScopedValue(registration, (args) => new Class(...args));
|
335
|
-
}
|
336
|
-
|
337
|
-
private resolveProviderValue<T>(registration: Registration<T>, provider: Provider<T>): T {
|
338
|
-
assert(registration.provider === provider, "internal error: mismatching provider");
|
339
|
-
|
340
|
-
if (isClassProvider(provider)) {
|
341
|
-
const Class = provider.useClass;
|
342
|
-
|
343
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
344
|
-
return this.resolveScopedValue(registration, (args) => new Class(...args));
|
345
|
-
}
|
346
|
-
|
347
|
-
if (isFactoryProvider(provider)) {
|
348
|
-
const factory = provider.useFactory;
|
349
|
-
return this.resolveScopedValue(registration, factory);
|
350
|
-
}
|
351
|
-
|
352
|
-
if (isValueProvider(provider)) {
|
353
|
-
return provider.useValue;
|
354
|
-
}
|
355
|
-
|
356
|
-
if (isExistingProvider(provider)) {
|
357
|
-
assert(false, "internal error: unexpected ExistingProvider");
|
358
|
-
}
|
359
|
-
|
360
|
-
expectNever(provider);
|
361
|
-
}
|
362
|
-
|
363
|
-
private resolveScopedValue<T>(registration: Registration<T>, factory: (...args: any[]) => T): T {
|
364
|
-
let context = useInjectionContext();
|
365
|
-
|
366
|
-
if (!context || context.container !== this) {
|
367
|
-
context = {
|
368
|
-
container: this,
|
369
|
-
resolution: createResolution(),
|
370
|
-
};
|
371
|
-
}
|
372
|
-
|
373
|
-
const resolution = context.resolution;
|
374
|
-
const provider = registration.provider;
|
375
|
-
const options = registration.options;
|
376
|
-
|
377
|
-
if (resolution.stack.has(provider)) {
|
378
|
-
const dependentRef = resolution.dependents.get(provider);
|
379
|
-
assert(dependentRef, "circular dependency detected");
|
380
|
-
return dependentRef.current;
|
381
|
-
}
|
382
|
-
|
383
|
-
const scope = this.resolveScope(options?.scope, context);
|
384
|
-
const cleanups = [
|
385
|
-
provideInjectionContext(context),
|
386
|
-
!isBuilder(provider) && resolution.stack.push(provider, { provider, scope }),
|
387
|
-
];
|
388
|
-
|
389
|
-
try {
|
390
|
-
switch (scope) {
|
391
|
-
case Scope.Container: {
|
392
|
-
const valueRef = registration.value;
|
393
|
-
|
394
|
-
if (valueRef) {
|
395
|
-
return valueRef.current;
|
396
|
-
}
|
397
|
-
|
398
|
-
const args = this.resolveConstructorDependencies(registration);
|
399
|
-
const value = this.injectDependencies(registration, factory(args));
|
400
|
-
registration.value = { current: value };
|
401
|
-
return value;
|
402
|
-
}
|
403
|
-
case Scope.Resolution: {
|
404
|
-
const valueRef = resolution.values.get(provider);
|
405
|
-
|
406
|
-
if (valueRef) {
|
407
|
-
return valueRef.current;
|
408
|
-
}
|
409
|
-
|
410
|
-
const args = this.resolveConstructorDependencies(registration);
|
411
|
-
const value = this.injectDependencies(registration, factory(args));
|
412
|
-
resolution.values.set(provider, { current: value });
|
413
|
-
return value;
|
414
|
-
}
|
415
|
-
case Scope.Transient: {
|
416
|
-
const args = this.resolveConstructorDependencies(registration);
|
417
|
-
return this.injectDependencies(registration, factory(args));
|
418
|
-
}
|
419
|
-
}
|
420
|
-
} finally {
|
421
|
-
cleanups.forEach((cleanup) => cleanup && cleanup());
|
422
|
-
}
|
423
|
-
}
|
424
|
-
|
425
|
-
private resolveScope(
|
426
|
-
scope = this.myOptions.defaultScope,
|
427
|
-
context = useInjectionContext(),
|
428
|
-
): Exclude<Scope, typeof Scope.Inherited> {
|
429
|
-
if (scope == Scope.Inherited) {
|
430
|
-
const dependentFrame = context?.resolution.stack.peek();
|
431
|
-
return dependentFrame?.scope || Scope.Transient;
|
432
|
-
}
|
433
|
-
|
434
|
-
return scope;
|
435
|
-
}
|
436
|
-
|
437
|
-
private resolveConstructorDependencies<T>(registration: Registration<T>): any[] {
|
438
|
-
const dependencies = registration.dependencies;
|
439
|
-
|
440
|
-
if (dependencies) {
|
441
|
-
assert(isClassProvider(registration.provider), `internal error: not a ClassProvider`);
|
442
|
-
const ctorDeps = dependencies.constructor;
|
443
|
-
|
444
|
-
if (ctorDeps.length > 0) {
|
445
|
-
// Let's check if all necessary constructor parameters are decorated.
|
446
|
-
// If not, we cannot safely create an instance.
|
447
|
-
const ctor = registration.provider.useClass;
|
448
|
-
assert(ctor.length === ctorDeps.length, () => {
|
449
|
-
const msg = `expected ${ctor.length} decorated constructor parameters in ${ctor.name}`;
|
450
|
-
return msg + `, but found ${ctorDeps.length}`;
|
451
|
-
});
|
452
|
-
|
453
|
-
return ctorDeps
|
454
|
-
.sort((a, b) => a.index - b.index)
|
455
|
-
.map((dep) => {
|
456
|
-
const token = dep.tokenRef.getRefToken();
|
457
|
-
switch (dep.decorator) {
|
458
|
-
case "Inject":
|
459
|
-
return this.resolve(token);
|
460
|
-
case "InjectAll":
|
461
|
-
return this.resolveAll(token);
|
462
|
-
case "Optional":
|
463
|
-
return this.resolve(token, true);
|
464
|
-
case "OptionalAll":
|
465
|
-
return this.resolveAll(token, true);
|
466
|
-
}
|
467
|
-
});
|
468
|
-
}
|
469
|
-
}
|
470
|
-
|
471
|
-
return [];
|
472
|
-
}
|
473
|
-
|
474
|
-
private injectDependencies<T>(registration: Registration<T>, instance: T): T {
|
475
|
-
const dependencies = registration.dependencies;
|
476
|
-
|
477
|
-
if (dependencies) {
|
478
|
-
assert(isClassProvider(registration.provider), `internal error: not a ClassProvider`);
|
479
|
-
const ctor = registration.provider.useClass;
|
480
|
-
|
481
|
-
// Perform method injection
|
482
|
-
for (const [key, methodDeps] of dependencies.methods) {
|
483
|
-
// Let's check if all necessary method parameters are decorated.
|
484
|
-
// If not, we cannot safely invoke the method.
|
485
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
486
|
-
const method = (instance as any)[key] as Function;
|
487
|
-
assert(methodDeps.length === method.length, () => {
|
488
|
-
const msg = `expected ${method.length} decorated method parameters`;
|
489
|
-
return msg + ` in ${ctor.name}.${String(key)}, but found ${methodDeps.length}`;
|
490
|
-
});
|
491
|
-
|
492
|
-
const args = methodDeps
|
493
|
-
.sort((a, b) => a.index - b.index)
|
494
|
-
.map((dep) => {
|
495
|
-
const token = dep.tokenRef.getRefToken();
|
496
|
-
switch (dep.decorator) {
|
497
|
-
case "Inject":
|
498
|
-
return injectBy(instance, token);
|
499
|
-
case "InjectAll":
|
500
|
-
return injectAll(token);
|
501
|
-
case "Optional":
|
502
|
-
return optionalBy(instance, token);
|
503
|
-
case "OptionalAll":
|
504
|
-
return optionalAll(token);
|
505
|
-
}
|
506
|
-
});
|
507
|
-
|
508
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
509
|
-
method.bind(instance)(...args);
|
510
|
-
}
|
511
|
-
}
|
512
|
-
|
513
|
-
return instance;
|
514
|
-
}
|
515
|
-
|
516
|
-
private checkDisposed(): void {
|
517
|
-
assert(!this.myDisposed, "the container is disposed");
|
518
|
-
}
|
519
|
-
}
|
package/src/errors.ts
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
import type { Token } from "./token";
|
2
|
-
|
3
|
-
// @internal
|
4
|
-
export function assert(condition: unknown, message: string | (() => string)): asserts condition {
|
5
|
-
if (!condition) {
|
6
|
-
const resolvedMessage = typeof message === "string" ? message : message();
|
7
|
-
throw new Error(tag(resolvedMessage));
|
8
|
-
}
|
9
|
-
}
|
10
|
-
|
11
|
-
// @internal
|
12
|
-
export function expectNever(value: never): never {
|
13
|
-
throw new TypeError(tag(`unexpected value ${String(value)}`));
|
14
|
-
}
|
15
|
-
|
16
|
-
// @internal
|
17
|
-
export function throwUnregisteredError(token: Token): never {
|
18
|
-
throw new Error(tag(`unregistered token ${token.name}`));
|
19
|
-
}
|
20
|
-
|
21
|
-
// @internal
|
22
|
-
export function throwExistingUnregisteredError(sourceToken: Token, targetTokenOrError: Token | Error): never {
|
23
|
-
let message = tag(`token resolution error encountered while resolving ${sourceToken.name}`);
|
24
|
-
|
25
|
-
if (isError(targetTokenOrError)) {
|
26
|
-
message += `\n [cause] ${untag(targetTokenOrError.message)}`;
|
27
|
-
} else {
|
28
|
-
message += `\n [cause] the aliased token ${targetTokenOrError.name} is not registered`;
|
29
|
-
}
|
30
|
-
|
31
|
-
throw new Error(message);
|
32
|
-
}
|
33
|
-
|
34
|
-
function isError(value: any): value is Error {
|
35
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
36
|
-
return value && value.stack && value.message && typeof value.message === "string";
|
37
|
-
}
|
38
|
-
|
39
|
-
function tag(message: string): string {
|
40
|
-
return `[di-wise-neo] ${message}`;
|
41
|
-
}
|
42
|
-
|
43
|
-
function untag(message: string): string {
|
44
|
-
return message.startsWith("[di-wise-neo]") //
|
45
|
-
? message.substring(13).trimStart()
|
46
|
-
: message;
|
47
|
-
}
|
package/src/index.ts
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
export type { Container, ContainerOptions } from "./container";
|
2
|
-
export { createContainer } from "./container";
|
3
|
-
export { AutoRegister, Inject, Injectable, InjectAll, Optional, OptionalAll, Scoped } from "./decorators";
|
4
|
-
export { inject, injectBy } from "./inject";
|
5
|
-
export { injectAll } from "./injectAll";
|
6
|
-
export { Injector } from "./injector";
|
7
|
-
export type { Middleware, MiddlewareComposer } from "./middleware";
|
8
|
-
export { applyMiddleware } from "./middleware";
|
9
|
-
export type { ClassProvider, ExistingProvider, FactoryProvider, Provider, ValueProvider } from "./provider";
|
10
|
-
export { Scope } from "./scope";
|
11
|
-
export type { Constructor, Token, Tokens } from "./token";
|
12
|
-
export { Type } from "./token";
|
13
|
-
export type { RegistrationOptions } from "./tokenRegistry";
|
14
|
-
export { Build } from "./tokenRegistry";
|
15
|
-
export type { TokenRef, TokensRef } from "./tokensRef";
|
16
|
-
export { forwardRef } from "./tokensRef";
|
package/src/inject.ts
DELETED
@@ -1,88 +0,0 @@
|
|
1
|
-
import { ensureInjectionContext } from "./injectionContext";
|
2
|
-
import type { Constructor, Token } from "./token";
|
3
|
-
|
4
|
-
/**
|
5
|
-
* Injects the instance associated with the given class.
|
6
|
-
*
|
7
|
-
* Throws an error if the class is not registered in the container.
|
8
|
-
*/
|
9
|
-
export function inject<Instance extends object>(Class: Constructor<Instance>): Instance;
|
10
|
-
|
11
|
-
/**
|
12
|
-
* Injects the value associated with the given token.
|
13
|
-
*
|
14
|
-
* Throws an error if the token is not registered in the container.
|
15
|
-
*/
|
16
|
-
export function inject<Value>(token: Token<Value>): Value;
|
17
|
-
|
18
|
-
export function inject<T>(token: Token<T>): T {
|
19
|
-
const context = ensureInjectionContext(inject);
|
20
|
-
return context.container.resolve(token);
|
21
|
-
}
|
22
|
-
|
23
|
-
/**
|
24
|
-
* Injects the instance associated with the given class.
|
25
|
-
*
|
26
|
-
* Throws an error if the class is not registered in the container.
|
27
|
-
*
|
28
|
-
* Compared to {@link inject}, `injectBy` accepts a `thisArg` argument
|
29
|
-
* (the containing class) which is used to resolve circular dependencies.
|
30
|
-
*
|
31
|
-
* @example
|
32
|
-
* ```ts
|
33
|
-
* class Wand {
|
34
|
-
* owner = inject(Wizard);
|
35
|
-
* }
|
36
|
-
*
|
37
|
-
* class Wizard {
|
38
|
-
* wand = injectBy(this, Wand);
|
39
|
-
* }
|
40
|
-
* ```
|
41
|
-
*
|
42
|
-
* @param thisArg - The containing instance, used to help resolve circular dependencies.
|
43
|
-
* @param Class - The class to resolve.
|
44
|
-
*/
|
45
|
-
export function injectBy<Instance extends object>(thisArg: any, Class: Constructor<Instance>): Instance;
|
46
|
-
|
47
|
-
/**
|
48
|
-
* Injects the value associated with the given token.
|
49
|
-
*
|
50
|
-
* Throws an error if the token is not registered in the container.
|
51
|
-
*
|
52
|
-
* Compared to {@link inject}, `injectBy` accepts a `thisArg` argument
|
53
|
-
* (the containing class) which is used to resolve circular dependencies.
|
54
|
-
*
|
55
|
-
* @example
|
56
|
-
* ```ts
|
57
|
-
* class Wand {
|
58
|
-
* owner = inject(Wizard);
|
59
|
-
* }
|
60
|
-
*
|
61
|
-
* class Wizard {
|
62
|
-
* wand = injectBy(this, Wand);
|
63
|
-
* }
|
64
|
-
* ```
|
65
|
-
*
|
66
|
-
* @param thisArg - The containing instance, used to help resolve circular dependencies.
|
67
|
-
* @param token - The token to resolve.
|
68
|
-
*/
|
69
|
-
export function injectBy<Value>(thisArg: any, token: Token<Value>): Value;
|
70
|
-
|
71
|
-
export function injectBy<T>(thisArg: any, token: Token<T>): T {
|
72
|
-
const context = ensureInjectionContext(injectBy);
|
73
|
-
const resolution = context.resolution;
|
74
|
-
const currentFrame = resolution.stack.peek();
|
75
|
-
|
76
|
-
if (!currentFrame) {
|
77
|
-
return inject(token);
|
78
|
-
}
|
79
|
-
|
80
|
-
const currentRef = { current: thisArg };
|
81
|
-
const cleanup = resolution.dependents.set(currentFrame.provider, currentRef);
|
82
|
-
|
83
|
-
try {
|
84
|
-
return inject(token);
|
85
|
-
} finally {
|
86
|
-
cleanup();
|
87
|
-
}
|
88
|
-
}
|
package/src/injectAll.ts
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
import { ensureInjectionContext } from "./injectionContext";
|
2
|
-
import type { Constructor, Token } from "./token";
|
3
|
-
|
4
|
-
/**
|
5
|
-
* Injects all instances provided by the registrations associated with the given class.
|
6
|
-
*
|
7
|
-
* Throws an error if the class is not registered in the container.
|
8
|
-
*/
|
9
|
-
export function injectAll<Instance extends object>(Class: Constructor<Instance>): Instance[];
|
10
|
-
|
11
|
-
/**
|
12
|
-
* Injects all values provided by the registrations associated with the given token.
|
13
|
-
*
|
14
|
-
* Throws an error if the token is not registered in the container.
|
15
|
-
*/
|
16
|
-
export function injectAll<Value>(token: Token<Value>): NonNullable<Value>[];
|
17
|
-
|
18
|
-
export function injectAll<T>(token: Token<T>): NonNullable<T>[] {
|
19
|
-
const context = ensureInjectionContext(injectAll);
|
20
|
-
return context.container.resolveAll(token);
|
21
|
-
}
|