@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/README.md +9 -8
- package/dist/cjs/index.d.ts +138 -114
- package/dist/cjs/index.js +210 -185
- package/dist/cjs/index.js.map +1 -1
- package/dist/es/index.d.mts +138 -114
- package/dist/es/index.mjs +210 -185
- package/dist/es/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
514
|
+
put(token, registration) {
|
|
444
515
|
check(!internals.has(token), `cannot register reserved token ${token.name}`);
|
|
445
|
-
let registrations = this.
|
|
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.
|
|
524
|
+
this.myRegistrations.set(token, registrations = []);
|
|
454
525
|
}
|
|
455
526
|
registrations.push(registration);
|
|
456
527
|
}
|
|
457
528
|
delete(token, name) {
|
|
458
|
-
const registrations = this.
|
|
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.
|
|
540
|
+
this.myRegistrations.set(token, updated);
|
|
470
541
|
return removed;
|
|
471
542
|
}
|
|
472
543
|
}
|
|
473
|
-
this.
|
|
544
|
+
this.myRegistrations.delete(token);
|
|
474
545
|
return registrations;
|
|
475
546
|
}
|
|
476
547
|
deleteAll() {
|
|
477
|
-
const tokens = Array.from(this.
|
|
478
|
-
const registrations = Array.from(this.
|
|
479
|
-
this.
|
|
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
|
-
|
|
556
|
+
clearCache() {
|
|
486
557
|
const values = new Set();
|
|
487
|
-
for (const registrations of this.
|
|
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.
|
|
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:
|
|
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
|
-
|
|
548
|
-
|
|
618
|
+
defaultScope: options?.defaultScope ?? "Transient",
|
|
619
|
+
autoRegister: options?.autoRegister ?? false
|
|
549
620
|
};
|
|
550
|
-
this.
|
|
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
|
-
|
|
570
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
671
|
-
if (
|
|
672
|
-
|
|
673
|
-
|
|
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
|
-
|
|
677
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
728
|
-
// Eager-instantiate only if the provided class is container-scoped
|
|
729
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
991
|
+
case "Transient":
|
|
907
992
|
{
|
|
908
993
|
const args = this.resolveCtorDependencies(registration);
|
|
909
|
-
|
|
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 ===
|
|
1139
|
+
check(!currentScope || currentScope.value === "Container", ()=>{
|
|
1044
1140
|
const { value, appliedBy } = currentScope;
|
|
1045
1141
|
const className = getTokenName(ctor);
|
|
1046
|
-
return `class ${className}:
|
|
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:
|
|
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(
|
|
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:
|
|
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}:
|
|
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
|
-
*
|
|
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
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
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.
|
|
1366
|
+
exports.assertInjectionContext = assertInjectionContext;
|
|
1342
1367
|
exports.build = build;
|
|
1343
1368
|
exports.classRef = classRef;
|
|
1344
1369
|
exports.createContainer = createContainer;
|