@lppedd/di-wise-neo 0.15.0 → 0.16.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.d.ts +55 -6
- package/dist/cjs/index.js +61 -19
- package/dist/cjs/index.js.map +1 -1
- package/dist/es/index.d.mts +55 -6
- package/dist/es/index.mjs +61 -20
- package/dist/es/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cjs/index.d.ts
CHANGED
|
@@ -274,18 +274,46 @@ type Provider<Value = any> = ClassProvider<Value & object> | FactoryProvider<Val
|
|
|
274
274
|
* Container creation options.
|
|
275
275
|
*/
|
|
276
276
|
interface ContainerOptions {
|
|
277
|
+
/**
|
|
278
|
+
* The default scope for registrations.
|
|
279
|
+
*
|
|
280
|
+
* @defaultValue Scope.Transient
|
|
281
|
+
*/
|
|
282
|
+
readonly defaultScope: Scope;
|
|
277
283
|
/**
|
|
278
284
|
* Whether to automatically register an unregistered class when resolving it as a token.
|
|
279
285
|
*
|
|
280
286
|
* @defaultValue false
|
|
281
287
|
*/
|
|
282
288
|
readonly autoRegister: boolean;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Child container creation options.
|
|
292
|
+
*/
|
|
293
|
+
interface ChildContainerOptions extends ContainerOptions {
|
|
283
294
|
/**
|
|
284
|
-
*
|
|
295
|
+
* Whether to copy {@link ContainerHook}(s) from the parent container.
|
|
285
296
|
*
|
|
286
|
-
* @defaultValue
|
|
297
|
+
* @defaultValue true
|
|
287
298
|
*/
|
|
288
|
-
readonly
|
|
299
|
+
readonly copyHooks: boolean;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* A hook into the lifecycle of a container-managed value.
|
|
303
|
+
*/
|
|
304
|
+
interface ContainerHook {
|
|
305
|
+
/**
|
|
306
|
+
* Called when the container provides a value for a {@link Token}.
|
|
307
|
+
* - For _container_ scoped tokens, it is called only once when the token is first resolved and cached.
|
|
308
|
+
* - For _resolution_ scoped tokens, it is called once per token resolution graph.
|
|
309
|
+
* - For _transient_ scoped tokens, it is called each time the token is resolved,
|
|
310
|
+
* which might mean multiple times per resolution graph.
|
|
311
|
+
*/
|
|
312
|
+
readonly onProvide: (value: unknown) => void;
|
|
313
|
+
/**
|
|
314
|
+
* Called when a _container_ scoped value is about to be disposed.
|
|
315
|
+
*/
|
|
316
|
+
readonly onDispose: (value: unknown) => void;
|
|
289
317
|
}
|
|
290
318
|
/**
|
|
291
319
|
* Container API.
|
|
@@ -308,7 +336,7 @@ interface Container {
|
|
|
308
336
|
*
|
|
309
337
|
* You can pass specific options to override the inherited ones.
|
|
310
338
|
*/
|
|
311
|
-
createChild(options?: Partial<
|
|
339
|
+
createChild(options?: Partial<ChildContainerOptions>): Container;
|
|
312
340
|
/**
|
|
313
341
|
* Clears and returns all distinct cached values from this container's internal registry.
|
|
314
342
|
* Values from {@link ValueProvider} registrations are not included, as they are never cached.
|
|
@@ -571,6 +599,18 @@ interface Container {
|
|
|
571
599
|
* in the container's internal registry.
|
|
572
600
|
*/
|
|
573
601
|
tryResolveAll<Value>(token: Token<Value>): Value[];
|
|
602
|
+
/**
|
|
603
|
+
* Adds a hook to observe the lifecycle of container-managed values.
|
|
604
|
+
*
|
|
605
|
+
* Does nothing if the hook has already been added.
|
|
606
|
+
*/
|
|
607
|
+
addHook(hook: ContainerHook): void;
|
|
608
|
+
/**
|
|
609
|
+
* Removes a previously added hook.
|
|
610
|
+
*
|
|
611
|
+
* Does nothing if the hook has not been added yet.
|
|
612
|
+
*/
|
|
613
|
+
removeHook(hook: ContainerHook): void;
|
|
574
614
|
/**
|
|
575
615
|
* Disposes this container and all its cached values.
|
|
576
616
|
*
|
|
@@ -938,6 +978,15 @@ declare function injectAll<Instance extends object>(Class: Constructor<Instance>
|
|
|
938
978
|
*/
|
|
939
979
|
declare function injectAll<Value>(token: Token<Value>): Value[];
|
|
940
980
|
|
|
981
|
+
/**
|
|
982
|
+
* Asserts that the current stack frame is within an injection context,
|
|
983
|
+
* meaning it has access to injection functions (`inject`, `optional`, etc.).
|
|
984
|
+
*
|
|
985
|
+
* @param fn The function performing the assertion, or a string name used in the error message.
|
|
986
|
+
* @throws {Error} If the current stack frame is not within an injection context.
|
|
987
|
+
*/
|
|
988
|
+
declare function assertInjectionContext(fn: Function | string): void;
|
|
989
|
+
|
|
941
990
|
/**
|
|
942
991
|
* Injector API.
|
|
943
992
|
*/
|
|
@@ -1147,5 +1196,5 @@ declare function optionalAll<Instance extends object>(Class: Constructor<Instanc
|
|
|
1147
1196
|
*/
|
|
1148
1197
|
declare function optionalAll<Value>(token: Token<Value>): Value[];
|
|
1149
1198
|
|
|
1150
|
-
export { AutoRegister, EagerInstantiate, Inject, InjectAll, Injectable, Injector, Named, Optional, OptionalAll, Scope, Scoped, applyMiddleware, build, classRef, createContainer, createType, inject, injectAll, injectBy, optional, optionalAll, optionalBy, setClassIdentityMapping, tokenRef };
|
|
1151
|
-
export type { ClassProvider, ClassRef, Constructor, Container, ContainerOptions, ExistingProvider, FactoryProvider, Middleware, MiddlewareComposer, Provider, ProviderType, RegistrationOptions, Token, TokenRef, Tokens, TokensRef, Type, ValueProvider };
|
|
1199
|
+
export { AutoRegister, EagerInstantiate, Inject, InjectAll, Injectable, Injector, Named, Optional, OptionalAll, Scope, Scoped, applyMiddleware, assertInjectionContext, build, classRef, createContainer, createType, inject, injectAll, injectBy, optional, optionalAll, optionalBy, setClassIdentityMapping, tokenRef };
|
|
1200
|
+
export type { ChildContainerOptions, ClassProvider, ClassRef, Constructor, Container, ContainerHook, ContainerOptions, ExistingProvider, FactoryProvider, Middleware, MiddlewareComposer, Provider, ProviderType, RegistrationOptions, Token, TokenRef, Tokens, TokensRef, Type, ValueProvider };
|
package/dist/cjs/index.js
CHANGED
|
@@ -34,7 +34,7 @@ function throwResolutionError(tokenInfo, aliases, cause) {
|
|
|
34
34
|
function throwParameterResolutionError(ctor, methodKey, dependency, cause) {
|
|
35
35
|
const location = getLocation(ctor, methodKey);
|
|
36
36
|
const tokenName = getFullTokenName([
|
|
37
|
-
dependency.tokenRef
|
|
37
|
+
dependency.tokenRef?.getRefToken(),
|
|
38
38
|
dependency.name
|
|
39
39
|
]);
|
|
40
40
|
const msg = tag(`failed to resolve dependency for ${location}(parameter #${dependency.index}: ${tokenName})`);
|
|
@@ -56,7 +56,7 @@ function getTokenName(token) {
|
|
|
56
56
|
return token.name || "<unnamed>";
|
|
57
57
|
}
|
|
58
58
|
function getFullTokenName([token, name]) {
|
|
59
|
-
const tokenName = token.name || "<unnamed>";
|
|
59
|
+
const tokenName = token ? token.name || "<unnamed>" : "<undefined token>";
|
|
60
60
|
return name ? `${tokenName}["${name}"]` : tokenName;
|
|
61
61
|
}
|
|
62
62
|
function getCause(error) {
|
|
@@ -144,6 +144,16 @@ function ensureInjectionContext(name) {
|
|
|
144
144
|
check(context, `${name} can only be invoked within an injection context`);
|
|
145
145
|
return context;
|
|
146
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Asserts that the current stack frame is within an injection context,
|
|
149
|
+
* meaning it has access to injection functions (`inject`, `optional`, etc.).
|
|
150
|
+
*
|
|
151
|
+
* @param fn The function performing the assertion, or a string name used in the error message.
|
|
152
|
+
* @throws {Error} If the current stack frame is not within an injection context.
|
|
153
|
+
*/ function assertInjectionContext(fn) {
|
|
154
|
+
const name = typeof fn === "function" ? `${fn.name || "<unnamed>"}()` : fn;
|
|
155
|
+
ensureInjectionContext(name);
|
|
156
|
+
}
|
|
147
157
|
function createInjectionContext() {
|
|
148
158
|
let current = null;
|
|
149
159
|
function provide(next) {
|
|
@@ -216,15 +226,15 @@ function tokenRef(token) {
|
|
|
216
226
|
}
|
|
217
227
|
// @internal
|
|
218
228
|
function isClassRef(value) {
|
|
219
|
-
return value && typeof value === "object" && typeof value.getRefClass === "function";
|
|
229
|
+
return value != null && typeof value === "object" && typeof value.getRefClass === "function";
|
|
220
230
|
}
|
|
221
231
|
// @internal
|
|
222
232
|
function isTokensRef(value) {
|
|
223
|
-
return value && typeof value === "object" && typeof value.getRefTokens === "function";
|
|
233
|
+
return value != null && typeof value === "object" && typeof value.getRefTokens === "function";
|
|
224
234
|
}
|
|
225
235
|
// @internal
|
|
226
236
|
function isTokenRef(value) {
|
|
227
|
-
return value && typeof value === "object" && typeof value.getRefToken === "function";
|
|
237
|
+
return value != null && typeof value === "object" && typeof value.getRefToken === "function";
|
|
228
238
|
}
|
|
229
239
|
|
|
230
240
|
// @internal
|
|
@@ -533,21 +543,22 @@ const builders = new WeakSet();
|
|
|
533
543
|
// @internal
|
|
534
544
|
// @internal
|
|
535
545
|
function isDisposable(value) {
|
|
536
|
-
return value && typeof value === "object" && typeof value.dispose === "function";
|
|
546
|
+
return value != null && typeof value === "object" && typeof value.dispose === "function";
|
|
537
547
|
}
|
|
538
548
|
|
|
539
549
|
/**
|
|
540
550
|
* The default implementation of a di-wise-neo {@link Container}.
|
|
541
551
|
*/ class ContainerImpl {
|
|
542
|
-
constructor(parent, options){
|
|
552
|
+
constructor(parent, hooks, options){
|
|
543
553
|
this.myChildren = new Set();
|
|
544
554
|
this.myDisposed = false;
|
|
545
555
|
this.myParent = parent;
|
|
556
|
+
this.myHooks = hooks ?? new Set();
|
|
546
557
|
this.myOptions = {
|
|
547
|
-
|
|
548
|
-
|
|
558
|
+
defaultScope: options?.defaultScope ?? Scope.Transient,
|
|
559
|
+
autoRegister: options?.autoRegister ?? false
|
|
549
560
|
};
|
|
550
|
-
this.myTokenRegistry = new TokenRegistry(
|
|
561
|
+
this.myTokenRegistry = new TokenRegistry(parent?.myTokenRegistry);
|
|
551
562
|
}
|
|
552
563
|
get registry() {
|
|
553
564
|
return this.myTokenRegistry;
|
|
@@ -565,9 +576,10 @@ function isDisposable(value) {
|
|
|
565
576
|
}
|
|
566
577
|
createChild(options) {
|
|
567
578
|
this.checkDisposed();
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
defaultScope: options?.defaultScope ?? this.myOptions.defaultScope
|
|
579
|
+
const hooks = options?.copyHooks === false ? undefined : new Set(this.myHooks);
|
|
580
|
+
const container = new ContainerImpl(this, hooks, {
|
|
581
|
+
defaultScope: options?.defaultScope ?? this.myOptions.defaultScope,
|
|
582
|
+
autoRegister: options?.autoRegister ?? this.myOptions.autoRegister
|
|
571
583
|
});
|
|
572
584
|
this.myChildren.add(container);
|
|
573
585
|
return container;
|
|
@@ -651,6 +663,12 @@ function isDisposable(value) {
|
|
|
651
663
|
this.checkDisposed();
|
|
652
664
|
return this.resolveAllToken(token, true);
|
|
653
665
|
}
|
|
666
|
+
addHook(hook) {
|
|
667
|
+
this.myHooks.add(hook);
|
|
668
|
+
}
|
|
669
|
+
removeHook(hook) {
|
|
670
|
+
this.myHooks.delete(hook);
|
|
671
|
+
}
|
|
654
672
|
dispose() {
|
|
655
673
|
if (this.myDisposed) {
|
|
656
674
|
return;
|
|
@@ -669,12 +687,14 @@ function isDisposable(value) {
|
|
|
669
687
|
for (const registration of registrations){
|
|
670
688
|
const value = registration.value?.current;
|
|
671
689
|
if (isDisposable(value) && !disposedRefs.has(value)) {
|
|
690
|
+
this.notifyDisposeHooks(value);
|
|
672
691
|
disposedRefs.add(value);
|
|
673
692
|
value.dispose();
|
|
674
693
|
}
|
|
675
694
|
}
|
|
676
695
|
// Allow values to be GCed
|
|
677
696
|
disposedRefs.clear();
|
|
697
|
+
this.myHooks.clear();
|
|
678
698
|
}
|
|
679
699
|
registerClass(Class) {
|
|
680
700
|
const metadata = getMetadata(Class);
|
|
@@ -703,7 +723,9 @@ function isDisposable(value) {
|
|
|
703
723
|
}
|
|
704
724
|
});
|
|
705
725
|
}
|
|
706
|
-
// Eager-instantiate only if the class is container-scoped
|
|
726
|
+
// Eager-instantiate only if the class is container-scoped.
|
|
727
|
+
// Note that we are comparing the scope using the registration configured just above,
|
|
728
|
+
// which takes into account both the metadata and the container option as a fallback.
|
|
707
729
|
if (metadata.eagerInstantiate && registration.options?.scope === Scope.Container) {
|
|
708
730
|
this.resolveProviderValue(Class, registration);
|
|
709
731
|
}
|
|
@@ -725,7 +747,9 @@ function isDisposable(value) {
|
|
|
725
747
|
dependencies: metadata.dependencies
|
|
726
748
|
};
|
|
727
749
|
this.myTokenRegistry.set(token, registration);
|
|
728
|
-
// Eager-instantiate only if the provided class is container-scoped
|
|
750
|
+
// Eager-instantiate only if the provided class is container-scoped.
|
|
751
|
+
// Note that we are comparing the scope using the registration configured just above,
|
|
752
|
+
// which takes into account both the metadata and the container option as a fallback.
|
|
729
753
|
if (metadata.eagerInstantiate && registration.options?.scope === Scope.Container) {
|
|
730
754
|
this.resolveProviderValue(token, registration);
|
|
731
755
|
}
|
|
@@ -839,7 +863,7 @@ function isDisposable(value) {
|
|
|
839
863
|
}
|
|
840
864
|
if (isFactoryProvider(provider)) {
|
|
841
865
|
const factory = provider.useFactory;
|
|
842
|
-
return this.resolveScopedValue(token, registration, factory);
|
|
866
|
+
return this.resolveScopedValue(token, registration, ()=>factory());
|
|
843
867
|
}
|
|
844
868
|
if (isValueProvider(provider)) {
|
|
845
869
|
return provider.useValue;
|
|
@@ -888,6 +912,7 @@ function isDisposable(value) {
|
|
|
888
912
|
registration.value = {
|
|
889
913
|
current: value
|
|
890
914
|
};
|
|
915
|
+
this.notifyProvideHooks(value);
|
|
891
916
|
return value;
|
|
892
917
|
}
|
|
893
918
|
case Scope.Resolution:
|
|
@@ -901,12 +926,15 @@ function isDisposable(value) {
|
|
|
901
926
|
resolution.values.set(provider, {
|
|
902
927
|
current: value
|
|
903
928
|
});
|
|
929
|
+
this.notifyProvideHooks(value);
|
|
904
930
|
return value;
|
|
905
931
|
}
|
|
906
932
|
case Scope.Transient:
|
|
907
933
|
{
|
|
908
934
|
const args = this.resolveCtorDependencies(registration);
|
|
909
|
-
|
|
935
|
+
const value = this.injectMethodDependencies(registration, factory(args));
|
|
936
|
+
this.notifyProvideHooks(value);
|
|
937
|
+
return value;
|
|
910
938
|
}
|
|
911
939
|
}
|
|
912
940
|
} finally{
|
|
@@ -955,6 +983,7 @@ function isDisposable(value) {
|
|
|
955
983
|
}
|
|
956
984
|
return instance;
|
|
957
985
|
}
|
|
986
|
+
// Call context: decorator-based injection
|
|
958
987
|
resolveArgs(deps, ctor, instance, methodKey) {
|
|
959
988
|
const sortedDeps = deps.sort((a, b)=>a.index - b.index);
|
|
960
989
|
const args = [];
|
|
@@ -967,8 +996,10 @@ function isDisposable(value) {
|
|
|
967
996
|
}
|
|
968
997
|
return args;
|
|
969
998
|
}
|
|
999
|
+
// Call context: decorator-based injection
|
|
970
1000
|
resolveDependency(dependency, instance) {
|
|
971
|
-
const token = dependency.tokenRef
|
|
1001
|
+
const token = dependency.tokenRef?.getRefToken();
|
|
1002
|
+
check(token, `token passed to @${dependency.appliedBy} was undefined (possible circular imports)`);
|
|
972
1003
|
const name = dependency.name;
|
|
973
1004
|
switch(dependency.appliedBy){
|
|
974
1005
|
case "Inject":
|
|
@@ -981,6 +1012,16 @@ function isDisposable(value) {
|
|
|
981
1012
|
return instance ? optionalAll(token) : this.tryResolveAll(token);
|
|
982
1013
|
}
|
|
983
1014
|
}
|
|
1015
|
+
notifyProvideHooks(value) {
|
|
1016
|
+
for (const hook of this.myHooks){
|
|
1017
|
+
hook.onProvide(value);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
notifyDisposeHooks(value) {
|
|
1021
|
+
for (const hook of this.myHooks){
|
|
1022
|
+
hook.onDispose(value);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
984
1025
|
checkDisposed() {
|
|
985
1026
|
check(!this.myDisposed, "container is disposed");
|
|
986
1027
|
}
|
|
@@ -989,7 +1030,7 @@ function isDisposable(value) {
|
|
|
989
1030
|
/**
|
|
990
1031
|
* Creates a new container.
|
|
991
1032
|
*/ function createContainer(options) {
|
|
992
|
-
return new ContainerImpl(undefined, options);
|
|
1033
|
+
return new ContainerImpl(undefined, undefined, options);
|
|
993
1034
|
}
|
|
994
1035
|
|
|
995
1036
|
/**
|
|
@@ -1335,6 +1376,7 @@ exports.OptionalAll = OptionalAll;
|
|
|
1335
1376
|
exports.Scope = Scope;
|
|
1336
1377
|
exports.Scoped = Scoped;
|
|
1337
1378
|
exports.applyMiddleware = applyMiddleware;
|
|
1379
|
+
exports.assertInjectionContext = assertInjectionContext;
|
|
1338
1380
|
exports.build = build;
|
|
1339
1381
|
exports.classRef = classRef;
|
|
1340
1382
|
exports.createContainer = createContainer;
|