atomic-di 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -87,7 +87,7 @@ npx jsr add @ensi/di
87
87
 
88
88
  ## Providers
89
89
 
90
- The library provides functions that create providers with behavior typical of singletons, transients, and scopeds.
90
+ A provider is a factory of instances with additional functionality. The library provides functions that create providers with behavior typical of singletons, transients, and scopes.
91
91
 
92
92
  ### Transient
93
93
 
@@ -566,7 +566,9 @@ A function that returns a value of a particular type with a resolution context b
566
566
 
567
567
  ### `Provider`
568
568
  ```ts
569
- type Provider<T> = (context?: ResolutionContext) => T
569
+ type Provider<T> = Resolver<T> & {
570
+ __brand: "provider"
571
+ }
570
572
  ```
571
573
 
572
574
  A function that resolves an instance or a `Promise` of a particular type based on a resolution context passed to it.
package/dist/index.d.mts CHANGED
@@ -69,7 +69,9 @@ type Resolver<T> = (context?: ResolutionContext) => T;
69
69
  * A function that resolves an instance or a `Promise` of a particular type
70
70
  * based on a resolution context passed to it.
71
71
  */
72
- type Provider<T> = Resolver<T>;
72
+ type Provider<T> = Resolver<T> & {
73
+ __brand: "provider";
74
+ };
73
75
  /**
74
76
  * A context used by providers to resolve instances
75
77
  * based on current scope and mocks.
package/dist/index.d.ts CHANGED
@@ -69,7 +69,9 @@ type Resolver<T> = (context?: ResolutionContext) => T;
69
69
  * A function that resolves an instance or a `Promise` of a particular type
70
70
  * based on a resolution context passed to it.
71
71
  */
