ts-ioc-container 47.3.1 → 47.5.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 +87 -61
- package/cjm/container/Container.js +1 -1
- package/cjm/hooks/HookContext.js +3 -1
- package/cjm/index.js +17 -15
- package/cjm/injector/MetadataInjector.js +1 -1
- package/cjm/injector/ProxyInjector.js +5 -12
- package/cjm/injector/inject.js +13 -4
- package/cjm/provider/IProvider.js +6 -6
- package/cjm/provider/Provider.js +2 -2
- package/cjm/registration/Registration.js +2 -2
- package/cjm/token/ClassToken.js +27 -17
- package/cjm/token/FunctionToken.js +26 -9
- package/cjm/token/GroupAliasToken.js +18 -17
- package/cjm/token/InjectionToken.js +5 -0
- package/cjm/token/SingleAliasToken.js +18 -17
- package/cjm/token/SingleToken.js +20 -16
- package/esm/container/Container.js +1 -1
- package/esm/hooks/HookContext.js +3 -1
- package/esm/index.js +7 -7
- package/esm/injector/MetadataInjector.js +1 -1
- package/esm/injector/ProxyInjector.js +5 -12
- package/esm/injector/inject.js +10 -3
- package/esm/provider/IProvider.js +3 -3
- package/esm/provider/Provider.js +2 -2
- package/esm/registration/Registration.js +2 -2
- package/esm/token/ClassToken.js +28 -18
- package/esm/token/FunctionToken.js +26 -9
- package/esm/token/GroupAliasToken.js +19 -18
- package/esm/token/InjectionToken.js +4 -0
- package/esm/token/SingleAliasToken.js +19 -18
- package/esm/token/SingleToken.js +21 -17
- package/package.json +4 -4
- package/typings/hooks/hook.d.ts +2 -1
- package/typings/index.d.ts +7 -7
- package/typings/injector/IInjector.d.ts +1 -2
- package/typings/injector/ProxyInjector.d.ts +1 -1
- package/typings/injector/inject.d.ts +4 -1
- package/typings/provider/IProvider.d.ts +4 -4
- package/typings/provider/Provider.d.ts +1 -1
- package/typings/registration/Registration.d.ts +3 -1
- package/typings/token/ClassToken.d.ts +13 -8
- package/typings/token/FunctionToken.d.ts +10 -4
- package/typings/token/GroupAliasToken.d.ts +13 -8
- package/typings/token/InjectionToken.d.ts +4 -8
- package/typings/token/SingleAliasToken.d.ts +13 -8
- package/typings/token/SingleToken.d.ts +14 -9
- /package/cjm/{metadata → utils}/debounce.js +0 -0
- /package/cjm/{metadata → utils}/errorHandler.js +0 -0
- /package/cjm/{metadata → utils}/once.js +0 -0
- /package/cjm/{metadata → utils}/shallowCache.js +0 -0
- /package/cjm/{metadata → utils}/throttle.js +0 -0
- /package/esm/{metadata → utils}/debounce.js +0 -0
- /package/esm/{metadata → utils}/errorHandler.js +0 -0
- /package/esm/{metadata → utils}/once.js +0 -0
- /package/esm/{metadata → utils}/shallowCache.js +0 -0
- /package/esm/{metadata → utils}/throttle.js +0 -0
- /package/typings/{metadata → utils}/debounce.d.ts +0 -0
- /package/typings/{metadata → utils}/errorHandler.d.ts +0 -0
- /package/typings/{metadata → utils}/once.d.ts +0 -0
- /package/typings/{metadata → utils}/shallowCache.d.ts +0 -0
- /package/typings/{metadata → utils}/throttle.d.ts +0 -0
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
- [Proxy](#proxy)
|
|
40
40
|
- [Provider](#provider) `provider`
|
|
41
41
|
- [Singleton](#singleton) `singleton`
|
|
42
|
-
- [Arguments](#arguments) `
|
|
42
|
+
- [Arguments](#arguments) `setArgs` `setArgsFn`
|
|
43
43
|
- [Visibility](#visibility) `visible`
|
|
44
44
|
- [Alias](#alias) `asAlias`
|
|
45
45
|
- [Decorator](#decorator) `decorate`
|
|
@@ -248,9 +248,14 @@ describe('Basic usage', function () {
|
|
|
248
248
|
### Scope
|
|
249
249
|
Sometimes you need to create a scope of container. For example, when you want to create a scope per request in web application. You can assign tags to scope and provider and resolve dependencies only from certain scope.
|
|
250
250
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
251
|
+
> [!IMPORTANT]
|
|
252
|
+
> Scope creation is snapshot-like. Existing parent registrations are applied to the child scope when `createScope()` is called, and when a scope doesn't have a dependency it resolves from the parent container.
|
|
253
|
+
|
|
254
|
+
> [!WARNING]
|
|
255
|
+
> Registrations added to a parent after a child scope has already been created are not automatically applied to that existing child. Create a new scope or add the registration to the child explicitly.
|
|
256
|
+
|
|
257
|
+
> [!WARNING]
|
|
258
|
+
> Scope matching happens when a registration is applied to a container. Only registrations whose scope rules match that container are registered there.
|
|
254
259
|
|
|
255
260
|
```typescript
|
|
256
261
|
import 'reflect-metadata';
|
|
@@ -347,10 +352,12 @@ describe('Scopes', function () {
|
|
|
347
352
|
You can dynamically add tags to a container after it's been created using the `addTags()` method. This is useful for environment-based configuration, feature flags, and progressive container setup.
|
|
348
353
|
|
|
349
354
|
- Tags can be added one at a time or multiple at once
|
|
350
|
-
- Tags must be added **before** registrations are applied - scope matching happens at registration time
|
|
351
355
|
- Useful for conditional configuration based on `NODE_ENV` or runtime flags
|
|
352
356
|
- Container can be configured incrementally as the application initializes
|
|
353
357
|
|
|
358
|
+
> [!WARNING]
|
|
359
|
+
> Tags must be added **before** registrations are applied. Scope matching happens at registration time, so adding tags later does not retroactively make providers available.
|
|
360
|
+
|
|
354
361
|
```typescript
|
|
355
362
|
import { bindTo, Container, register, Registration as R, scope } from 'ts-ioc-container';
|
|
356
363
|
|
|
@@ -636,11 +643,13 @@ describe('hasRegistration', function () {
|
|
|
636
643
|
```
|
|
637
644
|
|
|
638
645
|
### Dispose
|
|
639
|
-
Sometimes you want to dispose container
|
|
646
|
+
Sometimes you want to dispose a container or scope. For example, when a request, page, widget, or other local lifecycle ends.
|
|
640
647
|
|
|
641
648
|
- container can be disposed
|
|
642
|
-
- when container is disposed then
|
|
643
|
-
|
|
649
|
+
- when container is disposed then it runs its `onDispose` hooks, unregisters its providers, removes its local instances, and detaches from its parent
|
|
650
|
+
|
|
651
|
+
> [!IMPORTANT]
|
|
652
|
+
> Dispose is local to the container being disposed. Child scopes are not disposed automatically; dispose them explicitly when their own lifecycle ends.
|
|
644
653
|
|
|
645
654
|
```typescript
|
|
646
655
|
import 'reflect-metadata';
|
|
@@ -744,6 +753,14 @@ describe('Disposing', function () {
|
|
|
744
753
|
### Lazy
|
|
745
754
|
Sometimes you want to create dependency only when somebody want to invoke it's method or property. This is what `lazy` is for.
|
|
746
755
|
|
|
756
|
+
- Lazy class instances are wrapped in a JavaScript `Proxy`; the real class instance is created on first property or method access.
|
|
757
|
+
|
|
758
|
+
> [!IMPORTANT]
|
|
759
|
+
> `lazy` is designed only for class instances resolved from class providers.
|
|
760
|
+
|
|
761
|
+
> [!WARNING]
|
|
762
|
+
> Do not use `lazy` for primitive values, plain values, functions, or non-class provider results.
|
|
763
|
+
|
|
747
764
|
```typescript
|
|
748
765
|
import 'reflect-metadata';
|
|
749
766
|
import { Container, inject, register, Registration as R, select as s, singleton } from 'ts-ioc-container';
|
|
@@ -869,7 +886,7 @@ describe('lazy provider', () => {
|
|
|
869
886
|
```
|
|
870
887
|
|
|
871
888
|
### Lazy with registerPipe
|
|
872
|
-
The `lazy()` registerPipe can be used in two ways: with the `@register` decorator or directly on the `Provider` pipe. This allows you to defer expensive
|
|
889
|
+
The `lazy()` registerPipe can be used in two ways: with the `@register` decorator or directly on the `Provider` pipe. This allows you to defer expensive class instance initialization until first access.
|
|
873
890
|
|
|
874
891
|
**Use cases:**
|
|
875
892
|
- Defer expensive initialization (database connections, SMTP, external APIs)
|
|
@@ -1466,22 +1483,12 @@ describe('SimpleInjector', function () {
|
|
|
1466
1483
|
### Proxy
|
|
1467
1484
|
This type of injector injects dependencies as dictionary `Record<string, unknown>`.
|
|
1468
1485
|
|
|
1486
|
+
- **`args` reserved keyword**: accessing `deps.args` returns the raw `args[]` array passed at resolve time
|
|
1487
|
+
- **Alias convention**: any property name containing `"alias"` (case-insensitive) is resolved via `resolveByAlias` instead of `resolve`
|
|
1488
|
+
|
|
1469
1489
|
```typescript
|
|
1470
1490
|
import { Container, ProxyInjector, Registration as R } from 'ts-ioc-container';
|
|
1471
1491
|
|
|
1472
|
-
/**
|
|
1473
|
-
* Clean Architecture - Proxy Injector
|
|
1474
|
-
*
|
|
1475
|
-
* The ProxyInjector injects dependencies as a single object (props/options pattern).
|
|
1476
|
-
* This is popular in modern JavaScript/TypeScript (like React props or destructuring).
|
|
1477
|
-
*
|
|
1478
|
-
* Advantages:
|
|
1479
|
-
* - Named parameters are more readable than positional arguments
|
|
1480
|
-
* - Order of arguments doesn't matter
|
|
1481
|
-
* - Easy to add/remove dependencies without breaking inheritance chains
|
|
1482
|
-
* - Works well with "Clean Architecture" adapters
|
|
1483
|
-
*/
|
|
1484
|
-
|
|
1485
1492
|
describe('ProxyInjector', function () {
|
|
1486
1493
|
it('should inject dependencies as a props object', function () {
|
|
1487
1494
|
class Logger {
|
|
@@ -1490,13 +1497,11 @@ describe('ProxyInjector', function () {
|
|
|
1490
1497
|
}
|
|
1491
1498
|
}
|
|
1492
1499
|
|
|
1493
|
-
// Dependencies defined as an interface
|
|
1494
1500
|
interface UserControllerDeps {
|
|
1495
1501
|
logger: Logger;
|
|
1496
1502
|
prefix: string;
|
|
1497
1503
|
}
|
|
1498
1504
|
|
|
1499
|
-
// Controller receives all dependencies in a single object
|
|
1500
1505
|
class UserController {
|
|
1501
1506
|
private logger: Logger;
|
|
1502
1507
|
private prefix: string;
|
|
@@ -1521,19 +1526,20 @@ describe('ProxyInjector', function () {
|
|
|
1521
1526
|
expect(controller.createUser('bob')).toBe('Logged: USER: bob');
|
|
1522
1527
|
});
|
|
1523
1528
|
|
|
1524
|
-
it('should
|
|
1529
|
+
it('should expose runtime args through the reserved "args" property', function () {
|
|
1525
1530
|
class Database {}
|
|
1526
1531
|
|
|
1527
|
-
|
|
1532
|
+
class ReportGenerator {
|
|
1528
1533
|
database: Database;
|
|
1529
|
-
format: string;
|
|
1530
|
-
}
|
|
1534
|
+
format: string;
|
|
1531
1535
|
|
|
1532
|
-
|
|
1533
|
-
|
|
1536
|
+
constructor({ database, args }: { database: Database; args: string[] }) {
|
|
1537
|
+
this.database = database;
|
|
1538
|
+
this.format = args[0];
|
|
1539
|
+
}
|
|
1534
1540
|
|
|
1535
1541
|
generate(): string {
|
|
1536
|
-
return `Report in ${this.
|
|
1542
|
+
return `Report in ${this.format}`;
|
|
1537
1543
|
}
|
|
1538
1544
|
}
|
|
1539
1545
|
|
|
@@ -1541,24 +1547,20 @@ describe('ProxyInjector', function () {
|
|
|
1541
1547
|
.addRegistration(R.fromClass(Database).bindToKey('database'))
|
|
1542
1548
|
.addRegistration(R.fromClass(ReportGenerator).bindToKey('ReportGenerator'));
|
|
1543
1549
|
|
|
1544
|
-
// "format" is passed at resolution time
|
|
1545
1550
|
const generator = container.resolve<ReportGenerator>('ReportGenerator', {
|
|
1546
|
-
args: [
|
|
1551
|
+
args: ['PDF'],
|
|
1547
1552
|
});
|
|
1548
1553
|
|
|
1549
|
-
expect(generator.
|
|
1554
|
+
expect(generator.database).toBeInstanceOf(Database);
|
|
1550
1555
|
expect(generator.generate()).toBe('Report in PDF');
|
|
1551
1556
|
});
|
|
1552
1557
|
|
|
1553
|
-
it('should resolve
|
|
1554
|
-
// If a property is named "loggersArray", it looks for alias "loggersArray"
|
|
1555
|
-
// and resolves it as an array of all matches.
|
|
1556
|
-
|
|
1558
|
+
it('should resolve dependencies by alias when property name contains "alias"', function () {
|
|
1557
1559
|
class FileLogger {}
|
|
1558
1560
|
class ConsoleLogger {}
|
|
1559
1561
|
|
|
1560
1562
|
interface AppDeps {
|
|
1561
|
-
|
|
1563
|
+
loggersAlias: any[];
|
|
1562
1564
|
}
|
|
1563
1565
|
|
|
1564
1566
|
class App {
|
|
@@ -1567,17 +1569,13 @@ describe('ProxyInjector', function () {
|
|
|
1567
1569
|
|
|
1568
1570
|
const container = new Container({ injector: new ProxyInjector() });
|
|
1569
1571
|
|
|
1570
|
-
// Mocking the behavior for this specific test as ProxyInjector uses resolveByAlias
|
|
1571
|
-
// which delegates to the container.
|
|
1572
|
-
// In a real scenario, you'd register multiple loggers with the same alias.
|
|
1573
1572
|
const mockLoggers = [new FileLogger(), new ConsoleLogger()];
|
|
1574
|
-
|
|
1575
1573
|
container.resolveByAlias = vi.fn().mockReturnValue(mockLoggers);
|
|
1576
1574
|
|
|
1577
1575
|
const app = container.resolve(App);
|
|
1578
1576
|
|
|
1579
|
-
expect(app.deps.
|
|
1580
|
-
expect(container.resolveByAlias).toHaveBeenCalledWith('
|
|
1577
|
+
expect(app.deps.loggersAlias).toBe(mockLoggers);
|
|
1578
|
+
expect(container.resolveByAlias).toHaveBeenCalledWith('loggersAlias');
|
|
1581
1579
|
});
|
|
1582
1580
|
});
|
|
1583
1581
|
|
|
@@ -1592,8 +1590,8 @@ Provider is dependency factory which creates dependency.
|
|
|
1592
1590
|
|
|
1593
1591
|
```typescript
|
|
1594
1592
|
import {
|
|
1595
|
-
|
|
1596
|
-
|
|
1593
|
+
setArgs,
|
|
1594
|
+
setArgsFn,
|
|
1597
1595
|
bindTo,
|
|
1598
1596
|
Container,
|
|
1599
1597
|
lazy,
|
|
@@ -1672,7 +1670,10 @@ describe('Provider', () => {
|
|
|
1672
1670
|
constructor(readonly basePath: string) {}
|
|
1673
1671
|
}
|
|
1674
1672
|
|
|
1675
|
-
const container = new Container().register(
|
|
1673
|
+
const container = new Container().register(
|
|
1674
|
+
'FileService',
|
|
1675
|
+
Provider.fromClass(FileService).pipe(setArgs('/var/data')),
|
|
1676
|
+
);
|
|
1676
1677
|
|
|
1677
1678
|
const service = container.resolve<FileService>('FileService');
|
|
1678
1679
|
expect(service.basePath).toBe('/var/data');
|
|
@@ -1687,7 +1688,7 @@ describe('Provider', () => {
|
|
|
1687
1688
|
'Database',
|
|
1688
1689
|
Provider.fromClass(Database).pipe(
|
|
1689
1690
|
// Dynamically resolve connection string at creation time
|
|
1690
|
-
|
|
1691
|
+
setArgsFn((scope) => [`postgres://${scope.resolve('DbPath')}`]),
|
|
1691
1692
|
),
|
|
1692
1693
|
);
|
|
1693
1694
|
|
|
@@ -1737,7 +1738,9 @@ describe('Provider', () => {
|
|
|
1737
1738
|
Sometimes you need to create only one instance of dependency per scope. For example, you want to create only one logger per scope.
|
|
1738
1739
|
|
|
1739
1740
|
- Singleton provider creates only one instance in every scope where it's resolved.
|
|
1740
|
-
|
|
1741
|
+
|
|
1742
|
+
> [!IMPORTANT]
|
|
1743
|
+
> Singleton means one instance per scope. If you create a scope `A` of container `root`, then `Logger` of `A` !== `Logger` of `root`.
|
|
1741
1744
|
|
|
1742
1745
|
```typescript
|
|
1743
1746
|
import 'reflect-metadata';
|
|
@@ -1825,15 +1828,35 @@ describe('Singleton', function () {
|
|
|
1825
1828
|
|
|
1826
1829
|
### Arguments
|
|
1827
1830
|
Sometimes you want to bind some arguments to provider. This is what `ArgsProvider` is for.
|
|
1828
|
-
- `provider(
|
|
1829
|
-
- `provider(
|
|
1830
|
-
- `Provider.fromClass(Logger).pipe(
|
|
1831
|
-
-
|
|
1831
|
+
- `provider(setArgs('someArgument'))`
|
|
1832
|
+
- `provider(setArgsFn((container) => [container.resolve(Logger), 'someValue']))`
|
|
1833
|
+
- `Provider.fromClass(Logger).pipe(setArgs('someArgument'))`
|
|
1834
|
+
- Use `setArgsFn(resolveByArgs)` to resolve `InjectionToken` args at resolution time (tokens are resolved, primitives pass through)
|
|
1835
|
+
|
|
1836
|
+
### Token as argument
|
|
1837
|
+
When passing args via `token.args(...)`, if an argument is an `InjectionToken` and the provider uses `setArgsFn(resolveByArgs)`, the token is resolved to its value rather than wrapped as a constant.
|
|
1838
|
+
- `ServiceToken.args(ValueToken)` — `ValueToken` is resolved from the container, its value is passed as arg
|
|
1839
|
+
- `ServiceToken.args('literal')` — literal value passed directly
|
|
1840
|
+
|
|
1841
|
+
### Positional arg injection with `args(index)`
|
|
1842
|
+
Use `@inject(args(index))` to explicitly bind a constructor parameter to a positional argument from `ProviderOptions`. This is useful when combining `setArgsFn(resolveByArgs)` with token-based arg passing.
|
|
1843
|
+
- `@inject(args(0))` — resolves the first element of the `args` array passed at resolution time
|
|
1844
|
+
- Works together with `token.args(...)` to pass typed dependencies through the args context
|
|
1845
|
+
|
|
1846
|
+
### Immutable token chaining
|
|
1847
|
+
`token.args(...)`, `token.argsFn(...)`, and `token.lazy()` all return **new token instances** — the parent token is never mutated. This allows the same token to be specialized in multiple independent ways (one-way linked list: parent → many children).
|
|
1848
|
+
|
|
1849
|
+
```typescript
|
|
1850
|
+
const ApiToken = new SingleToken<IApiClient>('IApiClient');
|
|
1851
|
+
// Independent children — ApiToken is unchanged
|
|
1852
|
+
const dataToken = ApiToken.args('https://data.api.com', 5000);
|
|
1853
|
+
const userToken = ApiToken.args('https://users.api.com', 1000);
|
|
1854
|
+
```
|
|
1832
1855
|
|
|
1833
1856
|
```typescript
|
|
1834
1857
|
import {
|
|
1835
|
-
|
|
1836
|
-
|
|
1858
|
+
setArgs,
|
|
1859
|
+
setArgsFn,
|
|
1837
1860
|
bindTo,
|
|
1838
1861
|
Container,
|
|
1839
1862
|
inject,
|
|
@@ -1867,7 +1890,7 @@ describe('ArgsProvider', function () {
|
|
|
1867
1890
|
}
|
|
1868
1891
|
|
|
1869
1892
|
// Pre-configure the logger with a filename
|
|
1870
|
-
const root = createContainer().addRegistration(R.fromClass(FileLogger).pipe(
|
|
1893
|
+
const root = createContainer().addRegistration(R.fromClass(FileLogger).pipe(setArgs('/var/log/app.log')));
|
|
1871
1894
|
|
|
1872
1895
|
// Resolve by class name (default key) to use the registered provider
|
|
1873
1896
|
const logger = root.resolve<FileLogger>('FileLogger');
|
|
@@ -1880,7 +1903,7 @@ describe('ArgsProvider', function () {
|
|
|
1880
1903
|
}
|
|
1881
1904
|
|
|
1882
1905
|
// 'FixedContext' wins over any runtime args
|
|
1883
|
-
const root = createContainer().addRegistration(R.fromClass(Logger).pipe(
|
|
1906
|
+
const root = createContainer().addRegistration(R.fromClass(Logger).pipe(setArgs('FixedContext')));
|
|
1884
1907
|
|
|
1885
1908
|
// Even if we ask for 'RuntimeContext', we get 'FixedContext'
|
|
1886
1909
|
// Resolve by class name to use the registered provider
|
|
@@ -1906,7 +1929,7 @@ describe('ArgsProvider', function () {
|
|
|
1906
1929
|
R.fromClass(Service).pipe(
|
|
1907
1930
|
// Extract 'env' from Config service dynamically
|
|
1908
1931
|
// Note: We resolve 'Config' by string key to get the registered instance (if it were singleton)
|
|
1909
|
-
|
|
1932
|
+
setArgsFn((scope) => [scope.resolve<Config>('Config').env]),
|
|
1910
1933
|
),
|
|
1911
1934
|
);
|
|
1912
1935
|
|
|
@@ -1943,7 +1966,7 @@ describe('ArgsProvider', function () {
|
|
|
1943
1966
|
|
|
1944
1967
|
@register(
|
|
1945
1968
|
bindTo(EntityManagerToken),
|
|
1946
|
-
|
|
1969
|
+
setArgsFn(resolveByArgs), // <--- Key magic: resolves dependencies based on arguments passed to token
|
|
1947
1970
|
singleton(MultiCache.fromFirstArg), // Cache unique instance per repository type
|
|
1948
1971
|
)
|
|
1949
1972
|
class EntityManager {
|
|
@@ -2000,6 +2023,10 @@ describe('ArgsProvider', function () {
|
|
|
2000
2023
|
|
|
2001
2024
|
### Visibility
|
|
2002
2025
|
Sometimes you want to hide dependency if somebody wants to resolve it from certain scope. This uses `ScopeAccessRule` to control access.
|
|
2026
|
+
|
|
2027
|
+
> [!IMPORTANT]
|
|
2028
|
+
> Use `scope()` to decide where a provider is registered. Use `scopeAccess()` to decide which invocation scopes can see an already registered provider.
|
|
2029
|
+
|
|
2003
2030
|
- `provider(scopeAccess(({ invocationScope, providerScope }) => invocationScope === providerScope))` - dependency will be accessible only from the scope where it's registered
|
|
2004
2031
|
- `Provider.fromClass(Logger).pipe(scopeAccess(({ invocationScope, providerScope }) => invocationScope === providerScope))`
|
|
2005
2032
|
|
|
@@ -2800,4 +2827,3 @@ describe('inject property', () => {
|
|
|
2800
2827
|
- [MethodNotImplementedError.ts](..%2F..%2Flib%2Ferrors%2FMethodNotImplementedError.ts)
|
|
2801
2828
|
- [DependencyMissingKeyError.ts](..%2F..%2Flib%2Ferrors%2FDependencyMissingKeyError.ts)
|
|
2802
2829
|
- [ContainerDisposedError.ts](..%2F..%2Flib%2Ferrors%2FContainerDisposedError.ts)
|
|
2803
|
-
|
|
@@ -67,7 +67,7 @@ class Container {
|
|
|
67
67
|
const provider = key ? this.findProviderByKeyOrFail(key) : undefined;
|
|
68
68
|
return provider?.hasAccess({ invocationScope: child, providerScope: this })
|
|
69
69
|
? provider.resolve(this, { args, lazy })
|
|
70
|
-
: this.parent.resolveOneByAlias(
|
|
70
|
+
: this.parent.resolveOneByAlias(alias, { args, child, lazy });
|
|
71
71
|
}
|
|
72
72
|
createScope({ tags } = {}) {
|
|
73
73
|
this.validateContainer();
|
package/cjm/hooks/HookContext.js
CHANGED
|
@@ -14,7 +14,9 @@ class HookContext {
|
|
|
14
14
|
this.instance = (0, proxy_1.isProxy)(instance) ? (0, proxy_1.getProxyTarget)(instance) : instance;
|
|
15
15
|
}
|
|
16
16
|
resolveArgs(...args) {
|
|
17
|
-
return (0, inject_1.resolveArgs)(this.instance.constructor, this.methodName)(this.scope,
|
|
17
|
+
return (0, inject_1.resolveArgs)(this.instance.constructor, this.methodName)(this.scope, {
|
|
18
|
+
args: [...this.initialArgs, ...args],
|
|
19
|
+
});
|
|
18
20
|
}
|
|
19
21
|
invokeMethod({ args = this.resolveArgs() } = {}) {
|
|
20
22
|
// @ts-ignore
|
package/cjm/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.resolveConstructor = exports.Is = exports.pipe = exports.select = exports.
|
|
3
|
+
exports.SingleAliasToken = exports.toGroupAlias = exports.GroupAliasToken = exports.InjectionToken = exports.HooksRunner = exports.AddOnDisposeHookModule = exports.onDispose = exports.onDisposeHooksRunner = exports.AddOnConstructHookModule = exports.onConstruct = exports.onConstructHooksRunner = exports.injectProp = exports.createHookContext = exports.createHookContextFactory = exports.HookContext = exports.hasHooks = exports.hook = exports.getHooks = exports.UnexpectedHookResultError = exports.ContainerDisposedError = exports.MethodNotImplementedError = exports.DependencyMissingKeyError = exports.DependencyNotFoundError = exports.Registration = exports.bindTo = exports.register = exports.scope = exports.decorate = exports.MultiCache = exports.multiCache = exports.SingletonProvider = exports.singleton = exports.Provider = exports.resolveByArgs = exports.ProviderDecorator = exports.setArgs = exports.setArgsFn = exports.lazy = exports.scopeAccess = exports.ProxyInjector = exports.SimpleInjector = exports.MetadataInjector = exports.Injector = exports.argsFn = exports.args = exports.resolveArgs = exports.inject = exports.EmptyContainer = 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.methodTag = exports.getMethodLabels = exports.methodLabel = exports.getMethodMeta = exports.methodMeta = exports.getParamTags = exports.paramTag = exports.getParamLabels = exports.paramLabel = exports.getParamMeta = exports.paramMeta = exports.getClassTags = exports.classTag = exports.getClassLabels = exports.classLabel = exports.getClassMeta = exports.classMeta = exports.GroupInstanceToken = exports.ConstantToken = exports.FunctionToken = exports.SingleToken = exports.ClassToken = exports.toSingleAlias = void 0;
|
|
5
5
|
// Containers
|
|
6
6
|
var IContainer_1 = require("./container/IContainer");
|
|
7
7
|
Object.defineProperty(exports, "isDependencyKey", { enumerable: true, get: function () { return IContainer_1.isDependencyKey; } });
|
|
@@ -13,6 +13,8 @@ Object.defineProperty(exports, "EmptyContainer", { enumerable: true, get: functi
|
|
|
13
13
|
var inject_1 = require("./injector/inject");
|
|
14
14
|
Object.defineProperty(exports, "inject", { enumerable: true, get: function () { return inject_1.inject; } });
|
|
15
15
|
Object.defineProperty(exports, "resolveArgs", { enumerable: true, get: function () { return inject_1.resolveArgs; } });
|
|
16
|
+
Object.defineProperty(exports, "args", { enumerable: true, get: function () { return inject_1.args; } });
|
|
17
|
+
Object.defineProperty(exports, "argsFn", { enumerable: true, get: function () { return inject_1.argsFn; } });
|
|
16
18
|
var IInjector_1 = require("./injector/IInjector");
|
|
17
19
|
Object.defineProperty(exports, "Injector", { enumerable: true, get: function () { return IInjector_1.Injector; } });
|
|
18
20
|
var MetadataInjector_1 = require("./injector/MetadataInjector");
|
|
@@ -25,8 +27,8 @@ Object.defineProperty(exports, "ProxyInjector", { enumerable: true, get: functio
|
|
|
25
27
|
var IProvider_1 = require("./provider/IProvider");
|
|
26
28
|
Object.defineProperty(exports, "scopeAccess", { enumerable: true, get: function () { return IProvider_1.scopeAccess; } });
|
|
27
29
|
Object.defineProperty(exports, "lazy", { enumerable: true, get: function () { return IProvider_1.lazy; } });
|
|
28
|
-
Object.defineProperty(exports, "
|
|
29
|
-
Object.defineProperty(exports, "
|
|
30
|
+
Object.defineProperty(exports, "setArgsFn", { enumerable: true, get: function () { return IProvider_1.setArgsFn; } });
|
|
31
|
+
Object.defineProperty(exports, "setArgs", { enumerable: true, get: function () { return IProvider_1.setArgs; } });
|
|
30
32
|
Object.defineProperty(exports, "ProviderDecorator", { enumerable: true, get: function () { return IProvider_1.ProviderDecorator; } });
|
|
31
33
|
Object.defineProperty(exports, "resolveByArgs", { enumerable: true, get: function () { return IProvider_1.resolveByArgs; } });
|
|
32
34
|
var Provider_1 = require("./provider/Provider");
|
|
@@ -98,17 +100,6 @@ Object.defineProperty(exports, "ConstantToken", { enumerable: true, get: functio
|
|
|
98
100
|
var GroupInstanceToken_1 = require("./token/GroupInstanceToken");
|
|
99
101
|
Object.defineProperty(exports, "GroupInstanceToken", { enumerable: true, get: function () { return GroupInstanceToken_1.GroupInstanceToken; } });
|
|
100
102
|
// Metadata
|
|
101
|
-
var errorHandler_1 = require("./metadata/errorHandler");
|
|
102
|
-
Object.defineProperty(exports, "handleError", { enumerable: true, get: function () { return errorHandler_1.handleError; } });
|
|
103
|
-
Object.defineProperty(exports, "handleAsyncError", { enumerable: true, get: function () { return errorHandler_1.handleAsyncError; } });
|
|
104
|
-
var throttle_1 = require("./metadata/throttle");
|
|
105
|
-
Object.defineProperty(exports, "throttle", { enumerable: true, get: function () { return throttle_1.throttle; } });
|
|
106
|
-
var debounce_1 = require("./metadata/debounce");
|
|
107
|
-
Object.defineProperty(exports, "debounce", { enumerable: true, get: function () { return debounce_1.debounce; } });
|
|
108
|
-
var shallowCache_1 = require("./metadata/shallowCache");
|
|
109
|
-
Object.defineProperty(exports, "shallowCache", { enumerable: true, get: function () { return shallowCache_1.shallowCache; } });
|
|
110
|
-
var once_1 = require("./metadata/once");
|
|
111
|
-
Object.defineProperty(exports, "once", { enumerable: true, get: function () { return once_1.once; } });
|
|
112
103
|
var class_1 = require("./metadata/class");
|
|
113
104
|
Object.defineProperty(exports, "classMeta", { enumerable: true, get: function () { return class_1.classMeta; } });
|
|
114
105
|
Object.defineProperty(exports, "getClassMeta", { enumerable: true, get: function () { return class_1.getClassMeta; } });
|
|
@@ -130,6 +121,17 @@ Object.defineProperty(exports, "methodLabel", { enumerable: true, get: function
|
|
|
130
121
|
Object.defineProperty(exports, "getMethodLabels", { enumerable: true, get: function () { return method_1.getMethodLabels; } });
|
|
131
122
|
Object.defineProperty(exports, "methodTag", { enumerable: true, get: function () { return method_1.methodTag; } });
|
|
132
123
|
Object.defineProperty(exports, "getMethodTags", { enumerable: true, get: function () { return method_1.getMethodTags; } });
|
|
124
|
+
var errorHandler_1 = require("./utils/errorHandler");
|
|
125
|
+
Object.defineProperty(exports, "handleError", { enumerable: true, get: function () { return errorHandler_1.handleError; } });
|
|
126
|
+
Object.defineProperty(exports, "handleAsyncError", { enumerable: true, get: function () { return errorHandler_1.handleAsyncError; } });
|
|
127
|
+
var throttle_1 = require("./utils/throttle");
|
|
128
|
+
Object.defineProperty(exports, "throttle", { enumerable: true, get: function () { return throttle_1.throttle; } });
|
|
129
|
+
var debounce_1 = require("./utils/debounce");
|
|
130
|
+
Object.defineProperty(exports, "debounce", { enumerable: true, get: function () { return debounce_1.debounce; } });
|
|
131
|
+
var shallowCache_1 = require("./utils/shallowCache");
|
|
132
|
+
Object.defineProperty(exports, "shallowCache", { enumerable: true, get: function () { return shallowCache_1.shallowCache; } });
|
|
133
|
+
var once_1 = require("./utils/once");
|
|
134
|
+
Object.defineProperty(exports, "once", { enumerable: true, get: function () { return once_1.once; } });
|
|
133
135
|
// Utils
|
|
134
136
|
var select_1 = require("./select");
|
|
135
137
|
Object.defineProperty(exports, "select", { enumerable: true, get: function () { return select_1.select; } });
|
|
@@ -5,7 +5,7 @@ const IInjector_1 = require("./IInjector");
|
|
|
5
5
|
const inject_1 = require("./inject");
|
|
6
6
|
class MetadataInjector extends IInjector_1.Injector {
|
|
7
7
|
createInstance(scope, Target, { args: deps = [] } = {}) {
|
|
8
|
-
const args = (0, inject_1.resolveArgs)(Target)(scope,
|
|
8
|
+
const args = (0, inject_1.resolveArgs)(Target)(scope, { args: deps });
|
|
9
9
|
return new Target(...args);
|
|
10
10
|
}
|
|
11
11
|
}
|
|
@@ -2,21 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ProxyInjector = void 0;
|
|
4
4
|
const IInjector_1 = require("./IInjector");
|
|
5
|
-
function getProp(target, key) {
|
|
6
|
-
// @ts-ignore
|
|
7
|
-
return target[key];
|
|
8
|
-
}
|
|
9
5
|
class ProxyInjector extends IInjector_1.Injector {
|
|
10
|
-
createInstance(scope, Target, { args
|
|
11
|
-
const args = deps.reduce((acc, it) => ({ ...acc, ...it }), {});
|
|
6
|
+
createInstance(scope, Target, { args = [] } = {}) {
|
|
12
7
|
const proxy = new Proxy({}, {
|
|
13
8
|
get(target, prop) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
? scope.resolveByAlias(prop)
|
|
19
|
-
: scope.resolve(prop);
|
|
9
|
+
if (prop === 'args') {
|
|
10
|
+
return args;
|
|
11
|
+
}
|
|
12
|
+
return prop.toString().search(/alias/gi) >= 0 ? scope.resolveByAlias(prop) : scope.resolve(prop);
|
|
20
13
|
},
|
|
21
14
|
});
|
|
22
15
|
return new Target(proxy);
|
package/cjm/injector/inject.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveArgs = exports.inject = void 0;
|
|
3
|
+
exports.resolveArgs = exports.argsFn = exports.args = exports.inject = void 0;
|
|
4
|
+
const InjectionToken_1 = require("../token/InjectionToken");
|
|
4
5
|
const ConstantToken_1 = require("../token/ConstantToken");
|
|
5
6
|
const toToken_1 = require("../token/toToken");
|
|
6
7
|
const array_1 = require("../utils/array");
|
|
@@ -11,12 +12,20 @@ const inject = (fn) => (target, propertyKey, parameterIndex) => {
|
|
|
11
12
|
(0, parameter_1.paramMeta)(hookMetaKey(propertyKey), () => (0, toToken_1.toToken)(fn))(basic_1.Is.instance(target) ? target.constructor : target, propertyKey, parameterIndex);
|
|
12
13
|
};
|
|
13
14
|
exports.inject = inject;
|
|
15
|
+
const args = (index) => (c, { args = [] }) => {
|
|
16
|
+
return args[index];
|
|
17
|
+
};
|
|
18
|
+
exports.args = args;
|
|
19
|
+
const argsFn = (fn) => (c, options) => fn(options.args ?? []);
|
|
20
|
+
exports.argsFn = argsFn;
|
|
14
21
|
const resolveArgs = (Target, methodName) => {
|
|
15
22
|
const argsTokens = (0, parameter_1.getParamMeta)(hookMetaKey(methodName), Target);
|
|
16
|
-
return (scope,
|
|
17
|
-
const depsTokens =
|
|
23
|
+
return (scope, { args = [], lazy }) => {
|
|
24
|
+
const depsTokens = args.map((v) => {
|
|
25
|
+
return !(0, InjectionToken_1.isInjectionToken)(v) ? new ConstantToken_1.ConstantToken(v) : v;
|
|
26
|
+
});
|
|
18
27
|
const allTokens = (0, array_1.fillEmptyIndexes)(argsTokens, depsTokens);
|
|
19
|
-
return allTokens.map((fn) => fn.resolve(scope));
|
|
28
|
+
return allTokens.map((fn) => fn.resolve(scope, { args, lazy }));
|
|
20
29
|
};
|
|
21
30
|
};
|
|
22
31
|
exports.resolveArgs = resolveArgs;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ProviderDecorator = exports.lazy = exports.scopeAccess = exports.resolveByArgs = exports.
|
|
3
|
+
exports.ProviderDecorator = exports.lazy = exports.scopeAccess = exports.resolveByArgs = exports.setArgsFn = exports.setArgs = void 0;
|
|
4
4
|
const ProviderPipe_1 = require("./ProviderPipe");
|
|
5
5
|
const InjectionToken_1 = require("../token/InjectionToken");
|
|
6
6
|
const basic_1 = require("../utils/basic");
|
|
7
|
-
const
|
|
8
|
-
exports.
|
|
9
|
-
const
|
|
10
|
-
exports.
|
|
11
|
-
const resolveByArgs = (s,
|
|
7
|
+
const setArgs = (...extraArgs) => (0, ProviderPipe_1.registerPipe)((p) => p.setArgs(() => extraArgs));
|
|
8
|
+
exports.setArgs = setArgs;
|
|
9
|
+
const setArgsFn = (fn) => (0, ProviderPipe_1.registerPipe)((p) => p.setArgs(fn));
|
|
10
|
+
exports.setArgsFn = setArgsFn;
|
|
11
|
+
const resolveByArgs = (s, { args = [] } = {}) => args.map((d) => {
|
|
12
12
|
if (d instanceof InjectionToken_1.InjectionToken) {
|
|
13
13
|
return d.resolve(s);
|
|
14
14
|
}
|
package/cjm/provider/Provider.js
CHANGED
|
@@ -14,7 +14,7 @@ class Provider {
|
|
|
14
14
|
static fromKey(key) {
|
|
15
15
|
return new Provider((c) => c.resolve(key));
|
|
16
16
|
}
|
|
17
|
-
argsFn = () =>
|
|
17
|
+
argsFn = (s, { args = [] } = {}) => args;
|
|
18
18
|
checkAccess = () => true;
|
|
19
19
|
isLazy = false;
|
|
20
20
|
constructor(resolveDependency) {
|
|
@@ -26,7 +26,7 @@ class Provider {
|
|
|
26
26
|
}
|
|
27
27
|
resolve(container, { args = [], lazy } = {}) {
|
|
28
28
|
return this.resolveDependency(container, {
|
|
29
|
-
args:
|
|
29
|
+
args: this.argsFn(container, { args }),
|
|
30
30
|
lazy: lazy ?? this.isLazy,
|
|
31
31
|
});
|
|
32
32
|
}
|
|
@@ -13,9 +13,9 @@ class Registration {
|
|
|
13
13
|
createProvider;
|
|
14
14
|
key;
|
|
15
15
|
scopeRules;
|
|
16
|
-
static fromClass(Target) {
|
|
16
|
+
static fromClass(Target, { name } = {}) {
|
|
17
17
|
const transform = (0, fp_1.pipe)(...(0, IRegistration_1.getTransformers)(Target));
|
|
18
|
-
return transform(new Registration(() => Provider_1.Provider.fromClass(Target), Target.name));
|
|
18
|
+
return transform(new Registration(() => Provider_1.Provider.fromClass(Target), name ?? Target.name));
|
|
19
19
|
}
|
|
20
20
|
static fromValue(value) {
|
|
21
21
|
if (basic_1.Is.constructor(value)) {
|
package/cjm/token/ClassToken.js
CHANGED
|
@@ -3,33 +3,43 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ClassToken = void 0;
|
|
4
4
|
const InjectionToken_1 = require("./InjectionToken");
|
|
5
5
|
class ClassToken extends InjectionToken_1.InjectionToken {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
target;
|
|
7
|
+
_getArgsFn;
|
|
8
|
+
_isLazy;
|
|
9
|
+
constructor(target, { getArgsFn = () => [], isLazy = false } = {}) {
|
|
9
10
|
super();
|
|
10
|
-
this.
|
|
11
|
-
this.
|
|
11
|
+
this.target = target;
|
|
12
|
+
this._getArgsFn = getArgsFn;
|
|
13
|
+
this._isLazy = isLazy;
|
|
12
14
|
}
|
|
13
15
|
select(fn) {
|
|
14
16
|
return (s) => fn(this.resolve(s));
|
|
15
17
|
}
|
|
16
|
-
resolve(s) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
lazy: this.options.lazy,
|
|
18
|
+
resolve(s, { args = [], lazy } = {}) {
|
|
19
|
+
return s.resolve(this.target, {
|
|
20
|
+
args: this._getArgsFn(s, { args }),
|
|
21
|
+
lazy: this._isLazy || lazy,
|
|
21
22
|
});
|
|
22
23
|
}
|
|
23
|
-
args(...
|
|
24
|
-
const
|
|
25
|
-
return new ClassToken(this.
|
|
24
|
+
args(...newArgs) {
|
|
25
|
+
const parentFn = this._getArgsFn;
|
|
26
|
+
return new ClassToken(this.target, {
|
|
27
|
+
getArgsFn: (s, opts) => [...parentFn(s, opts), ...newArgs],
|
|
28
|
+
isLazy: this._isLazy,
|
|
29
|
+
});
|
|
26
30
|
}
|
|
27
|
-
argsFn(
|
|
28
|
-
const
|
|
29
|
-
return new ClassToken(this.
|
|
31
|
+
argsFn(fn) {
|
|
32
|
+
const parentFn = this._getArgsFn;
|
|
33
|
+
return new ClassToken(this.target, {
|
|
34
|
+
getArgsFn: (s, opts) => [...parentFn(s, opts), ...fn(s)],
|
|
35
|
+
isLazy: this._isLazy,
|
|
36
|
+
});
|
|
30
37
|
}
|
|
31
38
|
lazy() {
|
|
32
|
-
return new ClassToken(this.
|
|
39
|
+
return new ClassToken(this.target, {
|
|
40
|
+
getArgsFn: this._getArgsFn,
|
|
41
|
+
isLazy: true,
|
|
42
|
+
});
|
|
33
43
|
}
|
|
34
44
|
}
|
|
35
45
|
exports.ClassToken = ClassToken;
|