ts-ioc-container 57.0.0 → 57.1.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 +149 -0
- package/cjm/container/AutoResolveModule.js +13 -0
- package/cjm/container/Container.js +20 -1
- package/cjm/container/EmptyContainer.js +6 -0
- package/cjm/index.js +5 -2
- package/cjm/provider/Provider.js +10 -0
- package/cjm/registration/IRegistration.js +3 -1
- package/esm/container/AutoResolveModule.js +9 -0
- package/esm/container/Container.js +20 -1
- package/esm/container/EmptyContainer.js +6 -0
- package/esm/index.js +2 -1
- package/esm/provider/Provider.js +10 -0
- package/esm/registration/IRegistration.js +1 -0
- package/package.json +1 -1
- package/typings/container/AutoResolveModule.d.ts +6 -0
- package/typings/container/Container.d.ts +4 -1
- package/typings/container/EmptyContainer.d.ts +3 -1
- package/typings/container/IContainer.d.ts +5 -0
- package/typings/index.d.ts +3 -2
- package/typings/provider/IProvider.d.ts +2 -0
- package/typings/provider/Provider.d.ts +3 -0
- package/typings/registration/IRegistration.d.ts +1 -0
package/README.md
CHANGED
|
@@ -43,6 +43,7 @@ provider pipelines, aliases, and custom injector strategies.
|
|
|
43
43
|
- [Proxy](#proxy)
|
|
44
44
|
- [Provider](#provider) `provider`
|
|
45
45
|
- [Singleton](#singleton) `singleton`
|
|
46
|
+
- [Auto resolve](#auto-resolve) `autoResolve`
|
|
46
47
|
- [Arguments](#arguments) `appendArgs` `appendArgsFn`
|
|
47
48
|
- [Visibility](#visibility) `visible`
|
|
48
49
|
- [Alias](#alias) `asAlias`
|
|
@@ -153,6 +154,7 @@ describe('Quickstart', function () {
|
|
|
153
154
|
- Register value: `R.fromValue(config).bindTo('Config')`
|
|
154
155
|
- Register factory: `R.fromFn((c) => createX(c)).bindTo('X')`
|
|
155
156
|
- Singleton: `@register(singleton())`
|
|
157
|
+
- Eager service: `@register(autoResolve())` + `container.useModule(new AutoResolveModule())`
|
|
156
158
|
- Scoped registration: `@register(scope((s) => s.hasTag('request')))`
|
|
157
159
|
- Resolve by alias: `container.resolveByAlias('Alias')`
|
|
158
160
|
- Current scope token: `select.scope.current`
|
|
@@ -1616,6 +1618,153 @@ describe('Singleton', function () {
|
|
|
1616
1618
|
|
|
1617
1619
|
```
|
|
1618
1620
|
|
|
1621
|
+
### Auto resolve
|
|
1622
|
+
|
|
1623
|
+
Some services are never injected anywhere - they subscribe to a queue, start a
|
|
1624
|
+
timer, or warm a cache as soon as their scope exists. Lazy resolution would
|
|
1625
|
+
never create them, because nothing asks for them.
|
|
1626
|
+
|
|
1627
|
+
- `@register(autoResolve())` marks a provider as eager
|
|
1628
|
+
- `container.useModule(new AutoResolveModule())` enables eager resolution for
|
|
1629
|
+
every scope created afterwards
|
|
1630
|
+
- `container.autoResolve()` resolves the eager providers of one container on demand
|
|
1631
|
+
|
|
1632
|
+
Both accept an optional `AutoResolveOptions` (`{ args?: unknown[] }`) whose `args`
|
|
1633
|
+
are forwarded to every eagerly resolved provider, exactly as `resolve` forwards
|
|
1634
|
+
them - so they reach `@inject(arg(0))` parameters, `scopeAccess` rules and the
|
|
1635
|
+
`singleton()` cache key. `new AutoResolveModule({ args })` applies the same args
|
|
1636
|
+
to every created scope; call `scope.autoResolve({ args })` yourself when the
|
|
1637
|
+
value differs per scope.
|
|
1638
|
+
|
|
1639
|
+
> [!IMPORTANT]
|
|
1640
|
+
> `autoResolve()` on its own does nothing - the container has to opt in with
|
|
1641
|
+
> `AutoResolveModule`. The container the module is applied to is not a created
|
|
1642
|
+
> scope, so call `container.autoResolve()` explicitly to eagerly resolve its own
|
|
1643
|
+
> providers.
|
|
1644
|
+
|
|
1645
|
+
Eager resolution goes through the provider unchanged, so `singleton()` still
|
|
1646
|
+
returns the eagerly created instance later, and providers which are not
|
|
1647
|
+
registered in the created scope (`scope(...)`) or deny access to it
|
|
1648
|
+
(`scopeAccess(...)`) are skipped.
|
|
1649
|
+
|
|
1650
|
+
```typescript
|
|
1651
|
+
import 'reflect-metadata';
|
|
1652
|
+
import {
|
|
1653
|
+
arg,
|
|
1654
|
+
autoResolve,
|
|
1655
|
+
AutoResolveModule,
|
|
1656
|
+
bindTo,
|
|
1657
|
+
Container,
|
|
1658
|
+
type IContainer,
|
|
1659
|
+
inject,
|
|
1660
|
+
register,
|
|
1661
|
+
Registration as R,
|
|
1662
|
+
scope,
|
|
1663
|
+
singleton,
|
|
1664
|
+
} from 'ts-ioc-container';
|
|
1665
|
+
|
|
1666
|
+
/**
|
|
1667
|
+
* User Management Domain - Eager Services
|
|
1668
|
+
*
|
|
1669
|
+
* Some services are never injected anywhere: they subscribe to a queue, start a
|
|
1670
|
+
* timer, or warm a cache as soon as their scope exists. Nothing resolves them,
|
|
1671
|
+
* so lazy resolution would never create them at all.
|
|
1672
|
+
*
|
|
1673
|
+
* `autoResolve()` marks such a provider as eager, and `AutoResolveModule`
|
|
1674
|
+
* resolves every eager provider of a scope right after the scope is created.
|
|
1675
|
+
*/
|
|
1676
|
+
|
|
1677
|
+
const auditTrail: string[] = [];
|
|
1678
|
+
const openedLogs: string[] = [];
|
|
1679
|
+
|
|
1680
|
+
// Started for every request, even though no other class injects it
|
|
1681
|
+
@register(bindTo('IRequestAuditor'), scope((s) => s.hasTag('request')), autoResolve(), singleton())
|
|
1682
|
+
class RequestAuditor {
|
|
1683
|
+
constructor() {
|
|
1684
|
+
auditTrail.push('request started');
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
// Eager too, but parameterized - the args come from whoever triggers eager resolution
|
|
1689
|
+
@register(bindTo('IRequestLog'), scope((s) => s.hasTag('request')), autoResolve())
|
|
1690
|
+
class RequestLog {
|
|
1691
|
+
constructor(@inject(arg(0)) readonly requestId: string = 'anonymous') {
|
|
1692
|
+
openedLogs.push(requestId);
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
// Resolved on demand, the usual way
|
|
1697
|
+
@register(bindTo('IUserRepository'), singleton())
|
|
1698
|
+
class UserRepository {
|
|
1699
|
+
findById(id: string): string {
|
|
1700
|
+
return `user_${id}`;
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
describe('Auto resolve', function () {
|
|
1705
|
+
function createAppContainer(options: { args?: unknown[] } = {}): IContainer {
|
|
1706
|
+
return new Container({ tags: ['application'] })
|
|
1707
|
+
.useModule(new AutoResolveModule(options))
|
|
1708
|
+
.addRegistration(R.fromClass(RequestAuditor))
|
|
1709
|
+
.addRegistration(R.fromClass(RequestLog))
|
|
1710
|
+
.addRegistration(R.fromClass(UserRepository));
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
beforeEach(() => {
|
|
1714
|
+
auditTrail.length = 0;
|
|
1715
|
+
openedLogs.length = 0;
|
|
1716
|
+
});
|
|
1717
|
+
|
|
1718
|
+
it('should create eager services as soon as a scope is created', function () {
|
|
1719
|
+
const app = createAppContainer();
|
|
1720
|
+
|
|
1721
|
+
// Nothing has been created yet - the application container is not a created scope
|
|
1722
|
+
expect(auditTrail).toEqual([]);
|
|
1723
|
+
|
|
1724
|
+
app.createScope({ tags: ['request'] });
|
|
1725
|
+
app.createScope({ tags: ['request'] });
|
|
1726
|
+
|
|
1727
|
+
// One auditor per request scope, without anybody resolving it
|
|
1728
|
+
expect(auditTrail).toEqual(['request started', 'request started']);
|
|
1729
|
+
});
|
|
1730
|
+
|
|
1731
|
+
it('should reuse the eagerly created instance', function () {
|
|
1732
|
+
const requestScope = createAppContainer().createScope({ tags: ['request'] });
|
|
1733
|
+
|
|
1734
|
+
const auditor = requestScope.resolve<RequestAuditor>('IRequestAuditor');
|
|
1735
|
+
|
|
1736
|
+
expect(auditTrail).toEqual(['request started']);
|
|
1737
|
+
expect(requestScope.resolve<RequestAuditor>('IRequestAuditor')).toBe(auditor);
|
|
1738
|
+
});
|
|
1739
|
+
|
|
1740
|
+
it('should treat resolve options as optional', function () {
|
|
1741
|
+
createAppContainer().createScope({ tags: ['request'] });
|
|
1742
|
+
|
|
1743
|
+
expect(openedLogs).toEqual(['anonymous']);
|
|
1744
|
+
});
|
|
1745
|
+
|
|
1746
|
+
it('should forward args to every eagerly resolved provider', function () {
|
|
1747
|
+
// The same args reach every scope the module creates...
|
|
1748
|
+
createAppContainer({ args: ['req-42'] }).createScope({ tags: ['request'] });
|
|
1749
|
+
|
|
1750
|
+
expect(openedLogs).toEqual(['req-42']);
|
|
1751
|
+
|
|
1752
|
+
// ...or pass a per-scope value by calling autoResolve yourself
|
|
1753
|
+
const requestScope = createAppContainer().createScope({ tags: ['request'] });
|
|
1754
|
+
requestScope.autoResolve({ args: ['req-43'] });
|
|
1755
|
+
|
|
1756
|
+
expect(openedLogs).toEqual(['req-42', 'anonymous', 'req-43']);
|
|
1757
|
+
});
|
|
1758
|
+
|
|
1759
|
+
it('should leave other providers lazy', function () {
|
|
1760
|
+
const requestScope = createAppContainer().createScope({ tags: ['request'] });
|
|
1761
|
+
|
|
1762
|
+
expect(requestScope.resolve<UserRepository>('IUserRepository').findById('1')).toBe('user_1');
|
|
1763
|
+
});
|
|
1764
|
+
});
|
|
1765
|
+
|
|
1766
|
+
```
|
|
1767
|
+
|
|
1619
1768
|
### Arguments
|
|
1620
1769
|
|
|
1621
1770
|
Sometimes you want to bind some arguments to provider.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AutoResolveModule = void 0;
|
|
4
|
+
class AutoResolveModule {
|
|
5
|
+
options;
|
|
6
|
+
constructor(options = {}) {
|
|
7
|
+
this.options = options;
|
|
8
|
+
}
|
|
9
|
+
applyTo(container) {
|
|
10
|
+
container.addOnScopeCreatedHook((scope) => scope.autoResolve(this.options));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.AutoResolveModule = AutoResolveModule;
|
|
@@ -20,6 +20,7 @@ class Container {
|
|
|
20
20
|
injector;
|
|
21
21
|
onConstructHookList = [];
|
|
22
22
|
onDisposeHookList = [];
|
|
23
|
+
onScopeCreatedHookList = [];
|
|
23
24
|
constructor(options = {}) {
|
|
24
25
|
this.injector = options.injector ?? new MetadataInjector_1.MetadataInjector();
|
|
25
26
|
this.parent = options.parent ?? new EmptyContainer_1.EmptyContainer();
|
|
@@ -73,13 +74,26 @@ class Container {
|
|
|
73
74
|
this.validateContainer();
|
|
74
75
|
const scope = new Container({ injector: this.injector, parent: this, tags })
|
|
75
76
|
.addOnConstructHook(...this.onConstructHookList)
|
|
76
|
-
.addOnDisposeHook(...this.onDisposeHookList)
|
|
77
|
+
.addOnDisposeHook(...this.onDisposeHookList)
|
|
78
|
+
.addOnScopeCreatedHook(...this.onScopeCreatedHookList);
|
|
77
79
|
for (const registration of this.getRegistrations()) {
|
|
78
80
|
registration.applyTo(scope);
|
|
79
81
|
}
|
|
80
82
|
this.scopes.push(scope);
|
|
83
|
+
for (const onScopeCreated of this.onScopeCreatedHookList) {
|
|
84
|
+
onScopeCreated(scope);
|
|
85
|
+
}
|
|
81
86
|
return scope;
|
|
82
87
|
}
|
|
88
|
+
autoResolve({ args = [] } = {}) {
|
|
89
|
+
this.validateContainer();
|
|
90
|
+
for (const provider of this.providers.values()) {
|
|
91
|
+
if (provider.isAutoResolvable() && provider.hasAccess({ invocationScope: this, providerScope: this, args })) {
|
|
92
|
+
provider.resolve(this, { args });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
83
97
|
dispose() {
|
|
84
98
|
this.validateContainer();
|
|
85
99
|
this.isDisposed = true;
|
|
@@ -97,6 +111,7 @@ class Container {
|
|
|
97
111
|
this.instances = [];
|
|
98
112
|
this.registrations = [];
|
|
99
113
|
this.onConstructHookList.length = 0;
|
|
114
|
+
this.onScopeCreatedHookList.length = 0;
|
|
100
115
|
}
|
|
101
116
|
addRegistration(registration) {
|
|
102
117
|
this.registrations.push(registration);
|
|
@@ -117,6 +132,10 @@ class Container {
|
|
|
117
132
|
this.onDisposeHookList.push(...hooks);
|
|
118
133
|
return this;
|
|
119
134
|
}
|
|
135
|
+
addOnScopeCreatedHook(...hooks) {
|
|
136
|
+
this.onScopeCreatedHookList.push(...hooks);
|
|
137
|
+
return this;
|
|
138
|
+
}
|
|
120
139
|
addInstance(instance) {
|
|
121
140
|
this.instances.push(instance);
|
|
122
141
|
for (const onConstruct of this.onConstructHookList) {
|
|
@@ -27,6 +27,9 @@ class EmptyContainer {
|
|
|
27
27
|
createScope() {
|
|
28
28
|
throw new MethodNotImplementedError_1.MethodNotImplementedError();
|
|
29
29
|
}
|
|
30
|
+
autoResolve(options) {
|
|
31
|
+
throw new MethodNotImplementedError_1.MethodNotImplementedError();
|
|
32
|
+
}
|
|
30
33
|
dispose() {
|
|
31
34
|
throw new MethodNotImplementedError_1.MethodNotImplementedError();
|
|
32
35
|
}
|
|
@@ -67,5 +70,8 @@ class EmptyContainer {
|
|
|
67
70
|
addOnConstructHook(...hooks) {
|
|
68
71
|
throw new MethodNotImplementedError_1.MethodNotImplementedError();
|
|
69
72
|
}
|
|
73
|
+
addOnScopeCreatedHook(...hooks) {
|
|
74
|
+
throw new MethodNotImplementedError_1.MethodNotImplementedError();
|
|
75
|
+
}
|
|
70
76
|
}
|
|
71
77
|
exports.EmptyContainer = EmptyContainer;
|
package/cjm/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.resolveConstructor = exports.Is = exports.pipe = exports.select = exports.once = exports.shallowCache = exports.debounce = exports.throttle = exports.handleAsyncError = exports.handleError = exports.getMethodTags = exports.addMethodTag = exports.getMethodLabels = exports.addMethodLabel = exports.getMethodMeta = exports.addMethodMeta = exports.getParamTags = exports.addParamTag = exports.getParamLabels = exports.addParamLabel = exports.getParamMeta = exports.addParamMeta = exports.getClassTags = exports.addClassTag = exports.getClassLabels = exports.addClassLabel = exports.getClassMeta = exports.addClassMeta = exports.GroupInstanceToken = exports.ConstantToken = exports.FunctionToken = exports.SingleToken = exports.ClassToken = exports.toSingleAlias = exports.SingleAliasToken = exports.toGroupAlias = exports.GroupAliasToken = exports.argToToken = exports.toMappedToken = exports.toToken = exports.InjectionToken = exports.HooksRunner = exports.AddOnDisposeHookModule = exports.onContainerDisposed = exports.onContainerDisposedHooksRunner = void 0;
|
|
3
|
+
exports.onConstructAsyncHooksRunner = exports.AddOnConstructHookModule = exports.onConstruct = exports.onConstructHooksRunner = exports.injectProp = exports.createHookContext = exports.createHookContextFactory = exports.HookContext = exports.prepend = exports.append = exports.prependHooks = exports.appendHooks = exports.hasHooks = exports.hook = exports.getHooks = exports.UnsupportedTokenTypeError = exports.CannonSingletonApplyTwiceError = exports.UnexpectedHookResultError = exports.ProviderDisposedError = exports.ContainerDisposedError = exports.MethodNotImplementedError = exports.DependencyMissingKeyError = exports.ContainerNotFoundError = exports.DependencyNotFoundError = exports.ContainerError = exports.Registration = exports.appendArgsFn = exports.appendArgs = exports.decorate = exports.singleton = exports.autoResolve = exports.lazy = exports.scopeAccess = exports.scope = exports.bindTo = exports.register = exports.Provider = exports.ProxyInjector = exports.SimpleInjector = exports.resolveArgs = exports.argsFn = exports.args = exports.arg = exports.inject = exports.MetadataInjector = exports.Injector = exports.EmptyContainer = exports.AutoResolveModule = exports.Container = exports.isDependencyKey = void 0;
|
|
4
|
+
exports.resolveConstructor = exports.Is = exports.pipe = exports.select = exports.once = exports.shallowCache = exports.debounce = exports.throttle = exports.handleAsyncError = exports.handleError = exports.getMethodTags = exports.addMethodTag = exports.getMethodLabels = exports.addMethodLabel = exports.getMethodMeta = exports.addMethodMeta = exports.getParamTags = exports.addParamTag = exports.getParamLabels = exports.addParamLabel = exports.getParamMeta = exports.addParamMeta = exports.getClassTags = exports.addClassTag = exports.getClassLabels = exports.addClassLabel = exports.getClassMeta = exports.addClassMeta = exports.GroupInstanceToken = exports.ConstantToken = exports.FunctionToken = exports.SingleToken = exports.ClassToken = exports.toSingleAlias = exports.SingleAliasToken = exports.toGroupAlias = exports.GroupAliasToken = exports.argToToken = exports.toMappedToken = exports.toToken = exports.InjectionToken = exports.HooksRunner = exports.AddOnDisposeHookModule = exports.onContainerDisposed = exports.onContainerDisposedHooksRunner = exports.AddOnConstructAsyncHookModule = exports.onConstructAsync = void 0;
|
|
5
5
|
var IContainer_1 = require("./container/IContainer");
|
|
6
6
|
Object.defineProperty(exports, "isDependencyKey", { enumerable: true, get: function () { return IContainer_1.isDependencyKey; } });
|
|
7
7
|
var Container_1 = require("./container/Container");
|
|
8
8
|
Object.defineProperty(exports, "Container", { enumerable: true, get: function () { return Container_1.Container; } });
|
|
9
|
+
var AutoResolveModule_1 = require("./container/AutoResolveModule");
|
|
10
|
+
Object.defineProperty(exports, "AutoResolveModule", { enumerable: true, get: function () { return AutoResolveModule_1.AutoResolveModule; } });
|
|
9
11
|
var EmptyContainer_1 = require("./container/EmptyContainer");
|
|
10
12
|
Object.defineProperty(exports, "EmptyContainer", { enumerable: true, get: function () { return EmptyContainer_1.EmptyContainer; } });
|
|
11
13
|
var IInjector_1 = require("./injector/IInjector");
|
|
@@ -29,6 +31,7 @@ Object.defineProperty(exports, "bindTo", { enumerable: true, get: function () {
|
|
|
29
31
|
Object.defineProperty(exports, "scope", { enumerable: true, get: function () { return IRegistration_1.scope; } });
|
|
30
32
|
Object.defineProperty(exports, "scopeAccess", { enumerable: true, get: function () { return IRegistration_1.scopeAccess; } });
|
|
31
33
|
Object.defineProperty(exports, "lazy", { enumerable: true, get: function () { return IRegistration_1.lazy; } });
|
|
34
|
+
Object.defineProperty(exports, "autoResolve", { enumerable: true, get: function () { return IRegistration_1.autoResolve; } });
|
|
32
35
|
Object.defineProperty(exports, "singleton", { enumerable: true, get: function () { return IRegistration_1.singleton; } });
|
|
33
36
|
Object.defineProperty(exports, "decorate", { enumerable: true, get: function () { return IRegistration_1.decorate; } });
|
|
34
37
|
Object.defineProperty(exports, "appendArgs", { enumerable: true, get: function () { return IRegistration_1.appendArgs; } });
|
package/cjm/provider/Provider.js
CHANGED
|
@@ -18,6 +18,7 @@ class Provider {
|
|
|
18
18
|
accessRules = [];
|
|
19
19
|
mappers = [];
|
|
20
20
|
isLazy = false;
|
|
21
|
+
isAutoResolve = false;
|
|
21
22
|
cache = new Map();
|
|
22
23
|
getKey;
|
|
23
24
|
isDisposed = false;
|
|
@@ -54,6 +55,14 @@ class Provider {
|
|
|
54
55
|
this.isLazy = true;
|
|
55
56
|
return this;
|
|
56
57
|
}
|
|
58
|
+
autoResolve() {
|
|
59
|
+
this.isAutoResolve = true;
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
isAutoResolvable() {
|
|
63
|
+
ProviderDisposedError_1.ProviderDisposedError.assert(!this.isDisposed, 'Provider is already disposed');
|
|
64
|
+
return this.isAutoResolve;
|
|
65
|
+
}
|
|
57
66
|
addArgsFn(...fns) {
|
|
58
67
|
this.argsFnList.push(...fns);
|
|
59
68
|
return this;
|
|
@@ -70,6 +79,7 @@ class Provider {
|
|
|
70
79
|
dispose() {
|
|
71
80
|
ProviderDisposedError_1.ProviderDisposedError.assert(!this.isDisposed, 'Provider is already disposed');
|
|
72
81
|
this.isDisposed = true;
|
|
82
|
+
this.isAutoResolve = false;
|
|
73
83
|
this.getKey = undefined;
|
|
74
84
|
this.cache.clear();
|
|
75
85
|
this.accessRules.splice(0, this.accessRules.length);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.singleton = exports.decorate = exports.lazy = exports.scopeAccess = exports.appendArgsFn = exports.appendArgs = exports.scope = exports.bindTo = exports.register = exports.getTransformers = exports.registerPipe = exports.isProviderPipe = void 0;
|
|
3
|
+
exports.singleton = exports.decorate = exports.autoResolve = exports.lazy = exports.scopeAccess = exports.appendArgsFn = exports.appendArgs = exports.scope = exports.bindTo = exports.register = exports.getTransformers = exports.registerPipe = exports.isProviderPipe = void 0;
|
|
4
4
|
const IContainer_1 = require("../container/IContainer");
|
|
5
5
|
const SingleToken_1 = require("../token/SingleToken");
|
|
6
6
|
const BindToken_1 = require("../token/BindToken");
|
|
@@ -44,6 +44,8 @@ const scopeAccess = (rule) => (0, exports.registerPipe)((p) => p.addAccessRule(r
|
|
|
44
44
|
exports.scopeAccess = scopeAccess;
|
|
45
45
|
const lazy = () => (0, exports.registerPipe)((p) => p.lazy());
|
|
46
46
|
exports.lazy = lazy;
|
|
47
|
+
const autoResolve = () => (0, exports.registerPipe)((p) => p.autoResolve());
|
|
48
|
+
exports.autoResolve = autoResolve;
|
|
47
49
|
const decorate = (...fns) => (0, exports.registerPipe)((p) => p.map(...fns));
|
|
48
50
|
exports.decorate = decorate;
|
|
49
51
|
const singleton = (getCacheKey) => (0, exports.registerPipe)((p) => p.singleton(getCacheKey));
|
|
@@ -17,6 +17,7 @@ export class Container {
|
|
|
17
17
|
injector;
|
|
18
18
|
onConstructHookList = [];
|
|
19
19
|
onDisposeHookList = [];
|
|
20
|
+
onScopeCreatedHookList = [];
|
|
20
21
|
constructor(options = {}) {
|
|
21
22
|
this.injector = options.injector ?? new MetadataInjector();
|
|
22
23
|
this.parent = options.parent ?? new EmptyContainer();
|
|
@@ -70,13 +71,26 @@ export class Container {
|
|
|
70
71
|
this.validateContainer();
|
|
71
72
|
const scope = new Container({ injector: this.injector, parent: this, tags })
|
|
72
73
|
.addOnConstructHook(...this.onConstructHookList)
|
|
73
|
-
.addOnDisposeHook(...this.onDisposeHookList)
|
|
74
|
+
.addOnDisposeHook(...this.onDisposeHookList)
|
|
75
|
+
.addOnScopeCreatedHook(...this.onScopeCreatedHookList);
|
|
74
76
|
for (const registration of this.getRegistrations()) {
|
|
75
77
|
registration.applyTo(scope);
|
|
76
78
|
}
|
|
77
79
|
this.scopes.push(scope);
|
|
80
|
+
for (const onScopeCreated of this.onScopeCreatedHookList) {
|
|
81
|
+
onScopeCreated(scope);
|
|
82
|
+
}
|
|
78
83
|
return scope;
|
|
79
84
|
}
|
|
85
|
+
autoResolve({ args = [] } = {}) {
|
|
86
|
+
this.validateContainer();
|
|
87
|
+
for (const provider of this.providers.values()) {
|
|
88
|
+
if (provider.isAutoResolvable() && provider.hasAccess({ invocationScope: this, providerScope: this, args })) {
|
|
89
|
+
provider.resolve(this, { args });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
80
94
|
dispose() {
|
|
81
95
|
this.validateContainer();
|
|
82
96
|
this.isDisposed = true;
|
|
@@ -94,6 +108,7 @@ export class Container {
|
|
|
94
108
|
this.instances = [];
|
|
95
109
|
this.registrations = [];
|
|
96
110
|
this.onConstructHookList.length = 0;
|
|
111
|
+
this.onScopeCreatedHookList.length = 0;
|
|
97
112
|
}
|
|
98
113
|
addRegistration(registration) {
|
|
99
114
|
this.registrations.push(registration);
|
|
@@ -114,6 +129,10 @@ export class Container {
|
|
|
114
129
|
this.onDisposeHookList.push(...hooks);
|
|
115
130
|
return this;
|
|
116
131
|
}
|
|
132
|
+
addOnScopeCreatedHook(...hooks) {
|
|
133
|
+
this.onScopeCreatedHookList.push(...hooks);
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
117
136
|
addInstance(instance) {
|
|
118
137
|
this.instances.push(instance);
|
|
119
138
|
for (const onConstruct of this.onConstructHookList) {
|
|
@@ -24,6 +24,9 @@ export class EmptyContainer {
|
|
|
24
24
|
createScope() {
|
|
25
25
|
throw new MethodNotImplementedError();
|
|
26
26
|
}
|
|
27
|
+
autoResolve(options) {
|
|
28
|
+
throw new MethodNotImplementedError();
|
|
29
|
+
}
|
|
27
30
|
dispose() {
|
|
28
31
|
throw new MethodNotImplementedError();
|
|
29
32
|
}
|
|
@@ -64,4 +67,7 @@ export class EmptyContainer {
|
|
|
64
67
|
addOnConstructHook(...hooks) {
|
|
65
68
|
throw new MethodNotImplementedError();
|
|
66
69
|
}
|
|
70
|
+
addOnScopeCreatedHook(...hooks) {
|
|
71
|
+
throw new MethodNotImplementedError();
|
|
72
|
+
}
|
|
67
73
|
}
|
package/esm/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
export { isDependencyKey, } from './container/IContainer.js';
|
|
2
2
|
export { Container } from './container/Container.js';
|
|
3
|
+
export { AutoResolveModule } from './container/AutoResolveModule.js';
|
|
3
4
|
export { EmptyContainer } from './container/EmptyContainer.js';
|
|
4
5
|
export { Injector } from './injector/IInjector.js';
|
|
5
6
|
export { MetadataInjector, inject, arg, args, argsFn, resolveArgs } from './injector/MetadataInjector.js';
|
|
6
7
|
export { SimpleInjector } from './injector/SimpleInjector.js';
|
|
7
8
|
export { ProxyInjector } from './injector/ProxyInjector.js';
|
|
8
9
|
export { Provider } from './provider/Provider.js';
|
|
9
|
-
export { register, bindTo, scope, scopeAccess, lazy, singleton, decorate, appendArgs, appendArgsFn, } from './registration/IRegistration.js';
|
|
10
|
+
export { register, bindTo, scope, scopeAccess, lazy, autoResolve, singleton, decorate, appendArgs, appendArgsFn, } from './registration/IRegistration.js';
|
|
10
11
|
export { Registration } from './registration/Registration.js';
|
|
11
12
|
export { ContainerError } from './errors/ContainerError.js';
|
|
12
13
|
export { DependencyNotFoundError } from './errors/DependencyNotFoundError.js';
|
package/esm/provider/Provider.js
CHANGED
|
@@ -15,6 +15,7 @@ export class Provider {
|
|
|
15
15
|
accessRules = [];
|
|
16
16
|
mappers = [];
|
|
17
17
|
isLazy = false;
|
|
18
|
+
isAutoResolve = false;
|
|
18
19
|
cache = new Map();
|
|
19
20
|
getKey;
|
|
20
21
|
isDisposed = false;
|
|
@@ -51,6 +52,14 @@ export class Provider {
|
|
|
51
52
|
this.isLazy = true;
|
|
52
53
|
return this;
|
|
53
54
|
}
|
|
55
|
+
autoResolve() {
|
|
56
|
+
this.isAutoResolve = true;
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
isAutoResolvable() {
|
|
60
|
+
ProviderDisposedError.assert(!this.isDisposed, 'Provider is already disposed');
|
|
61
|
+
return this.isAutoResolve;
|
|
62
|
+
}
|
|
54
63
|
addArgsFn(...fns) {
|
|
55
64
|
this.argsFnList.push(...fns);
|
|
56
65
|
return this;
|
|
@@ -67,6 +76,7 @@ export class Provider {
|
|
|
67
76
|
dispose() {
|
|
68
77
|
ProviderDisposedError.assert(!this.isDisposed, 'Provider is already disposed');
|
|
69
78
|
this.isDisposed = true;
|
|
79
|
+
this.isAutoResolve = false;
|
|
70
80
|
this.getKey = undefined;
|
|
71
81
|
this.cache.clear();
|
|
72
82
|
this.accessRules.splice(0, this.accessRules.length);
|
|
@@ -31,5 +31,6 @@ export const appendArgs = (...extraArgs) => registerPipe((p) => p.addArgsFn((_,
|
|
|
31
31
|
export const appendArgsFn = (fn) => registerPipe((p) => p.addArgsFn((scope, options) => [...(options?.args ?? []), ...fn(scope, options)]));
|
|
32
32
|
export const scopeAccess = (rule) => registerPipe((p) => p.addAccessRule(rule));
|
|
33
33
|
export const lazy = () => registerPipe((p) => p.lazy());
|
|
34
|
+
export const autoResolve = () => registerPipe((p) => p.autoResolve());
|
|
34
35
|
export const decorate = (...fns) => registerPipe((p) => p.map(...fns));
|
|
35
36
|
export const singleton = (getCacheKey) => registerPipe((p) => p.singleton(getCacheKey));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-ioc-container",
|
|
3
|
-
"version": "57.
|
|
3
|
+
"version": "57.1.0",
|
|
4
4
|
"description": "Fast, lightweight TypeScript dependency injection container with a clean API, scoped lifecycles, decorators, tokens, hooks, lazy injection, customizable providers, and no global container objects.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type AutoResolveOptions, type IContainer, type IContainerModule } from './IContainer.js';
|
|
2
|
+
export declare class AutoResolveModule implements IContainerModule {
|
|
3
|
+
private readonly options;
|
|
4
|
+
constructor(options?: AutoResolveOptions);
|
|
5
|
+
applyTo(container: IContainer): void;
|
|
6
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CreateScopeOptions, type DependencyKey, type IContainer, type IContainerModule, type RegisterOptions, ResolveManyOptions, type ResolveOneOptions, type Tag } from './IContainer.js';
|
|
1
|
+
import { type AutoResolveOptions, type CreateScopeOptions, type DependencyKey, type IContainer, type IContainerModule, type OnScopeCreatedHook, type RegisterOptions, ResolveManyOptions, type ResolveOneOptions, type Tag } from './IContainer.js';
|
|
2
2
|
import { type IInjector } from '../injector/IInjector.js';
|
|
3
3
|
import { type IProvider } from '../provider/IProvider.js';
|
|
4
4
|
import { type IRegistration } from '../registration/IRegistration.js';
|
|
@@ -17,6 +17,7 @@ export declare class Container implements IContainer {
|
|
|
17
17
|
private readonly injector;
|
|
18
18
|
private readonly onConstructHookList;
|
|
19
19
|
private readonly onDisposeHookList;
|
|
20
|
+
private readonly onScopeCreatedHookList;
|
|
20
21
|
constructor(options?: {
|
|
21
22
|
injector?: IInjector;
|
|
22
23
|
parent?: IContainer;
|
|
@@ -27,12 +28,14 @@ export declare class Container implements IContainer {
|
|
|
27
28
|
resolveByAlias<T>(alias: DependencyKey, { args, child, lazy, excludedKeys }?: ResolveManyOptions): T[];
|
|
28
29
|
resolveOneByAlias<T>(alias: DependencyKey, { args, child, lazy }?: ResolveOneOptions): T;
|
|
29
30
|
createScope({ tags }?: CreateScopeOptions): IContainer;
|
|
31
|
+
autoResolve({ args }?: AutoResolveOptions): this;
|
|
30
32
|
dispose(): void;
|
|
31
33
|
addRegistration(registration: IRegistration): this;
|
|
32
34
|
getRegistrations(): IRegistration[];
|
|
33
35
|
hasRegistration(key: DependencyKey): boolean;
|
|
34
36
|
addOnConstructHook(...hooks: OnConstructHook[]): this;
|
|
35
37
|
addOnDisposeHook(...hooks: OnDisposeHook[]): this;
|
|
38
|
+
addOnScopeCreatedHook(...hooks: OnScopeCreatedHook[]): this;
|
|
36
39
|
addInstance(instance: Instance): void;
|
|
37
40
|
getScopes(): IContainer[];
|
|
38
41
|
hasInstance(instance: object): boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type DependencyKey, type IContainer, type IContainerModule, type ResolveManyOptions, type ResolveOneOptions, type Tag } from './IContainer.js';
|
|
1
|
+
import { type AutoResolveOptions, type DependencyKey, type IContainer, type IContainerModule, type OnScopeCreatedHook, type ResolveManyOptions, type ResolveOneOptions, type Tag } from './IContainer.js';
|
|
2
2
|
import { type IProvider } from '../provider/IProvider.js';
|
|
3
3
|
import { type IRegistration } from '../registration/IRegistration.js';
|
|
4
4
|
import { OnDisposeHook } from '../hooks/onContainerDisposed.js';
|
|
@@ -13,6 +13,7 @@ export declare class EmptyContainer implements IContainer {
|
|
|
13
13
|
getInstances(): never[];
|
|
14
14
|
hasInstance(instance: object): boolean;
|
|
15
15
|
createScope(): IContainer;
|
|
16
|
+
autoResolve(options?: AutoResolveOptions): this;
|
|
16
17
|
dispose(): void;
|
|
17
18
|
register(key: DependencyKey, value: IProvider): this;
|
|
18
19
|
hasTag(tag: Tag): boolean;
|
|
@@ -27,4 +28,5 @@ export declare class EmptyContainer implements IContainer {
|
|
|
27
28
|
resolveOneByAlias<T>(alias: DependencyKey, options?: ResolveOneOptions): T;
|
|
28
29
|
addOnDisposeHook(...hooks: OnDisposeHook[]): this;
|
|
29
30
|
addOnConstructHook(...hooks: OnConstructHook[]): this;
|
|
31
|
+
addOnScopeCreatedHook(...hooks: OnScopeCreatedHook[]): this;
|
|
30
32
|
}
|
|
@@ -2,6 +2,7 @@ import { type IProvider, ProviderOptions } from '../provider/IProvider.js';
|
|
|
2
2
|
import { type IRegistration } from '../registration/IRegistration.js';
|
|
3
3
|
import { OnConstructHook } from '../hooks/onConstruct.js';
|
|
4
4
|
import { OnDisposeHook } from '../hooks/onContainerDisposed.js';
|
|
5
|
+
import { type WithArgs } from '../injector/IInjector.js';
|
|
5
6
|
import { type constructor, Instance } from '../utils/basic.js';
|
|
6
7
|
export type DependencyKey = string | symbol;
|
|
7
8
|
export declare function isDependencyKey(target: unknown): target is DependencyKey;
|
|
@@ -28,6 +29,8 @@ export interface IContainerModule {
|
|
|
28
29
|
applyTo(container: IContainer): void;
|
|
29
30
|
}
|
|
30
31
|
export type CreateScopeOptions = Partial<WithTags>;
|
|
32
|
+
export type AutoResolveOptions = Partial<WithArgs>;
|
|
33
|
+
export type OnScopeCreatedHook = (scope: IContainer) => void;
|
|
31
34
|
export type RegisterOptions = {
|
|
32
35
|
aliases?: DependencyKey[];
|
|
33
36
|
};
|
|
@@ -35,6 +38,7 @@ export interface IContainer extends Tagged {
|
|
|
35
38
|
readonly isDisposed: boolean;
|
|
36
39
|
addOnConstructHook(...hooks: OnConstructHook[]): this;
|
|
37
40
|
addOnDisposeHook(...hooks: OnDisposeHook[]): this;
|
|
41
|
+
addOnScopeCreatedHook(...hooks: OnScopeCreatedHook[]): this;
|
|
38
42
|
register(key: DependencyKey, value: IProvider, options?: RegisterOptions): this;
|
|
39
43
|
addRegistration(registration: IRegistration): this;
|
|
40
44
|
getRegistrations(): IRegistration[];
|
|
@@ -43,6 +47,7 @@ export interface IContainer extends Tagged {
|
|
|
43
47
|
resolveByAlias<T>(alias: DependencyKey, options?: ResolveManyOptions): T[];
|
|
44
48
|
resolveOneByAlias<T>(alias: DependencyKey, options?: ResolveOneOptions): T;
|
|
45
49
|
createScope(options?: CreateScopeOptions): IContainer;
|
|
50
|
+
autoResolve(options?: AutoResolveOptions): this;
|
|
46
51
|
getScopes(): IContainer[];
|
|
47
52
|
getScopeByInstanceOrFail(instance: object): IContainer;
|
|
48
53
|
removeScope(child: IContainer): void;
|
package/typings/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { type IContainer, type Resolvable, type IContainerModule, type DependencyKey, type Tag, type Tagged, type ResolveOneOptions, type ResolveManyOptions, isDependencyKey, } from './container/IContainer.js';
|
|
1
|
+
export { type IContainer, type Resolvable, type IContainerModule, type DependencyKey, type Tag, type Tagged, type ResolveOneOptions, type ResolveManyOptions, type OnScopeCreatedHook, type AutoResolveOptions, isDependencyKey, } from './container/IContainer.js';
|
|
2
2
|
export { Container } from './container/Container.js';
|
|
3
|
+
export { AutoResolveModule } from './container/AutoResolveModule.js';
|
|
3
4
|
export { EmptyContainer } from './container/EmptyContainer.js';
|
|
4
5
|
export { type IInjector, type InjectOptions, type IInjectFnResolver, Injector } from './injector/IInjector.js';
|
|
5
6
|
export { MetadataInjector, inject, arg, args, argsFn, resolveArgs } from './injector/MetadataInjector.js';
|
|
@@ -7,7 +8,7 @@ export { SimpleInjector } from './injector/SimpleInjector.js';
|
|
|
7
8
|
export { ProxyInjector } from './injector/ProxyInjector.js';
|
|
8
9
|
export { type ResolveDependency, type IProvider, type DecorateFn, type ArgsFn, type ProviderOptions, type GetCacheKey, type ScopeAccessOptions, type ScopeAccessRule, } from './provider/IProvider.js';
|
|
9
10
|
export { Provider } from './provider/Provider.js';
|
|
10
|
-
export { type IRegistration, type ReturnTypeOfRegistration, type ScopeMatchRule, type ProviderPipe, register, bindTo, scope, scopeAccess, lazy, singleton, decorate, appendArgs, appendArgsFn, } from './registration/IRegistration.js';
|
|
11
|
+
export { type IRegistration, type ReturnTypeOfRegistration, type ScopeMatchRule, type ProviderPipe, register, bindTo, scope, scopeAccess, lazy, autoResolve, singleton, decorate, appendArgs, appendArgsFn, } from './registration/IRegistration.js';
|
|
11
12
|
export { Registration } from './registration/Registration.js';
|
|
12
13
|
export { ContainerError } from './errors/ContainerError.js';
|
|
13
14
|
export { DependencyNotFoundError } from './errors/DependencyNotFoundError.js';
|
|
@@ -21,6 +21,8 @@ export interface IProvider<T = any> {
|
|
|
21
21
|
addAccessRule(...rules: ScopeAccessRule[]): this;
|
|
22
22
|
addArgsFn(argsFn: ArgsFn): this;
|
|
23
23
|
lazy(): this;
|
|
24
|
+
autoResolve(): this;
|
|
25
|
+
isAutoResolvable(): boolean;
|
|
24
26
|
singleton(getCacheKey?: GetCacheKey): this;
|
|
25
27
|
dispose(): void;
|
|
26
28
|
}
|
|
@@ -10,6 +10,7 @@ export declare class Provider<T = any> implements IProvider<T> {
|
|
|
10
10
|
private readonly accessRules;
|
|
11
11
|
private readonly mappers;
|
|
12
12
|
private isLazy;
|
|
13
|
+
private isAutoResolve;
|
|
13
14
|
private cache;
|
|
14
15
|
private getKey;
|
|
15
16
|
private isDisposed;
|
|
@@ -19,6 +20,8 @@ export declare class Provider<T = any> implements IProvider<T> {
|
|
|
19
20
|
map(...mappers: DecorateFn<T>[]): this;
|
|
20
21
|
addAccessRule(...rules: ScopeAccessRule[]): this;
|
|
21
22
|
lazy(): this;
|
|
23
|
+
autoResolve(): this;
|
|
24
|
+
isAutoResolvable(): boolean;
|
|
22
25
|
addArgsFn(...fns: ArgsFn[]): this;
|
|
23
26
|
hasAccess(options: ScopeAccessOptions): boolean;
|
|
24
27
|
singleton(getCacheKey?: GetCacheKey): this;
|
|
@@ -27,5 +27,6 @@ export declare const appendArgs: <T>(...extraArgs: unknown[]) => ProviderPipe<T>
|
|
|
27
27
|
export declare const appendArgsFn: <T>(fn: ArgsFn) => ProviderPipe<T>;
|
|
28
28
|
export declare const scopeAccess: <T>(rule: ScopeAccessRule) => ProviderPipe<T>;
|
|
29
29
|
export declare const lazy: <T>() => ProviderPipe<T>;
|
|
30
|
+
export declare const autoResolve: <T>() => ProviderPipe<T>;
|
|
30
31
|
export declare const decorate: (...fns: DecorateFn[]) => ProviderPipe<unknown>;
|
|
31
32
|
export declare const singleton: <T = unknown>(getCacheKey?: GetCacheKey) => ProviderPipe<T>;
|