ts-ioc-container 47.4.0 → 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.
Files changed (42) hide show
  1. package/README.md +65 -47
  2. package/cjm/container/Container.js +1 -1
  3. package/cjm/index.js +3 -2
  4. package/cjm/injector/ProxyInjector.js +5 -12
  5. package/cjm/injector/inject.js +7 -2
  6. package/cjm/provider/IProvider.js +1 -1
  7. package/cjm/provider/Provider.js +2 -2
  8. package/cjm/registration/Registration.js +2 -2
  9. package/cjm/token/ClassToken.js +27 -17
  10. package/cjm/token/FunctionToken.js +25 -15
  11. package/cjm/token/GroupAliasToken.js +18 -17
  12. package/cjm/token/InjectionToken.js +5 -0
  13. package/cjm/token/SingleAliasToken.js +18 -17
  14. package/cjm/token/SingleToken.js +20 -16
  15. package/esm/container/Container.js +1 -1
  16. package/esm/index.js +1 -1
  17. package/esm/injector/ProxyInjector.js +5 -12
  18. package/esm/injector/inject.js +5 -1
  19. package/esm/provider/IProvider.js +1 -1
  20. package/esm/provider/Provider.js +2 -2
  21. package/esm/registration/Registration.js +2 -2
  22. package/esm/token/ClassToken.js +28 -18
  23. package/esm/token/FunctionToken.js +25 -15
  24. package/esm/token/GroupAliasToken.js +19 -18
  25. package/esm/token/InjectionToken.js +4 -0
  26. package/esm/token/SingleAliasToken.js +19 -18
  27. package/esm/token/SingleToken.js +21 -17
  28. package/package.json +1 -1
  29. package/typings/hooks/hook.d.ts +1 -1
  30. package/typings/index.d.ts +1 -1
  31. package/typings/injector/IInjector.d.ts +1 -2
  32. package/typings/injector/ProxyInjector.d.ts +1 -1
  33. package/typings/injector/inject.d.ts +2 -1
  34. package/typings/provider/IProvider.d.ts +2 -2
  35. package/typings/provider/Provider.d.ts +1 -1
  36. package/typings/registration/Registration.d.ts +3 -1
  37. package/typings/token/ClassToken.d.ts +13 -8
  38. package/typings/token/FunctionToken.d.ts +10 -7
  39. package/typings/token/GroupAliasToken.d.ts +13 -8
  40. package/typings/token/InjectionToken.d.ts +4 -8
  41. package/typings/token/SingleAliasToken.d.ts +13 -8
  42. package/typings/token/SingleToken.d.ts +14 -9
