ts-ioc-container 49.0.0 → 50.0.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 +104 -151
- package/cjm/index.js +2 -5
- package/cjm/injector/inject.js +2 -7
- package/cjm/provider/SingletonProvider.js +9 -9
- package/cjm/token/toToken.js +4 -1
- package/esm/index.js +0 -1
- package/esm/injector/inject.js +3 -8
- package/esm/provider/SingletonProvider.js +9 -9
- package/esm/token/toToken.js +3 -1
- package/package.json +1 -1
- package/typings/index.d.ts +0 -1
- package/typings/provider/SingletonProvider.d.ts +7 -5
- package/typings/token/toToken.d.ts +1 -0
- package/cjm/provider/Cache.js +0 -41
- package/esm/provider/Cache.js +0 -35
- package/typings/provider/Cache.d.ts +0 -24
package/README.md
CHANGED
|
@@ -1461,51 +1461,22 @@ This type of injector just passes container to constructor with others arguments
|
|
|
1461
1461
|
```typescript
|
|
1462
1462
|
import { bindTo, Container, type IContainer, register, Registration as R, SimpleInjector } from 'ts-ioc-container';
|
|
1463
1463
|
|
|
1464
|
-
/**
|
|
1465
|
-
* Command Pattern - Simple Injector
|
|
1466
|
-
*
|
|
1467
|
-
* The SimpleInjector passes the container itself as the first argument to the constructor.
|
|
1468
|
-
* This is useful for:
|
|
1469
|
-
* - Service Locators (like Command Dispatchers or Routers)
|
|
1470
|
-
* - Factory classes that need to resolve dependencies dynamically
|
|
1471
|
-
* - Legacy code migration where passing the container is common
|
|
1472
|
-
*
|
|
1473
|
-
* In this example, a CommandDispatcher uses the container to dynamically
|
|
1474
|
-
* resolve the correct handler for each command type.
|
|
1475
|
-
*/
|
|
1476
|
-
|
|
1477
|
-
interface ICommand {
|
|
1478
|
-
type: string;
|
|
1479
|
-
}
|
|
1480
|
-
|
|
1481
|
-
interface ICommandHandler {
|
|
1482
|
-
handle(command: ICommand): string;
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
class CreateUserCommand implements ICommand {
|
|
1486
|
-
readonly type = 'CreateUser';
|
|
1487
|
-
constructor(readonly username: string) {}
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
1464
|
@register(bindTo('HandlerCreateUser'))
|
|
1491
|
-
class CreateUserHandler
|
|
1492
|
-
handle(
|
|
1493
|
-
return `User ${
|
|
1465
|
+
class CreateUserHandler {
|
|
1466
|
+
handle(username: string): string {
|
|
1467
|
+
return `User ${username} created`;
|
|
1494
1468
|
}
|
|
1495
1469
|
}
|
|
1496
1470
|
|
|
1497
1471
|
describe('SimpleInjector', function () {
|
|
1498
|
-
it('should inject container to allow dynamic resolution
|
|
1499
|
-
// Dispatcher needs the container to find handlers dynamically based on command type
|
|
1472
|
+
it('should inject container to allow dynamic resolution', function () {
|
|
1500
1473
|
@register(bindTo('Dispatcher'))
|
|
1501
1474
|
class CommandDispatcher {
|
|
1502
1475
|
constructor(private container: IContainer) {}
|
|
1503
1476
|
|
|
1504
|
-
dispatch(
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
const handler = this.container.resolve<ICommandHandler>(handlerKey);
|
|
1508
|
-
return handler.handle(command);
|
|
1477
|
+
dispatch(type: string, payload: string): string {
|
|
1478
|
+
const handler = this.container.resolve<CreateUserHandler>(`Handler${type}`);
|
|
1479
|
+
return handler.handle(payload);
|
|
1509
1480
|
}
|
|
1510
1481
|
}
|
|
1511
1482
|
|
|
@@ -1514,30 +1485,27 @@ describe('SimpleInjector', function () {
|
|
|
1514
1485
|
.addRegistration(R.fromClass(CreateUserHandler));
|
|
1515
1486
|
|
|
1516
1487
|
const dispatcher = container.resolve<CommandDispatcher>('Dispatcher');
|
|
1517
|
-
const result = dispatcher.dispatch(new CreateUserCommand('alice'));
|
|
1518
1488
|
|
|
1519
|
-
expect(
|
|
1489
|
+
expect(dispatcher.dispatch('CreateUser', 'alice')).toBe('User alice created');
|
|
1520
1490
|
});
|
|
1521
1491
|
|
|
1522
1492
|
it('should pass additional arguments alongside the container', function () {
|
|
1523
|
-
// Factory that creates widgets with a specific theme
|
|
1524
1493
|
class WidgetFactory {
|
|
1525
1494
|
constructor(
|
|
1526
1495
|
private container: IContainer,
|
|
1527
|
-
private theme: string,
|
|
1496
|
+
private theme: string,
|
|
1528
1497
|
) {}
|
|
1529
1498
|
|
|
1530
1499
|
createWidget(name: string): string {
|
|
1531
|
-
return `Widget ${name} with ${this.theme} theme (
|
|
1500
|
+
return `Widget ${name} with ${this.theme} theme (container: ${!!this.container})`;
|
|
1532
1501
|
}
|
|
1533
1502
|
}
|
|
1534
1503
|
|
|
1535
1504
|
const container = new Container({ injector: new SimpleInjector() }).addRegistration(R.fromClass(WidgetFactory));
|
|
1536
1505
|
|
|
1537
|
-
// Pass "dark" as the theme argument
|
|
1538
1506
|
const factory = container.resolve<WidgetFactory>('WidgetFactory', { args: ['dark'] });
|
|
1539
1507
|
|
|
1540
|
-
expect(factory.createWidget('Button')).toBe('Widget Button with dark theme (
|
|
1508
|
+
expect(factory.createWidget('Button')).toBe('Widget Button with dark theme (container: true)');
|
|
1541
1509
|
});
|
|
1542
1510
|
});
|
|
1543
1511
|
|
|
@@ -1562,16 +1530,11 @@ describe('ProxyInjector', function () {
|
|
|
1562
1530
|
}
|
|
1563
1531
|
}
|
|
1564
1532
|
|
|
1565
|
-
interface UserControllerDeps {
|
|
1566
|
-
logger: Logger;
|
|
1567
|
-
prefix: string;
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
1533
|
class UserController {
|
|
1571
1534
|
private logger: Logger;
|
|
1572
1535
|
private prefix: string;
|
|
1573
1536
|
|
|
1574
|
-
constructor({ logger, prefix }:
|
|
1537
|
+
constructor({ logger, prefix }: { logger: Logger; prefix: string }) {
|
|
1575
1538
|
this.logger = logger;
|
|
1576
1539
|
this.prefix = prefix;
|
|
1577
1540
|
}
|
|
@@ -1586,21 +1549,14 @@ describe('ProxyInjector', function () {
|
|
|
1586
1549
|
.addRegistration(R.fromValue('USER:').bindToKey('prefix'))
|
|
1587
1550
|
.addRegistration(R.fromClass(UserController));
|
|
1588
1551
|
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
expect(controller.createUser('bob')).toBe('Logged: USER: bob');
|
|
1552
|
+
expect(container.resolve<UserController>('UserController').createUser('bob')).toBe('Logged: USER: bob');
|
|
1592
1553
|
});
|
|
1593
1554
|
|
|
1594
1555
|
it('should expose runtime args through the reserved "args" property', function () {
|
|
1595
|
-
@register(bindTo('database'))
|
|
1596
|
-
class Database {}
|
|
1597
|
-
|
|
1598
1556
|
class ReportGenerator {
|
|
1599
|
-
database: Database;
|
|
1600
1557
|
format: string;
|
|
1601
1558
|
|
|
1602
|
-
constructor({
|
|
1603
|
-
this.database = database;
|
|
1559
|
+
constructor({ args }: { args: string[] }) {
|
|
1604
1560
|
this.format = args[0];
|
|
1605
1561
|
}
|
|
1606
1562
|
|
|
@@ -1609,40 +1565,12 @@ describe('ProxyInjector', function () {
|
|
|
1609
1565
|
}
|
|
1610
1566
|
}
|
|
1611
1567
|
|
|
1612
|
-
const container = new Container({ injector: new ProxyInjector() })
|
|
1613
|
-
.addRegistration(R.fromClass(Database))
|
|
1614
|
-
.addRegistration(R.fromClass(ReportGenerator));
|
|
1568
|
+
const container = new Container({ injector: new ProxyInjector() }).addRegistration(R.fromClass(ReportGenerator));
|
|
1615
1569
|
|
|
1616
|
-
const generator = container.resolve<ReportGenerator>('ReportGenerator', {
|
|
1617
|
-
args: ['PDF'],
|
|
1618
|
-
});
|
|
1570
|
+
const generator = container.resolve<ReportGenerator>('ReportGenerator', { args: ['PDF'] });
|
|
1619
1571
|
|
|
1620
|
-
expect(generator.database).toBeInstanceOf(Database);
|
|
1621
1572
|
expect(generator.generate()).toBe('Report in PDF');
|
|
1622
1573
|
});
|
|
1623
|
-
|
|
1624
|
-
it('should resolve dependencies by alias when property name contains "alias"', function () {
|
|
1625
|
-
class FileLogger {}
|
|
1626
|
-
class ConsoleLogger {}
|
|
1627
|
-
|
|
1628
|
-
interface AppDeps {
|
|
1629
|
-
loggersAlias: any[];
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1632
|
-
class App {
|
|
1633
|
-
constructor(public deps: AppDeps) {}
|
|
1634
|
-
}
|
|
1635
|
-
|
|
1636
|
-
const container = new Container({ injector: new ProxyInjector() });
|
|
1637
|
-
|
|
1638
|
-
const mockLoggers = [new FileLogger(), new ConsoleLogger()];
|
|
1639
|
-
container.resolveByAlias = vi.fn().mockReturnValue(mockLoggers);
|
|
1640
|
-
|
|
1641
|
-
const app = container.resolve(App);
|
|
1642
|
-
|
|
1643
|
-
expect(app.deps.loggersAlias).toBe(mockLoggers);
|
|
1644
|
-
expect(container.resolveByAlias).toHaveBeenCalledWith('loggersAlias');
|
|
1645
|
-
});
|
|
1646
1574
|
});
|
|
1647
1575
|
|
|
1648
1576
|
```
|
|
@@ -1934,17 +1862,16 @@ const userToken = ApiToken.args('https://users.api.com', 1000);
|
|
|
1934
1862
|
|
|
1935
1863
|
```typescript
|
|
1936
1864
|
import {
|
|
1937
|
-
|
|
1938
|
-
setArgsFn,
|
|
1865
|
+
args,
|
|
1939
1866
|
bindTo,
|
|
1940
1867
|
Container,
|
|
1941
1868
|
inject,
|
|
1942
|
-
args,
|
|
1943
|
-
MultiCache,
|
|
1944
1869
|
register,
|
|
1945
1870
|
Registration as R,
|
|
1946
|
-
|
|
1871
|
+
setArgs,
|
|
1872
|
+
setArgsFn,
|
|
1947
1873
|
SingleToken,
|
|
1874
|
+
singleton,
|
|
1948
1875
|
} from 'ts-ioc-container';
|
|
1949
1876
|
|
|
1950
1877
|
/**
|
|
@@ -2044,7 +1971,7 @@ describe('ArgsProvider', function () {
|
|
|
2044
1971
|
|
|
2045
1972
|
@register(
|
|
2046
1973
|
bindTo(EntityManagerToken),
|
|
2047
|
-
singleton(
|
|
1974
|
+
singleton((arg1) => (arg1 as SingleToken).token), // Cache unique instance per repository type
|
|
2048
1975
|
)
|
|
2049
1976
|
class EntityManager {
|
|
2050
1977
|
constructor(@inject(args(0)) public repository: IRepository) {}
|
|
@@ -2574,37 +2501,91 @@ Sometimes you need to register provider only in scope which matches to certain c
|
|
|
2574
2501
|
- `Registration.fromClass(Logger).when((container) => container.hasTag('root'))`
|
|
2575
2502
|
|
|
2576
2503
|
```typescript
|
|
2577
|
-
import
|
|
2504
|
+
import 'reflect-metadata';
|
|
2505
|
+
import {
|
|
2506
|
+
bindTo,
|
|
2507
|
+
Container,
|
|
2508
|
+
DependencyNotFoundError,
|
|
2509
|
+
type IContainer,
|
|
2510
|
+
inject,
|
|
2511
|
+
register,
|
|
2512
|
+
Registration as R,
|
|
2513
|
+
scope,
|
|
2514
|
+
select,
|
|
2515
|
+
singleton,
|
|
2516
|
+
} from 'ts-ioc-container';
|
|
2578
2517
|
|
|
2579
2518
|
/**
|
|
2580
|
-
*
|
|
2519
|
+
* User Management Domain - Request Scopes
|
|
2520
|
+
*
|
|
2521
|
+
* In web applications, each HTTP request typically gets its own scope.
|
|
2522
|
+
* This allows request-specific data (current user, request ID, etc.)
|
|
2523
|
+
* to be isolated between concurrent requests.
|
|
2581
2524
|
*
|
|
2582
|
-
*
|
|
2583
|
-
*
|
|
2584
|
-
*
|
|
2525
|
+
* Scope hierarchy:
|
|
2526
|
+
* Application (singleton services)
|
|
2527
|
+
* └── Request (per-request services)
|
|
2528
|
+
* └── Transaction (database transaction boundary)
|
|
2585
2529
|
*/
|
|
2586
2530
|
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
@register(
|
|
2592
|
-
bindTo('SharedState'),
|
|
2593
|
-
scope((s) => s.hasTag('application')), // Only register in application scope
|
|
2594
|
-
singleton(), // One instance per application
|
|
2595
|
-
)
|
|
2596
|
-
class SharedState {
|
|
2597
|
-
data = 'shared';
|
|
2598
|
-
}
|
|
2531
|
+
// SessionService is only available in request scope - not at application level
|
|
2532
|
+
@register(bindTo('ISessionService'), scope((s) => s.hasTag('request')), singleton())
|
|
2533
|
+
class SessionService {
|
|
2534
|
+
private userId: string | null = null;
|
|
2599
2535
|
|
|
2600
|
-
|
|
2601
|
-
|
|
2536
|
+
setCurrentUser(userId: string) {
|
|
2537
|
+
this.userId = userId;
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2540
|
+
getCurrentUserId(): string | null {
|
|
2541
|
+
return this.userId;
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
describe('Scopes', function () {
|
|
2546
|
+
it('should isolate request-scoped services', function () {
|
|
2547
|
+
// Application container - lives for entire app lifetime
|
|
2548
|
+
const appContainer = new Container({ tags: ['application'] }).addRegistration(R.fromClass(SessionService));
|
|
2549
|
+
|
|
2550
|
+
// Simulate two concurrent HTTP requests
|
|
2551
|
+
const request1Scope = appContainer.createScope({ tags: ['request'] });
|
|
2552
|
+
const request2Scope = appContainer.createScope({ tags: ['request'] });
|
|
2602
2553
|
|
|
2603
|
-
//
|
|
2604
|
-
const
|
|
2605
|
-
const
|
|
2554
|
+
// Each request has its own SessionService instance
|
|
2555
|
+
const session1 = request1Scope.resolve<SessionService>('ISessionService');
|
|
2556
|
+
const session2 = request2Scope.resolve<SessionService>('ISessionService');
|
|
2606
2557
|
|
|
2607
|
-
|
|
2558
|
+
session1.setCurrentUser('user-1');
|
|
2559
|
+
session2.setCurrentUser('user-2');
|
|
2560
|
+
|
|
2561
|
+
// Sessions are isolated - user data doesn't leak between requests
|
|
2562
|
+
expect(session1.getCurrentUserId()).toBe('user-1');
|
|
2563
|
+
expect(session2.getCurrentUserId()).toBe('user-2');
|
|
2564
|
+
expect(session1).not.toBe(session2);
|
|
2565
|
+
|
|
2566
|
+
// SessionService is NOT available at application level (security!)
|
|
2567
|
+
expect(() => appContainer.resolve('ISessionService')).toThrow(DependencyNotFoundError);
|
|
2568
|
+
});
|
|
2569
|
+
|
|
2570
|
+
it('should create child scopes for transactions', function () {
|
|
2571
|
+
const appContainer = new Container({ tags: ['application'] });
|
|
2572
|
+
|
|
2573
|
+
// RequestHandler can create a transaction scope for database operations
|
|
2574
|
+
class RequestHandler {
|
|
2575
|
+
constructor(@inject(select.scope.create({ tags: ['transaction'] })) public transactionScope: IContainer) {}
|
|
2576
|
+
|
|
2577
|
+
executeInTransaction(): boolean {
|
|
2578
|
+
// Transaction scope inherits from request scope
|
|
2579
|
+
// Database operations can be rolled back together
|
|
2580
|
+
return this.transactionScope.hasTag('transaction');
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2584
|
+
const handler = appContainer.resolve(RequestHandler);
|
|
2585
|
+
|
|
2586
|
+
expect(handler.transactionScope).not.toBe(appContainer);
|
|
2587
|
+
expect(handler.transactionScope.hasTag('transaction')).toBe(true);
|
|
2588
|
+
expect(handler.executeInTransaction()).toBe(true);
|
|
2608
2589
|
});
|
|
2609
2590
|
});
|
|
2610
2591
|
|
|
@@ -2741,30 +2722,10 @@ Sometimes you need to invoke methods after construct or dispose of class. This i
|
|
|
2741
2722
|
### OnConstruct
|
|
2742
2723
|
|
|
2743
2724
|
```typescript
|
|
2744
|
-
import
|
|
2745
|
-
|
|
2746
|
-
Container,
|
|
2747
|
-
IHookContext,
|
|
2748
|
-
HookFn,
|
|
2749
|
-
inject,
|
|
2750
|
-
onConstruct,
|
|
2751
|
-
Registration as R,
|
|
2752
|
-
} from 'ts-ioc-container';
|
|
2753
|
-
|
|
2754
|
-
/**
|
|
2755
|
-
* Lifecycle - OnConstruct Hook
|
|
2756
|
-
*
|
|
2757
|
-
* The @onConstruct hook allows you to run logic immediately after an object is created.
|
|
2758
|
-
* This is useful for:
|
|
2759
|
-
* - Initialization logic that depends on injected services
|
|
2760
|
-
* - Setting up event listeners
|
|
2761
|
-
* - Establishing connections (though lazy is often better)
|
|
2762
|
-
* - Computing initial state
|
|
2763
|
-
*
|
|
2764
|
-
* Note: You must register the AddOnConstructHookModule or manually add the hook runner.
|
|
2765
|
-
*/
|
|
2725
|
+
import 'reflect-metadata';
|
|
2726
|
+
import { AddOnConstructHookModule, Container, type HookFn, inject, onConstruct, Registration as R } from 'ts-ioc-container';
|
|
2766
2727
|
|
|
2767
|
-
const execute: HookFn = (ctx
|
|
2728
|
+
const execute: HookFn = (ctx) => {
|
|
2768
2729
|
ctx.invokeMethod({ args: ctx.resolveArgs() });
|
|
2769
2730
|
};
|
|
2770
2731
|
|
|
@@ -2774,8 +2735,6 @@ describe('onConstruct', function () {
|
|
|
2774
2735
|
isConnected = false;
|
|
2775
2736
|
connectionString = '';
|
|
2776
2737
|
|
|
2777
|
-
// @onConstruct marks this method to be called after instantiation
|
|
2778
|
-
// Arguments are resolved from the container like constructor params
|
|
2779
2738
|
@onConstruct(execute)
|
|
2780
2739
|
connect(@inject('ConnectionString') connectionString: string) {
|
|
2781
2740
|
this.connectionString = connectionString;
|
|
@@ -2784,12 +2743,9 @@ describe('onConstruct', function () {
|
|
|
2784
2743
|
}
|
|
2785
2744
|
|
|
2786
2745
|
const container = new Container()
|
|
2787
|
-
// Enable @onConstruct support
|
|
2788
2746
|
.useModule(new AddOnConstructHookModule())
|
|
2789
|
-
// Register config
|
|
2790
2747
|
.addRegistration(R.fromValue('postgres://localhost:5432').bindTo('ConnectionString'));
|
|
2791
2748
|
|
|
2792
|
-
// Resolve class - constructor is called, then @onConstruct method
|
|
2793
2749
|
const db = container.resolve(DatabaseConnection);
|
|
2794
2750
|
|
|
2795
2751
|
expect(db.isConnected).toBe(true);
|
|
@@ -2802,6 +2758,7 @@ describe('onConstruct', function () {
|
|
|
2802
2758
|
### OnDispose
|
|
2803
2759
|
|
|
2804
2760
|
```typescript
|
|
2761
|
+
import 'reflect-metadata';
|
|
2805
2762
|
import {
|
|
2806
2763
|
AddOnDisposeHookModule,
|
|
2807
2764
|
bindTo,
|
|
@@ -2829,10 +2786,6 @@ class LogsRepo {
|
|
|
2829
2786
|
|
|
2830
2787
|
@register(bindTo('logger'))
|
|
2831
2788
|
class Logger {
|
|
2832
|
-
@onDispose(({ instance, methodName }) => {
|
|
2833
|
-
// @ts-ignore
|
|
2834
|
-
instance[methodName].push('world');
|
|
2835
|
-
}) // <--- or extract it to @onDispose
|
|
2836
2789
|
private messages: string[] = [];
|
|
2837
2790
|
|
|
2838
2791
|
constructor(@inject('logsRepo') private logsRepo: LogsRepo) {}
|
|
@@ -2848,7 +2801,7 @@ class Logger {
|
|
|
2848
2801
|
}
|
|
2849
2802
|
|
|
2850
2803
|
describe('onDispose', function () {
|
|
2851
|
-
it('should invoke hooks on all instances', function () {
|
|
2804
|
+
it('should invoke hooks on all instances when container is disposed', function () {
|
|
2852
2805
|
const container = new Container()
|
|
2853
2806
|
.useModule(new AddOnDisposeHookModule())
|
|
2854
2807
|
.addRegistration(R.fromClass(Logger))
|
|
@@ -2860,7 +2813,7 @@ describe('onDispose', function () {
|
|
|
2860
2813
|
|
|
2861
2814
|
container.dispose();
|
|
2862
2815
|
|
|
2863
|
-
expect(logsRepo.savedLogs
|
|
2816
|
+
expect(logsRepo.savedLogs).toEqual(['Hello']);
|
|
2864
2817
|
});
|
|
2865
2818
|
});
|
|
2866
2819
|
|
package/cjm/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toSingleAlias = 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.
|
|
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 =
|
|
3
|
+
exports.SingleToken = exports.ClassToken = exports.toSingleAlias = 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.SingletonProvider = exports.singleton = exports.Provider = 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 = 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; } });
|
|
@@ -35,9 +35,6 @@ Object.defineProperty(exports, "Provider", { enumerable: true, get: function ()
|
|
|
35
35
|
var SingletonProvider_1 = require("./provider/SingletonProvider");
|
|
36
36
|
Object.defineProperty(exports, "singleton", { enumerable: true, get: function () { return SingletonProvider_1.singleton; } });
|
|
37
37
|
Object.defineProperty(exports, "SingletonProvider", { enumerable: true, get: function () { return SingletonProvider_1.SingletonProvider; } });
|
|
38
|
-
var Cache_1 = require("./provider/Cache");
|
|
39
|
-
Object.defineProperty(exports, "multiCache", { enumerable: true, get: function () { return Cache_1.multiCache; } });
|
|
40
|
-
Object.defineProperty(exports, "MultiCache", { enumerable: true, get: function () { return Cache_1.MultiCache; } });
|
|
41
38
|
var DecoratorProvider_1 = require("./provider/DecoratorProvider");
|
|
42
39
|
Object.defineProperty(exports, "decorate", { enumerable: true, get: function () { return DecoratorProvider_1.decorate; } });
|
|
43
40
|
// Registrations
|
package/cjm/injector/inject.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.resolveArgs = exports.argsFn = exports.args = exports.inject = void 0;
|
|
4
|
-
const InjectionToken_1 = require("../token/InjectionToken");
|
|
5
|
-
const ConstantToken_1 = require("../token/ConstantToken");
|
|
6
4
|
const toToken_1 = require("../token/toToken");
|
|
7
5
|
const basic_1 = require("../utils/basic");
|
|
8
6
|
const parameter_1 = require("../metadata/parameter");
|
|
@@ -17,11 +15,8 @@ const args = (index) => (c, { args = [] }) => {
|
|
|
17
15
|
exports.args = args;
|
|
18
16
|
const argsFn = (fn) => (c, options) => fn(...(options.args ?? []));
|
|
19
17
|
exports.argsFn = argsFn;
|
|
20
|
-
const resolveTokens = (scope, deps) => deps.map((v) => ((0, InjectionToken_1.isInjectionToken)(v) ? v : new ConstantToken_1.ConstantToken(v))).map((t) => t.resolve(scope));
|
|
21
18
|
const resolveArgs = (Target, methodName) => {
|
|
22
|
-
const
|
|
23
|
-
return (scope, { args = [], lazy }) => {
|
|
24
|
-
return argsMetaTokens.map((fn) => fn.resolve(scope, { args: resolveTokens(scope, args), lazy }));
|
|
25
|
-
};
|
|
19
|
+
const tokens = (0, parameter_1.getParamMeta)(hookMetaKey(methodName), Target);
|
|
20
|
+
return (scope, { args = [], lazy }) => tokens.map((fn) => fn.resolve(scope, { args: args.map(toToken_1.argToToken).map((t) => t.resolve(scope)), lazy }));
|
|
26
21
|
};
|
|
27
22
|
exports.resolveArgs = resolveArgs;
|
|
@@ -2,25 +2,25 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.singleton = exports.SingletonProvider = void 0;
|
|
4
4
|
const IProvider_1 = require("./IProvider");
|
|
5
|
-
const Cache_1 = require("./Cache");
|
|
6
5
|
const ProviderPipe_1 = require("./ProviderPipe");
|
|
7
6
|
class SingletonProvider extends IProvider_1.ProviderDecorator {
|
|
8
7
|
provider;
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
getKey;
|
|
9
|
+
cache = new Map();
|
|
10
|
+
constructor(provider, getKey) {
|
|
11
11
|
super(provider);
|
|
12
12
|
this.provider = provider;
|
|
13
|
-
this.
|
|
13
|
+
this.getKey = getKey;
|
|
14
14
|
}
|
|
15
15
|
resolve(container, options) {
|
|
16
16
|
const { args = [] } = options;
|
|
17
|
-
const key = this.
|
|
18
|
-
if (!this.cache.
|
|
19
|
-
this.cache.
|
|
17
|
+
const key = this.getKey(...args);
|
|
18
|
+
if (!this.cache.has(key)) {
|
|
19
|
+
this.cache.set(key, this.provider.resolve(container, options));
|
|
20
20
|
}
|
|
21
|
-
return this.cache.
|
|
21
|
+
return this.cache.get(key);
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
exports.SingletonProvider = SingletonProvider;
|
|
25
|
-
const singleton = (
|
|
25
|
+
const singleton = (getCacheKey = () => '1') => (0, ProviderPipe_1.registerPipe)((p) => new SingletonProvider(p, getCacheKey));
|
|
26
26
|
exports.singleton = singleton;
|
package/cjm/token/toToken.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toToken = void 0;
|
|
3
|
+
exports.argToToken = exports.toToken = void 0;
|
|
4
4
|
const IContainer_1 = require("../container/IContainer");
|
|
5
5
|
const SingleToken_1 = require("./SingleToken");
|
|
6
6
|
const ClassToken_1 = require("./ClassToken");
|
|
@@ -8,6 +8,7 @@ const FunctionToken_1 = require("./FunctionToken");
|
|
|
8
8
|
const UnsupportedTokenTypeError_1 = require("../errors/UnsupportedTokenTypeError");
|
|
9
9
|
const InjectionToken_1 = require("./InjectionToken");
|
|
10
10
|
const basic_1 = require("../utils/basic");
|
|
11
|
+
const ConstantToken_1 = require("./ConstantToken");
|
|
11
12
|
const toToken = (token) => {
|
|
12
13
|
if (token instanceof InjectionToken_1.InjectionToken) {
|
|
13
14
|
return token;
|
|
@@ -24,3 +25,5 @@ const toToken = (token) => {
|
|
|
24
25
|
throw new UnsupportedTokenTypeError_1.UnsupportedTokenTypeError(`Unknown token ${token}`);
|
|
25
26
|
};
|
|
26
27
|
exports.toToken = toToken;
|
|
28
|
+
const argToToken = (v) => ((0, InjectionToken_1.isInjectionToken)(v) ? v : new ConstantToken_1.ConstantToken(v));
|
|
29
|
+
exports.argToToken = argToToken;
|
package/esm/index.js
CHANGED
|
@@ -12,7 +12,6 @@ export { ProxyInjector } from './injector/ProxyInjector';
|
|
|
12
12
|
export { scopeAccess, lazy, setArgsFn, setArgs, ProviderDecorator, } from './provider/IProvider';
|
|
13
13
|
export { Provider } from './provider/Provider';
|
|
14
14
|
export { singleton, SingletonProvider } from './provider/SingletonProvider';
|
|
15
|
-
export { multiCache, MultiCache } from './provider/Cache';
|
|
16
15
|
export { decorate } from './provider/DecoratorProvider';
|
|
17
16
|
// Registrations
|
|
18
17
|
export { scope, register, bindTo, } from './registration/IRegistration';
|
package/esm/injector/inject.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ConstantToken } from '../token/ConstantToken';
|
|
3
|
-
import { toToken } from '../token/toToken';
|
|
1
|
+
import { argToToken, toToken } from '../token/toToken';
|
|
4
2
|
import { Is } from '../utils/basic';
|
|
5
3
|
import { getParamMeta, paramMeta } from '../metadata/parameter';
|
|
6
4
|
const hookMetaKey = (methodName = 'constructor') => `inject:${methodName}`;
|
|
@@ -11,10 +9,7 @@ export const args = (index) => (c, { args = [] }) => {
|
|
|
11
9
|
return args[index];
|
|
12
10
|
};
|
|
13
11
|
export const argsFn = (fn) => (c, options) => fn(...(options.args ?? []));
|
|
14
|
-
const resolveTokens = (scope, deps) => deps.map((v) => (isInjectionToken(v) ? v : new ConstantToken(v))).map((t) => t.resolve(scope));
|
|
15
12
|
export const resolveArgs = (Target, methodName) => {
|
|
16
|
-
const
|
|
17
|
-
return (scope, { args = [], lazy }) => {
|
|
18
|
-
return argsMetaTokens.map((fn) => fn.resolve(scope, { args: resolveTokens(scope, args), lazy }));
|
|
19
|
-
};
|
|
13
|
+
const tokens = getParamMeta(hookMetaKey(methodName), Target);
|
|
14
|
+
return (scope, { args = [], lazy }) => tokens.map((fn) => fn.resolve(scope, { args: args.map(argToToken).map((t) => t.resolve(scope)), lazy }));
|
|
20
15
|
};
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { ProviderDecorator } from './IProvider';
|
|
2
|
-
import { SingleCache } from './Cache';
|
|
3
2
|
import { registerPipe } from './ProviderPipe';
|
|
4
3
|
export class SingletonProvider extends ProviderDecorator {
|
|
5
4
|
provider;
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
getKey;
|
|
6
|
+
cache = new Map();
|
|
7
|
+
constructor(provider, getKey) {
|
|
8
8
|
super(provider);
|
|
9
9
|
this.provider = provider;
|
|
10
|
-
this.
|
|
10
|
+
this.getKey = getKey;
|
|
11
11
|
}
|
|
12
12
|
resolve(container, options) {
|
|
13
13
|
const { args = [] } = options;
|
|
14
|
-
const key = this.
|
|
15
|
-
if (!this.cache.
|
|
16
|
-
this.cache.
|
|
14
|
+
const key = this.getKey(...args);
|
|
15
|
+
if (!this.cache.has(key)) {
|
|
16
|
+
this.cache.set(key, this.provider.resolve(container, options));
|
|
17
17
|
}
|
|
18
|
-
return this.cache.
|
|
18
|
+
return this.cache.get(key);
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
export const singleton = (
|
|
21
|
+
export const singleton = (getCacheKey = () => '1') => registerPipe((p) => new SingletonProvider(p, getCacheKey));
|
package/esm/token/toToken.js
CHANGED
|
@@ -3,8 +3,9 @@ import { SingleToken } from './SingleToken';
|
|
|
3
3
|
import { ClassToken } from './ClassToken';
|
|
4
4
|
import { FunctionToken } from './FunctionToken';
|
|
5
5
|
import { UnsupportedTokenTypeError } from '../errors/UnsupportedTokenTypeError';
|
|
6
|
-
import { InjectionToken } from './InjectionToken';
|
|
6
|
+
import { InjectionToken, isInjectionToken } from './InjectionToken';
|
|
7
7
|
import { Is } from '../utils/basic';
|
|
8
|
+
import { ConstantToken } from './ConstantToken';
|
|
8
9
|
export const toToken = (token) => {
|
|
9
10
|
if (token instanceof InjectionToken) {
|
|
10
11
|
return token;
|
|
@@ -20,3 +21,4 @@ export const toToken = (token) => {
|
|
|
20
21
|
}
|
|
21
22
|
throw new UnsupportedTokenTypeError(`Unknown token ${token}`);
|
|
22
23
|
};
|
|
24
|
+
export const argToToken = (v) => (isInjectionToken(v) ? v : new ConstantToken(v));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-ioc-container",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "50.0.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
|
"workspaces": [
|
|
6
6
|
"docs"
|
package/typings/index.d.ts
CHANGED
|
@@ -9,7 +9,6 @@ export { ProxyInjector } from './injector/ProxyInjector';
|
|
|
9
9
|
export { type ResolveDependency, type IProvider, scopeAccess, lazy, setArgsFn, setArgs, type ArgsFn, ProviderDecorator, type IMapper, type ProviderOptions, } from './provider/IProvider';
|
|
10
10
|
export { Provider } from './provider/Provider';
|
|
11
11
|
export { singleton, SingletonProvider } from './provider/SingletonProvider';
|
|
12
|
-
export { type Cache, multiCache, MultiCache } from './provider/Cache';
|
|
13
12
|
export { decorate, type DecorateFn } from './provider/DecoratorProvider';
|
|
14
13
|
export { type ProviderPipe } from './provider/ProviderPipe';
|
|
15
14
|
export { type IRegistration, type ReturnTypeOfRegistration, scope, register, type ScopeMatchRule, bindTo, } from './registration/IRegistration';
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { IContainer } from '../container/IContainer';
|
|
2
2
|
import type { IProvider } from './IProvider';
|
|
3
3
|
import { ProviderDecorator } from './IProvider';
|
|
4
|
-
import type { Cache } from './Cache';
|
|
5
4
|
import { InjectOptions } from '../injector/IInjector';
|
|
5
|
+
type GetCacheKey = (...args: unknown[]) => string | symbol;
|
|
6
6
|
export declare class SingletonProvider<T> extends ProviderDecorator<T> {
|
|
7
|
-
private
|
|
8
|
-
private
|
|
9
|
-
|
|
7
|
+
private provider;
|
|
8
|
+
private getKey;
|
|
9
|
+
private cache;
|
|
10
|
+
constructor(provider: IProvider<T>, getKey: GetCacheKey);
|
|
10
11
|
resolve(container: IContainer, options: InjectOptions): T;
|
|
11
12
|
}
|
|
12
|
-
export declare const singleton: <T = unknown>(
|
|
13
|
+
export declare const singleton: <T = unknown>(getCacheKey?: GetCacheKey) => import("./ProviderPipe").ProviderPipe<T>;
|
|
14
|
+
export {};
|
|
@@ -3,3 +3,4 @@ import { InjectionToken } from './InjectionToken';
|
|
|
3
3
|
import { InjectFn } from '../hooks/hook';
|
|
4
4
|
import { type constructor } from '../utils/basic';
|
|
5
5
|
export declare const toToken: <T = any>(token: InjectFn<T> | InjectionToken<T> | DependencyKey | constructor<T>) => InjectionToken<T>;
|
|
6
|
+
export declare const argToToken: (v: unknown) => InjectionToken<unknown>;
|
package/cjm/provider/Cache.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SingleCache = exports.multiCache = exports.MultiCache = void 0;
|
|
4
|
-
class MultiCache {
|
|
5
|
-
getKey;
|
|
6
|
-
instances = new Map();
|
|
7
|
-
static fromFirstArg() {
|
|
8
|
-
return new MultiCache((key) => key);
|
|
9
|
-
}
|
|
10
|
-
constructor(getKey = () => '1') {
|
|
11
|
-
this.getKey = getKey;
|
|
12
|
-
}
|
|
13
|
-
hasValue(token) {
|
|
14
|
-
return this.instances.has(token);
|
|
15
|
-
}
|
|
16
|
-
getValue(token) {
|
|
17
|
-
return this.instances.get(token);
|
|
18
|
-
}
|
|
19
|
-
setValue(token, value) {
|
|
20
|
-
this.instances.set(token, value);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
exports.MultiCache = MultiCache;
|
|
24
|
-
const multiCache = (getKey) => new MultiCache(getKey);
|
|
25
|
-
exports.multiCache = multiCache;
|
|
26
|
-
class SingleCache {
|
|
27
|
-
instance;
|
|
28
|
-
getKey(...args) {
|
|
29
|
-
return '1';
|
|
30
|
-
}
|
|
31
|
-
getValue(key) {
|
|
32
|
-
return this.instance.value;
|
|
33
|
-
}
|
|
34
|
-
hasValue(key) {
|
|
35
|
-
return this.instance !== undefined;
|
|
36
|
-
}
|
|
37
|
-
setValue(key, value) {
|
|
38
|
-
this.instance = { value };
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
exports.SingleCache = SingleCache;
|
package/esm/provider/Cache.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
export class MultiCache {
|
|
2
|
-
getKey;
|
|
3
|
-
instances = new Map();
|
|
4
|
-
static fromFirstArg() {
|
|
5
|
-
return new MultiCache((key) => key);
|
|
6
|
-
}
|
|
7
|
-
constructor(getKey = () => '1') {
|
|
8
|
-
this.getKey = getKey;
|
|
9
|
-
}
|
|
10
|
-
hasValue(token) {
|
|
11
|
-
return this.instances.has(token);
|
|
12
|
-
}
|
|
13
|
-
getValue(token) {
|
|
14
|
-
return this.instances.get(token);
|
|
15
|
-
}
|
|
16
|
-
setValue(token, value) {
|
|
17
|
-
this.instances.set(token, value);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
export const multiCache = (getKey) => new MultiCache(getKey);
|
|
21
|
-
export class SingleCache {
|
|
22
|
-
instance;
|
|
23
|
-
getKey(...args) {
|
|
24
|
-
return '1';
|
|
25
|
-
}
|
|
26
|
-
getValue(key) {
|
|
27
|
-
return this.instance.value;
|
|
28
|
-
}
|
|
29
|
-
hasValue(key) {
|
|
30
|
-
return this.instance !== undefined;
|
|
31
|
-
}
|
|
32
|
-
setValue(key, value) {
|
|
33
|
-
this.instance = { value };
|
|
34
|
-
}
|
|
35
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { DependencyKey } from '../container/IContainer';
|
|
2
|
-
export interface Cache<K, V> {
|
|
3
|
-
getKey(...args: unknown[]): K;
|
|
4
|
-
hasValue(key: K): boolean;
|
|
5
|
-
getValue(key: K): V;
|
|
6
|
-
setValue(key: K, value: V): void;
|
|
7
|
-
}
|
|
8
|
-
export declare class MultiCache<K, V> implements Cache<K, V> {
|
|
9
|
-
readonly getKey: (...args: unknown[]) => K;
|
|
10
|
-
private instances;
|
|
11
|
-
static fromFirstArg(): MultiCache<DependencyKey, unknown>;
|
|
12
|
-
constructor(getKey?: (...args: unknown[]) => K);
|
|
13
|
-
hasValue(token: K): boolean;
|
|
14
|
-
getValue(token: K): V;
|
|
15
|
-
setValue(token: K, value: V): void;
|
|
16
|
-
}
|
|
17
|
-
export declare const multiCache: <K, V>(getKey: (...args: unknown[]) => K) => MultiCache<K, V>;
|
|
18
|
-
export declare class SingleCache<V> implements Cache<string, V> {
|
|
19
|
-
private instance?;
|
|
20
|
-
getKey(...args: unknown[]): string;
|
|
21
|
-
getValue(key: string): V;
|
|
22
|
-
hasValue(key: string): boolean;
|
|
23
|
-
setValue(key: string, value: V): void;
|
|
24
|
-
}
|