72
- type Provider<T> = Resolver<T>;
72
+ type Provider<T> = Resolver<T> & {
73
+ __brand: "provider";
74
+ };
73
75
  /**
74
76
  * A context used by providers to resolve instances
75
77
  * based on current scope and mocks.
package/dist/index.js CHANGED
@@ -30,40 +30,35 @@ __export(src_exports, {
30
30
  module.exports = __toCommonJS(src_exports);
31
31
 
32
32
  // src/provider.ts
33
- var transient = (resolver) => {
33
+ var createProvider = (resolver) => {
34
34
  const instance = (context) => {
35
35
  var _a;
36
36
  const maybeMock = (_a = context == null ? void 0 : context.mocks) == null ? void 0 : _a.get(instance);
37
37
  if (maybeMock) return maybeMock(context);
38
38
  return resolver(context);
39
39
  };
40
- return instance;
40
+ return Object.assign(instance, { __brand: "provider" });
41
41
  };
42
+ var transient = createProvider;
42
43
  var singleton = (resolver) => {
43
44
  let resolved = false;
44
45
  let resolution;
45
- const instance = (context) => {
46
- var _a;
47
- const maybeMock = (_a = context == null ? void 0 : context.mocks) == null ? void 0 : _a.get(instance);
48
- if (maybeMock) return maybeMock(context);
46
+ const instance = createProvider((context) => {
49
47
  if (resolved) return resolution;
50
48
  resolution = resolver(context);
51
49
  resolved = true;
52
50
  return resolution;
53
- };
51
+ });
54
52
  return instance;
55
53
  };
56
54
  var scoped = (resolver) => {
57
55
  const singletonFallback = singleton(resolver);
58
- const instance = (context) => {
59
- var _a;
60
- const maybeMock = (_a = context == null ? void 0 : context.mocks) == null ? void 0 : _a.get(instance);
61
- if (maybeMock) return maybeMock(context);
56
+ const instance = createProvider((context) => {
62
57
  if (!(context == null ? void 0 : context.scope)) return singletonFallback(context);
63
- const resolution = context.scope.has(resolver) ? context.scope.get(resolver) : resolver(context);
64
- context.scope.set(resolver, resolution);
58
+ const resolution = context.scope.has(instance) ? context.scope.get(instance) : resolver(context);
59
+ context.scope.set(instance, resolution);
65
60
  return resolution;
66
- };
61
+ });
67
62
  return instance;
68
63
  };
69
64
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/provider.ts","../src/scope.ts","../src/mock-map.ts","../src/collection-resolution.ts"],"sourcesContent":["export * from \"./provider\";\nexport * from \"./scope\";\nexport * from \"./mock-map\";\nexport * from \"./collection-resolution\";\n","import { MockMap } from \"./mock-map\";\nimport { Scope } from \"./scope\";\n\n/**\n * A function that returns a value of a particular type\n * with a resolution context being passed to it.\n */\nexport type Resolver<T> = (context?: ResolutionContext) => T;\n\n/**\n * A function that resolves an instance or a `Promise` of a particular type\n * based on a resolution context passed to it.\n */\nexport type Provider<T> = Resolver<T>;\n\n/**\n * A context used by providers to resolve instances\n * based on current scope and mocks.\n */\nexport type ResolutionContext = {\n scope?: Scope;\n mocks?: MockMap;\n};\n\n/**\n * Creates a transient provider that will resolve a new instance on each call.\n *\n * @example\n * ```ts\n * const getThing = transient(() => createThing())\n * getThing() !== getThing()\n * ```\n *\n * @param resolver\n * A function that returns a value of a particular type\n * with a resolution context being passed to it.\n *\n * @returns The transient provider.\n */\nexport const transient = <T>(resolver: Resolver<T>): Provider<T> => {\n const instance: Resolver<T> = (context) => {\n const maybeMock = context?.mocks?.get(instance);\n if (maybeMock) return maybeMock(context);\n\n return resolver(context);\n };\n\n return instance;\n};\n\n/**\n * Creates a singleton provider that will resolve an instance once\n * and return it on every call.\n *\n * @example\n * ```ts\n * const getThing = singleton(() => createThing())\n * getThing() === getThing()\n * ```\n *\n * @param resolver\n * A function that returns a value of a particular type\n * with a resolution context being passed to it.\n *\n * @returns The singleton provider.\n */\nexport const singleton = <T>(resolver: Resolver<T>): Provider<T> => {\n let resolved = false;\n let resolution: T | undefined;\n\n const instance: Resolver<T> = (context) => {\n const maybeMock = context?.mocks?.get(instance);\n if (maybeMock) return maybeMock(context);\n\n if (resolved) return resolution!;\n\n resolution = resolver(context);\n resolved = true;\n\n return resolution;\n };\n\n return instance;\n};\n\n/**\n * Creates a scoped provider that will take its resolution from a passed scope\n * or create a new one and save it if there is none.\n * If no scope is passed, it will act as a singleton.\n *\n * @example\n * ```ts\n * const getThing = scoped(() => createThing())\n * getThing() === getThing()\n * ```\n *\n * @example\n * ```ts\n * const getThing = scoped(() => createThing())\n * const scope = createScope()\n * getThing({ scope }) === getThing({ scope }) !== getThing()\n * ```\n *\n * @param resolver\n * A function that returns a value of a particular type\n * with a resolution context being passed to it.\n *\n * @returns The scoped provider.\n */\nexport const scoped = <T>(resolver: Resolver<T>): Provider<T> => {\n const singletonFallback = singleton(resolver);\n\n const instance: Resolver<T> = (context) => {\n const maybeMock = context?.mocks?.get(instance);\n if (maybeMock) return maybeMock(context);\n\n if (!context?.scope) return singletonFallback(context);\n\n const resolution = context.scope.has(resolver)\n ? context.scope.get(resolver)\n : resolver(context);\n context.scope.set(resolver, resolution);\n\n return resolution;\n };\n\n return instance;\n};\n","import { Resolver } from \"./provider\";\n\n/**\n * A `Map` of providers to their instances\n * that is then passed to a provider call in a resolution context object\n * to resolve instances of scoped providers within it.\n */\nexport type Scope = Map<Resolver<any>, any>;\n\n/**\n * Creates a `Map` of providers to their instances\n * that is then passed to a provider call in a resolution context object\n * to resolve instances of scoped providers within it.\n *\n * @example\n * ```ts\n * const requestScope = createScope()\n *\n * app.use(() => {\n * const db = getDb({ scope: requestScope })\n * // ...\n * })\n * ```\n *\n * @returns The map instance.\n */\nexport const createScope = (): Scope => new Map();\n","import { Resolver } from \"./provider\";\n\n/**\n * A `Map` of providers to providers of the same type\n * which is then passed to a provider call in a resolution context object\n * in order to replace providers with their mocks.\n */\nexport type MockMap = Omit<Map<Resolver<any>, Resolver<any>>, \"set\" | \"get\"> & {\n /**\n * Sets a mock for a provider.\n *\n * @param provider - The original provider.\n * @param mock - The mock provider.\n */\n set<T>(provider: Resolver<T>, mock: Resolver<T>): MockMap;\n /**\n * Retrieves a mock of a provider. Returns undefined if there's none.\n *\n * @param provider - The provider.\n */\n get<T>(provider: Resolver<T>): Resolver<T> | undefined;\n};\n\n/**\n * Creates a `Map` of providers to providers of the same type\n * which is then passed to a provider call in a resolution context object\n * in order to replace providers with their mocks.\n *\n * @example\n * ```ts\n * const mocks = createMockMap()\n * .set(getConfig, getTestConfig)\n *\n * getThing({ mocks })\n * ```\n *\n * @returns The map instance.\n */\nexport const createMockMap = (): MockMap => new Map();\n","import { Provider, ResolutionContext } from \"./provider\";\n\ntype ProviderList = Provider<any>[];\ntype ProviderRecord = Record<string, Provider<any>>;\n\ntype InferProviderCollectionResolutions<\n Providers extends ProviderList | ProviderRecord,\n> = {\n [K in keyof Providers]: Providers[K] extends Provider<infer T> ? T : never;\n};\n\n/**\n * Awaits all promises and wraps the collection in a promise\n * if there'ss at least one `Promise` in the collection,\n * otherwise returns an untouched type.\n */\ntype AwaitValuesInCollection<T extends any[] | Record<any, any>> =\n Promise<any> extends T[keyof T]\n ? Promise<{\n [I in keyof T]: T[I] extends Promise<infer T> ? T : T[I];\n }>\n : T;\n\n/**\n * Calls every provider in a list with a provided resolution context\n * and returns a list of resolutions. Returns a `Promise` of a list\n * of awaited resolutions if there's at least one `Promise` in the resolutions.\n *\n * @example\n * Only sync providers:\n * ```ts\n * const getA = scoped(() => createA())\n * const getB = scoped(() => createB())\n * const getC = scoped(() => createC())\n *\n * const scope = createScope()\n * const resolutions = resolveList(\n * [getA, getB, getC],\n * { scope }\n * )\n *\n * resolutions == [\n * getA({ scope }),\n * getB({ scope }),\n * getC({ scope })\n * ]\n * ```\n *\n * @example\n * Some provider is async:\n * ```ts\n * const getA = scoped(() => createA())\n * const getB = scoped(async () => await createB())\n * const getC = scoped(() => createC())\n *\n * const scope = createScope()\n * const resolutions = resolveList(\n * [getA, getB, getC],\n * { scope }\n * )\n *\n * resolutions == [\n * getA({ scope }),\n * await getB({ scope }),\n * getC({ scope })\n * ]\n * ```\n *\n * @param providers - The list of providers.\n * @param context - The resolution context.\n *\n * @returns The list of resolutions.\n */\nexport const resolveList = <const Providers extends ProviderList>(\n providers: Providers,\n context?: ResolutionContext,\n): AwaitValuesInCollection<\n InferProviderCollectionResolutions<Providers>\n> => {\n const resolutions = providers.map((provider) => provider(context));\n\n const hasPromises = resolutions.some(\n (resolution) => resolution instanceof Promise,\n );\n\n return (hasPromises ? Promise.all(resolutions) : resolutions) as any;\n};\n\n/**\n * Calls every provider in a map with a provided resolution context\n * and returns a map with identical keys but with resolutions in values instead.\n * Returns a `Promise` of a map of awaited resolutions if there's at least one\n * `Promise` in the resolutions.\n *\n * @example\n * Only sync providers:\n * ```ts\n * const getA = scoped(() => createA())\n * const getB = scoped(() => createB())\n * const getC = scoped(() => createC())\n *\n * const scope = createScope()\n * const resolutions = resolveMap(\n * { a: getA, b: getB, c: getC },\n * { scope }\n * )\n *\n * resolutions == {\n * a: getA({ scope }),\n * b: getB({ scope }),\n * c: getC({ scope })\n * }\n * ```\n *\n * @example\n * Some provider is async:\n * ```ts\n * const getA = scoped(() => createA())\n * const getB = scoped(async () => await createB())\n * const getC = scoped(() => createC())\n *\n * const scope = createScope()\n * const resolutions = await resolveMap(\n * { a: getA, b: getB, c: getC },\n * { scope }\n * )\n *\n * resolutions == {\n * a: getA({ scope }),\n * b: await getB({ scope }),\n * c: getC({ scope })\n * }\n * ```\n *\n * @param providers - The map of providers.\n * @param context - The resolution context.\n *\n * @returns The map of resolutions.\n */\nexport const resolveMap = <const Providers extends ProviderRecord>(\n providers: Providers,\n context?: ResolutionContext,\n): AwaitValuesInCollection<\n InferProviderCollectionResolutions<Providers>\n> => {\n const resolutionMapEntries = Object.entries(providers).map(\n ([key, provider]) => [key, provider(context)],\n );\n\n const hasPromises = resolutionMapEntries.some(\n ([, resolution]) => resolution instanceof Promise,\n );\n\n if (hasPromises) {\n return (async () => {\n const awaitedEntries = await Promise.all(\n resolutionMapEntries.map(async ([key, resolution]) => [\n key,\n await resolution,\n ]),\n );\n return Object.fromEntries(awaitedEntries);\n })() as any;\n }\n\n return Object.fromEntries(resolutionMapEntries);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACuCO,IAAM,YAAY,CAAI,aAAuC;AAChE,QAAM,WAAwB,CAAC,YAAY;AAxC/C;AAyCQ,UAAM,aAAY,wCAAS,UAAT,mBAAgB,IAAI;AACtC,QAAI,UAAW,QAAO,UAAU,OAAO;AAEvC,WAAO,SAAS,OAAO;AAAA,EAC3B;AAEA,SAAO;AACX;AAkBO,IAAM,YAAY,CAAI,aAAuC;AAChE,MAAI,WAAW;AACf,MAAI;AAEJ,QAAM,WAAwB,CAAC,YAAY;AAtE/C;AAuEQ,UAAM,aAAY,wCAAS,UAAT,mBAAgB,IAAI;AACtC,QAAI,UAAW,QAAO,UAAU,OAAO;AAEvC,QAAI,SAAU,QAAO;AAErB,iBAAa,SAAS,OAAO;AAC7B,eAAW;AAEX,WAAO;AAAA,EACX;AAEA,SAAO;AACX;AA0BO,IAAM,SAAS,CAAI,aAAuC;AAC7D,QAAM,oBAAoB,UAAU,QAAQ;AAE5C,QAAM,WAAwB,CAAC,YAAY;AAhH/C;AAiHQ,UAAM,aAAY,wCAAS,UAAT,mBAAgB,IAAI;AACtC,QAAI,UAAW,QAAO,UAAU,OAAO;AAEvC,QAAI,EAAC,mCAAS,OAAO,QAAO,kBAAkB,OAAO;AAErD,UAAM,aAAa,QAAQ,MAAM,IAAI,QAAQ,IACvC,QAAQ,MAAM,IAAI,QAAQ,IAC1B,SAAS,OAAO;AACtB,YAAQ,MAAM,IAAI,UAAU,UAAU;AAEtC,WAAO;AAAA,EACX;AAEA,SAAO;AACX;;;ACrGO,IAAM,cAAc,MAAa,oBAAI,IAAI;;;ACYzC,IAAM,gBAAgB,MAAe,oBAAI,IAAI;;;ACmC7C,IAAM,cAAc,CACvB,WACA,YAGC;AACD,QAAM,cAAc,UAAU,IAAI,CAAC,aAAa,SAAS,OAAO,CAAC;AAEjE,QAAM,cAAc,YAAY;AAAA,IAC5B,CAAC,eAAe,sBAAsB;AAAA,EAC1C;AAEA,SAAQ,cAAc,QAAQ,IAAI,WAAW,IAAI;AACrD;AAqDO,IAAM,aAAa,CACtB,WACA,YAGC;AACD,QAAM,uBAAuB,OAAO,QAAQ,SAAS,EAAE;AAAA,IACnD,CAAC,CAAC,KAAK,QAAQ,MAAM,CAAC,KAAK,SAAS,OAAO,CAAC;AAAA,EAChD;AAEA,QAAM,cAAc,qBAAqB;AAAA,IACrC,CAAC,CAAC,EAAE,UAAU,MAAM,sBAAsB;AAAA,EAC9C;AAEA,MAAI,aAAa;AACb,YAAQ,YAAY;AAChB,YAAM,iBAAiB,MAAM,QAAQ;AAAA,QACjC,qBAAqB,IAAI,OAAO,CAAC,KAAK,UAAU,MAAM;AAAA,UAClD;AAAA,UACA,MAAM;AAAA,QACV,CAAC;AAAA,MACL;AACA,aAAO,OAAO,YAAY,cAAc;AAAA,IAC5C,GAAG;AAAA,EACP;AAEA,SAAO,OAAO,YAAY,oBAAoB;AAClD;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/provider.ts","../src/scope.ts","../src/mock-map.ts","../src/collection-resolution.ts"],"sourcesContent":["export * from \"./provider\";\nexport * from \"./scope\";\nexport * from \"./mock-map\";\nexport * from \"./collection-resolution\";\n","import { MockMap } from \"./mock-map\";\nimport { Scope } from \"./scope\";\n\n/**\n * A function that returns a value of a particular type\n * with a resolution context being passed to it.\n */\nexport type Resolver<T> = (context?: ResolutionContext) => T;\n\n/**\n * A function that resolves an instance or a `Promise` of a particular type\n * based on a resolution context passed to it.\n */\nexport type Provider<T> = Resolver<T> & { __brand: \"provider\" };\n\n/**\n * A context used by providers to resolve instances\n * based on current scope and mocks.\n */\nexport type ResolutionContext = {\n scope?: Scope;\n mocks?: MockMap;\n};\n\n/**\n * Creating a nominal type value and introducing common functionality.\n */\nconst createProvider = <T>(resolver: Resolver<T>): Provider<T> => {\n const instance: Resolver<T> = (context) => {\n const maybeMock = context?.mocks?.get(instance);\n if (maybeMock) return maybeMock(context);\n\n return resolver(context);\n };\n\n return Object.assign(instance, { __brand: \"provider\" as const });\n};\n\n/**\n * Creates a transient provider that will resolve a new instance on each call.\n *\n * @example\n * ```ts\n * const getThing = transient(() => createThing())\n * getThing() !== getThing()\n * ```\n *\n * @param resolver\n * A function that returns a value of a particular type\n * with a resolution context being passed to it.\n *\n * @returns The transient provider.\n */\nexport const transient = createProvider;\n\n/**\n * Creates a singleton provider that will resolve an instance once\n * and return it on every call.\n *\n * @example\n * ```ts\n * const getThing = singleton(() => createThing())\n * getThing() === getThing()\n * ```\n *\n * @param resolver\n * A function that returns a value of a particular type\n * with a resolution context being passed to it.\n *\n * @returns The singleton provider.\n */\nexport const singleton = <T>(resolver: Resolver<T>): Provider<T> => {\n let resolved = false;\n let resolution: T | undefined;\n\n const instance = createProvider((context) => {\n if (resolved) return resolution!;\n\n resolution = resolver(context);\n resolved = true;\n\n return resolution;\n });\n\n return instance;\n};\n\n/**\n * Creates a scoped provider that will take its resolution from a passed scope\n * or create a new one and save it if there is none.\n * If no scope is passed, it will act as a singleton.\n *\n * @example\n * ```ts\n * const getThing = scoped(() => createThing())\n * getThing() === getThing()\n * ```\n *\n * @example\n * ```ts\n * const getThing = scoped(() => createThing())\n * const scope = createScope()\n * getThing({ scope }) === getThing({ scope }) !== getThing()\n * ```\n *\n * @param resolver\n * A function that returns a value of a particular type\n * with a resolution context being passed to it.\n *\n * @returns The scoped provider.\n */\nexport const scoped = <T>(resolver: Resolver<T>): Provider<T> => {\n const singletonFallback = singleton(resolver);\n\n const instance = createProvider((context) => {\n if (!context?.scope) return singletonFallback(context);\n\n const resolution = context.scope.has(instance)\n ? context.scope.get(instance)\n : resolver(context);\n context.scope.set(instance, resolution);\n\n return resolution;\n });\n\n return instance;\n};\n","import { Resolver } from \"./provider\";\n\n/**\n * A `Map` of providers to their instances\n * that is then passed to a provider call in a resolution context object\n * to resolve instances of scoped providers within it.\n */\nexport type Scope = Map<Resolver<any>, any>;\n\n/**\n * Creates a `Map` of providers to their instances\n * that is then passed to a provider call in a resolution context object\n * to resolve instances of scoped providers within it.\n *\n * @example\n * ```ts\n * const requestScope = createScope()\n *\n * app.use(() => {\n * const db = getDb({ scope: requestScope })\n * // ...\n * })\n * ```\n *\n * @returns The map instance.\n */\nexport const createScope = (): Scope => new Map();\n","import { Resolver } from \"./provider\";\n\n/**\n * A `Map` of providers to providers of the same type\n * which is then passed to a provider call in a resolution context object\n * in order to replace providers with their mocks.\n */\nexport type MockMap = Omit<Map<Resolver<any>, Resolver<any>>, \"set\" | \"get\"> & {\n /**\n * Sets a mock for a provider.\n *\n * @param provider - The original provider.\n * @param mock - The mock provider.\n */\n set<T>(provider: Resolver<T>, mock: Resolver<T>): MockMap;\n /**\n * Retrieves a mock of a provider. Returns undefined if there's none.\n *\n * @param provider - The provider.\n */\n get<T>(provider: Resolver<T>): Resolver<T> | undefined;\n};\n\n/**\n * Creates a `Map` of providers to providers of the same type\n * which is then passed to a provider call in a resolution context object\n * in order to replace providers with their mocks.\n *\n * @example\n * ```ts\n * const mocks = createMockMap()\n * .set(getConfig, getTestConfig)\n *\n * getThing({ mocks })\n * ```\n *\n * @returns The map instance.\n */\nexport const createMockMap = (): MockMap => new Map();\n","import { Provider, ResolutionContext } from \"./provider\";\n\ntype ProviderList = Provider<any>[];\ntype ProviderRecord = Record<string, Provider<any>>;\n\ntype InferProviderCollectionResolutions<\n Providers extends ProviderList | ProviderRecord,\n> = {\n [K in keyof Providers]: Providers[K] extends Provider<infer T> ? T : never;\n};\n\n/**\n * Awaits all promises and wraps the collection in a promise\n * if there'ss at least one `Promise` in the collection,\n * otherwise returns an untouched type.\n */\ntype AwaitValuesInCollection<T extends any[] | Record<any, any>> =\n Promise<any> extends T[keyof T]\n ? Promise<{\n [I in keyof T]: T[I] extends Promise<infer T> ? T : T[I];\n }>\n : T;\n\n/**\n * Calls every provider in a list with a provided resolution context\n * and returns a list of resolutions. Returns a `Promise` of a list\n * of awaited resolutions if there's at least one `Promise` in the resolutions.\n *\n * @example\n * Only sync providers:\n * ```ts\n * const getA = scoped(() => createA())\n * const getB = scoped(() => createB())\n * const getC = scoped(() => createC())\n *\n * const scope = createScope()\n * const resolutions = resolveList(\n * [getA, getB, getC],\n * { scope }\n * )\n *\n * resolutions == [\n * getA({ scope }),\n * getB({ scope }),\n * getC({ scope })\n * ]\n * ```\n *\n * @example\n * Some provider is async:\n * ```ts\n * const getA = scoped(() => createA())\n * const getB = scoped(async () => await createB())\n * const getC = scoped(() => createC())\n *\n * const scope = createScope()\n * const resolutions = resolveList(\n * [getA, getB, getC],\n * { scope }\n * )\n *\n * resolutions == [\n * getA({ scope }),\n * await getB({ scope }),\n * getC({ scope })\n * ]\n * ```\n *\n * @param providers - The list of providers.\n * @param context - The resolution context.\n *\n * @returns The list of resolutions.\n */\nexport const resolveList = <const Providers extends ProviderList>(\n providers: Providers,\n context?: ResolutionContext,\n): AwaitValuesInCollection<\n InferProviderCollectionResolutions<Providers>\n> => {\n const resolutions = providers.map((provider) => provider(context));\n\n const hasPromises = resolutions.some(\n (resolution) => resolution instanceof Promise,\n );\n\n return (hasPromises ? Promise.all(resolutions) : resolutions) as any;\n};\n\n/**\n * Calls every provider in a map with a provided resolution context\n * and returns a map with identical keys but with resolutions in values instead.\n * Returns a `Promise` of a map of awaited resolutions if there's at least one\n * `Promise` in the resolutions.\n *\n * @example\n * Only sync providers:\n * ```ts\n * const getA = scoped(() => createA())\n * const getB = scoped(() => createB())\n * const getC = scoped(() => createC())\n *\n * const scope = createScope()\n * const resolutions = resolveMap(\n * { a: getA, b: getB, c: getC },\n * { scope }\n * )\n *\n * resolutions == {\n * a: getA({ scope }),\n * b: getB({ scope }),\n * c: getC({ scope })\n * }\n * ```\n *\n * @example\n * Some provider is async:\n * ```ts\n * const getA = scoped(() => createA())\n * const getB = scoped(async () => await createB())\n * const getC = scoped(() => createC())\n *\n * const scope = createScope()\n * const resolutions = await resolveMap(\n * { a: getA, b: getB, c: getC },\n * { scope }\n * )\n *\n * resolutions == {\n * a: getA({ scope }),\n * b: await getB({ scope }),\n * c: getC({ scope })\n * }\n * ```\n *\n * @param providers - The map of providers.\n * @param context - The resolution context.\n *\n * @returns The map of resolutions.\n */\nexport const resolveMap = <const Providers extends ProviderRecord>(\n providers: Providers,\n context?: ResolutionContext,\n): AwaitValuesInCollection<\n InferProviderCollectionResolutions<Providers>\n> => {\n const resolutionMapEntries = Object.entries(providers).map(\n ([key, provider]) => [key, provider(context)],\n );\n\n const hasPromises = resolutionMapEntries.some(\n ([, resolution]) => resolution instanceof Promise,\n );\n\n if (hasPromises) {\n return (async () => {\n const awaitedEntries = await Promise.all(\n resolutionMapEntries.map(async ([key, resolution]) => [\n key,\n await resolution,\n ]),\n );\n return Object.fromEntries(awaitedEntries);\n })() as any;\n }\n\n return Object.fromEntries(resolutionMapEntries);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC2BA,IAAM,iBAAiB,CAAI,aAAuC;AAC9D,QAAM,WAAwB,CAAC,YAAY;AA5B/C;AA6BQ,UAAM,aAAY,wCAAS,UAAT,mBAAgB,IAAI;AACtC,QAAI,UAAW,QAAO,UAAU,OAAO;AAEvC,WAAO,SAAS,OAAO;AAAA,EAC3B;AAEA,SAAO,OAAO,OAAO,UAAU,EAAE,SAAS,WAAoB,CAAC;AACnE;AAiBO,IAAM,YAAY;AAkBlB,IAAM,YAAY,CAAI,aAAuC;AAChE,MAAI,WAAW;AACf,MAAI;AAEJ,QAAM,WAAW,eAAe,CAAC,YAAY;AACzC,QAAI,SAAU,QAAO;AAErB,iBAAa,SAAS,OAAO;AAC7B,eAAW;AAEX,WAAO;AAAA,EACX,CAAC;AAED,SAAO;AACX;AA0BO,IAAM,SAAS,CAAI,aAAuC;AAC7D,QAAM,oBAAoB,UAAU,QAAQ;AAE5C,QAAM,WAAW,eAAe,CAAC,YAAY;AACzC,QAAI,EAAC,mCAAS,OAAO,QAAO,kBAAkB,OAAO;AAErD,UAAM,aAAa,QAAQ,MAAM,IAAI,QAAQ,IACvC,QAAQ,MAAM,IAAI,QAAQ,IAC1B,SAAS,OAAO;AACtB,YAAQ,MAAM,IAAI,UAAU,UAAU;AAEtC,WAAO;AAAA,EACX,CAAC;AAED,SAAO;AACX;;;ACpGO,IAAM,cAAc,MAAa,oBAAI,IAAI;;;ACYzC,IAAM,gBAAgB,MAAe,oBAAI,IAAI;;;ACmC7C,IAAM,cAAc,CACvB,WACA,YAGC;AACD,QAAM,cAAc,UAAU,IAAI,CAAC,aAAa,SAAS,OAAO,CAAC;AAEjE,QAAM,cAAc,YAAY;AAAA,IAC5B,CAAC,eAAe,sBAAsB;AAAA,EAC1C;AAEA,SAAQ,cAAc,QAAQ,IAAI,WAAW,IAAI;AACrD;AAqDO,IAAM,aAAa,CACtB,WACA,YAGC;AACD,QAAM,uBAAuB,OAAO,QAAQ,SAAS,EAAE;AAAA,IACnD,CAAC,CAAC,KAAK,QAAQ,MAAM,CAAC,KAAK,SAAS,OAAO,CAAC;AAAA,EAChD;AAEA,QAAM,cAAc,qBAAqB;AAAA,IACrC,CAAC,CAAC,EAAE,UAAU,MAAM,sBAAsB;AAAA,EAC9C;AAEA,MAAI,aAAa;AACb,YAAQ,YAAY;AAChB,YAAM,iBAAiB,MAAM,QAAQ;AAAA,QACjC,qBAAqB,IAAI,OAAO,CAAC,KAAK,UAAU,MAAM;AAAA,UAClD;AAAA,UACA,MAAM;AAAA,QACV,CAAC;AAAA,MACL;AACA,aAAO,OAAO,YAAY,cAAc;AAAA,IAC5C,GAAG;AAAA,EACP;AAEA,SAAO,OAAO,YAAY,oBAAoB;AAClD;","names":[]}
package/dist/index.mjs CHANGED
@@ -1,38 +1,33 @@
1
1
  // src/provider.ts
