@lppedd/di-wise-neo 0.15.1 → 0.17.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/dist/cjs/index.js CHANGED
@@ -76,6 +76,25 @@ function untag(message) {
76
76
  return message.startsWith("[di-wise-neo]") ? message.substring(13).trimStart() : message;
77
77
  }
78
78
 
79
+ // @internal
80
+ class HookRegistry {
81
+ constructor(parent){
82
+ this.myHooks = new Set(parent?.myHooks);
83
+ }
84
+ clear() {
85
+ this.myHooks.clear();
86
+ }
87
+ get() {
88
+ return this.myHooks;
89
+ }
90
+ add(hook) {
91
+ this.myHooks.add(hook);
92
+ }
93
+ delete(hook) {
94
+ this.myHooks.delete(hook);
95
+ }
96
+ }
97
+
79
98
  // @internal
80
99
  class KeyedStack {
81
100
  has(key) {
@@ -144,6 +163,16 @@ function ensureInjectionContext(name) {
144
163
  check(context, `${name} can only be invoked within an injection context`);
145
164
  return context;
146
165
  }
166
+ /**
167
+ * Asserts that the current stack frame is within an injection context,
168
+ * meaning it has access to injection functions (`inject`, `optional`, etc.).
169
+ *
170
+ * @param fn The function performing the assertion, or a string name used in the error message.
171
+ * @throws {Error} If the current stack frame is not within an injection context.
172
+ */ function assertInjectionContext(fn) {
173
+ const name = typeof fn === "function" ? `${fn.name || "<unnamed>"}()` : fn;
174
+ ensureInjectionContext(name);
175
+ }
147
176
  function createInjectionContext() {
148
177
  let current = null;
149
178
  function provide(next) {
@@ -160,10 +189,16 @@ function createInjectionContext() {
160
189
  ];
161
190
  }
162
191
 
192
+ function injectAll(token) {
193
+ const context = ensureInjectionContext("injectAll()");
194
+ return context.container.resolveAll(token);
195
+ }
196
+
163
197
  function inject(token, name) {
164
198
  const context = ensureInjectionContext("inject()");
165
199
  return context.container.resolve(token, name);
166
200
  }
201
+
167
202
  function injectBy(thisArg, token, name) {
168
203
  const context = ensureInjectionContext("injectBy()");
169
204
  const resolution = context.resolution;
@@ -181,11 +216,6 @@ function injectBy(thisArg, token, name) {
181
216
  }
182
217
  }
183
218
 
184
- function injectAll(token) {
185
- const context = ensureInjectionContext("injectAll()");
186
- return context.container.resolveAll(token);
187
- }
188
-
189
219
  /**
190
220
  * Allows referencing a class declared later in the file by wrapping it
191
221
  * in a lazily evaluated function.
@@ -216,15 +246,15 @@ function tokenRef(token) {
216
246
  }
217
247
  // @internal
218
248
  function isClassRef(value) {
219
- return value && typeof value === "object" && typeof value.getRefClass === "function";
249
+ return value != null && typeof value === "object" && typeof value.getRefClass === "function";
220
250
  }
221
251
  // @internal
222
252
  function isTokensRef(value) {
223
- return value && typeof value === "object" && typeof value.getRefTokens === "function";
253
+ return value != null && typeof value === "object" && typeof value.getRefTokens === "function";
224
254
  }
225
255
  // @internal
226
256
  function isTokenRef(value) {
227
- return value && typeof value === "object" && typeof value.getRefToken === "function";
257
+ return value != null && typeof value === "object" && typeof value.getRefToken === "function";
228
258
  }
229
259
 
230
260
  // @internal
@@ -319,10 +349,16 @@ function getMetadata(classOrRef) {
319
349
  const classIdentityMap = new WeakMap();
320
350
  const metadataMap = new WeakMap();
321
351
 
352
+ function optionalAll(token) {
353
+ const context = ensureInjectionContext("optionalAll()");
354
+ return context.container.tryResolveAll(token);
355
+ }
356
+
322
357
  function optional(token, name) {
323
358
  const context = ensureInjectionContext("optional()");
324
359
  return context.container.tryResolve(token, name);
325
360
  }
361
+
326
362
  function optionalBy(thisArg, token, name) {
327
363
  const context = ensureInjectionContext("optionalBy()");
328
364
  const resolution = context.resolution;
@@ -340,11 +376,6 @@ function optionalBy(thisArg, token, name) {
340
376
  }
341
377
  }
342
378
 
343
- function optionalAll(token) {
344
- const context = ensureInjectionContext("optionalAll()");
345
- return context.container.tryResolveAll(token);
346
- }
347
-
348
379
  // @internal
349
380
  function isClassProvider(provider) {
350
381
  return "useClass" in provider;
@@ -362,41 +393,81 @@ function isExistingProvider(provider) {
362
393
  return "useExisting" in provider;
363
394
  }
364
395
 
365
- const Scope = {
366
- /**
367
- * Creates a new value every time the token is resolved.
368
- */ Transient: "Transient",
369
- /**
370
- * Creates and caches a single value per token resolution graph.
371
- *
372
- * The same value is reused during a single resolution request and is subsequently discarded.
373
- */ Resolution: "Resolution",
374
- /**
375
- * Creates and caches a single value per container.
376
- *
377
- * If the value is not found in the current container, it is looked up in the parent container,
378
- * and so on. It effectively behaves like a _singleton_ scope but allows container-specific overrides.
379
- */ Container: "Container"
380
- };
396
+ // @internal
397
+ function updateParameterMetadata(decorator, target, methodKey, parameterIndex, updateFn) {
398
+ // Error out immediately if the decorator has been applied to a static method
399
+ if (methodKey !== undefined && typeof target === "function") {
400
+ check(false, `@${decorator} cannot be used on static method ${target.name}.${String(methodKey)}`);
401
+ }
402
+ if (methodKey === undefined) {
403
+ // Constructor
404
+ const metadata = getMetadata(target);
405
+ const dependency = metadata.getCtorDependency(parameterIndex);
406
+ updateFn(dependency);
407
+ } else {
408
+ // Instance method
409
+ const metadata = getMetadata(target.constructor);
410
+ const dependency = metadata.getMethodDependency(methodKey, parameterIndex);
411
+ updateFn(dependency);
412
+ }
413
+ }
414
+ // Checks that a constructor or method parameter has only one injection decorator.
415
+ // For example, if both `@Inject` and `@Optional` are used, it becomes difficult to
416
+ // understand which one "wins", unless the user is aware of the decorator evaluation order.
417
+ //
418
+ // @internal
419
+ function checkSingleDecorator(dependency, target, methodKey, parameterIndex) {
420
+ check(dependency.appliedBy === undefined, ()=>{
421
+ const param = describeParam(target, methodKey, parameterIndex);
422
+ return `multiple injection decorators on ${param}, but only one is allowed`;
423
+ });
424
+ }
425
+ // Checks that the `@Named` decorator is not used in combination with
426
+ // `@InjectAll` or `@OptionalAll`, which ignore the name qualification.
427
+ //
428
+ // @internal
429
+ function checkNamedDecorator(dependency, target, methodKey, parameterIndex) {
430
+ const { appliedBy, name } = dependency;
431
+ check(name === undefined || appliedBy !== "InjectAll" && appliedBy !== "OptionalAll", ()=>{
432
+ const param = describeParam(target, methodKey, parameterIndex);
433
+ return `@Named has no effect on ${param} when used with @${appliedBy}`;
434
+ });
435
+ }
436
+ // Returns a human-readable description of the parameter location.
437
+ // For example: "Wizard constructor parameter 2" or "Wizard.set parameter 0"
438
+ //
439
+ // @internal
440
+ function describeParam(target, methodKey, parameterIndex) {
441
+ const location = methodKey === undefined //
442
+ ? target.name : `${target.constructor.name}.${String(methodKey)}`;
443
+ return `${location}(parameter #${parameterIndex})`;
444
+ }
381
445
 
382
446
  // @__NO_SIDE_EFFECTS__
383
447
  function createType(typeName, provider, options) {
384
448
  const name = `Type<${typeName}>`;
385
- const toString = ()=>name;
386
- return provider //
387
- ? {
388
- name,
389
- provider,
390
- options,
391
- toString
392
- } : {
393
- name,
394
- toString
449
+ const decorator = (target, propertyKey, parameterIndex)=>{
450
+ updateParameterMetadata(name, target, propertyKey, parameterIndex, (dependency)=>{
451
+ checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
452
+ dependency.appliedBy = "Inject";
453
+ dependency.tokenRef = tokenRef(()=>decorator);
454
+ });
395
455
  };
456
+ const type = decorator;
457
+ Object.defineProperty(type, "name", {
458
+ value: name
459
+ });
460
+ if (provider) {
461
+ type.provider = provider;
462
+ type.options = options;
463
+ }
464
+ type.__type = undefined;
465
+ type.toString = ()=>name;
466
+ return type;
396
467
  }
397
468
  // @internal
398
469
  function isConstructor(token) {
399
- return typeof token === "function";
470
+ return !("__type" in token);
400
471
  }
401
472
 
402
473
  // @internal
@@ -426,7 +497,7 @@ function getTypeName(value) {
426
497
  // @internal
427
498
  class TokenRegistry {
428
499
  constructor(parent){
429
- this.myMap = new Map();
500
+ this.myRegistrations = new Map();
430
501
  this.myParent = parent;
431
502
  }
432
503
  get(token, name) {
@@ -440,9 +511,9 @@ class TokenRegistry {
440
511
  internal
441
512
  ] || this.getAllFromParent(token, name);
442
513
  }
443
- set(token, registration) {
514
+ put(token, registration) {
444
515
  check(!internals.has(token), `cannot register reserved token ${token.name}`);
445
- let registrations = this.myMap.get(token);
516
+ let registrations = this.myRegistrations.get(token);
446
517
  if (registrations) {
447
518
  const name = registration.name;
448
519
  if (name !== undefined) {
@@ -450,12 +521,12 @@ class TokenRegistry {
450
521
  check(existing.length === 0, `token ${getTokenName(token)} with name '${name}' is already registered`);
451
522
  }
452
523
  } else {
453
- this.myMap.set(token, registrations = []);
524
+ this.myRegistrations.set(token, registrations = []);
454
525
  }
455
526
  registrations.push(registration);
456
527
  }
457
528
  delete(token, name) {
458
- const registrations = this.myMap.get(token);
529
+ const registrations = this.myRegistrations.get(token);
459
530
  if (!registrations) {
460
531
  return [];
461
532
  }
@@ -466,25 +537,25 @@ class TokenRegistry {
466
537
  (registration.name === name ? removed : updated).push(registration);
467
538
  }
468
539
  if (removed.length > 0) {
469
- this.myMap.set(token, updated);
540
+ this.myRegistrations.set(token, updated);
470
541
  return removed;
471
542
  }
472
543
  }
473
- this.myMap.delete(token);
544
+ this.myRegistrations.delete(token);
474
545
  return registrations;
475
546
  }
476
547
  deleteAll() {
477
- const tokens = Array.from(this.myMap.keys());
478
- const registrations = Array.from(this.myMap.values()).flat();
479
- this.myMap.clear();
548
+ const tokens = Array.from(this.myRegistrations.keys());
549
+ const registrations = Array.from(this.myRegistrations.values()).flat();
550
+ this.myRegistrations.clear();
480
551
  return [
481
552
  tokens,
482
553
  registrations
483
554
  ];
484
555
  }
485
- clearRegistrations() {
556
+ clearCache() {
486
557
  const values = new Set();
487
- for (const registrations of this.myMap.values()){
558
+ for (const registrations of this.myRegistrations.values()){
488
559
  for(let i = 0; i < registrations.length; i++){
489
560
  const registration = registrations[i];
490
561
  const value = registration.value;
@@ -500,7 +571,7 @@ class TokenRegistry {
500
571
  return Array.from(values);
501
572
  }
502
573
  getAllFromParent(token, name) {
503
- let registrations = this.myMap.get(token) || this.myParent?.getAllFromParent(token, name);
574
+ let registrations = this.myRegistrations.get(token) || this.myParent?.getAllFromParent(token, name);
504
575
  if (registrations && name !== undefined) {
505
576
  registrations = registrations.filter((r)=>r.name === name);
506
577
  check(registrations.length < 2, `internal error: more than one registration named '${name}'`);
@@ -521,7 +592,7 @@ function build(factory, name) {
521
592
  internals.set(token, {
522
593
  provider: provider,
523
594
  options: {
524
- scope: Scope.Transient
595
+ scope: "Transient"
525
596
  }
526
597
  });
527
598
  builders.add(provider);
@@ -533,7 +604,7 @@ const builders = new WeakSet();
533
604
  // @internal
534
605
  // @internal
535
606
  function isDisposable(value) {
536
- return value && typeof value === "object" && typeof value.dispose === "function";
607
+ return value != null && typeof value === "object" && typeof value.dispose === "function";
537
608
  }
538
609
 
539
610
  /**
@@ -544,10 +615,11 @@ function isDisposable(value) {
544
615
  this.myDisposed = false;
545
616
  this.myParent = parent;
546
617
  this.myOptions = {
547
- autoRegister: options?.autoRegister ?? false,
548
- defaultScope: options?.defaultScope ?? Scope.Transient
618
+ defaultScope: options?.defaultScope ?? "Transient",
619
+ autoRegister: options?.autoRegister ?? false
549
620
  };
550
- this.myTokenRegistry = new TokenRegistry(this.myParent?.myTokenRegistry);
621
+ this.myHookRegistry = new HookRegistry(parent?.myHookRegistry);
622
+ this.myTokenRegistry = new TokenRegistry(parent?.myTokenRegistry);
551
623
  }
552
624
  get registry() {
553
625
  return this.myTokenRegistry;
@@ -566,15 +638,15 @@ function isDisposable(value) {
566
638
  createChild(options) {
567
639
  this.checkDisposed();
568
640
  const container = new ContainerImpl(this, {
569
- autoRegister: options?.autoRegister ?? this.myOptions.autoRegister,
570
- defaultScope: options?.defaultScope ?? this.myOptions.defaultScope
641
+ defaultScope: options?.defaultScope ?? this.myOptions.defaultScope,
642
+ autoRegister: options?.autoRegister ?? this.myOptions.autoRegister
571
643
  });
572
644
  this.myChildren.add(container);
573
645
  return container;
574
646
  }
575
647
  clearCache() {
576
648
  this.checkDisposed();
577
- return this.myTokenRegistry.clearRegistrations();
649
+ return this.myTokenRegistry.clearCache();
578
650
  }
579
651
  getCached(token) {
580
652
  this.checkDisposed();
@@ -651,6 +723,12 @@ function isDisposable(value) {
651
723
  this.checkDisposed();
652
724
  return this.resolveAllToken(token, true);
653
725
  }
726
+ addHook(hook) {
727
+ this.myHookRegistry.add(hook);
728
+ }
729
+ removeHook(hook) {
730
+ this.myHookRegistry.delete(hook);
731
+ }
654
732
  dispose() {
655
733
  if (this.myDisposed) {
656
734
  return;
@@ -659,22 +737,23 @@ function isDisposable(value) {
659
737
  for (const child of this.myChildren){
660
738
  child.dispose();
661
739
  }
740
+ this.myDisposed = true;
662
741
  this.myChildren.clear();
663
- // Remove ourselves from our parent container
664
742
  this.myParent?.myChildren?.delete(this);
665
- this.myDisposed = true;
666
743
  const [, registrations] = this.myTokenRegistry.deleteAll();
667
- const disposedRefs = new Set();
668
- // Dispose all resolved (aka instantiated) tokens that implement the Disposable interface
744
+ const values = new Set();
669
745
  for (const registration of registrations){
670
- const value = registration.value?.current;
671
- if (isDisposable(value) && !disposedRefs.has(value)) {
672
- disposedRefs.add(value);
673
- value.dispose();
746
+ // Only container-scoped registrations use 'registration.value'
747
+ if (registration.value) {
748
+ const value = registration.value.current;
749
+ // Dispose all cached values that implement the Disposable interface
750
+ if (!values.has(value) && values.add(value) && isDisposable(value)) {
751
+ value.dispose();
752
+ }
674
753
  }
675
754
  }
676
- // Allow values to be GCed
677
- disposedRefs.clear();
755
+ this.notifyDisposeHooks(Array.from(values));
756
+ this.myHookRegistry.clear();
678
757
  }
679
758
  registerClass(Class) {
680
759
  const metadata = getMetadata(Class);
@@ -689,11 +768,11 @@ function isDisposable(value) {
689
768
  dependencies: metadata.dependencies
690
769
  };
691
770
  // Register the class itself
692
- this.myTokenRegistry.set(Class, registration);
771
+ this.myTokenRegistry.put(Class, registration);
693
772
  // Register the additional tokens specified via class decorators.
694
773
  // These tokens will point to the original Class token and will have the same scope.
695
774
  for (const token of metadata.tokensRef.getRefTokens()){
696
- this.myTokenRegistry.set(token, {
775
+ this.myTokenRegistry.put(token, {
697
776
  name: name,
698
777
  provider: {
699
778
  useExisting: [
@@ -703,8 +782,10 @@ function isDisposable(value) {
703
782
  }
704
783
  });
705
784
  }
706
- // Eager-instantiate only if the class is container-scoped
707
- if (metadata.eagerInstantiate && registration.options?.scope === Scope.Container) {
785
+ // Eager-instantiate only if the class is container-scoped.
786
+ // Note that we are comparing the scope using the registration configured just above,
787
+ // which takes into account both the metadata and the container option as a fallback.
788
+ if (metadata.eagerInstantiate && registration.options?.scope === "Container") {
708
789
  this.resolveProviderValue(Class, registration);
709
790
  }
710
791
  }
@@ -724,9 +805,11 @@ function isDisposable(value) {
724
805
  },
725
806
  dependencies: metadata.dependencies
726
807
  };
727
- this.myTokenRegistry.set(token, registration);
728
- // Eager-instantiate only if the provided class is container-scoped
729
- if (metadata.eagerInstantiate && registration.options?.scope === Scope.Container) {
808
+ this.myTokenRegistry.put(token, registration);
809
+ // Eager-instantiate only if the provided class is container-scoped.
810
+ // Note that we are comparing the scope using the registration configured just above,
811
+ // which takes into account both the metadata and the container option as a fallback.
812
+ if (metadata.eagerInstantiate && registration.options?.scope === "Container") {
730
813
  this.resolveProviderValue(token, registration);
731
814
  }
732
815
  } else {
@@ -734,7 +817,7 @@ function isDisposable(value) {
734
817
  const [targetToken] = this.getTargetToken(provider);
735
818
  check(token !== targetToken, `token ${getTokenName(token)} cannot alias itself via useExisting`);
736
819
  }
737
- this.myTokenRegistry.set(token, {
820
+ this.myTokenRegistry.put(token, {
738
821
  name: name,
739
822
  provider: provider,
740
823
  options: options
@@ -839,7 +922,7 @@ function isDisposable(value) {
839
922
  }
840
923
  if (isFactoryProvider(provider)) {
841
924
  const factory = provider.useFactory;
842
- return this.resolveScopedValue(token, registration, factory);
925
+ return this.resolveScopedValue(token, registration, ()=>factory());
843
926
  }
844
927
  if (isValueProvider(provider)) {
845
928
  return provider.useValue;
@@ -877,7 +960,7 @@ function isDisposable(value) {
877
960
  ];
878
961
  try {
879
962
  switch(scope){
880
- case Scope.Container:
963
+ case "Container":
881
964
  {
882
965
  const valueRef = registration.value;
883
966
  if (valueRef) {
@@ -888,9 +971,10 @@ function isDisposable(value) {
888
971
  registration.value = {
889
972
  current: value
890
973
  };
974
+ this.notifyProvideHooks(value, scope);
891
975
  return value;
892
976
  }
893
- case Scope.Resolution:
977
+ case "Resolution":
894
978
  {
895
979
  const valueRef = resolution.values.get(provider);
896
980
  if (valueRef) {
@@ -901,12 +985,15 @@ function isDisposable(value) {
901
985
  resolution.values.set(provider, {
902
986
  current: value
903
987
  });
988
+ this.notifyProvideHooks(value, scope);
904
989
  return value;
905
990
  }
906
- case Scope.Transient:
991
+ case "Transient":
907
992
  {
908
993
  const args = this.resolveCtorDependencies(registration);
909
- return this.injectMethodDependencies(registration, factory(args));
994
+ const value = this.injectMethodDependencies(registration, factory(args));
995
+ this.notifyProvideHooks(value, scope);
996
+ return value;
910
997
  }
911
998
  }
912
999
  } finally{
@@ -932,7 +1019,6 @@ function isDisposable(value) {
932
1019
  }
933
1020
  return [];
934
1021
  }
935
- // Call context: decorator-based injection
936
1022
  injectMethodDependencies(registration, instance) {
937
1023
  const dependencies = registration.dependencies;
938
1024
  if (dependencies) {
@@ -985,6 +1071,16 @@ function isDisposable(value) {
985
1071
  return instance ? optionalAll(token) : this.tryResolveAll(token);
986
1072
  }
987
1073
  }
1074
+ notifyProvideHooks(value, scope) {
1075
+ for (const hook of this.myHookRegistry.get()){
1076
+ hook.onProvide?.(value, scope);
1077
+ }
1078
+ }
1079
+ notifyDisposeHooks(values) {
1080
+ for (const hook of this.myHookRegistry.get()){
1081
+ hook.onDispose?.(values);
1082
+ }
1083
+ }
988
1084
  checkDisposed() {
989
1085
  check(!this.myDisposed, "container is disposed");
990
1086
  }
@@ -1040,68 +1136,19 @@ function isDisposable(value) {
1040
1136
  const ctor = Class;
1041
1137
  const metadata = getMetadata(ctor);
1042
1138
  const currentScope = metadata.scope;
1043
- check(!currentScope || currentScope.value === Scope.Container, ()=>{
1139
+ check(!currentScope || currentScope.value === "Container", ()=>{
1044
1140
  const { value, appliedBy } = currentScope;
1045
1141
  const className = getTokenName(ctor);
1046
- return `class ${className}: 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.`;
1142
+ return `class ${className}: 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.`;
1047
1143
  });
1048
1144
  metadata.eagerInstantiate = true;
1049
1145
  metadata.scope = {
1050
- value: Scope.Container,
1146
+ value: "Container",
1051
1147
  appliedBy: "EagerInstantiate"
1052
1148
  };
1053
1149
  };
1054
1150
  }
1055
1151
 
1056
- // @internal
1057
- function updateParameterMetadata(decorator, target, methodKey, parameterIndex, updateFn) {
1058
- // Error out immediately if the decorator has been applied to a static method
1059
- if (methodKey !== undefined && typeof target === "function") {
1060
- check(false, `@${decorator} cannot be used on static method ${target.name}.${String(methodKey)}`);
1061
- }
1062
- if (methodKey === undefined) {
1063
- // Constructor
1064
- const metadata = getMetadata(target);
1065
- const dependency = metadata.getCtorDependency(parameterIndex);
1066
- updateFn(dependency);
1067
- } else {
1068
- // Instance method
1069
- const metadata = getMetadata(target.constructor);
1070
- const dependency = metadata.getMethodDependency(methodKey, parameterIndex);
1071
- updateFn(dependency);
1072
- }
1073
- }
1074
- // Checks that a constructor or method parameter has only one injection decorator.
1075
- // For example, if both `@Inject` and `@Optional` are used, it becomes difficult to
1076
- // understand which one "wins", unless the user is aware of the decorator evaluation order.
1077
- //
1078
- // @internal
1079
- function checkSingleDecorator(dependency, target, methodKey, parameterIndex) {
1080
- check(dependency.appliedBy === undefined, ()=>{
1081
- const param = describeParam(target, methodKey, parameterIndex);
1082
- return `multiple injection decorators on ${param}, but only one is allowed`;
1083
- });
1084
- }
1085
- // Checks that the `@Named` decorator is not used in combination with
1086
- // `@InjectAll` or `@OptionalAll`, which ignore the name qualification.
1087
- //
1088
- // @internal
1089
- function checkNamedDecorator(dependency, target, methodKey, parameterIndex) {
1090
- const { appliedBy, name } = dependency;
1091
- check(name === undefined || appliedBy !== "InjectAll" && appliedBy !== "OptionalAll", ()=>{
1092
- const param = describeParam(target, methodKey, parameterIndex);
1093
- return `@Named has no effect on ${param} when used with @${appliedBy}`;
1094
- });
1095
- }
1096
- // Returns a human-readable description of the parameter location.
1097
- // For example: "Wizard constructor parameter 2" or "Wizard.set parameter 0"
1098
- //
1099
- // @internal
1100
- function describeParam(target, methodKey, parameterIndex) {
1101
- const location = methodKey === undefined ? target.name : `${target.constructor.name}.${String(methodKey)}`;
1102
- return `${location}(parameter #${parameterIndex})`;
1103
- }
1104
-
1105
1152
  // @__NO_SIDE_EFFECTS__
1106
1153
  function Inject(token) {
1107
1154
  return function(target, propertyKey, parameterIndex) {
@@ -1215,7 +1262,7 @@ function OptionalAll(token) {
1215
1262
  *
1216
1263
  * @example
1217
1264
  * ```ts
1218
- * @Scoped(Scope.Container)
1265
+ * @Scoped("Container")
1219
1266
  * class Wizard {}
1220
1267
  *
1221
1268
  * container.register(Wizard);
@@ -1224,7 +1271,7 @@ function OptionalAll(token) {
1224
1271
  * container.register(
1225
1272
  * Wizard,
1226
1273
  * { useClass: Wizard },
1227
- * { scope: Scope.Container },
1274
+ * { scope: "Container" },
1228
1275
  * );
1229
1276
  * ```
1230
1277
  *
@@ -1238,7 +1285,7 @@ function OptionalAll(token) {
1238
1285
  const { value, appliedBy } = currentScope;
1239
1286
  const by = appliedBy === "Scoped" ? `another @${appliedBy} decorator` : `@${appliedBy}`;
1240
1287
  const className = getTokenName(ctor);
1241
- return `class ${className}: 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.`;
1288
+ return `class ${className}: 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.`;
1242
1289
  });
1243
1290
  metadata.scope = {
1244
1291
  value: scope,
@@ -1248,21 +1295,23 @@ function OptionalAll(token) {
1248
1295
  }
1249
1296
 
1250
1297
  /**
1251
- * Injector token for dynamic injections.
1298
+ * Allows performing injections outside the normal injection context window.
1252
1299
  *
1253
1300
  * @example
1254
1301
  * ```ts
1255
1302
  * class Wizard {
1256
1303
  * private injector = inject(Injector);
1304
+ *
1305
+ * // Lazily initialize the wand property
1257
1306
  * private wand?: Wand;
1258
1307
  *
1259
1308
  * getWand(): Wand {
1309
+ * // An injection context does not exist here, but the
1310
+ * // Injector instance retains and reuse the context
1311
+ * // that was present at the time of its injection
1260
1312
  * return (this.wand ??= this.injector.inject(Wand));
1261
1313
  * }
1262
1314
  * }
1263
- *
1264
- * const wizard = container.resolve(Wizard);
1265
- * wizard.getWand(); // => Wand
1266
1315
  * ```
1267
1316
  */ const Injector = /*@__PURE__*/ build(()=>{
1268
1317
  const context = ensureInjectionContext("Injector factory");
@@ -1286,46 +1335,22 @@ function OptionalAll(token) {
1286
1335
  };
1287
1336
  }, "Injector");
1288
1337
 
1289
- /**
1290
- * Applies middleware functions to a container.
1291
- *
1292
- * Middlewares are applied in array order but execute in reverse order.
1293
- *
1294
- * @example
1295
- * ```ts
1296
- * const container = applyMiddleware(
1297
- * createContainer(),
1298
- * [A, B],
1299
- * );
1300
- * ```
1301
- *
1302
- * The execution order will be:
1303
- *
1304
- * 1. B before
1305
- * 2. A before
1306
- * 3. original function
1307
- * 4. A after
1308
- * 5. B after
1309
- *
1310
- * This allows outer middlewares to wrap and control the behavior of inner middlewares.
1311
- */ function applyMiddleware(container, middlewares) {
1312
- const composer = {
1313
- use (key, wrap) {
1314
- // We need to bind the 'this' context of the function to the container
1315
- // before passing it to the middleware wrapper.
1316
- const fn = container[key].bind(container);
1317
- container[key] = wrap(fn);
1318
- return composer;
1319
- }
1320
- };
1321
- const api = container.api ||= {
1322
- ...container
1323
- };
1324
- for (const middleware of middlewares){
1325
- middleware(composer, api);
1326
- }
1327
- return container;
1328
- }
1338
+ const Scope = {
1339
+ /**
1340
+ * Creates a new value every time the token is resolved.
1341
+ */ Transient: "Transient",
1342
+ /**
1343
+ * Creates and caches a single value per token resolution graph.
1344
+ *
1345
+ * The same value is reused during a single resolution request and is subsequently discarded.
1346
+ */ Resolution: "Resolution",
1347
+ /**
1348
+ * Creates and caches a single value per container.
1349
+ *
1350
+ * If the value is not found in the current container, it is looked up in the parent container,
1351
+ * and so on. It effectively behaves like a _singleton_ scope but allows container-specific overrides.
1352
+ */ Container: "Container"
1353
+ };
1329
1354
 
1330
1355
  exports.AutoRegister = AutoRegister;
1331
1356
  exports.EagerInstantiate = EagerInstantiate;
@@ -1338,7 +1363,7 @@ exports.Optional = Optional;
1338
1363
  exports.OptionalAll = OptionalAll;
1339
1364
  exports.Scope = Scope;
1340
1365
  exports.Scoped = Scoped;
1341
- exports.applyMiddleware = applyMiddleware;
1366
+ exports.assertInjectionContext = assertInjectionContext;
1342
1367
  exports.build = build;
1343
1368
  exports.classRef = classRef;
1344
1369
  exports.createContainer = createContainer;