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