2
- var transient = (resolver) => {
2
+ var createProvider = (resolver) => {
3
3
  const instance = (context) => {
4
4
  var _a;
5
5
  const maybeMock = (_a = context == null ? void 0 : context.mocks) == null ? void 0 : _a.get(instance);
6
6
  if (maybeMock) return maybeMock(context);
7
7
  return resolver(context);
8
8
  };
9
- return instance;
9
+ return Object.assign(instance, { __brand: "provider" });
10
10
  };
11
+ var transient = createProvider;
11
12
  var singleton = (resolver) => {
12
13
  let resolved = false;
13
14
  let resolution;
14
- const instance = (context) => {
15
- var _a;
16
- const maybeMock = (_a = context == null ? void 0 : context.mocks) == null ? void 0 : _a.get(instance);
17
- if (maybeMock) return maybeMock(context);
15
+ const instance = createProvider((context) => {
18
16
  if (resolved) return resolution;
19
17
  resolution = resolver(context);
20
18
  resolved = true;
21
19
  return resolution;
22
- };
20
+ });
23
21
  return instance;
24
22
  };
25
23
  var scoped = (resolver) => {
26
24
  const singletonFallback = singleton(resolver);
27
- const instance = (context) => {
28
- var _a;
29
- const maybeMock = (_a = context == null ? void 0 : context.mocks) == null ? void 0 : _a.get(instance);
30
- if (maybeMock) return maybeMock(context);
25
+ const instance = createProvider((context) => {
31
26
  if (!(context == null ? void 0 : context.scope)) return singletonFallback(context);
32
- const resolution = context.scope.has(resolver) ? context.scope.get(resolver) : resolver(context);
33
- context.scope.set(resolver, resolution);
27
+ const resolution = context.scope.has(instance) ? context.scope.get(instance) : resolver(context);
28
+ context.scope.set(instance, resolution);
34
29
  return resolution;
35
- };
30
+ });
36
31
  return instance;
37
32
  };
38
33
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/provider.ts","../src/scope.ts","../src/mock-map.ts","../src/collection-resolution.ts"],"sourcesContent":["import { MockMap } from \"./mock-map\";\nimport { Scope } from \"./scope\";\n\n/**\n * A function that returns a value of a particular type\n * with a resolution context being passed to it.\n */\nexport type Resolver<T> = (context?: ResolutionContext) => T;\n\n/**\n * A function that resolves an instance or a `Promise` of a particular type\n * based on a resolution context passed to it.\n */\nexport type Provider<T> = Resolver<T>;\n\n/**\n * A context used by providers to resolve instances\n * based on current scope and mocks.\n */\nexport type ResolutionContext = {\n scope?: Scope;\n mocks?: MockMap;\n};\n\n/**\n * Creates a transient provider that will resolve a new instance on each call.\n *\n * @example\n * ```ts\n * const getThing = transient(() => createThing())\n * getThing() !== getThing()\n * ```\n *\n * @param resolver\n * A function that returns a value of a particular type\n * with a resolution context being passed to it.\n *\n * @returns The transient provider.\n */\nexport const transient = <T>(resolver: Resolver<T>): Provider<T> => {\n const instance: Resolver<T> = (context) => {\n const maybeMock = context?.mocks?.get(instance);\n if (maybeMock) return maybeMock(context);\n\n return resolver(context);\n };\n\n return instance;\n};\n\n/**\n * Creates a singleton provider that will resolve an instance once\n * and return it on every call.\n *\n * @example\n * ```ts\n * const getThing = singleton(() => createThing())\n * getThing() === getThing()\n * ```\n *\n * @param resolver\n * A function that returns a value of a particular type\n * with a resolution context being passed to it.\n *\n * @returns The singleton provider.\n */\nexport const singleton = <T>(resolver: Resolver<T>): Provider<T> => {\n let resolved = false;\n let resolution: T | undefined;\n\n const instance: Resolver<T> = (context) => {\n const maybeMock = context?.mocks?.get(instance);\n if (maybeMock) return maybeMock(context);\n\n if (resolved) return resolution!;\n\n resolution = resolver(context);\n resolved = true;\n\n return resolution;\n };\n\n return instance;\n};\n\n/**\n * Creates a scoped provider that will take its resolution from a passed scope\n * or create a new one and save it if there is none.\n * If no scope is passed, it will act as a singleton.\n *\n * @example\n * ```ts\n * const getThing = scoped(() => createThing())\n * getThing() === getThing()\n * ```\n *\n * @example\n * ```ts\n * const getThing = scoped(() => createThing())\n * const scope = createScope()\n * getThing({ scope }) === getThing({ scope }) !== getThing()\n * ```\n *\n * @param resolver\n * A function that returns a value of a particular type\n * with a resolution context being passed to it.\n *\n * @returns The scoped provider.\n */\nexport const scoped = <T>(resolver: Resolver<T>): Provider<T> => {\n const singletonFallback = singleton(resolver);\n\n const instance: Resolver<T> = (context) => {\n const maybeMock = context?.mocks?.get(instance);\n if (maybeMock) return maybeMock(context);\n\n if (!context?.scope) return singletonFallback(context);\n\n const resolution = context.scope.has(resolver)\n ? context.scope.get(resolver)\n : resolver(context);\n context.scope.set(resolver, resolution);\n\n return resolution;\n };\n\n return instance;\n};\n","import { Resolver } from \"./provider\";\n\n/**\n * A `Map` of providers to their instances\n * that is then passed to a provider call in a resolution context object\n * to resolve instances of scoped providers within it.\n */\nexport type Scope = Map<Resolver<any>, any>;\n\n/**\n * Creates a `Map` of providers to their instances\n * that is then passed to a provider call in a resolution context object\n * to resolve instances of scoped providers within it.\n *\n * @example\n * ```ts\n * const requestScope = createScope()\n *\n * app.use(() => {\n * const db = getDb({ scope: requestScope })\n * // ...\n * })\n * ```\n *\n * @returns The map instance.\n */\nexport const createScope = (): Scope => new Map();\n","import { Resolver } from \"./provider\";\n\n/**\n * A `Map` of providers to providers of the same type\n * which is then passed to a provider call in a resolution context object\n * in order to replace providers with their mocks.\n */\nexport type MockMap = Omit<Map<Resolver<any>, Resolver<any>>, \"set\" | \"get\"> & {\n /**\n * Sets a mock for a provider.\n *\n * @param provider - The original provider.\n * @param mock - The mock provider.\n */\n set<T>(provider: Resolver<T>, mock: Resolver<T>): MockMap;\n /**\n * Retrieves a mock of a provider. Returns undefined if there's none.\n *\n * @param provider - The provider.\n */\n get<T>(provider: Resolver<T>): Resolver<T> | undefined;\n};\n\n/**\n * Creates a `Map` of providers to providers of the same type\n * which is then passed to a provider call in a resolution context object\n * in order to replace providers with their mocks.\n *\n * @example\n * ```ts\n * const mocks = createMockMap()\n * .set(getConfig, getTestConfig)\n *\n * getThing({ mocks })\n * ```\n *\n * @returns The map instance.\n */\nexport const createMockMap = (): MockMap => new Map();\n","import { Provider, ResolutionContext } from \"./provider\";\n\ntype ProviderList = Provider<any>[];\ntype ProviderRecord = Record<string, Provider<any>>;\n\ntype InferProviderCollectionResolutions<\n Providers extends ProviderList | ProviderRecord,\n> = {\n [K in keyof Providers]: Providers[K] extends Provider<infer T> ? T : never;\n};\n\n/**\n * Awaits all promises and wraps the collection in a promise\n * if there'ss at least one `Promise` in the collection,\n * otherwise returns an untouched type.\n */\ntype AwaitValuesInCollection<T extends any[] | Record<any, any>> =\n Promise<any> extends T[keyof T]\n ? Promise<{\n [I in keyof T]: T[I] extends Promise<infer T> ? T : T[I];\n }>\n : T;\n\n/**\n * Calls every provider in a list with a provided resolution context\n * and returns a list of resolutions. Returns a `Promise` of a list\n * of awaited resolutions if there's at least one `Promise` in the resolutions.\n *\n * @example\n * Only sync providers:\n * ```ts\n * const getA = scoped(() => createA())\n * const getB = scoped(() => createB())\n * const getC = scoped(() => createC())\n *\n * const scope = createScope()\n * const resolutions = resolveList(\n * [getA, getB, getC],\n * { scope }\n * )\n *\n * resolutions == [\n * getA({ scope }),\n * getB({ scope }),\n * getC({ scope })\n * ]\n * ```\n *\n * @example\n * Some provider is async:\n * ```ts\n * const getA = scoped(() => createA())\n * const getB = scoped(async () => await createB())\n * const getC = scoped(() => createC())\n *\n * const scope = createScope()\n * const resolutions = resolveList(\n * [getA, getB, getC],\n * { scope }\n * )\n *\n * resolutions == [\n * getA({ scope }),\n * await getB({ scope }),\n * getC({ scope })\n * ]\n * ```\n *\n * @param providers - The list of providers.\n * @param context - The resolution context.\n *\n * @returns The list of resolutions.\n */\nexport const resolveList = <const Providers extends ProviderList>(\n providers: Providers,\n context?: ResolutionContext,\n): AwaitValuesInCollection<\n InferProviderCollectionResolutions<Providers>\n> => {\n const resolutions = providers.map((provider) => provider(context));\n\n const hasPromises = resolutions.some(\n (resolution) => resolution instanceof Promise,\n );\n\n return (hasPromises ? Promise.all(resolutions) : resolutions) as any;\n};\n\n/**\n * Calls every provider in a map with a provided resolution context\n * and returns a map with identical keys but with resolutions in values instead.\n * Returns a `Promise` of a map of awaited resolutions if there's at least one\n * `Promise` in the resolutions.\n *\n * @example\n * Only sync providers:\n * ```ts\n * const getA = scoped(() => createA())\n * const getB = scoped(() => createB())\n * const getC = scoped(() => createC())\n *\n * const scope = createScope()\n * const resolutions = resolveMap(\n * { a: getA, b: getB, c: getC },\n * { scope }\n * )\n *\n * resolutions == {\n * a: getA({ scope }),\n * b: getB({ scope }),\n * c: getC({ scope })\n * }\n * ```\n *\n * @example\n * Some provider is async:\n * ```ts\n * const getA = scoped(() => createA())\n * const getB = scoped(async () => await createB())\n * const getC = scoped(() => createC())\n *\n * const scope = createScope()\n * const resolutions = await resolveMap(\n * { a: getA, b: getB, c: getC },\n * { scope }\n * )\n *\n * resolutions == {\n * a: getA({ scope }),\n * b: await getB({ scope }),\n * c: getC({ scope })\n * }\n * ```\n *\n * @param providers - The map of providers.\n * @param context - The resolution context.\n *\n * @returns The map of resolutions.\n */\nexport const resolveMap = <const Providers extends ProviderRecord>(\n providers: Providers,\n context?: ResolutionContext,\n): AwaitValuesInCollection<\n InferProviderCollectionResolutions<Providers>\n> => {\n const resolutionMapEntries = Object.entries(providers).map(\n ([key, provider]) => [key, provider(context)],\n );\n\n const hasPromises = resolutionMapEntries.some(\n ([, resolution]) => resolution instanceof Promise,\n );\n\n if (hasPromises) {\n return (async () => {\n const awaitedEntries = await Promise.all(\n resolutionMapEntries.map(async ([key, resolution]) => [\n key,\n await resolution,\n ]),\n );\n return Object.fromEntries(awaitedEntries);\n })() as any;\n }\n\n return Object.fromEntries(resolutionMapEntries);\n};\n"],"mappings":";AAuCO,IAAM,YAAY,CAAI,aAAuC;AAChE,QAAM,WAAwB,CAAC,YAAY;AAxC/C;AAyCQ,UAAM,aAAY,wCAAS,UAAT,mBAAgB,IAAI;AACtC,QAAI,UAAW,QAAO,UAAU,OAAO;AAEvC,WAAO,SAAS,OAAO;AAAA,EAC3B;AAEA,SAAO;AACX;AAkBO,IAAM,YAAY,CAAI,aAAuC;AAChE,MAAI,WAAW;AACf,MAAI;AAEJ,QAAM,WAAwB,CAAC,YAAY;AAtE/C;AAuEQ,UAAM,aAAY,wCAAS,UAAT,mBAAgB,IAAI;AACtC,QAAI,UAAW,QAAO,UAAU,OAAO;AAEvC,QAAI,SAAU,QAAO;AAErB,iBAAa,SAAS,OAAO;AAC7B,eAAW;AAEX,WAAO;AAAA,EACX;AAEA,SAAO;AACX;AA0BO,IAAM,SAAS,CAAI,aAAuC;AAC7D,QAAM,oBAAoB,UAAU,QAAQ;AAE5C,QAAM,WAAwB,CAAC,YAAY;AAhH/C;AAiHQ,UAAM,aAAY,wCAAS,UAAT,mBAAgB,IAAI;AACtC,QAAI,UAAW,QAAO,UAAU,OAAO;AAEvC,QAAI,EAAC,mCAAS,OAAO,QAAO,kBAAkB,OAAO;AAErD,UAAM,aAAa,QAAQ,MAAM,IAAI,QAAQ,IACvC,QAAQ,MAAM,IAAI,QAAQ,IAC1B,SAAS,OAAO;AACtB,YAAQ,MAAM,IAAI,UAAU,UAAU;AAEtC,WAAO;AAAA,EACX;AAEA,SAAO;AACX;;;ACrGO,IAAM,cAAc,MAAa,oBAAI,IAAI;;;ACYzC,IAAM,gBAAgB,MAAe,oBAAI,IAAI;;;ACmC7C,IAAM,cAAc,CACvB,WACA,YAGC;AACD,QAAM,cAAc,UAAU,IAAI,CAAC,aAAa,SAAS,OAAO,CAAC;AAEjE,QAAM,cAAc,YAAY;AAAA,IAC5B,CAAC,eAAe,sBAAsB;AAAA,EAC1C;AAEA,SAAQ,cAAc,QAAQ,IAAI,WAAW,IAAI;AACrD;AAqDO,IAAM,aAAa,CACtB,WACA,YAGC;AACD,QAAM,uBAAuB,OAAO,QAAQ,SAAS,EAAE;AAAA,IACnD,CAAC,CAAC,KAAK,QAAQ,MAAM,CAAC,KAAK,SAAS,OAAO,CAAC;AAAA,EAChD;AAEA,QAAM,cAAc,qBAAqB;AAAA,IACrC,CAAC,CAAC,EAAE,UAAU,MAAM,sBAAsB;AAAA,EAC9C;AAEA,MAAI,aAAa;AACb,YAAQ,YAAY;AAChB,YAAM,iBAAiB,MAAM,QAAQ;AAAA,QACjC,qBAAqB,IAAI,OAAO,CAAC,KAAK,UAAU,MAAM;AAAA,UAClD;AAAA,UACA,MAAM;AAAA,QACV,CAAC;AAAA,MACL;AACA,aAAO,OAAO,YAAY,cAAc;AAAA,IAC5C,GAAG;AAAA,EACP;AAEA,SAAO,OAAO,YAAY,oBAAoB;AAClD;","names":[]}
1
+ {"version":3,"sources":["../src/provider.ts","../src/scope.ts","../src/mock-map.ts","../src/collection-resolution.ts"],"sourcesContent":["import { MockMap } from \"./mock-map\";\nimport { Scope } from \"./scope\";\n\n/**\n * A function that returns a value of a particular type\n * with a resolution context being passed to it.\n */\nexport type Resolver<T> = (context?: ResolutionContext) => T;\n\n/**\n * A function that resolves an instance or a `Promise` of a particular type\n * based on a resolution context passed to it.\n */\nexport type Provider<T> = Resolver<T> & { __brand: \"provider\" };\n\n/**\n * A context used by providers to resolve instances\n * based on current scope and mocks.\n */\nexport type ResolutionContext = {\n scope?: Scope;\n mocks?: MockMap;\n};\n\n/**\n * Creating a nominal type value and introducing common functionality.\n */\nconst createProvider = <T>(resolver: Resolver<T>): Provider<T> => {\n const instance: Resolver<T> = (context) => {\n const maybeMock = context?.mocks?.get(instance);\n if (maybeMock) return maybeMock(context);\n\n return resolver(context);\n };\n\n return Object.assign(instance, { __brand: \"provider\" as const });\n};\n\n/**\n * Creates a transient provider that will resolve a new instance on each call.\n *\n * @example\n * ```ts\n * const getThing = transient(() => createThing())\n * getThing() !== getThing()\n * ```\n *\n * @param resolver\n * A function that returns a value of a particular type\n * with a resolution context being passed to it.\n *\n * @returns The transient provider.\n */\nexport const transient = createProvider;\n\n/**\n * Creates a singleton provider that will resolve an instance once\n * and return it on every call.\n *\n * @example\n * ```ts\n * const getThing = singleton(() => createThing())\n * getThing() === getThing()\n * ```\n *\n * @param resolver\n * A function that returns a value of a particular type\n * with a resolution context being passed to it.\n *\n * @returns The singleton provider.\n */\nexport const singleton = <T>(resolver: Resolver<T>): Provider<T> => {\n let resolved = false;\n let resolution: T | undefined;\n\n const instance = createProvider((context) => {\n if (resolved) return resolution!;\n\n resolution = resolver(context);\n resolved = true;\n\n return resolution;\n });\n\n return instance;\n};\n\n/**\n * Creates a scoped provider that will take its resolution from a passed scope\n * or create a new one and save it if there is none.\n * If no scope is passed, it will act as a singleton.\n *\n * @example\n * ```ts\n * const getThing = scoped(() => createThing())\n * getThing() === getThing()\n * ```\n *\n * @example\n * ```ts\n * const getThing = scoped(() => createThing())\n * const scope = createScope()\n * getThing({ scope }) === getThing({ scope }) !== getThing()\n * ```\n *\n * @param resolver\n * A function that returns a value of a particular type\n * with a resolution context being passed to it.\n *\n * @returns The scoped provider.\n */\nexport const scoped = <T>(resolver: Resolver<T>): Provider<T> => {\n const singletonFallback = singleton(resolver);\n\n const instance = createProvider((context) => {\n if (!context?.scope) return singletonFallback(context);\n\n const resolution = context.scope.has(instance)\n ? context.scope.get(instance)\n : resolver(context);\n context.scope.set(instance, resolution);\n\n return resolution;\n });\n\n return instance;\n};\n","import { Resolver } from \"./provider\";\n\n/**\n * A `Map` of providers to their instances\n * that is then passed to a provider call in a resolution context object\n * to resolve instances of scoped providers within it.\n */\nexport type Scope = Map<Resolver<any>, any>;\n\n/**\n * Creates a `Map` of providers to their instances\n * that is then passed to a provider call in a resolution context object\n * to resolve instances of scoped providers within it.\n *\n * @example\n * ```ts\n * const requestScope = createScope()\n *\n * app.use(() => {\n * const db = getDb({ scope: requestScope })\n * // ...\n * })\n * ```\n *\n * @returns The map instance.\n */\nexport const createScope = (): Scope => new Map();\n","import { Resolver } from \"./provider\";\n\n/**\n * A `Map` of providers to providers of the same type\n * which is then passed to a provider call in a resolution context object\n * in order to replace providers with their mocks.\n */\nexport type MockMap = Omit<Map<Resolver<any>, Resolver<any>>, \"set\" | \"get\"> & {\n /**\n * Sets a mock for a provider.\n *\n * @param provider - The original provider.\n * @param mock - The mock provider.\n */\n set<T>(provider: Resolver<T>, mock: Resolver<T>): MockMap;\n /**\n * Retrieves a mock of a provider. Returns undefined if there's none.\n *\n * @param provider - The provider.\n */\n get<T>(provider: Resolver<T>): Resolver<T> | undefined;\n};\n\n/**\n * Creates a `Map` of providers to providers of the same type\n * which is then passed to a provider call in a resolution context object\n * in order to replace providers with their mocks.\n *\n * @example\n * ```ts\n * const mocks = createMockMap()\n * .set(getConfig, getTestConfig)\n *\n * getThing({ mocks })\n * ```\n *\n * @returns The map instance.\n */\nexport const createMockMap = (): MockMap => new Map();\n","import { Provider, ResolutionContext } from \"./provider\";\n\ntype ProviderList = Provider<any>[];\ntype ProviderRecord = Record<string, Provider<any>>;\n\ntype InferProviderCollectionResolutions<\n Providers extends ProviderList | ProviderRecord,\n> = {\n [K in keyof Providers]: Providers[K] extends Provider<infer T> ? T : never;\n};\n\n/**\n * Awaits all promises and wraps the collection in a promise\n * if there'ss at least one `Promise` in the collection,\n * otherwise returns an untouched type.\n */\ntype AwaitValuesInCollection<T extends any[] | Record<any, any>> =\n Promise<any> extends T[keyof T]\n ? Promise<{\n [I in keyof T]: T[I] extends Promise<infer T> ? T : T[I];\n }>\n : T;\n\n/**\n * Calls every provider in a list with a provided resolution context\n * and returns a list of resolutions. Returns a `Promise` of a list\n * of awaited resolutions if there's at least one `Promise` in the resolutions.\n *\n * @example\n * Only sync providers:\n * ```ts\n * const getA = scoped(() => createA())\n * const getB = scoped(() => createB())\n * const getC = scoped(() => createC())\n *\n * const scope = createScope()\n * const resolutions = resolveList(\n * [getA, getB, getC],\n * { scope }\n * )\n *\n * resolutions == [\n * getA({ scope }),\n * getB({ scope }),\n * getC({ scope })\n * ]\n * ```\n *\n * @example\n * Some provider is async:\n * ```ts\n * const getA = scoped(() => createA())\n * const getB = scoped(async () => await createB())\n * const getC = scoped(() => createC())\n *\n * const scope = createScope()\n * const resolutions = resolveList(\n * [getA, getB, getC],\n * { scope }\n * )\n *\n * resolutions == [\n * getA({ scope }),\n * await getB({ scope }),\n * getC({ scope })\n * ]\n * ```\n *\n * @param providers - The list of providers.\n * @param context - The resolution context.\n *\n * @returns The list of resolutions.\n */\nexport const resolveList = <const Providers extends ProviderList>(\n providers: Providers,\n context?: ResolutionContext,\n): AwaitValuesInCollection<\n InferProviderCollectionResolutions<Providers>\n> => {\n const resolutions = providers.map((provider) => provider(context));\n\n const hasPromises = resolutions.some(\n (resolution) => resolution instanceof Promise,\n );\n\n return (hasPromises ? Promise.all(resolutions) : resolutions) as any;\n};\n\n/**\n * Calls every provider in a map with a provided resolution context\n * and returns a map with identical keys but with resolutions in values instead.\n * Returns a `Promise` of a map of awaited resolutions if there's at least one\n * `Promise` in the resolutions.\n *\n * @example\n * Only sync providers:\n * ```ts\n * const getA = scoped(() => createA())\n * const getB = scoped(() => createB())\n * const getC = scoped(() => createC())\n *\n * const scope = createScope()\n * const resolutions = resolveMap(\n * { a: getA, b: getB, c: getC },\n * { scope }\n * )\n *\n * resolutions == {\n * a: getA({ scope }),\n * b: getB({ scope }),\n * c: getC({ scope })\n * }\n * ```\n *\n * @example\n * Some provider is async:\n * ```ts\n * const getA = scoped(() => createA())\n * const getB = scoped(async () => await createB())\n * const getC = scoped(() => createC())\n *\n * const scope = createScope()\n * const resolutions = await resolveMap(\n * { a: getA, b: getB, c: getC },\n * { scope }\n * )\n *\n * resolutions == {\n * a: getA({ scope }),\n * b: await getB({ scope }),\n * c: getC({ scope })\n * }\n * ```\n *\n * @param providers - The map of providers.\n * @param context - The resolution context.\n *\n * @returns The map of resolutions.\n */\nexport const resolveMap = <const Providers extends ProviderRecord>(\n providers: Providers,\n context?: ResolutionContext,\n): AwaitValuesInCollection<\n InferProviderCollectionResolutions<Providers>\n> => {\n const resolutionMapEntries = Object.entries(providers).map(\n ([key, provider]) => [key, provider(context)],\n );\n\n const hasPromises = resolutionMapEntries.some(\n ([, resolution]) => resolution instanceof Promise,\n );\n\n if (hasPromises) {\n return (async () => {\n const awaitedEntries = await Promise.all(\n resolutionMapEntries.map(async ([key, resolution]) => [\n key,\n await resolution,\n ]),\n );\n return Object.fromEntries(awaitedEntries);\n })() as any;\n }\n\n return Object.fromEntries(resolutionMapEntries);\n};\n"],"mappings":";AA2BA,IAAM,iBAAiB,CAAI,aAAuC;AAC9D,QAAM,WAAwB,CAAC,YAAY;AA5B/C;AA6BQ,UAAM,aAAY,wCAAS,UAAT,mBAAgB,IAAI;AACtC,QAAI,UAAW,QAAO,UAAU,OAAO;AAEvC,WAAO,SAAS,OAAO;AAAA,EAC3B;AAEA,SAAO,OAAO,OAAO,UAAU,EAAE,SAAS,WAAoB,CAAC;AACnE;AAiBO,IAAM,YAAY;AAkBlB,IAAM,YAAY,CAAI,aAAuC;AAChE,MAAI,WAAW;AACf,MAAI;AAEJ,QAAM,WAAW,eAAe,CAAC,YAAY;AACzC,QAAI,SAAU,QAAO;AAErB,iBAAa,SAAS,OAAO;AAC7B,eAAW;AAEX,WAAO;AAAA,EACX,CAAC;AAED,SAAO;AACX;AA0BO,IAAM,SAAS,CAAI,aAAuC;AAC7D,QAAM,oBAAoB,UAAU,QAAQ;AAE5C,QAAM,WAAW,eAAe,CAAC,YAAY;AACzC,QAAI,EAAC,mCAAS,OAAO,QAAO,kBAAkB,OAAO;AAErD,UAAM,aAAa,QAAQ,MAAM,IAAI,QAAQ,IACvC,QAAQ,MAAM,IAAI,QAAQ,IAC1B,SAAS,OAAO;AACtB,YAAQ,MAAM,IAAI,UAAU,UAAU;AAEtC,WAAO;AAAA,EACX,CAAC;AAED,SAAO;AACX;;;ACpGO,IAAM,cAAc,MAAa,oBAAI,IAAI;;;ACYzC,IAAM,gBAAgB,MAAe,oBAAI,IAAI;;;ACmC7C,IAAM,cAAc,CACvB,WACA,YAGC;AACD,QAAM,cAAc,UAAU,IAAI,CAAC,aAAa,SAAS,OAAO,CAAC;AAEjE,QAAM,cAAc,YAAY;AAAA,IAC5B,CAAC,eAAe,sBAAsB;AAAA,EAC1C;AAEA,SAAQ,cAAc,QAAQ,IAAI,WAAW,IAAI;AACrD;AAqDO,IAAM,aAAa,CACtB,WACA,YAGC;AACD,QAAM,uBAAuB,OAAO,QAAQ,SAAS,EAAE;AAAA,IACnD,CAAC,CAAC,KAAK,QAAQ,MAAM,CAAC,KAAK,SAAS,OAAO,CAAC;AAAA,EAChD;AAEA,QAAM,cAAc,qBAAqB;AAAA,IACrC,CAAC,CAAC,EAAE,UAAU,MAAM,sBAAsB;AAAA,EAC9C;AAEA,MAAI,aAAa;AACb,YAAQ,YAAY;AAChB,YAAM,iBAAiB,MAAM,QAAQ;AAAA,QACjC,qBAAqB,IAAI,OAAO,CAAC,KAAK,UAAU,MAAM;AAAA,UAClD;AAAA,UACA,MAAM;AAAA,QACV,CAAC;AAAA,MACL;AACA,aAAO,OAAO,YAAY,cAAc;AAAA,IAC5C,GAAG;AAAA,EACP;AAEA,SAAO,OAAO,YAAY,oBAAoB;AAClD;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atomic-di",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Lifetimes, scopes and mocking for pure dependency injection.",
5
5
  "repository": "https://github.com/ncor/atomic-di",
6
6
  "bugs": "https://github.com/ncor/atomic-di/issues",