package/README.md CHANGED
@@ -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
- - NOTICE: remember that when scope doesn't have dependency then it will be resolved from parent container
252
- - NOTICE: when you create a scope of container then all providers are cloned to new scope. For that reason every provider has methods `clone` and `isValid` to clone itself and check if it's valid for certain scope accordingly.
253
- - NOTICE: when you create a scope then we clone ONLY tags-matched providers.
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 and all its scopes. For example, when you want to prevent memory leaks. Or you want to ensure that nobody can use container after it was disposed.
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 all scopes are disposed too
643
- - when container is disposed then it unregisters all providers and remove all instances
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 service initialization until first access.
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 support mixing injected dependencies with runtime arguments', function () {
1529
+ it('should expose runtime args through the reserved "args" property', function () {
1525
1530
  class Database {}
1526
1531
 
1527
- interface ReportGeneratorDeps {
1532
+ class ReportGenerator {
1528
1533
  database: Database;
1529
- format: string; // Runtime argument
1530
- }
1534
+ format: string;
1531
1535
 
1532
- class ReportGenerator {
1533
- constructor(public deps: ReportGeneratorDeps) {}
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.deps.format}`;
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: [{ format: 'PDF' }],
1551
+ args: ['PDF'],
1547
1552
  });
1548
1553
 
1549
- expect(generator.deps.database).toBeInstanceOf(Database);
1554
+ expect(generator.database).toBeInstanceOf(Database);
1550
1555
  expect(generator.generate()).toBe('Report in PDF');
1551
1556
  });
1552
1557
 
1553
- it('should resolve array dependencies by alias (convention over configuration)', function () {
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
- loggersArray: any[]; // Injected as array of all loggers
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.loggersArray).toBe(mockLoggers);
1580
- expect(container.resolveByAlias).toHaveBeenCalledWith('loggersArray');
1577
+ expect(app.deps.loggersAlias).toBe(mockLoggers);
1578
+ expect(container.resolveByAlias).toHaveBeenCalledWith('loggersAlias');
1581
1579
  });
1582
1580
  });
1583
1581
 
@@ -1740,7 +1738,9 @@ describe('Provider', () => {
1740
1738
  Sometimes you need to create only one instance of dependency per scope. For example, you want to create only one logger per scope.
1741
1739
 
1742
1740
  - Singleton provider creates only one instance in every scope where it's resolved.
1743
- - NOTICE: if you create a scope 'A' of container 'root' then Logger of A !== Logger of root.
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`.
1744
1744
 
1745
1745
  ```typescript
1746
1746
  import 'reflect-metadata';
@@ -1831,13 +1831,28 @@ Sometimes you want to bind some arguments to provider. This is what `ArgsProvide
1831
1831
  - `provider(setArgs('someArgument'))`
1832
1832
  - `provider(setArgsFn((container) => [container.resolve(Logger), 'someValue']))`
1833
1833
  - `Provider.fromClass(Logger).pipe(setArgs('someArgument'))`
1834
- - NOTICE: args from this provider has higher priority than args from `resolve` method.
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
1835
1840
 
1836
1841
  ### Positional arg injection with `args(index)`
1837
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.
1838
1843
  - `@inject(args(0))` — resolves the first element of the `args` array passed at resolution time
1839
1844
  - Works together with `token.args(...)` to pass typed dependencies through the args context
1840
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
+ ```
1855
+
1841
1856
  ```typescript
1842
1857
  import {
1843
1858
  setArgs,
@@ -2008,6 +2023,10 @@ describe('ArgsProvider', function () {
2008
2023
 
2009
2024
  ### Visibility
2010
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
+
2011
2030
  - `provider(scopeAccess(({ invocationScope, providerScope }) => invocationScope === providerScope))` - dependency will be accessible only from the scope where it's registered
2012
2031
  - `Provider.fromClass(Logger).pipe(scopeAccess(({ invocationScope, providerScope }) => invocationScope === providerScope))`
2013
2032
 
@@ -2808,4 +2827,3 @@ describe('inject property', () => {
2808
2827
  - [MethodNotImplementedError.ts](..%2F..%2Flib%2Ferrors%2FMethodNotImplementedError.ts)
2809
2828
  - [DependencyMissingKeyError.ts](..%2F..%2Flib%2Ferrors%2FDependencyMissingKeyError.ts)
2810
2829
  - [ContainerDisposedError.ts](..%2F..%2Flib%2Ferrors%2FContainerDisposedError.ts)
2811
-
@@ -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(key, { args, child, lazy });
70
+ : this.parent.resolveOneByAlias(alias, { args, child, lazy });
71
71
  }
72
72
  createScope({ tags } = {}) {
73
73
  this.validateContainer();
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.resolveByArgs = exports.ProviderDecorator = exports.setArgs = exports.setArgsFn = exports.lazy = exports.scopeAccess = exports.ProxyInjector = exports.SimpleInjector = exports.MetadataInjector = exports.Injector = 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.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; } });
@@ -14,6 +14,7 @@ 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
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; } });
17
18
  var IInjector_1 = require("./injector/IInjector");
18
19
  Object.defineProperty(exports, "Injector", { enumerable: true, get: function () { return IInjector_1.Injector; } });
19
20
  var MetadataInjector_1 = require("./injector/MetadataInjector");
@@ -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: deps = [] } = {}) {
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
- // eslint-disable-next-line no-prototype-builtins
15
- return args.hasOwnProperty(prop)
16
- ? getProp(args, prop)
17
- : prop.toString().search(/array/gi) >= 0
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);
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveArgs = exports.args = 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");
@@ -15,10 +16,14 @@ const args = (index) => (c, { args = [] }) => {
15
16
  return args[index];
16
17
  };
17
18
  exports.args = args;
