@lppedd/di-wise-neo 0.6.0 → 0.7.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/dist/es/index.mjs CHANGED
@@ -1,8 +1,7 @@
1
1
  // @internal
2
2
  function assert(condition, message) {
3
3
  if (!condition) {
4
- const resolvedMessage = typeof message === "string" ? message : message();
5
- throw new Error(tag(resolvedMessage));
4
+ throw new Error(tag(typeof message === "string" ? message : message()));
6
5
  }
7
6
  }
8
7
  // @internal
@@ -16,11 +15,7 @@ function throwUnregisteredError(token) {
16
15
  // @internal
17
16
  function throwExistingUnregisteredError(sourceToken, targetTokenOrError) {
18
17
  let message = tag(`token resolution error encountered while resolving ${sourceToken.name}`);
19
- if (isError(targetTokenOrError)) {
20
- message += `\n [cause] ${untag(targetTokenOrError.message)}`;
21
- } else {
22
- message += `\n [cause] the aliased token ${targetTokenOrError.name} is not registered`;
23
- }
18
+ message += isError(targetTokenOrError) ? `\n [cause] ${untag(targetTokenOrError.message)}` : `\n [cause] the aliased token ${targetTokenOrError.name} is not registered`;
24
19
  throw new Error(message);
25
20
  }
26
21
  function isError(value) {
@@ -31,8 +26,7 @@ function tag(message) {
31
26
  return `[di-wise-neo] ${message}`;
32
27
  }
33
28
  function untag(message) {
34
- return message.startsWith("[di-wise-neo]") //
35
- ? message.substring(13).trimStart() : message;
29
+ return message.startsWith("[di-wise-neo]") ? message.substring(13).trimStart() : message;
36
30
  }
37
31
 
38
32
  // @internal
@@ -127,23 +121,23 @@ function ensureInjectionContext(fn) {
127
121
  return context;
128
122
  }
129
123
 
130
- function inject(token) {
124
+ function inject(token, name) {
131
125
  const context = ensureInjectionContext(inject);
132
- return context.container.resolve(token);
126
+ return context.container.resolve(token, name);
133
127
  }
134
- function injectBy(thisArg, token) {
128
+ function injectBy(thisArg, token, name) {
135
129
  const context = ensureInjectionContext(injectBy);
136
130
  const resolution = context.resolution;
137
131
  const currentFrame = resolution.stack.peek();
138
132
  if (!currentFrame) {
139
- return inject(token);
133
+ return inject(token, name);
140
134
  }
141
135
  const currentRef = {
142
136
  current: thisArg
143
137
  };
144
138
  const cleanup = resolution.dependents.set(currentFrame.provider, currentRef);
145
139
  try {
146
- return inject(token);
140
+ return inject(token, name);
147
141
  } finally{
148
142
  cleanup();
149
143
  }
@@ -154,41 +148,59 @@ function injectAll(token) {
154
148
  return context.container.resolveAll(token);
155
149
  }
156
150
 
157
- /**
158
- * Registers a mapping between a generated (e.g., decorated or proxied) constructor
159
- * and its original, underlying constructor.
160
- *
161
- * This allows libraries or consumers that manipulate constructors, such as through
162
- * class decorators, to inform the DI system about the real "identity" of a class.
163
- *
164
- * @param transformedClass The constructor function returned by a class decorator or factory.
165
- * @param originalClass The original constructor function.
166
- *
167
- * @remarks
168
- * This API affects the core class identity resolution mechanism of the DI system.
169
- * Incorrect usage may cause metadata to be misassociated, leading to subtle errors.
170
- * Use only when manipulating constructors (e.g., via decorators or proxies),
171
- * and ensure the mapping is correct.
172
- */ function setClassIdentityMapping(transformedClass, originalClass) {
173
- classIdentityMap.set(transformedClass, originalClass);
151
+ // @internal
152
+ class Metadata {
153
+ constructor(Class){
154
+ this.dependencies = {
155
+ constructor: [],
156
+ methods: new Map()
157
+ };
158
+ this.tokensRef = {
159
+ getRefTokens: ()=>new Set()
160
+ };
161
+ this.provider = {
162
+ useClass: Class
163
+ };
164
+ }
165
+ get name() {
166
+ return this.provider.name;
167
+ }
168
+ set name(name) {
169
+ this.provider.name = name;
170
+ }
171
+ getConstructorDependency(index) {
172
+ const i = this.dependencies.constructor.findIndex((d)=>d.index === index);
173
+ if (i > -1) {
174
+ return this.dependencies.constructor[i];
175
+ }
176
+ const dependency = {
177
+ index: index
178
+ };
179
+ this.dependencies.constructor.push(dependency);
180
+ return dependency;
181
+ }
182
+ getMethodDependency(method, index) {
183
+ let methodDeps = this.dependencies.methods.get(method);
184
+ if (!methodDeps) {
185
+ this.dependencies.methods.set(method, methodDeps = []);
186
+ }
187
+ const i = methodDeps.findIndex((d)=>d.index === index);
188
+ if (i > -1) {
189
+ return methodDeps[i];
190
+ }
191
+ const dependency = {
192
+ index: index
193
+ };
194
+ methodDeps.push(dependency);
195
+ return dependency;
196
+ }
174
197
  }
175
198
  // @internal
176
199
  function getMetadata(Class) {
177
200
  const originalClass = classIdentityMap.get(Class) ?? Class;
178
201
  let metadata = metadataMap.get(originalClass);
179
202
  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
- });
203
+ metadataMap.set(originalClass, metadata = new Metadata(originalClass));
192
204
  }
193
205
  if (metadata.provider.useClass !== Class) {
194
206
  // This is part of the class identity mapping API (see setClassIdentityMapping).
@@ -202,32 +214,48 @@ function getMetadata(Class) {
202
214
  // We must update useClass to be the extender class B so that instances created by the
203
215
  // DI container match the consumer's registered class. Without this update, the DI
204
216
  // system would instantiate the original class A, causing behavior inconsistencies.
205
- //
206
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
207
217
  metadata.provider.useClass = Class;
208
218
  }
209
219
  return metadata;
210
220
  }
221
+ /**
222
+ * Registers a mapping between a generated (e.g., decorated or proxied) constructor
223
+ * and its original, underlying constructor.
224
+ *
225
+ * This allows libraries or consumers that manipulate constructors, such as through
226
+ * class decorators, to inform the DI system about the real "identity" of a class.
227
+ *
228
+ * @param transformedClass The constructor function returned by a class decorator or factory.
229
+ * @param originalClass The original constructor function.
230
+ *
231
+ * @remarks
232
+ * This API affects the core class identity resolution mechanism of the DI system.
233
+ * Incorrect usage may cause metadata to be misassociated, leading to subtle errors.
234
+ * Use only when manipulating constructors (e.g., via decorators or proxies),
235
+ * and ensure the mapping is correct.
236
+ */ function setClassIdentityMapping(transformedClass, originalClass) {
237
+ classIdentityMap.set(transformedClass, originalClass);
238
+ }
211
239
  const classIdentityMap = new WeakMap();
212
240
  const metadataMap = new WeakMap();
213
241
 
214
- function optional(token) {
242
+ function optional(token, name) {
215
243
  const context = ensureInjectionContext(optional);
216
- return context.container.resolve(token, true);
244
+ return context.container.resolve(token, true, name);
217
245
  }
218
- function optionalBy(thisArg, token) {
246
+ function optionalBy(thisArg, token, name) {
219
247
  const context = ensureInjectionContext(optionalBy);
220
248
  const resolution = context.resolution;
221
249
  const currentFrame = resolution.stack.peek();
222
250
  if (!currentFrame) {
223
- return optional(token);
251
+ return optional(token, name);
224
252
  }
225
253
  const currentRef = {
226
254
  current: thisArg
227
255
  };
228
256
  const cleanup = resolution.dependents.set(currentFrame.provider, currentRef);
229
257
  try {
230
- return optional(token);
258
+ return optional(token, name);
231
259
  } finally{
232
260
  cleanup();
233
261
  }
@@ -286,28 +314,29 @@ const Scope = {
286
314
  }
287
315
  // @internal
288
316
  function isConstructor(token) {
289
- return typeof token == "function";
317
+ return typeof token === "function";
290
318
  }
291
319
 
292
320
  // @internal
293
321
  function getTypeName(value) {
294
- if (typeof value == "string") {
295
- return `"${value}"`;
296
- }
297
- if (typeof value == "function") {
298
- return value.name && `typeof ${value.name}` || "Function";
299
- }
300
- if (typeof value == "object") {
301
- if (value === null) {
302
- return "null";
303
- }
304
- const proto = Object.getPrototypeOf(value);
305
- if (proto && proto !== Object.prototype) {
306
- const constructor = proto.constructor;
307
- if (typeof constructor == "function" && constructor.name) {
308
- return constructor.name;
322
+ switch(typeof value){
323
+ case "string":
324
+ return `"${value}"`;
325
+ case "function":
326
+ return value.name && `typeof ${value.name}` || "Function";
327
+ case "object":
328
+ {
329
+ if (value === null) {
330
+ return "null";
331
+ }
332
+ const proto = Object.getPrototypeOf(value);
333
+ if (proto && proto !== Object.prototype) {
334
+ const constructor = proto.constructor;
335
+ if (typeof constructor === "function" && constructor.name) {
336
+ return constructor.name;
337
+ }
338
+ }
309
339
  }
310
- }
311
340
  }
312
341
  return typeof value;
313
342
  }
@@ -318,28 +347,46 @@ class TokenRegistry {
318
347
  this.myMap = new Map();
319
348
  this.myParent = parent;
320
349
  }
321
- get(token) {
350
+ get(token, name) {
322
351
  // To clarify, at(-1) means we take the last added registration for this token
323
- return this.getAll(token)?.at(-1);
352
+ return this.getAll(token, name).at(-1);
324
353
  }
325
- getAll(token) {
326
- const internal = internals.get(token);
354
+ getAll(token, name) {
355
+ // Internal registrations cannot have a name
356
+ const internal = name !== undefined ? undefined : internals.get(token);
327
357
  return internal && [
328
358
  internal
329
- ] || this.getAllFromParent(token);
359
+ ] || this.getAllFromParent(token, name);
330
360
  }
331
361
  set(token, registration) {
332
362
  assert(!internals.has(token), `cannot register reserved token ${token.name}`);
333
363
  let registrations = this.myMap.get(token);
334
364
  if (!registrations) {
335
365
  this.myMap.set(token, registrations = []);
366
+ } else if (registration.name !== undefined) {
367
+ const existing = registrations.filter((r)=>r.name === registration.name);
368
+ assert(existing.length === 0, `a ${token.name} token named '${registration.name}' is already registered`);
336
369
  }
337
370
  registrations.push(registration);
338
371
  }
339
- delete(token) {
372
+ delete(token, name) {
340
373
  const registrations = this.myMap.get(token);
341
- this.myMap.delete(token);
342
- return registrations;
374
+ if (registrations) {
375
+ if (name !== undefined) {
376
+ const removedRegistrations = [];
377
+ const newRegistrations = [];
378
+ for (const registration of registrations){
379
+ const array = registration.name === name ? removedRegistrations : newRegistrations;
380
+ array.push(registration);
381
+ }
382
+ if (removedRegistrations.length > 0) {
383
+ this.myMap.set(token, newRegistrations);
384
+ return removedRegistrations;
385
+ }
386
+ }
387
+ this.myMap.delete(token);
388
+ }
389
+ return registrations ?? [];
343
390
  }
344
391
  deleteAll() {
345
392
  const tokens = Array.from(this.myMap.keys());
@@ -367,9 +414,14 @@ class TokenRegistry {
367
414
  }
368
415
  return Array.from(values);
369
416
  }
370
- getAllFromParent(token) {
371
- const registrations = this.myMap.get(token);
372
- return registrations || this.myParent?.getAllFromParent(token);
417
+ getAllFromParent(token, name) {
418
+ const thisRegistrations = this.myMap.get(token);
419
+ let registrations = thisRegistrations || this.myParent?.getAllFromParent(token, name);
420
+ if (registrations && name !== undefined) {
421
+ registrations = registrations.filter((r)=>r.name === name);
422
+ assert(registrations.length < 2, `internal error: more than one registration named '${name}'`);
423
+ }
424
+ return registrations ?? [];
373
425
  }
374
426
  }
375
427
  // @internal
@@ -467,9 +519,6 @@ function isDisposable(value) {
467
519
  getAllCached(token) {
468
520
  this.checkDisposed();
469
521
  const registrations = this.myTokenRegistry.getAll(token);
470
- if (!registrations) {
471
- return [];
472
- }
473
522
  const values = new Set();
474
523
  for (const registration of registrations){
475
524
  const value = registration.value;
@@ -491,46 +540,17 @@ function isDisposable(value) {
491
540
  }
492
541
  return Array.from(values);
493
542
  }
494
- isRegistered(token) {
543
+ isRegistered(token, name) {
495
544
  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
- }
545
+ return this.myTokenRegistry.get(token, name) !== undefined;
527
546
  }
528
547
  register(...args) {
529
548
  this.checkDisposed();
530
- if (args.length == 1) {
549
+ if (args.length === 1) {
531
550
  const Class = args[0];
532
551
  const metadata = getMetadata(Class);
533
552
  const registration = {
553
+ name: metadata.name,
534
554
  // The provider is of type ClassProvider, initialized by getMetadata
535
555
  provider: metadata.provider,
536
556
  options: {
@@ -558,10 +578,11 @@ function isDisposable(value) {
558
578
  if (isClassProvider(provider)) {
559
579
  const metadata = getMetadata(provider.useClass);
560
580
  const registration = {
581
+ // An explicit provider name overrides what is specified via @Named
582
+ name: metadata.name ?? provider.name,
561
583
  provider: metadata.provider,
562
584
  options: {
563
- // The explicit registration options override what is specified
564
- // via class decorators (e.g., @Scoped)
585
+ // Explicit registration options override what is specified via class decorators (e.g., @Scoped)
565
586
  scope: metadata.scope?.value ?? this.myOptions.defaultScope,
566
587
  ...options
567
588
  },
@@ -573,10 +594,12 @@ function isDisposable(value) {
573
594
  this.resolveProviderValue(registration, registration.provider);
574
595
  }
575
596
  } else {
576
- if (isExistingProvider(provider)) {
597
+ const existingProvider = isExistingProvider(provider);
598
+ if (existingProvider) {
577
599
  assert(token !== provider.useExisting, `the useExisting token ${token.name} cannot be the same as the token being registered`);
578
600
  }
579
601
  this.myTokenRegistry.set(token, {
602
+ name: existingProvider ? undefined : provider.name,
580
603
  provider: provider,
581
604
  options: options
582
605
  });
@@ -584,12 +607,9 @@ function isDisposable(value) {
584
607
  }
585
608
  return this;
586
609
  }
587
- unregister(token) {
610
+ unregister(token, name) {
588
611
  this.checkDisposed();
589
- const registrations = this.myTokenRegistry.delete(token);
590
- if (!registrations) {
591
- return [];
592
- }
612
+ const registrations = this.myTokenRegistry.delete(token, name);
593
613
  const values = new Set();
594
614
  for (const registration of registrations){
595
615
  const value = registration.value;
@@ -599,22 +619,31 @@ function isDisposable(value) {
599
619
  }
600
620
  return Array.from(values);
601
621
  }
602
- resolve(token, optional) {
622
+ resolve(token, optionalOrName, name) {
603
623
  this.checkDisposed();
604
- const registration = this.myTokenRegistry.get(token);
624
+ let localOptional;
625
+ let localName;
626
+ if (typeof optionalOrName === "string") {
627
+ localName = optionalOrName;
628
+ } else {
629
+ localOptional = optionalOrName;
630
+ localName = name;
631
+ }
632
+ const registration = this.myTokenRegistry.get(token, localName);
605
633
  if (registration) {
606
- return this.resolveRegistration(token, registration);
634
+ return this.resolveRegistration(token, registration, localName);
607
635
  }
608
636
  if (isConstructor(token)) {
609
- return this.instantiateClass(token, optional);
637
+ return this.instantiateClass(token, localOptional);
610
638
  }
611
- return optional ? undefined : throwUnregisteredError(token);
639
+ return optionalOrName ? undefined : throwUnregisteredError(token);
612
640
  }
613
641
  resolveAll(token, optional) {
614
642
  this.checkDisposed();
615
643
  const registrations = this.myTokenRegistry.getAll(token);
616
- if (registrations) {
617
- return registrations.map((registration)=>this.resolveRegistration(token, registration)).filter((value)=>value != null);
644
+ if (registrations.length > 0) {
645
+ return registrations //
646
+ .map((registration)=>this.resolveRegistration(token, registration)).filter((value)=>value != null);
618
647
  }
619
648
  if (isConstructor(token)) {
620
649
  const instance = this.instantiateClass(token, optional);
@@ -650,12 +679,12 @@ function isDisposable(value) {
650
679
  // Allow values to be GCed
651
680
  disposedRefs.clear();
652
681
  }
653
- resolveRegistration(token, registration) {
682
+ resolveRegistration(token, registration, name) {
654
683
  let currRegistration = registration;
655
684
  let currProvider = currRegistration.provider;
656
685
  while(isExistingProvider(currProvider)){
657
686
  const targetToken = currProvider.useExisting;
658
- currRegistration = this.myTokenRegistry.get(targetToken);
687
+ currRegistration = this.myTokenRegistry.get(targetToken, name);
659
688
  if (!currRegistration) {
660
689
  throwExistingUnregisteredError(token, targetToken);
661
690
  }
@@ -786,7 +815,7 @@ function isDisposable(value) {
786
815
  }
787
816
  }
788
817
  resolveScope(scope = this.myOptions.defaultScope, context = useInjectionContext()) {
789
- if (scope == Scope.Inherited) {
818
+ if (scope === Scope.Inherited) {
790
819
  const dependentFrame = context?.resolution.stack.peek();
791
820
  return dependentFrame?.scope || Scope.Transient;
792
821
  }
@@ -796,7 +825,7 @@ function isDisposable(value) {
796
825
  const dependencies = registration.dependencies;
797
826
  if (dependencies) {
798
827
  assert(isClassProvider(registration.provider), `internal error: not a ClassProvider`);
799
- const ctorDeps = dependencies.constructor;
828
+ const ctorDeps = dependencies.constructor.filter((d)=>d.appliedBy);
800
829
  if (ctorDeps.length > 0) {
801
830
  // Let's check if all necessary constructor parameters are decorated.
802
831
  // If not, we cannot safely create an instance.
@@ -807,13 +836,13 @@ function isDisposable(value) {
807
836
  });
808
837
  return ctorDeps.sort((a, b)=>a.index - b.index).map((dep)=>{
809
838
  const token = dep.tokenRef.getRefToken();
810
- switch(dep.decorator){
839
+ switch(dep.appliedBy){
811
840
  case "Inject":
812
- return this.resolve(token);
841
+ return this.resolve(token, dep.name);
813
842
  case "InjectAll":
814
843
  return this.resolveAll(token);
815
844
  case "Optional":
816
- return this.resolve(token, true);
845
+ return this.resolve(token, true, dep.name);
817
846
  case "OptionalAll":
818
847
  return this.resolveAll(token, true);
819
848
  }
@@ -828,7 +857,9 @@ function isDisposable(value) {
828
857
  assert(isClassProvider(registration.provider), `internal error: not a ClassProvider`);
829
858
  const ctor = registration.provider.useClass;
830
859
  // Perform method injection
831
- for (const [key, methodDeps] of dependencies.methods){
860
+ for (const entry of dependencies.methods){
861
+ const key = entry[0];
862
+ const methodDeps = entry[1].filter((d)=>d.appliedBy);
832
863
  // Let's check if all necessary method parameters are decorated.
833
864
  // If not, we cannot safely invoke the method.
834
865
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
@@ -839,13 +870,13 @@ function isDisposable(value) {
839
870
  });
840
871
  const args = methodDeps.sort((a, b)=>a.index - b.index).map((dep)=>{
841
872
  const token = dep.tokenRef.getRefToken();
842
- switch(dep.decorator){
873
+ switch(dep.appliedBy){
843
874
  case "Inject":
844
- return injectBy(instance, token);
875
+ return injectBy(instance, token, dep.name);
845
876
  case "InjectAll":
846
877
  return injectAll(token);
847
878
  case "Optional":
848
- return optionalBy(instance, token);
879
+ return optionalBy(instance, token, dep.name);
849
880
  case "OptionalAll":
850
881
  return optionalAll(token);
851
882
  }
@@ -905,7 +936,7 @@ function isDisposable(value) {
905
936
  *
906
937
  * // Wizard is registered with Container scope, and an instance
907
938
  * // is immediately created and cached by the container
908
- * const wizard = container.register(Wizard);
939
+ * container.register(Wizard);
909
940
  * ```
910
941
  *
911
942
  * @__NO_SIDE_EFFECTS__
@@ -954,41 +985,31 @@ function isTokenRef(value) {
954
985
  return value && typeof value === "object" && typeof value.getRefToken === "function";
955
986
  }
956
987
 
957
- function processDecoratedParameter(decorator, token, target, propertyKey, parameterIndex) {
958
- // Error out immediately if the decorator has been applied
959
- // to a static property or a static method
988
+ // @internal
989
+ function updateParameterMetadata(decorator, target, propertyKey, parameterIndex, updateFn) {
990
+ // Error out immediately if the decorator has been applied to a static method
960
991
  if (propertyKey !== undefined && typeof target === "function") {
961
- assert(false, `@${decorator} cannot be used on static member ${target.name}.${String(propertyKey)}`);
992
+ assert(false, `@${decorator} cannot be used on static method ${target.name}.${String(propertyKey)}`);
962
993
  }
963
- const tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
964
994
  if (propertyKey === undefined) {
965
995
  // Constructor
966
996
  const metadata = getMetadata(target);
967
- metadata.dependencies.constructor.push({
968
- decorator: decorator,
969
- tokenRef: tokenRef,
970
- index: parameterIndex
971
- });
997
+ const dependency = metadata.getConstructorDependency(parameterIndex);
998
+ updateFn(dependency);
972
999
  } else {
973
- // Normal instance method
1000
+ // Instance method
974
1001
  const metadata = getMetadata(target.constructor);
975
- const methods = metadata.dependencies.methods;
976
- let dep = methods.get(propertyKey);
977
- if (dep === undefined) {
978
- dep = [];
979
- methods.set(propertyKey, dep);
980
- }
981
- dep.push({
982
- decorator: decorator,
983
- tokenRef: tokenRef,
984
- index: parameterIndex
985
- });
1002
+ const dependency = metadata.getMethodDependency(propertyKey, parameterIndex);
1003
+ updateFn(dependency);
986
1004
  }
987
1005
  }
988
1006
 
989
1007
  function Inject(token) {
990
1008
  return function(target, propertyKey, parameterIndex) {
991
- processDecoratedParameter("Inject", token, target, propertyKey, parameterIndex);
1009
+ updateParameterMetadata("Inject", target, propertyKey, parameterIndex, (dependency)=>{
1010
+ dependency.appliedBy = "Inject";
1011
+ dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
1012
+ });
992
1013
  };
993
1014
  }
994
1015
 
@@ -1014,19 +1035,66 @@ function Inject(token) {
1014
1035
 
1015
1036
  function InjectAll(token) {
1016
1037
  return function(target, propertyKey, parameterIndex) {
1017
- processDecoratedParameter("InjectAll", token, target, propertyKey, parameterIndex);
1038
+ updateParameterMetadata("InjectAll", target, propertyKey, parameterIndex, (dependency)=>{
1039
+ dependency.appliedBy = "InjectAll";
1040
+ dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
1041
+ });
1042
+ };
1043
+ }
1044
+
1045
+ /**
1046
+ * Qualifies a class or an injected parameter with a unique name.
1047
+ *
1048
+ * This allows the container to distinguish between multiple implementations
1049
+ * of the same interface or type during registration and injection.
1050
+ *
1051
+ * @example
1052
+ * ```ts
1053
+ * @Named("dumbledore")
1054
+ * class Dumbledore implements Wizard {}
1055
+ *
1056
+ * // Register Dumbledore with Type<Wizard>
1057
+ * container.register(IWizard, { useClass: Dumbledore });
1058
+ * const dumbledore = container.resolve(IWizard, "dumbledore");
1059
+ * ```
1060
+ *
1061
+ * @__NO_SIDE_EFFECTS__
1062
+ */ function Named(name) {
1063
+ if (!name.trim()) {
1064
+ assert(false, "the @Named qualifier cannot be empty or blank");
1065
+ }
1066
+ return function(target, propertyKey, parameterIndex) {
1067
+ if (parameterIndex === undefined) {
1068
+ // The decorator has been applied to the class
1069
+ const ctor = target;
1070
+ const metadata = getMetadata(ctor);
1071
+ assert(!metadata.name, `a @Named('${metadata.name}') qualifier has already been applied to ${ctor.name}`);
1072
+ metadata.name = name;
1073
+ } else {
1074
+ // The decorator has been applied to a method parameter
1075
+ updateParameterMetadata("Named", target, propertyKey, parameterIndex, (dependency)=>{
1076
+ assert(!dependency.name, `a @Named('${dependency.name}') qualifier has already been applied to the parameter`);
1077
+ dependency.name = name;
1078
+ });
1079
+ }
1018
1080
  };
1019
1081
  }
1020
1082
 
1021
1083
  function Optional(token) {
1022
1084
  return function(target, propertyKey, parameterIndex) {
1023
- processDecoratedParameter("Optional", token, target, propertyKey, parameterIndex);
1085
+ updateParameterMetadata("Optional", target, propertyKey, parameterIndex, (dependency)=>{
1086
+ dependency.appliedBy = "Optional";
1087
+ dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
1088
+ });
1024
1089
  };
1025
1090
  }
1026
1091
 
1027
1092
  function OptionalAll(token) {
1028
1093
  return function(target, propertyKey, parameterIndex) {
1029
- processDecoratedParameter("OptionalAll", token, target, propertyKey, parameterIndex);
1094
+ updateParameterMetadata("OptionalAll", target, propertyKey, parameterIndex, (dependency)=>{
1095
+ dependency.appliedBy = "OptionalAll";
1096
+ dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
1097
+ });
1030
1098
  };
1031
1099
  }
1032
1100
 
@@ -1107,9 +1175,9 @@ function OptionalAll(token) {
1107
1175
  }
1108
1176
  }
1109
1177
  return {
1110
- inject: (token)=>withCurrentContext(()=>inject(token)),
1178
+ inject: (token, name)=>withCurrentContext(()=>inject(token, name)),
1111
1179
  injectAll: (token)=>withCurrentContext(()=>injectAll(token)),
1112
- optional: (token)=>withCurrentContext(()=>optional(token)),
1180
+ optional: (token, name)=>withCurrentContext(()=>optional(token, name)),
1113
1181
  optionalAll: (token)=>withCurrentContext(()=>optionalAll(token))
1114
1182
  };
1115
1183
  });
@@ -1158,5 +1226,5 @@ function OptionalAll(token) {
1158
1226
  return container;
1159
1227
  }
1160
1228
 
1161
- export { AutoRegister, EagerInstantiate, Inject, InjectAll, Injectable, Injector, Optional, OptionalAll, Scope, Scoped, applyMiddleware, build, createContainer, createType, forwardRef, inject, injectAll, injectBy, setClassIdentityMapping };
1229
+ export { AutoRegister, EagerInstantiate, Inject, InjectAll, Injectable, Injector, Named, Optional, OptionalAll, Scope, Scoped, applyMiddleware, build, createContainer, createType, forwardRef, inject, injectAll, injectBy, optional, optionalAll, optionalBy, setClassIdentityMapping };
1162
1230
  //# sourceMappingURL=index.mjs.map