ts-ioc-container 56.4.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 +174 -24
- 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 +6 -2
- package/cjm/injector/MetadataInjector.js +4 -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 +3 -2
- package/esm/injector/MetadataInjector.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 +4 -3
- package/typings/injector/MetadataInjector.d.ts +2 -1
- 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`
|
|
@@ -708,7 +710,7 @@ The `lazy()` registerPipe can be used in two ways: with the `@register` decorato
|
|
|
708
710
|
import 'reflect-metadata';
|
|
709
711
|
import {
|
|
710
712
|
appendArgs,
|
|
711
|
-
|
|
713
|
+
arg,
|
|
712
714
|
bindTo,
|
|
713
715
|
Container,
|
|
714
716
|
inject,
|
|
@@ -1017,8 +1019,8 @@ describe('lazy registerPipe', () => {
|
|
|
1017
1019
|
@register(bindTo('Config'))
|
|
1018
1020
|
class ConfigService {
|
|
1019
1021
|
constructor(
|
|
1020
|
-
@inject(
|
|
1021
|
-
@inject(
|
|
1022
|
+
@inject(arg(0)) public apiUrl: string,
|
|
1023
|
+
@inject(arg(1)) public timeout: number,
|
|
1022
1024
|
) {
|
|
1023
1025
|
initLog.push(`ConfigService initialized with ${apiUrl}`);
|
|
1024
1026
|
}
|
|
@@ -1391,7 +1393,7 @@ Provider is dependency factory which creates dependency.
|
|
|
1391
1393
|
- `new Provider((container, options) => container.resolve(Logger, options))`
|
|
1392
1394
|
|
|
1393
1395
|
```typescript
|
|
1394
|
-
import {
|
|
1396
|
+
import { arg, bindTo, Container, inject, lazy, Provider, register, Registration as R } from 'ts-ioc-container';
|
|
1395
1397
|
|
|
1396
1398
|
/**
|
|
1397
1399
|
* Data Processing Pipeline - Provider Patterns
|
|
@@ -1458,7 +1460,7 @@ describe('Provider', () => {
|
|
|
1458
1460
|
|
|
1459
1461
|
it('supports args decorator for providing extra arguments', () => {
|
|
1460
1462
|
class FileService {
|
|
1461
|
-
constructor(@inject(
|
|
1463
|
+
constructor(@inject(arg(0)) readonly basePath: string) {}
|
|
1462
1464
|
}
|
|
1463
1465
|
|
|
1464
1466
|
const container = new Container().register(
|
|
@@ -1472,7 +1474,7 @@ describe('Provider', () => {
|
|
|
1472
1474
|
|
|
1473
1475
|
it('supports argsFn decorator for dynamic arguments', () => {
|
|
1474
1476
|
class Database {
|
|
1475
|
-
constructor(@inject(
|
|
1477
|
+
constructor(@inject(arg(0)) readonly connectionString: string) {}
|
|
1476
1478
|
}
|
|
1477
1479
|
|
|
1478
1480
|
const container = new Container().register('DbPath', Provider.fromValue('localhost:5432')).register(
|
|
@@ -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.
|
|
@@ -1632,14 +1781,15 @@ When you pass an `InjectionToken` via `token.args(...)`, the container resolves
|
|
|
1632
1781
|
- `ServiceToken.args(new ClassToken(SomeService))` — `SomeService` is constructed by the container
|
|
1633
1782
|
- `ServiceToken.args('literal')` — literal value passed directly
|
|
1634
1783
|
|
|
1635
|
-
### Positional arg injection with `
|
|
1784
|
+
### Positional arg injection with `arg(index)`, `args`, and `argsFn`
|
|
1636
1785
|
|
|
1637
|
-
Constructor parameters that should pick up positional args from `ProviderOptions` must be annotated with `@inject(
|
|
1786
|
+
Constructor parameters that should pick up positional args from `ProviderOptions` must be annotated with `@inject(arg(index))`. Parameters without `@inject` resolve to `undefined`.
|
|
1638
1787
|
|
|
1639
|
-
- `@inject(
|
|
1788
|
+
- `@inject(arg(0))` — resolves the first element of the `args` array passed at resolution time
|
|
1789
|
+
- `@inject(args)` — resolves the whole runtime `args` array
|
|
1640
1790
|
- Works together with `token.args(...)` to pass typed dependencies through the args context
|
|
1641
1791
|
|
|
1642
|
-
`argsFn(predicate)` is the general form: it iterates the runtime `args` array and returns the **first argument matching** `predicate(value, index)` — think `args.find(predicate)`. `
|
|
1792
|
+
`argsFn(predicate)` is the general form: it iterates the runtime `args` array and returns the **first argument matching** `predicate(value, index)` — think `args.find(predicate)`. `arg(index)` is just a shortcut for matching by position: `arg(0)` is `argsFn((value, index) => index === 0)`. `args` is `(scope, options) => options.args`, i.e. it returns the runtime args array as-is. Every `InjectFn` receives `(scope, options)`, where `options.args` is the runtime args array.
|
|
1643
1793
|
|
|
1644
1794
|
### Immutable token chaining
|
|
1645
1795
|
|
|
@@ -1654,7 +1804,7 @@ const userToken = ApiToken.args('https://users.api.com', 1000);
|
|
|
1654
1804
|
|
|
1655
1805
|
```typescript
|
|
1656
1806
|
import {
|
|
1657
|
-
|
|
1807
|
+
arg,
|
|
1658
1808
|
appendArgs,
|
|
1659
1809
|
appendArgsFn,
|
|
1660
1810
|
bindTo,
|
|
@@ -1686,7 +1836,7 @@ describe('IProvider', function () {
|
|
|
1686
1836
|
// Pre-configure the logger with a filename
|
|
1687
1837
|
@register(appendArgs('/var/log/app.log'))
|
|
1688
1838
|
class FileLogger {
|
|
1689
|
-
constructor(@inject(
|
|
1839
|
+
constructor(@inject(arg(0)) public filename: string) {}
|
|
1690
1840
|
}
|
|
1691
1841
|
|
|
1692
1842
|
const root = createContainer().addRegistration(R.fromClass(FileLogger));
|
|
@@ -1700,8 +1850,8 @@ describe('IProvider', function () {
|
|
|
1700
1850
|
@register(appendArgs('ConfiguredContext'))
|
|
1701
1851
|
class Logger {
|
|
1702
1852
|
constructor(
|
|
1703
|
-
@inject(
|
|
1704
|
-
@inject(
|
|
1853
|
+
@inject(arg(0)) public runtimeContext: string,
|
|
1854
|
+
@inject(arg(1)) public configuredContext: string,
|
|
1705
1855
|
) {}
|
|
1706
1856
|
}
|
|
1707
1857
|
|
|
@@ -1723,7 +1873,7 @@ describe('IProvider', function () {
|
|
|
1723
1873
|
// Extract 'env' from Config service dynamically
|
|
1724
1874
|
@register(appendArgsFn((scope) => [scope.resolve<Config>('Config').env]))
|
|
1725
1875
|
class Service {
|
|
1726
|
-
constructor(@inject(
|
|
1876
|
+
constructor(@inject(arg(0)) public env: string) {}
|
|
1727
1877
|
}
|
|
1728
1878
|
|
|
1729
1879
|
const root = createContainer()
|
|
@@ -1740,8 +1890,8 @@ describe('IProvider', function () {
|
|
|
1740
1890
|
@register(appendArgs('configured'))
|
|
1741
1891
|
class Service {
|
|
1742
1892
|
constructor(
|
|
1743
|
-
@inject(
|
|
1744
|
-
@inject(
|
|
1893
|
+
@inject(arg(0)) public runtime: string,
|
|
1894
|
+
@inject(arg(1)) public configured: string,
|
|
1745
1895
|
) {}
|
|
1746
1896
|
}
|
|
1747
1897
|
|
|
@@ -1760,9 +1910,9 @@ describe('IProvider', function () {
|
|
|
1760
1910
|
@register(appendArgs('fixed'), appendArgsFn((scope) => [scope.resolve<Config>('Config').tenant]))
|
|
1761
1911
|
class Service {
|
|
1762
1912
|
constructor(
|
|
1763
|
-
@inject(
|
|
1764
|
-
@inject(
|
|
1765
|
-
@inject(
|
|
1913
|
+
@inject(arg(0)) public runtime: string,
|
|
1914
|
+
@inject(arg(1)) public fixed: string,
|
|
1915
|
+
@inject(arg(2)) public tenant: string,
|
|
1766
1916
|
) {}
|
|
1767
1917
|
}
|
|
1768
1918
|
|
|
@@ -1799,7 +1949,7 @@ describe('IProvider', function () {
|
|
|
1799
1949
|
|
|
1800
1950
|
// EntityManager is generic - it works with ANY repository.
|
|
1801
1951
|
// The repository is the first arg passed via `EntityManagerToken.args(...)`.
|
|
1802
|
-
// `@inject(
|
|
1952
|
+
// `@inject(arg(0))` reads it; the container auto-resolves InjectionToken args
|
|
1803
1953
|
// before they reach the constructor.
|
|
1804
1954
|
const EntityManagerToken = new SingleToken<EntityManager>('EntityManager');
|
|
1805
1955
|
|
|
@@ -1808,7 +1958,7 @@ describe('IProvider', function () {
|
|
|
1808
1958
|
singleton((arg1) => (arg1 as SingleToken).token), // Cache unique instance per repository type
|
|
1809
1959
|
)
|
|
1810
1960
|
class EntityManager {
|
|
1811
|
-
constructor(@inject(
|
|
1961
|
+
constructor(@inject(arg(0)) public repository: IRepository) {}
|
|
1812
1962
|
}
|
|
1813
1963
|
|
|
1814
1964
|
class App {
|
|
@@ -2117,7 +2267,7 @@ Sometimes you want to decorate you class with some logic. Use the `decorate(...)
|
|
|
2117
2267
|
|
|
2118
2268
|
```typescript
|
|
2119
2269
|
import {
|
|
2120
|
-
|
|
2270
|
+
arg,
|
|
2121
2271
|
bindTo,
|
|
2122
2272
|
Container,
|
|
2123
2273
|
decorate,
|
|
@@ -2171,7 +2321,7 @@ describe('Decorator Pattern', () => {
|
|
|
2171
2321
|
// Decorator: Wraps any IRepository with logging behavior
|
|
2172
2322
|
class LoggingRepository implements IRepository {
|
|
2173
2323
|
constructor(
|
|
2174
|
-
@inject(
|
|
2324
|
+
@inject(arg(0)) private repository: IRepository,
|
|
2175
2325
|
@inject(s.token('Logger').lazy()) private logger: Logger,
|
|
2176
2326
|
) {}
|
|
2177
2327
|
|
|
@@ -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 = 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");
|
|
@@ -13,6 +15,7 @@ Object.defineProperty(exports, "Injector", { enumerable: true, get: function ()
|
|
|
13
15
|
var MetadataInjector_1 = require("./injector/MetadataInjector");
|
|
14
16
|
Object.defineProperty(exports, "MetadataInjector", { enumerable: true, get: function () { return MetadataInjector_1.MetadataInjector; } });
|
|
15
17
|
Object.defineProperty(exports, "inject", { enumerable: true, get: function () { return MetadataInjector_1.inject; } });
|
|
18
|
+
Object.defineProperty(exports, "arg", { enumerable: true, get: function () { return MetadataInjector_1.arg; } });
|
|
16
19
|
Object.defineProperty(exports, "args", { enumerable: true, get: function () { return MetadataInjector_1.args; } });
|
|
17
20
|
Object.defineProperty(exports, "argsFn", { enumerable: true, get: function () { return MetadataInjector_1.argsFn; } });
|
|
18
21
|
Object.defineProperty(exports, "resolveArgs", { enumerable: true, get: function () { return MetadataInjector_1.resolveArgs; } });
|
|
@@ -28,6 +31,7 @@ Object.defineProperty(exports, "bindTo", { enumerable: true, get: function () {
|
|
|
28
31
|
Object.defineProperty(exports, "scope", { enumerable: true, get: function () { return IRegistration_1.scope; } });
|
|
29
32
|
Object.defineProperty(exports, "scopeAccess", { enumerable: true, get: function () { return IRegistration_1.scopeAccess; } });
|
|
30
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; } });
|
|
31
35
|
Object.defineProperty(exports, "singleton", { enumerable: true, get: function () { return IRegistration_1.singleton; } });
|
|
32
36
|
Object.defineProperty(exports, "decorate", { enumerable: true, get: function () { return IRegistration_1.decorate; } });
|
|
33
37
|
Object.defineProperty(exports, "appendArgs", { enumerable: true, get: function () { return IRegistration_1.appendArgs; } });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveArgs = exports.args = exports.argsFn = exports.MetadataInjector = void 0;
|
|
3
|
+
exports.resolveArgs = exports.args = exports.arg = exports.argsFn = exports.MetadataInjector = void 0;
|
|
4
4
|
exports.inject = inject;
|
|
5
5
|
const IInjector_1 = require("./IInjector");
|
|
6
6
|
const basic_1 = require("../utils/basic");
|
|
@@ -21,7 +21,9 @@ function inject(fn, ...mappers) {
|
|
|
21
21
|
}
|
|
22
22
|
const argsFn = (predicate) => (c, { args = [] }) => args.find((value, index) => predicate(value, index));
|
|
23
23
|
exports.argsFn = argsFn;
|
|
24
|
-
const
|
|
24
|
+
const arg = (index) => (0, exports.argsFn)((value, i) => i === index);
|
|
25
|
+
exports.arg = arg;
|
|
26
|
+
const args = (c, { args = [] }) => args;
|
|
25
27
|
exports.args = args;
|
|
26
28
|
const resolveArgs = (Target, methodName) => {
|
|
27
29
|
const tokens = (0, parameter_1.getParamMeta)(hookMetaKey(methodName), Target);
|
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
|
-
export { MetadataInjector, inject, args, argsFn, resolveArgs } from './injector/MetadataInjector.js';
|
|
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';
|
|
@@ -15,7 +15,8 @@ export function inject(fn, ...mappers) {
|
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
17
|
export const argsFn = (predicate) => (c, { args = [] }) => args.find((value, index) => predicate(value, index));
|
|
18
|
-
export const
|
|
18
|
+
export const arg = (index) => argsFn((value, i) => i === index);
|
|
19
|
+
export const args = (c, { args = [] }) => args;
|
|
19
20
|
export const resolveArgs = (Target, methodName) => {
|
|
20
21
|
const tokens = getParamMeta(hookMetaKey(methodName), Target);
|
|
21
22
|
return (scope, { args = [], lazy }) => tokens.map((fn) => fn.resolve(scope, { args: args.map(argToToken).map((t) => t.resolve(scope)), lazy }));
|
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": "
|
|
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,13 +1,14 @@
|
|
|
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
|
-
export { MetadataInjector, inject, args, argsFn, resolveArgs } from './injector/MetadataInjector.js';
|
|
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 { 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';
|
|
@@ -20,5 +20,6 @@ export declare function inject<T, B, C, D, E, F, G, H, I, J>(fn: Injectable<T>,
|
|
|
20
20
|
export declare function inject<T, B, C, D, E, F, G, H, I, J, K>(fn: Injectable<T>, fn1: MapFn<T, B>, fn2: MapFn<B, C>, fn3: MapFn<C, D>, fn4: MapFn<D, E>, fn5: MapFn<E, F>, fn6: MapFn<F, G>, fn7: MapFn<G, H>, fn8: MapFn<H, I>, fn9: MapFn<I, J>, fn10: MapFn<J, K>): ParameterDecorator;
|
|
21
21
|
export declare function inject<T>(fn: Injectable<T>, ...mappers: MapFn<T>[]): ParameterDecorator;
|
|
22
22
|
export declare const argsFn: <T = unknown>(predicate: (value: unknown, index: number) => boolean) => InjectFn<T>;
|
|
23
|
-
export declare const
|
|
23
|
+
export declare const arg: <T = unknown>(index: number) => InjectFn<T>;
|
|
24
|
+
export declare const args: InjectFn<unknown[]>;
|
|
24
25
|
export declare const resolveArgs: (Target: constructor<unknown>, methodName?: string) => (scope: IContainer, { args, lazy }: ProviderOptions) => unknown[];
|
|
@@ -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>;
|