@lppedd/di-wise-neo 0.5.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -15
- package/dist/cjs/index.d.ts +115 -75
- package/dist/cjs/index.js +273 -176
- package/dist/cjs/index.js.map +1 -1
- package/dist/es/index.d.mts +115 -75
- package/dist/es/index.mjs +273 -177
- package/dist/es/index.mjs.map +1 -1
- package/package.json +6 -6
package/dist/es/index.mjs
CHANGED
@@ -127,23 +127,23 @@ function ensureInjectionContext(fn) {
|
|
127
127
|
return context;
|
128
128
|
}
|
129
129
|
|
130
|
-
function inject(token) {
|
130
|
+
function inject(token, name) {
|
131
131
|
const context = ensureInjectionContext(inject);
|
132
|
-
return context.container.resolve(token);
|
132
|
+
return context.container.resolve(token, name);
|
133
133
|
}
|
134
|
-
function injectBy(thisArg, token) {
|
134
|
+
function injectBy(thisArg, token, name) {
|
135
135
|
const context = ensureInjectionContext(injectBy);
|
136
136
|
const resolution = context.resolution;
|
137
137
|
const currentFrame = resolution.stack.peek();
|
138
138
|
if (!currentFrame) {
|
139
|
-
return inject(token);
|
139
|
+
return inject(token, name);
|
140
140
|
}
|
141
141
|
const currentRef = {
|
142
142
|
current: thisArg
|
143
143
|
};
|
144
144
|
const cleanup = resolution.dependents.set(currentFrame.provider, currentRef);
|
145
145
|
try {
|
146
|
-
return inject(token);
|
146
|
+
return inject(token, name);
|
147
147
|
} finally{
|
148
148
|
cleanup();
|
149
149
|
}
|
@@ -154,41 +154,59 @@ function injectAll(token) {
|
|
154
154
|
return context.container.resolveAll(token);
|
155
155
|
}
|
156
156
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
157
|
+
// @internal
|
158
|
+
class Metadata {
|
159
|
+
constructor(Class){
|
160
|
+
this.dependencies = {
|
161
|
+
constructor: [],
|
162
|
+
methods: new Map()
|
163
|
+
};
|
164
|
+
this.tokensRef = {
|
165
|
+
getRefTokens: ()=>new Set()
|
166
|
+
};
|
167
|
+
this.provider = {
|
168
|
+
useClass: Class
|
169
|
+
};
|
170
|
+
}
|
171
|
+
get name() {
|
172
|
+
return this.provider.name;
|
173
|
+
}
|
174
|
+
set name(name) {
|
175
|
+
this.provider.name = name;
|
176
|
+
}
|
177
|
+
getConstructorDependency(index) {
|
178
|
+
const i = this.dependencies.constructor.findIndex((d)=>d.index === index);
|
179
|
+
if (i > -1) {
|
180
|
+
return this.dependencies.constructor[i];
|
181
|
+
}
|
182
|
+
const dependency = {
|
183
|
+
index: index
|
184
|
+
};
|
185
|
+
this.dependencies.constructor.push(dependency);
|
186
|
+
return dependency;
|
187
|
+
}
|
188
|
+
getMethodDependency(method, index) {
|
189
|
+
let methodDeps = this.dependencies.methods.get(method);
|
190
|
+
if (!methodDeps) {
|
191
|
+
this.dependencies.methods.set(method, methodDeps = []);
|
192
|
+
}
|
193
|
+
const i = methodDeps.findIndex((d)=>d.index === index);
|
194
|
+
if (i > -1) {
|
195
|
+
return methodDeps[i];
|
196
|
+
}
|
197
|
+
const dependency = {
|
198
|
+
index: index
|
199
|
+
};
|
200
|
+
methodDeps.push(dependency);
|
201
|
+
return dependency;
|
202
|
+
}
|
174
203
|
}
|
175
204
|
// @internal
|
176
205
|
function getMetadata(Class) {
|
177
206
|
const originalClass = classIdentityMap.get(Class) ?? Class;
|
178
207
|
let metadata = metadataMap.get(originalClass);
|
179
208
|
if (!metadata) {
|
180
|
-
metadataMap.set(originalClass, metadata =
|
181
|
-
tokensRef: {
|
182
|
-
getRefTokens: ()=>new Set()
|
183
|
-
},
|
184
|
-
provider: {
|
185
|
-
useClass: originalClass
|
186
|
-
},
|
187
|
-
dependencies: {
|
188
|
-
constructor: [],
|
189
|
-
methods: new Map()
|
190
|
-
}
|
191
|
-
});
|
209
|
+
metadataMap.set(originalClass, metadata = new Metadata(originalClass));
|
192
210
|
}
|
193
211
|
if (metadata.provider.useClass !== Class) {
|
194
212
|
// This is part of the class identity mapping API (see setClassIdentityMapping).
|
@@ -202,32 +220,48 @@ function getMetadata(Class) {
|
|
202
220
|
// We must update useClass to be the extender class B so that instances created by the
|
203
221
|
// DI container match the consumer's registered class. Without this update, the DI
|
204
222
|
// system would instantiate the original class A, causing behavior inconsistencies.
|
205
|
-
//
|
206
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
207
223
|
metadata.provider.useClass = Class;
|
208
224
|
}
|
209
225
|
return metadata;
|
210
226
|
}
|
227
|
+
/**
|
228
|
+
* Registers a mapping between a generated (e.g., decorated or proxied) constructor
|
229
|
+
* and its original, underlying constructor.
|
230
|
+
*
|
231
|
+
* This allows libraries or consumers that manipulate constructors, such as through
|
232
|
+
* class decorators, to inform the DI system about the real "identity" of a class.
|
233
|
+
*
|
234
|
+
* @param transformedClass The constructor function returned by a class decorator or factory.
|
235
|
+
* @param originalClass The original constructor function.
|
236
|
+
*
|
237
|
+
* @remarks
|
238
|
+
* This API affects the core class identity resolution mechanism of the DI system.
|
239
|
+
* Incorrect usage may cause metadata to be misassociated, leading to subtle errors.
|
240
|
+
* Use only when manipulating constructors (e.g., via decorators or proxies),
|
241
|
+
* and ensure the mapping is correct.
|
242
|
+
*/ function setClassIdentityMapping(transformedClass, originalClass) {
|
243
|
+
classIdentityMap.set(transformedClass, originalClass);
|
244
|
+
}
|
211
245
|
const classIdentityMap = new WeakMap();
|
212
246
|
const metadataMap = new WeakMap();
|
213
247
|
|
214
|
-
function optional(token) {
|
248
|
+
function optional(token, name) {
|
215
249
|
const context = ensureInjectionContext(optional);
|
216
|
-
return context.container.resolve(token, true);
|
250
|
+
return context.container.resolve(token, true, name);
|
217
251
|
}
|
218
|
-
function optionalBy(thisArg, token) {
|
252
|
+
function optionalBy(thisArg, token, name) {
|
219
253
|
const context = ensureInjectionContext(optionalBy);
|
220
254
|
const resolution = context.resolution;
|
221
255
|
const currentFrame = resolution.stack.peek();
|
222
256
|
if (!currentFrame) {
|
223
|
-
return optional(token);
|
257
|
+
return optional(token, name);
|
224
258
|
}
|
225
259
|
const currentRef = {
|
226
260
|
current: thisArg
|
227
261
|
};
|
228
262
|
const cleanup = resolution.dependents.set(currentFrame.provider, currentRef);
|
229
263
|
try {
|
230
|
-
return optional(token);
|
264
|
+
return optional(token, name);
|
231
265
|
} finally{
|
232
266
|
cleanup();
|
233
267
|
}
|
@@ -286,28 +320,29 @@ const Scope = {
|
|
286
320
|
}
|
287
321
|
// @internal
|
288
322
|
function isConstructor(token) {
|
289
|
-
return typeof token
|
323
|
+
return typeof token === "function";
|
290
324
|
}
|
291
325
|
|
292
326
|
// @internal
|
293
327
|
function getTypeName(value) {
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
328
|
+
switch(typeof value){
|
329
|
+
case "string":
|
330
|
+
return `"${value}"`;
|
331
|
+
case "function":
|
332
|
+
return value.name && `typeof ${value.name}` || "Function";
|
333
|
+
case "object":
|
334
|
+
{
|
335
|
+
if (value === null) {
|
336
|
+
return "null";
|
337
|
+
}
|
338
|
+
const proto = Object.getPrototypeOf(value);
|
339
|
+
if (proto && proto !== Object.prototype) {
|
340
|
+
const constructor = proto.constructor;
|
341
|
+
if (typeof constructor === "function" && constructor.name) {
|
342
|
+
return constructor.name;
|
343
|
+
}
|
344
|
+
}
|
309
345
|
}
|
310
|
-
}
|
311
346
|
}
|
312
347
|
return typeof value;
|
313
348
|
}
|
@@ -318,28 +353,46 @@ class TokenRegistry {
|
|
318
353
|
this.myMap = new Map();
|
319
354
|
this.myParent = parent;
|
320
355
|
}
|
321
|
-
get(token) {
|
356
|
+
get(token, name) {
|
322
357
|
// To clarify, at(-1) means we take the last added registration for this token
|
323
|
-
return this.getAll(token)
|
358
|
+
return this.getAll(token, name).at(-1);
|
324
359
|
}
|
325
|
-
getAll(token) {
|
326
|
-
|
360
|
+
getAll(token, name) {
|
361
|
+
// Internal registrations cannot have a name
|
362
|
+
const internal = name !== undefined ? undefined : internals.get(token);
|
327
363
|
return internal && [
|
328
364
|
internal
|
329
|
-
] || this.getAllFromParent(token);
|
365
|
+
] || this.getAllFromParent(token, name);
|
330
366
|
}
|
331
367
|
set(token, registration) {
|
332
368
|
assert(!internals.has(token), `cannot register reserved token ${token.name}`);
|
333
369
|
let registrations = this.myMap.get(token);
|
334
370
|
if (!registrations) {
|
335
371
|
this.myMap.set(token, registrations = []);
|
372
|
+
} else if (registration.name !== undefined) {
|
373
|
+
const existing = registrations.filter((r)=>r.name === registration.name);
|
374
|
+
assert(existing.length === 0, `a ${token.name} token named '${registration.name}' is already registered`);
|
336
375
|
}
|
337
376
|
registrations.push(registration);
|
338
377
|
}
|
339
|
-
delete(token) {
|
378
|
+
delete(token, name) {
|
340
379
|
const registrations = this.myMap.get(token);
|
341
|
-
|
342
|
-
|
380
|
+
if (registrations) {
|
381
|
+
if (name !== undefined) {
|
382
|
+
const removedRegistrations = [];
|
383
|
+
const newRegistrations = [];
|
384
|
+
for (const registration of registrations){
|
385
|
+
const array = registration.name === name ? removedRegistrations : newRegistrations;
|
386
|
+
array.push(registration);
|
387
|
+
}
|
388
|
+
if (removedRegistrations.length > 0) {
|
389
|
+
this.myMap.set(token, newRegistrations);
|
390
|
+
return removedRegistrations;
|
391
|
+
}
|
392
|
+
}
|
393
|
+
this.myMap.delete(token);
|
394
|
+
}
|
395
|
+
return registrations ?? [];
|
343
396
|
}
|
344
397
|
deleteAll() {
|
345
398
|
const tokens = Array.from(this.myMap.keys());
|
@@ -367,9 +420,14 @@ class TokenRegistry {
|
|
367
420
|
}
|
368
421
|
return Array.from(values);
|
369
422
|
}
|
370
|
-
getAllFromParent(token) {
|
371
|
-
const
|
372
|
-
|
423
|
+
getAllFromParent(token, name) {
|
424
|
+
const thisRegistrations = this.myMap.get(token);
|
425
|
+
let registrations = thisRegistrations || this.myParent?.getAllFromParent(token, name);
|
426
|
+
if (registrations && name !== undefined) {
|
427
|
+
registrations = registrations.filter((r)=>r.name === name);
|
428
|
+
assert(registrations.length < 2, `internal error: more than one registration named '${name}'`);
|
429
|
+
}
|
430
|
+
return registrations ?? [];
|
373
431
|
}
|
374
432
|
}
|
375
433
|
// @internal
|
@@ -467,9 +525,6 @@ function isDisposable(value) {
|
|
467
525
|
getAllCached(token) {
|
468
526
|
this.checkDisposed();
|
469
527
|
const registrations = this.myTokenRegistry.getAll(token);
|
470
|
-
if (!registrations) {
|
471
|
-
return [];
|
472
|
-
}
|
473
528
|
const values = new Set();
|
474
529
|
for (const registration of registrations){
|
475
530
|
const value = registration.value;
|
@@ -491,50 +546,21 @@ function isDisposable(value) {
|
|
491
546
|
}
|
492
547
|
return Array.from(values);
|
493
548
|
}
|
494
|
-
isRegistered(token) {
|
549
|
+
isRegistered(token, name) {
|
495
550
|
this.checkDisposed();
|
496
|
-
return this.myTokenRegistry.get(token) !== undefined;
|
497
|
-
}
|
498
|
-
registerClass(token, Class, options) {
|
499
|
-
// This mess will go away once/if we remove the register method
|
500
|
-
// in favor of the multiple specialized ones
|
501
|
-
if (Class) {
|
502
|
-
const ctor = Class ?? token;
|
503
|
-
this.register(token, {
|
504
|
-
useClass: ctor
|
505
|
-
}, options);
|
506
|
-
} else {
|
507
|
-
this.register(token);
|
508
|
-
}
|
509
|
-
}
|
510
|
-
registerFactory(token, factory, options) {
|
511
|
-
this.register(token, {
|
512
|
-
useFactory: factory
|
513
|
-
}, options);
|
514
|
-
}
|
515
|
-
registerValue(token, value) {
|
516
|
-
this.register(token, {
|
517
|
-
useValue: value
|
518
|
-
});
|
519
|
-
}
|
520
|
-
registerAlias(targetToken, aliasTokens) {
|
521
|
-
// De-duplicate tokens
|
522
|
-
for (const alias of new Set(aliasTokens)){
|
523
|
-
this.register(alias, {
|
524
|
-
useExisting: targetToken
|
525
|
-
});
|
526
|
-
}
|
551
|
+
return this.myTokenRegistry.get(token, name) !== undefined;
|
527
552
|
}
|
528
553
|
register(...args) {
|
529
554
|
this.checkDisposed();
|
530
|
-
if (args.length
|
555
|
+
if (args.length === 1) {
|
531
556
|
const Class = args[0];
|
532
557
|
const metadata = getMetadata(Class);
|
533
558
|
const registration = {
|
559
|
+
name: metadata.name,
|
534
560
|
// The provider is of type ClassProvider, initialized by getMetadata
|
535
561
|
provider: metadata.provider,
|
536
562
|
options: {
|
537
|
-
scope: metadata.scope ?? this.myOptions.defaultScope
|
563
|
+
scope: metadata.scope?.value ?? this.myOptions.defaultScope
|
538
564
|
},
|
539
565
|
dependencies: metadata.dependencies
|
540
566
|
};
|
@@ -551,18 +577,19 @@ function isDisposable(value) {
|
|
551
577
|
}
|
552
578
|
// Eager-instantiate only if the class is container-scoped
|
553
579
|
if (metadata.eagerInstantiate && registration.options?.scope === Scope.Container) {
|
554
|
-
this.
|
580
|
+
this.resolveProviderValue(registration, registration.provider);
|
555
581
|
}
|
556
582
|
} else {
|
557
583
|
const [token, provider, options] = args;
|
558
584
|
if (isClassProvider(provider)) {
|
559
585
|
const metadata = getMetadata(provider.useClass);
|
560
586
|
const registration = {
|
587
|
+
// An explicit provider name overrides what is specified via @Named
|
588
|
+
name: metadata.name ?? provider.name,
|
561
589
|
provider: metadata.provider,
|
562
590
|
options: {
|
563
|
-
//
|
564
|
-
|
565
|
-
scope: metadata.scope ?? this.myOptions.defaultScope,
|
591
|
+
// Explicit registration options override what is specified via class decorators (e.g., @Scoped)
|
592
|
+
scope: metadata.scope?.value ?? this.myOptions.defaultScope,
|
566
593
|
...options
|
567
594
|
},
|
568
595
|
dependencies: metadata.dependencies
|
@@ -570,13 +597,15 @@ function isDisposable(value) {
|
|
570
597
|
this.myTokenRegistry.set(token, registration);
|
571
598
|
// Eager-instantiate only if the provided class is container-scoped
|
572
599
|
if (metadata.eagerInstantiate && registration.options?.scope === Scope.Container) {
|
573
|
-
this.
|
600
|
+
this.resolveProviderValue(registration, registration.provider);
|
574
601
|
}
|
575
602
|
} else {
|
576
|
-
|
603
|
+
const existingProvider = isExistingProvider(provider);
|
604
|
+
if (existingProvider) {
|
577
605
|
assert(token !== provider.useExisting, `the useExisting token ${token.name} cannot be the same as the token being registered`);
|
578
606
|
}
|
579
607
|
this.myTokenRegistry.set(token, {
|
608
|
+
name: existingProvider ? undefined : provider.name,
|
580
609
|
provider: provider,
|
581
610
|
options: options
|
582
611
|
});
|
@@ -584,12 +613,9 @@ function isDisposable(value) {
|
|
584
613
|
}
|
585
614
|
return this;
|
586
615
|
}
|
587
|
-
unregister(token) {
|
616
|
+
unregister(token, name) {
|
588
617
|
this.checkDisposed();
|
589
|
-
const registrations = this.myTokenRegistry.delete(token);
|
590
|
-
if (!registrations) {
|
591
|
-
return [];
|
592
|
-
}
|
618
|
+
const registrations = this.myTokenRegistry.delete(token, name);
|
593
619
|
const values = new Set();
|
594
620
|
for (const registration of registrations){
|
595
621
|
const value = registration.value;
|
@@ -599,22 +625,31 @@ function isDisposable(value) {
|
|
599
625
|
}
|
600
626
|
return Array.from(values);
|
601
627
|
}
|
602
|
-
resolve(token,
|
628
|
+
resolve(token, optionalOrName, name) {
|
603
629
|
this.checkDisposed();
|
604
|
-
|
630
|
+
let localOptional;
|
631
|
+
let localName;
|
632
|
+
if (typeof optionalOrName === "string") {
|
633
|
+
localName = optionalOrName;
|
634
|
+
} else {
|
635
|
+
localOptional = optionalOrName;
|
636
|
+
localName = name;
|
637
|
+
}
|
638
|
+
const registration = this.myTokenRegistry.get(token, localName);
|
605
639
|
if (registration) {
|
606
|
-
return this.resolveRegistration(token, registration);
|
640
|
+
return this.resolveRegistration(token, registration, localName);
|
607
641
|
}
|
608
642
|
if (isConstructor(token)) {
|
609
|
-
return this.instantiateClass(token,
|
643
|
+
return this.instantiateClass(token, localOptional);
|
610
644
|
}
|
611
|
-
return
|
645
|
+
return optionalOrName ? undefined : throwUnregisteredError(token);
|
612
646
|
}
|
613
647
|
resolveAll(token, optional) {
|
614
648
|
this.checkDisposed();
|
615
649
|
const registrations = this.myTokenRegistry.getAll(token);
|
616
|
-
if (registrations) {
|
617
|
-
return registrations
|
650
|
+
if (registrations.length > 0) {
|
651
|
+
return registrations //
|
652
|
+
.map((registration)=>this.resolveRegistration(token, registration)).filter((value)=>value != null);
|
618
653
|
}
|
619
654
|
if (isConstructor(token)) {
|
620
655
|
const instance = this.instantiateClass(token, optional);
|
@@ -650,12 +685,12 @@ function isDisposable(value) {
|
|
650
685
|
// Allow values to be GCed
|
651
686
|
disposedRefs.clear();
|
652
687
|
}
|
653
|
-
resolveRegistration(token, registration) {
|
688
|
+
resolveRegistration(token, registration, name) {
|
654
689
|
let currRegistration = registration;
|
655
690
|
let currProvider = currRegistration.provider;
|
656
691
|
while(isExistingProvider(currProvider)){
|
657
692
|
const targetToken = currProvider.useExisting;
|
658
|
-
currRegistration = this.myTokenRegistry.get(targetToken);
|
693
|
+
currRegistration = this.myTokenRegistry.get(targetToken, name);
|
659
694
|
if (!currRegistration) {
|
660
695
|
throwExistingUnregisteredError(token, targetToken);
|
661
696
|
}
|
@@ -686,7 +721,7 @@ function isDisposable(value) {
|
|
686
721
|
metadata.eagerInstantiate = eagerInstantiate;
|
687
722
|
}
|
688
723
|
}
|
689
|
-
const scope = this.resolveScope(metadata.scope);
|
724
|
+
const scope = this.resolveScope(metadata.scope?.value);
|
690
725
|
if (optional && scope === Scope.Container) {
|
691
726
|
// It would not be possible to resolve the class in container scope,
|
692
727
|
// as that would require prior registration.
|
@@ -786,7 +821,7 @@ function isDisposable(value) {
|
|
786
821
|
}
|
787
822
|
}
|
788
823
|
resolveScope(scope = this.myOptions.defaultScope, context = useInjectionContext()) {
|
789
|
-
if (scope
|
824
|
+
if (scope === Scope.Inherited) {
|
790
825
|
const dependentFrame = context?.resolution.stack.peek();
|
791
826
|
return dependentFrame?.scope || Scope.Transient;
|
792
827
|
}
|
@@ -796,7 +831,7 @@ function isDisposable(value) {
|
|
796
831
|
const dependencies = registration.dependencies;
|
797
832
|
if (dependencies) {
|
798
833
|
assert(isClassProvider(registration.provider), `internal error: not a ClassProvider`);
|
799
|
-
const ctorDeps = dependencies.constructor;
|
834
|
+
const ctorDeps = dependencies.constructor.filter((d)=>d.appliedBy);
|
800
835
|
if (ctorDeps.length > 0) {
|
801
836
|
// Let's check if all necessary constructor parameters are decorated.
|
802
837
|
// If not, we cannot safely create an instance.
|
@@ -807,13 +842,13 @@ function isDisposable(value) {
|
|
807
842
|
});
|
808
843
|
return ctorDeps.sort((a, b)=>a.index - b.index).map((dep)=>{
|
809
844
|
const token = dep.tokenRef.getRefToken();
|
810
|
-
switch(dep.
|
845
|
+
switch(dep.appliedBy){
|
811
846
|
case "Inject":
|
812
|
-
return this.resolve(token);
|
847
|
+
return this.resolve(token, dep.name);
|
813
848
|
case "InjectAll":
|
814
849
|
return this.resolveAll(token);
|
815
850
|
case "Optional":
|
816
|
-
return this.resolve(token, true);
|
851
|
+
return this.resolve(token, true, dep.name);
|
817
852
|
case "OptionalAll":
|
818
853
|
return this.resolveAll(token, true);
|
819
854
|
}
|
@@ -828,7 +863,9 @@ function isDisposable(value) {
|
|
828
863
|
assert(isClassProvider(registration.provider), `internal error: not a ClassProvider`);
|
829
864
|
const ctor = registration.provider.useClass;
|
830
865
|
// Perform method injection
|
831
|
-
for (const
|
866
|
+
for (const entry of dependencies.methods){
|
867
|
+
const key = entry[0];
|
868
|
+
const methodDeps = entry[1].filter((d)=>d.appliedBy);
|
832
869
|
// Let's check if all necessary method parameters are decorated.
|
833
870
|
// If not, we cannot safely invoke the method.
|
834
871
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
@@ -839,13 +876,13 @@ function isDisposable(value) {
|
|
839
876
|
});
|
840
877
|
const args = methodDeps.sort((a, b)=>a.index - b.index).map((dep)=>{
|
841
878
|
const token = dep.tokenRef.getRefToken();
|
842
|
-
switch(dep.
|
879
|
+
switch(dep.appliedBy){
|
843
880
|
case "Inject":
|
844
|
-
return injectBy(instance, token);
|
881
|
+
return injectBy(instance, token, dep.name);
|
845
882
|
case "InjectAll":
|
846
883
|
return injectAll(token);
|
847
884
|
case "Optional":
|
848
|
-
return optionalBy(instance, token);
|
885
|
+
return optionalBy(instance, token, dep.name);
|
849
886
|
case "OptionalAll":
|
850
887
|
return optionalAll(token);
|
851
888
|
}
|
@@ -876,7 +913,7 @@ function isDisposable(value) {
|
|
876
913
|
*
|
877
914
|
* @example
|
878
915
|
* ```ts
|
879
|
-
* @AutoRegister
|
916
|
+
* @AutoRegister()
|
880
917
|
* class Wizard {}
|
881
918
|
*
|
882
919
|
* const wizard = container.resolve(Wizard);
|
@@ -884,32 +921,45 @@ function isDisposable(value) {
|
|
884
921
|
* ```
|
885
922
|
*
|
886
923
|
* @__NO_SIDE_EFFECTS__
|
887
|
-
*/ function AutoRegister(
|
888
|
-
|
889
|
-
|
924
|
+
*/ function AutoRegister() {
|
925
|
+
return function(Class) {
|
926
|
+
const metadata = getMetadata(Class);
|
927
|
+
metadata.autoRegister = true;
|
928
|
+
};
|
890
929
|
}
|
891
930
|
|
892
931
|
/**
|
893
|
-
* Class decorator that
|
894
|
-
* in the container
|
932
|
+
* Class decorator that sets the class scope to **Container** and enables
|
933
|
+
* eager instantiation when the class is registered in the container.
|
895
934
|
*
|
896
935
|
* This causes the container to immediately create and cache the instance of the class,
|
897
936
|
* instead of deferring instantiation until the first resolution.
|
898
937
|
*
|
899
938
|
* @example
|
900
939
|
* ```ts
|
901
|
-
* @EagerInstantiate
|
902
|
-
* @Scoped(Scope.Container)
|
940
|
+
* @EagerInstantiate()
|
903
941
|
* class Wizard {}
|
904
942
|
*
|
905
|
-
* //
|
906
|
-
*
|
943
|
+
* // Wizard is registered with Container scope, and an instance
|
944
|
+
* // is immediately created and cached by the container
|
945
|
+
* container.register(Wizard);
|
907
946
|
* ```
|
908
947
|
*
|
909
948
|
* @__NO_SIDE_EFFECTS__
|
910
|
-
*/ function EagerInstantiate(
|
911
|
-
|
912
|
-
|
949
|
+
*/ function EagerInstantiate() {
|
950
|
+
return function(Class) {
|
951
|
+
const metadata = getMetadata(Class);
|
952
|
+
const currentScope = metadata.scope;
|
953
|
+
assert(!currentScope || currentScope.value === Scope.Container, ()=>{
|
954
|
+
const { value, appliedBy } = currentScope;
|
955
|
+
return `class ${Class.name}: Scope.${value} was already set by @${appliedBy},\n ` + `but @EagerInstantiate is trying to set a conflicting Scope.Container.\n ` + `Only one decorator should set the class scope, or all must agree on the same value.`;
|
956
|
+
});
|
957
|
+
metadata.eagerInstantiate = true;
|
958
|
+
metadata.scope = {
|
959
|
+
value: Scope.Container,
|
960
|
+
appliedBy: "EagerInstantiate"
|
961
|
+
};
|
962
|
+
};
|
913
963
|
}
|
914
964
|
|
915
965
|
function forwardRef(token) {
|
@@ -941,41 +991,31 @@ function isTokenRef(value) {
|
|
941
991
|
return value && typeof value === "object" && typeof value.getRefToken === "function";
|
942
992
|
}
|
943
993
|
|
944
|
-
|
945
|
-
|
946
|
-
//
|
994
|
+
// @internal
|
995
|
+
function updateParameterMetadata(decorator, target, propertyKey, parameterIndex, updateFn) {
|
996
|
+
// Error out immediately if the decorator has been applied to a static method
|
947
997
|
if (propertyKey !== undefined && typeof target === "function") {
|
948
|
-
assert(false, `@${decorator} cannot be used on static
|
998
|
+
assert(false, `@${decorator} cannot be used on static method ${target.name}.${String(propertyKey)}`);
|
949
999
|
}
|
950
|
-
const tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
|
951
1000
|
if (propertyKey === undefined) {
|
952
1001
|
// Constructor
|
953
1002
|
const metadata = getMetadata(target);
|
954
|
-
metadata.
|
955
|
-
|
956
|
-
tokenRef: tokenRef,
|
957
|
-
index: parameterIndex
|
958
|
-
});
|
1003
|
+
const dependency = metadata.getConstructorDependency(parameterIndex);
|
1004
|
+
updateFn(dependency);
|
959
1005
|
} else {
|
960
|
-
//
|
1006
|
+
// Instance method
|
961
1007
|
const metadata = getMetadata(target.constructor);
|
962
|
-
const
|
963
|
-
|
964
|
-
if (dep === undefined) {
|
965
|
-
dep = [];
|
966
|
-
methods.set(propertyKey, dep);
|
967
|
-
}
|
968
|
-
dep.push({
|
969
|
-
decorator: decorator,
|
970
|
-
tokenRef: tokenRef,
|
971
|
-
index: parameterIndex
|
972
|
-
});
|
1008
|
+
const dependency = metadata.getMethodDependency(propertyKey, parameterIndex);
|
1009
|
+
updateFn(dependency);
|
973
1010
|
}
|
974
1011
|
}
|
975
1012
|
|
976
1013
|
function Inject(token) {
|
977
1014
|
return function(target, propertyKey, parameterIndex) {
|
978
|
-
|
1015
|
+
updateParameterMetadata("Inject", target, propertyKey, parameterIndex, (dependency)=>{
|
1016
|
+
dependency.appliedBy = "Inject";
|
1017
|
+
dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
|
1018
|
+
});
|
979
1019
|
};
|
980
1020
|
}
|
981
1021
|
|
@@ -1001,19 +1041,66 @@ function Inject(token) {
|
|
1001
1041
|
|
1002
1042
|
function InjectAll(token) {
|
1003
1043
|
return function(target, propertyKey, parameterIndex) {
|
1004
|
-
|
1044
|
+
updateParameterMetadata("InjectAll", target, propertyKey, parameterIndex, (dependency)=>{
|
1045
|
+
dependency.appliedBy = "InjectAll";
|
1046
|
+
dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
|
1047
|
+
});
|
1048
|
+
};
|
1049
|
+
}
|
1050
|
+
|
1051
|
+
/**
|
1052
|
+
* Qualifies a class or an injected parameter with a unique name.
|
1053
|
+
*
|
1054
|
+
* This allows the container to distinguish between multiple implementations
|
1055
|
+
* of the same interface or type during registration and injection.
|
1056
|
+
*
|
1057
|
+
* @example
|
1058
|
+
* ```ts
|
1059
|
+
* @Named("dumbledore")
|
1060
|
+
* class Dumbledore implements Wizard {}
|
1061
|
+
*
|
1062
|
+
* // Register Dumbledore with Type<Wizard>
|
1063
|
+
* container.register(IWizard, { useClass: Dumbledore });
|
1064
|
+
* const dumbledore = container.resolve(IWizard, "dumbledore");
|
1065
|
+
* ```
|
1066
|
+
*
|
1067
|
+
* @__NO_SIDE_EFFECTS__
|
1068
|
+
*/ function Named(name) {
|
1069
|
+
if (!name.trim()) {
|
1070
|
+
assert(false, "the @Named qualifier cannot be empty or blank");
|
1071
|
+
}
|
1072
|
+
return function(target, propertyKey, parameterIndex) {
|
1073
|
+
if (parameterIndex === undefined) {
|
1074
|
+
// The decorator has been applied to the class
|
1075
|
+
const ctor = target;
|
1076
|
+
const metadata = getMetadata(ctor);
|
1077
|
+
assert(!metadata.name, `a @Named('${metadata.name}') qualifier has already been applied to ${ctor.name}`);
|
1078
|
+
metadata.name = name;
|
1079
|
+
} else {
|
1080
|
+
// The decorator has been applied to a method parameter
|
1081
|
+
updateParameterMetadata("Named", target, propertyKey, parameterIndex, (dependency)=>{
|
1082
|
+
assert(!dependency.name, `a @Named('${dependency.name}') qualifier has already been applied to the parameter`);
|
1083
|
+
dependency.name = name;
|
1084
|
+
});
|
1085
|
+
}
|
1005
1086
|
};
|
1006
1087
|
}
|
1007
1088
|
|
1008
1089
|
function Optional(token) {
|
1009
1090
|
return function(target, propertyKey, parameterIndex) {
|
1010
|
-
|
1091
|
+
updateParameterMetadata("Optional", target, propertyKey, parameterIndex, (dependency)=>{
|
1092
|
+
dependency.appliedBy = "Optional";
|
1093
|
+
dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
|
1094
|
+
});
|
1011
1095
|
};
|
1012
1096
|
}
|
1013
1097
|
|
1014
1098
|
function OptionalAll(token) {
|
1015
1099
|
return function(target, propertyKey, parameterIndex) {
|
1016
|
-
|
1100
|
+
updateParameterMetadata("OptionalAll", target, propertyKey, parameterIndex, (dependency)=>{
|
1101
|
+
dependency.appliedBy = "OptionalAll";
|
1102
|
+
dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
|
1103
|
+
});
|
1017
1104
|
};
|
1018
1105
|
}
|
1019
1106
|
|
@@ -1041,7 +1128,16 @@ function OptionalAll(token) {
|
|
1041
1128
|
*/ function Scoped(scope) {
|
1042
1129
|
return function(Class) {
|
1043
1130
|
const metadata = getMetadata(Class);
|
1044
|
-
|
1131
|
+
const currentScope = metadata.scope;
|
1132
|
+
assert(!currentScope || currentScope.value === scope, ()=>{
|
1133
|
+
const { value, appliedBy } = currentScope;
|
1134
|
+
const by = appliedBy === "Scoped" ? `another @${appliedBy} decorator` : `@${appliedBy}`;
|
1135
|
+
return `class ${Class.name}: Scope.${value} was already set by ${by},\n ` + `but @Scoped is trying to set a conflicting Scope.${scope}.\n ` + `Only one decorator should set the class scope, or all must agree on the same value.`;
|
1136
|
+
});
|
1137
|
+
metadata.scope = {
|
1138
|
+
value: scope,
|
1139
|
+
appliedBy: "Scoped"
|
1140
|
+
};
|
1045
1141
|
};
|
1046
1142
|
}
|
1047
1143
|
|
@@ -1136,5 +1232,5 @@ function OptionalAll(token) {
|
|
1136
1232
|
return container;
|
1137
1233
|
}
|
1138
1234
|
|
1139
|
-
export { AutoRegister, EagerInstantiate, Inject, InjectAll, Injectable, Injector, Optional, OptionalAll, Scope, Scoped, applyMiddleware, build, createContainer, createType, forwardRef, inject, injectAll, injectBy, setClassIdentityMapping };
|
1235
|
+
export { AutoRegister, EagerInstantiate, Inject, InjectAll, Injectable, Injector, Named, Optional, OptionalAll, Scope, Scoped, applyMiddleware, build, createContainer, createType, forwardRef, inject, injectAll, injectBy, setClassIdentityMapping };
|
1140
1236
|
//# sourceMappingURL=index.mjs.map
|