19
+ const argsFn = (fn) => (c, options) => fn(options.args ?? []);
20
+ exports.argsFn = argsFn;
18
21
  const resolveArgs = (Target, methodName) => {
19
22
  const argsTokens = (0, parameter_1.getParamMeta)(hookMetaKey(methodName), Target);
20
23
  return (scope, { args = [], lazy }) => {
21
- const depsTokens = args.map((v) => new ConstantToken_1.ConstantToken(v));
24
+ const depsTokens = args.map((v) => {
25
+ return !(0, InjectionToken_1.isInjectionToken)(v) ? new ConstantToken_1.ConstantToken(v) : v;
26
+ });
22
27
  const allTokens = (0, array_1.fillEmptyIndexes)(argsTokens, depsTokens);
23
28
  return allTokens.map((fn) => fn.resolve(scope, { args, lazy }));
24
29
  };
@@ -8,7 +8,7 @@ const setArgs = (...extraArgs) => (0, ProviderPipe_1.registerPipe)((p) => p.setA
8
8
  exports.setArgs = setArgs;
9
9
  const setArgsFn = (fn) => (0, ProviderPipe_1.registerPipe)((p) => p.setArgs(fn));
10
10
  exports.setArgsFn = setArgsFn;
11
- const resolveByArgs = (s, ...deps) => deps.map((d) => {
11
+ const resolveByArgs = (s, { args = [] } = {}) => args.map((d) => {
12
12
  if (d instanceof InjectionToken_1.InjectionToken) {
13
13
  return d.resolve(s);
14
14
  }
@@ -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: [...this.argsFn(container, ...args), ...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)) {
@@ -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
- token;
7
- options;
8
- constructor(token, options = {}) {
6
+ target;
7
+ _getArgsFn;
8
+ _isLazy;
9
+ constructor(target, { getArgsFn = () => [], isLazy = false } = {}) {
9
10
  super();
10
- this.token = token;
11
- this.options = options;
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
- const argsFn = this.options.argsFn ?? (0, InjectionToken_1.setArgs)();
18
- return s.resolve(this.token, {
19
- args: argsFn(s),
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(...args) {
24
- const argsFn = this.options.argsFn ?? (0, InjectionToken_1.setArgs)();
25
- return new ClassToken(this.token, { ...this.options, argsFn: (s) => [...argsFn(s), ...args] });
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(getArgsFn) {
28
- const argsFn = this.options.argsFn ?? (0, InjectionToken_1.setArgs)();
29
- return new ClassToken(this.token, { ...this.options, argsFn: (s) => [...argsFn(s), ...getArgsFn(s)] });
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.token, { ...this.options, lazy: true });
39
+ return new ClassToken(this.target, {
40
+ getArgsFn: this._getArgsFn,
41
+ isLazy: true,
42
+ });
33
43
  }
34
44
  }
35
45
  exports.ClassToken = ClassToken;
@@ -4,29 +4,39 @@ exports.FunctionToken = void 0;
4
4
  const InjectionToken_1 = require("./InjectionToken");
5
5
  class FunctionToken extends InjectionToken_1.InjectionToken {
6
6
  fn;
7
- getArgsFn = () => [];
8
- isLazy = false;
9
- constructor(fn) {
7
+ _getArgsFn;
8
+ _isLazy;
9
+ constructor(fn, { getArgsFn = (_, { args = [] } = {}) => args, isLazy = false } = {}) {
10
10
  super();
11
11
  this.fn = fn;
12
+ this._getArgsFn = getArgsFn;
13
+ this._isLazy = isLazy;
12
14
  }
13
- resolve(s, { args = [], lazy = false } = {}) {
15
+ resolve(s, { args = [], lazy } = {}) {
14
16
  return this.fn(s, {
15
- args: [...args, ...this.getArgsFn(s)],
16
- lazy: this.isLazy || lazy,
17
+ args: this._getArgsFn(s, { args }),
18
+ lazy: this._isLazy || lazy,
17
19
  });
18
20
  }
19
- args(...deps) {
20
- this.getArgsFn = () => deps;
21
- return this;
21
+ args(...newArgs) {
22
+ const parentFn = this._getArgsFn;
23
+ return new FunctionToken(this.fn, {
24
+ getArgsFn: (s, opts) => [...parentFn(s, opts), ...newArgs],
25
+ isLazy: this._isLazy,
26
+ });
22
27
  }
23
- argsFn(getArgsFn) {
24
- this.getArgsFn = getArgsFn;
25
- return this;
28
+ argsFn(fn) {
29
+ const parentFn = this._getArgsFn;
30
+ return new FunctionToken(this.fn, {
31
+ getArgsFn: (s, opts) => [...parentFn(s, opts), ...fn(s)],
32
+ isLazy: this._isLazy,
33
+ });
26
34
  }
27
- lazy(isLazy = true) {
28
- this.isLazy = isLazy;
29
- return this;
35
+ lazy() {
36
+ return new FunctionToken(this.fn, {
37
+ getArgsFn: this._getArgsFn,
38
+ isLazy: true,
39
+ });
30
40
  }
31
41
  }
32
42
  exports.FunctionToken = FunctionToken;
@@ -4,43 +4,44 @@ exports.toGroupAlias = exports.GroupAliasToken = void 0;
4
4
  const InjectionToken_1 = require("./InjectionToken");
5
5
  class GroupAliasToken extends InjectionToken_1.InjectionToken {
6
6
  token;
7
- options;
8
- constructor(token, options = {}) {
7
+ _getArgsFn;
8
+ _isLazy;
9
+ constructor(token, { getArgsFn = () => [], isLazy = false } = {}) {
9
10
  super();
10
11
  this.token = token;
11
- this.options = options;
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
- const argsFn = this.options.argsFn ?? (0, InjectionToken_1.setArgs)();
18
+ resolve(s, { args = [], lazy } = {}) {
18
19
  return s.resolveByAlias(this.token, {
19
- args: argsFn(s),
20
- lazy: this.options.lazy,
20
+ args: this._getArgsFn(s, { args }),
21
+ lazy: this._isLazy || lazy,
21
22
  });
22
23
  }
23
24
  bindTo(r) {
24
25
  r.bindToAlias(this.token);
25
26
  }
26
- args(...args) {
27
- const argsFn = this.options.argsFn ?? (0, InjectionToken_1.setArgs)();
27
+ args(...newArgs) {
28
+ const parentFn = this._getArgsFn;
28
29
  return new GroupAliasToken(this.token, {
29
- ...this.options,
30
- argsFn: (s) => [...argsFn(s), ...args],
30
+ getArgsFn: (s, opts) => [...parentFn(s, opts), ...newArgs],
31
+ isLazy: this._isLazy,
31
32
  });
32
33
  }
33
- argsFn(getArgsFn) {
34
- const argsFn = this.options.argsFn ?? (0, InjectionToken_1.setArgs)();
34
+ argsFn(fn) {
35
+ const parentFn = this._getArgsFn;
35
36
  return new GroupAliasToken(this.token, {
36
- ...this.options,
37
- argsFn: (s) => [...argsFn(s), ...getArgsFn(s)],
37
+ getArgsFn: (s, opts) => [...parentFn(s, opts), ...fn(s)],
38
+ isLazy: this._isLazy,
38
39
  });
39
40
  }
40
41
  lazy() {
41
42
  return new GroupAliasToken(this.token, {
42
- ...this.options,
43
- lazy: true,
43
+ getArgsFn: this._getArgsFn,
44
+ isLazy: true,
44
45
  });
45
46
  }
46
47
  }
@@ -1,8 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.setArgs = exports.InjectionToken = void 0;
4
+ exports.isInjectionToken = isInjectionToken;
5
+ const basic_1 = require("../utils/basic");
4
6
  class InjectionToken {
5
7
  }
6
8
  exports.InjectionToken = InjectionToken;
7
9
  const setArgs = (...args) => (s) => args;
8
10
  exports.setArgs = setArgs;
11
+ function isInjectionToken(target) {
12
+ return basic_1.Is.object(target) && 'resolve' in target && 'args' in target && 'argsFn' in target && 'lazy' in target;
13
+ }
@@ -4,43 +4,44 @@ exports.toSingleAlias = exports.SingleAliasToken = void 0;
4
4
  const InjectionToken_1 = require("./InjectionToken");
5
5
  class SingleAliasToken extends InjectionToken_1.InjectionToken {
6
6
  token;
7
- options;
8
- constructor(token, options = {}) {
7
+ _getArgsFn;
8
+ _isLazy;
9
+ constructor(token, { getArgsFn = () => [], isLazy = false } = {}) {
9
10
  super();
10
11
  this.token = token;
11
- this.options = options;
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
- const argsFn = this.options.argsFn ?? (0, InjectionToken_1.setArgs)();
18
+ resolve(s, { args = [], lazy } = {}) {
18
19
  return s.resolveOneByAlias(this.token, {
19
- args: argsFn(s),
20
- lazy: this.options.lazy,
20
+ args: this._getArgsFn(s, { args }),
21
+ lazy: this._isLazy || lazy,
21
22
  });
22
23
  }
23
24
  bindTo(r) {
24
25
  r.bindToAlias(this.token);
25
26
  }
26
- args(...args) {
27
- const argsFn = this.options.argsFn ?? (0, InjectionToken_1.setArgs)();
27
+ args(...newArgs) {
28
+ const parentFn = this._getArgsFn;
28
29
  return new SingleAliasToken(this.token, {
29
- ...this.options,
30
- argsFn: (s) => [...argsFn(s), ...args],
30
+ getArgsFn: (s, opts) => [...parentFn(s, opts), ...newArgs],
31
+ isLazy: this._isLazy,
31
32
  });
32
33
  }
33
- argsFn(getArgsFn) {
34
- const argsFn = this.options.argsFn ?? (0, InjectionToken_1.setArgs)();
34
+ argsFn(fn) {
35
+ const parentFn = this._getArgsFn;
35
36
  return new SingleAliasToken(this.token, {
36
- ...this.options,
37
- argsFn: (s) => [...argsFn(s), ...getArgsFn(s)],
37
+ getArgsFn: (s, opts) => [...parentFn(s, opts), ...fn(s)],
38
+ isLazy: this._isLazy,
38
39
  });
39
40
  }
40
41
  lazy() {
41
42
  return new SingleAliasToken(this.token, {
42
- ...this.options,
43
- lazy: true,
43
+ getArgsFn: this._getArgsFn,
44
+ isLazy: true,
44
45
  });
45
46
  }
46
47
  }