ts-ioc-container 48.0.0 → 49.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1912,13 +1912,15 @@ When you pass an `InjectionToken` via `token.args(...)`, the container resolves
1912
1912
  - `ServiceToken.args(new ClassToken(SomeService))` — `SomeService` is constructed by the container
1913
1913
  - `ServiceToken.args('literal')` — literal value passed directly
1914
1914
 
1915
- ### Positional arg injection with `args(index)`
1915
+ ### Positional arg injection with `args(index)` and `argsFn`
1916
1916
 
1917
1917
  Constructor parameters that should pick up positional args from `ProviderOptions` must be annotated with `@inject(args(index))`. Parameters without `@inject` resolve to `undefined`.
1918
1918
 
1919
1919
  - `@inject(args(0))` — resolves the first element of the `args` array passed at resolution time
1920
1920
  - Works together with `token.args(...)` to pass typed dependencies through the args context
1921
1921
 
1922
+ `args(index)` and `argsFn(fn)` are thin shortcuts for an `InjectFn`. Every `InjectFn` receives `(scope, options)`, where `options.args` is the runtime args array — so `args(0)` is just `(scope, { args = [] }) => args[0]`, and `argsFn(fn)` is `(scope, { args = [] }) => fn(...args)` (the runtime args are spread into your callback as positional parameters).
1923
+
1922
1924
  ### Immutable token chaining
1923
1925
 
1924
1926
  `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).
@@ -1962,12 +1964,13 @@ describe('ArgsProvider', function () {
1962
1964
 
1963
1965
  describe('Static Arguments', () => {
1964
1966
  it('can pass static arguments to constructor', function () {
1967
+ // Pre-configure the logger with a filename
1968
+ @register(setArgs('/var/log/app.log'))
1965
1969
  class FileLogger {
1966
1970
  constructor(@inject(args(0)) public filename: string) {}
1967
1971
  }
1968
1972
 
1969
- // Pre-configure the logger with a filename
1970
- const root = createContainer().addRegistration(R.fromClass(FileLogger).pipe(setArgs('/var/log/app.log')));
1973
+ const root = createContainer().addRegistration(R.fromClass(FileLogger));
1971
1974
 
1972
1975
  // Resolve by class name (default key) to use the registered provider
1973
1976
  const logger = root.resolve<FileLogger>('FileLogger');
@@ -1975,15 +1978,15 @@ describe('ArgsProvider', function () {
1975
1978
  });
1976
1979
 
1977
1980
  it('prioritizes provided args over resolve args', function () {
1981
+ // 'FixedContext' wins over any runtime args
1982
+ @register(setArgs('FixedContext'))
1978
1983
  class Logger {
1979
1984
  constructor(@inject(args(0)) public context: string) {}
1980
1985
  }
1981
1986
 
1982
- // 'FixedContext' wins over any runtime args
1983
- const root = createContainer().addRegistration(R.fromClass(Logger).pipe(setArgs('FixedContext')));
1987
+ const root = createContainer().addRegistration(R.fromClass(Logger));
1984
1988
 
1985
1989
  // Even if we ask for 'RuntimeContext', we get 'FixedContext'
1986
- // Resolve by class name to use the registered provider
1987
1990
  const logger = root.resolve<Logger>('Logger', { args: ['RuntimeContext'] });
1988
1991
 
1989
1992
  expect(logger.context).toBe('FixedContext');
@@ -1996,19 +1999,15 @@ describe('ArgsProvider', function () {
1996
1999
  env = 'production';
1997
2000
  }
1998
2001
 
2002
+ // Extract 'env' from Config service dynamically
2003
+ @register(setArgsFn((scope) => [scope.resolve<Config>('Config').env]))
1999
2004
  class Service {
2000
2005
  constructor(@inject(args(0)) public env: string) {}
2001
2006
  }
2002
2007
 
2003
2008
  const root = createContainer()
2004
2009
  .addRegistration(R.fromClass(Config)) // Key: 'Config'
2005
- .addRegistration(
2006
- R.fromClass(Service).pipe(
2007
- // Extract 'env' from Config service dynamically
2008
- // Note: We resolve 'Config' by string key to get the registered instance (if it were singleton)
2009
- setArgsFn((scope) => [scope.resolve<Config>('Config').env]),
2010
- ),
2011
- );
2010
+ .addRegistration(R.fromClass(Service));
2012
2011
 
2013
2012
  const service = root.resolve<Service>('Service');
2014
2013
  expect(service.env).toBe('production');
@@ -15,13 +15,11 @@ const args = (index) => (c, { args = [] }) => {
15
15
  return args[index];
16
16
  };
17
17
  exports.args = args;
18
- const argsFn = (fn) => (c, options) => fn(options.args ?? []);
18
+ const argsFn = (fn) => (c, options) => fn(...(options.args ?? []));
19
19
  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));
20
+ const argToToken = (v) => ((0, InjectionToken_1.isInjectionToken)(v) ? v : new ConstantToken_1.ConstantToken(v));
21
21
  const resolveArgs = (Target, methodName) => {
22
- const argsMetaTokens = (0, parameter_1.getParamMeta)(hookMetaKey(methodName), Target);
23
- return (scope, { args = [], lazy }) => {
24
- return argsMetaTokens.map((fn) => fn.resolve(scope, { args: resolveTokens(scope, args), lazy }));
25
- };
22
+ 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 }));
26
24
  };
27
25
  exports.resolveArgs = resolveArgs;
@@ -10,11 +10,9 @@ export const inject = (fn) => (target, propertyKey, parameterIndex) => {
10
10
  export const args = (index) => (c, { args = [] }) => {
11
11
  return args[index];
12
12
  };
13
- 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));
13
+ export const argsFn = (fn) => (c, options) => fn(...(options.args ?? []));
14
+ const argToToken = (v) => (isInjectionToken(v) ? v : new ConstantToken(v));
15
15
  export const resolveArgs = (Target, methodName) => {
16
- const argsMetaTokens = getParamMeta(hookMetaKey(methodName), Target);
17
- return (scope, { args = [], lazy }) => {
18
- return argsMetaTokens.map((fn) => fn.resolve(scope, { args: resolveTokens(scope, args), lazy }));
19
- };
16
+ const tokens = getParamMeta(hookMetaKey(methodName), Target);
17
+ return (scope, { args = [], lazy }) => tokens.map((fn) => fn.resolve(scope, { args: args.map(argToToken).map((t) => t.resolve(scope)), lazy }));
20
18
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-ioc-container",
3
- "version": "48.0.0",
3
+ "version": "49.1.0",
4
4
  "description": "Fast, lightweight TypeScript dependency injection container with a clean API, scoped lifecycles, decorators, tokens, hooks, lazy injection, customizable providers, and no global container objects.",
5
5
  "workspaces": [
6
6
  "docs"
@@ -5,5 +5,5 @@ import { type constructor } from '../utils/basic';
5
5
  import { ProviderOptions } from '../provider/IProvider';
6
6
  export declare const inject: <T>(fn: InjectionToken<T> | InjectFn<T> | symbol | string | constructor<T>) => ParameterDecorator;
7
7
  export declare const args: (index: number) => InjectFn;
8
- export declare const argsFn: <T>(fn: (args: unknown[]) => T) => InjectFn<T>;
8
+ export declare const argsFn: <T>(fn: (...args: unknown[]) => T) => InjectFn<T>;
9
9
  export declare const resolveArgs: (Target: constructor<unknown>, methodName?: string) => (scope: IContainer, { args, lazy }: ProviderOptions) => unknown[];