ts-ioc-container 49.1.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 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 implements ICommandHandler {
1492
- handle(command: CreateUserCommand): string {
1493
- return `User ${command.username} created`;
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 (Service Locator pattern)', function () {
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(command: ICommand): string {
1505
- // Dynamically resolve handler: "Handler" + "CreateUser"
1506
- const handlerKey = `Handler${command.type}`;
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(result).toBe('User alice created');
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, // Passed as argument during resolve
1496
+ private theme: string,
1528
1497
  ) {}
1529
1498
 
1530
1499
  createWidget(name: string): string {
1531
- return `Widget ${name} with ${this.theme} theme (Container available: ${!!this.container})`;
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 (Container available: true)');
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 }: UserControllerDeps) {
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
- const controller = container.resolve<UserController>('UserController');
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({ database, args }: { database: Database; args: string[] }) {
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
- setArgs,
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
- singleton,
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(MultiCache.fromFirstArg), // Cache unique instance per repository type
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 { bindTo, Container, register, Registration as R, scope, singleton } from 'ts-ioc-container';
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
- * Scoping - Scope Match Rule
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
- * You can restrict WHERE a provider is registered.
2583
- * This is useful for singleton services that should only exist in the root scope,
2584
- * or per-request services that should only exist in request scopes.
2525
+ * Scope hierarchy:
2526
+ * Application (singleton services)
2527
+ * └── Request (per-request services)
2528
+ * └── Transaction (database transaction boundary)
2585
2529
  */
2586
2530
 
2587
- describe('ScopeProvider', function () {
2588
- it('should register provider only in matching scope', function () {
2589
- // SharedState should be a singleton in the root 'application' scope
2590
- // It will be visible to all child scopes, but physically resides in 'application'
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
- const appContainer = new Container({ tags: ['application'] }).addRegistration(R.fromClass(SharedState));
2601
- const requestScope = appContainer.createScope({ tags: ['request'] });
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
- // Both resolve to the SAME instance because it's a singleton in the app scope
2604
- const appState = appContainer.resolve('SharedState');
2605
- const requestState = requestScope.resolve('SharedState');
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
- expect(appState).toBe(requestState);
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
- AddOnConstructHookModule,
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: IHookContext) => {
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.join(',')).toBe('Hello,world');
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.MultiCache = exports.multiCache = 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 = exports.SingleToken = exports.ClassToken = void 0;
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
@@ -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,9 +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 argToToken = (v) => ((0, InjectionToken_1.isInjectionToken)(v) ? v : new ConstantToken_1.ConstantToken(v));
21
18
  const resolveArgs = (Target, methodName) => {
22
19
  const tokens = (0, parameter_1.getParamMeta)(hookMetaKey(methodName), Target);
23
- return (scope, { args = [], lazy }) => tokens.map((fn) => fn.resolve(scope, { args: args.map(argToToken).map((t) => t.resolve(scope)), lazy }));
20
+ return (scope, { args = [], lazy }) => tokens.map((fn) => fn.resolve(scope, { args: args.map(toToken_1.argToToken).map((t) => t.resolve(scope)), lazy }));
24
21
  };
25
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
- cache;
10
- constructor(provider, cache) {
8
+ getKey;
9
+ cache = new Map();
10
+ constructor(provider, getKey) {
11
11
  super(provider);
12
12
  this.provider = provider;
13
- this.cache = cache;
13
+ this.getKey = getKey;
14
14
  }
15
15
  resolve(container, options) {
16
16
  const { args = [] } = options;
17
- const key = this.cache.getKey(...args);
18
- if (!this.cache.hasValue(key)) {
19
- this.cache.setValue(key, this.provider.resolve(container, options));
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.getValue(key);
21
+ return this.cache.get(key);
22
22
  }
23
23
  }
24
24
  exports.SingletonProvider = SingletonProvider;
25
- const singleton = (cacheProvider) => (0, ProviderPipe_1.registerPipe)((p) => new SingletonProvider(p, cacheProvider ? cacheProvider() : new Cache_1.SingleCache()));
25
+ const singleton = (getCacheKey = () => '1') => (0, ProviderPipe_1.registerPipe)((p) => new SingletonProvider(p, getCacheKey));
26
26
  exports.singleton = singleton;
@@ -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';
@@ -1,6 +1,4 @@
1
- import { isInjectionToken } from '../token/InjectionToken';
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,7 +9,6 @@ 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 argToToken = (v) => (isInjectionToken(v) ? v : new ConstantToken(v));
15
12
  export const resolveArgs = (Target, methodName) => {
16
13
  const tokens = getParamMeta(hookMetaKey(methodName), Target);
17
14
  return (scope, { args = [], lazy }) => tokens.map((fn) => fn.resolve(scope, { args: args.map(argToToken).map((t) => t.resolve(scope)), lazy }));
@@ -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
- cache;
7
- constructor(provider, cache) {
5
+ getKey;
6
+ cache = new Map();
7
+ constructor(provider, getKey) {
8
8
  super(provider);
9
9
  this.provider = provider;
10
- this.cache = cache;
10
+ this.getKey = getKey;
11
11
  }
12
12
  resolve(container, options) {
13
13
  const { args = [] } = options;
14
- const key = this.cache.getKey(...args);
15
- if (!this.cache.hasValue(key)) {
16
- this.cache.setValue(key, this.provider.resolve(container, options));
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.getValue(key);
18
+ return this.cache.get(key);
19
19
  }
20
20
  }
21
- export const singleton = (cacheProvider) => registerPipe((p) => new SingletonProvider(p, cacheProvider ? cacheProvider() : new SingleCache()));
21
+ export const singleton = (getCacheKey = () => '1') => registerPipe((p) => new SingletonProvider(p, getCacheKey));
@@ -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": "49.1.0",
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"
@@ -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 readonly provider;
8
- private readonly cache;
9
- constructor(provider: IProvider<T>, cache: Cache<unknown, T>);
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>(cacheProvider?: () => Cache<unknown, T>) => import("./ProviderPipe").ProviderPipe<T>;
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>;
@@ -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;
@@ -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
- }