atomic-di 1.2.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.js CHANGED
@@ -29,33 +29,40 @@ __export(src_exports, {
29
29
  });
30
30
  module.exports = __toCommonJS(src_exports);
31
31
 
32
- // src/provider.ts
33
- var createProvider = (resolver) => {
32
+ // src/resolver.ts
33
+ var mockable = (fn) => {
34
34
  const instance = (context) => {
35
35
  var _a;
36
- const maybeMock = (_a = context == null ? void 0 : context.mocks) == null ? void 0 : _a.get(instance);
37
- if (maybeMock) return maybeMock(context);
38
- return resolver(context);
36
+ const mock = (_a = context == null ? void 0 : context.mocks) == null ? void 0 : _a.get(instance);
37
+ if (!mock) return fn(context);
38
+ if (!mock.isPartial) return mock.resolver(context);
39
+ const resolution = fn(context);
40
+ const mockResolution = mock.resolver(context);
41
+ if (resolution instanceof Promise && mockResolution instanceof Promise)
42
+ return Promise.all([resolution, mockResolution]).then(
43
+ ([a, b]) => Object.assign(a, b)
44
+ );
45
+ return Object.assign(resolution, mockResolution);
39
46
  };
40
- return Object.assign(instance, { __brand: "provider" });
47
+ return instance;
41
48
  };
42
- var transient = createProvider;
43
- var singleton = (resolver) => {
49
+ var transient = (fn) => mockable(fn);
50
+ var singleton = (fn) => {
44
51
  let resolved = false;
45
52
  let resolution;
46
- const instance = createProvider((context) => {
53
+ const instance = mockable((context) => {
47
54
  if (resolved) return resolution;
48
- resolution = resolver(context);
55
+ resolution = fn(context);
49
56
  resolved = true;
50
57
  return resolution;
51
58
  });
52
59
  return instance;
53
60
  };
54
- var scoped = (resolver) => {
55
- const singletonFallback = singleton(resolver);
56
- const instance = createProvider((context) => {
61
+ var scoped = (fn) => {
62
+ const singletonFallback = singleton(fn);
63
+ const instance = mockable((context) => {
57
64
  if (!(context == null ? void 0 : context.scope)) return singletonFallback(context);
58
- const resolution = context.scope.has(instance) ? context.scope.get(instance) : resolver(context);
65
+ const resolution = context.scope.has(instance) ? context.scope.get(instance) : fn(context);
59
66
  context.scope.set(instance, resolution);
60
67
  return resolution;
61
68
  });
@@ -66,19 +73,43 @@ var scoped = (resolver) => {
66
73
  var createScope = () => /* @__PURE__ */ new Map();
67
74
 
68
75
  // src/mock-map.ts
69
- var createMockMap = () => /* @__PURE__ */ new Map();
76
+ var createMockMapWithEntries = (entries = []) => {
77
+ const set = (key, value) => createMockMapWithEntries([
78
+ ...entries.filter((entry) => entry[0] !== key),
79
+ [key, value]
80
+ ]);
81
+ return {
82
+ mock(original, mock) {
83
+ return set(original, {
84
+ isPartial: false,
85
+ resolver: mock
86
+ });
87
+ },
88
+ mockPartially(original, mock) {
89
+ return set(original, {
90
+ isPartial: true,
91
+ resolver: mock
92
+ });
93
+ },
94
+ get(original) {
95
+ var _a;
96
+ return (_a = entries.find((entry) => entry[0] === original)) == null ? void 0 : _a[1];
97
+ }
98
+ };
99
+ };
100
+ var createMockMap = () => createMockMapWithEntries();
70
101
 
71
102
  // src/collection-resolution.ts
72
- var resolveList = (providers, context) => {
73
- const resolutions = providers.map((provider) => provider(context));
103
+ var resolveList = (resolvers, context) => {
104
+ const resolutions = resolvers.map((resolver) => resolver(context));
74
105
  const hasPromises = resolutions.some(
75
106
  (resolution) => resolution instanceof Promise
76
107
  );
77
108
  return hasPromises ? Promise.all(resolutions) : resolutions;
78
109
  };
79
- var resolveMap = (providers, context) => {
80
- const resolutionMapEntries = Object.entries(providers).map(
81
- ([key, provider]) => [key, provider(context)]
110
+ var resolveMap = (resolvers, context) => {
111
+ const resolutionMapEntries = Object.entries(resolvers).map(
112
+ ([key, resolver]) => [key, resolver(context)]
82
113
  );
83
114
  const hasPromises = resolutionMapEntries.some(
84
115
  ([, resolution]) => resolution instanceof Promise
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> & { __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":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/resolver.ts","../src/scope.ts","../src/mock-map.ts","../src/collection-resolution.ts"],"sourcesContent":["export * from \"./resolver\";\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 takes a resolution context\n * and returns a value of some type.\n */\nexport type ResolverFn<T> = (context?: ResolutionContext) => T;\n\n/**\n * A function that returns a value of some type\n * based on a resolution context.\n */\nexport type Resolver<T> = ResolverFn<T>;\n\n/**\n * A context used by resolvers that defines the behaviour of the resolver\n * with the passed mocks and scope.\n */\nexport type ResolutionContext = {\n scope?: Scope;\n mocks?: MockMap;\n};\n\n/**\n * Makes the resolver function capable of replacing itself\n * with a mock if one is defined in the resolution context.\n */\nconst mockable = <T>(fn: ResolverFn<T>): ResolverFn<T> => {\n const instance = (context?: ResolutionContext) => {\n const mock = context?.mocks?.get(instance);\n if (!mock) return fn(context);\n\n if (!mock.isPartial) return mock.resolver(context);\n\n const resolution = fn(context);\n const mockResolution = mock.resolver(context);\n\n if (resolution instanceof Promise && mockResolution instanceof Promise)\n return Promise.all([resolution, mockResolution]).then(([a, b]) =>\n Object.assign(a as object, b),\n ) as T;\n\n return Object.assign(resolution as object, mockResolution);\n };\n\n return instance;\n};\n\n/**\n * Creates a resolver that creates a new resolution on each call.\n *\n * @example\n * ```ts\n * const getEntity = transient(() => createEntity())\n * getEntity() !== getEntity()\n * ```\n *\n * @param resolver\n * A function that takes a resolution context\n * and returns a value of some type.\n *\n * @returns The transient resolver.\n */\nexport const transient = <T>(fn: ResolverFn<T>): Resolver<T> => mockable(fn);\n\n/**\n * Creates a resolver that creates\n * a resolution once and return it on each call.\n *\n * @example\n * ```ts\n * const getEntity = singleton(() => createEntity())\n * getEntity() === getEntity()\n * ```\n *\n * @param fn\n * A function that takes a resolution context\n * and returns a value of some type.\n *\n * @returns The singleton resolver.\n */\nexport const singleton = <T>(fn: ResolverFn<T>): Resolver<T> => {\n let resolved = false;\n let resolution: T | undefined;\n\n const instance = mockable((context) => {\n if (resolved) return resolution!;\n\n resolution = fn(context);\n resolved = true;\n\n return resolution;\n });\n\n return instance;\n};\n\n/**\n * Creates a resolver that takes its resolution\n * from a scope or create a new one and save it if there is none.\n * If no scope was passed in a resolution context,\n * it will act as a singleton.\n *\n * @example\n * ```ts\n * const getEntity = scoped(() => createEntity())\n * getEntity() === getEntity()\n * ```\n *\n * @example\n * ```ts\n * const getEntity = scoped(() => createEntity())\n * const scope = createScope()\n * getEntity({ scope }) === getEntity({ scope }) !== getEntity()\n * ```\n *\n * @param fn\n * A function that takes a resolution context\n * and returns a value of some type.\n *\n * @returns The scoped resolver.\n */\nexport const scoped = <T>(fn: ResolverFn<T>): Resolver<T> => {\n const singletonFallback = singleton(fn);\n\n const instance = mockable((context) => {\n if (!context?.scope) return singletonFallback(context);\n\n const resolution = context.scope.has(instance)\n ? context.scope.get(instance)\n : fn(context);\n\n context.scope.set(instance, resolution);\n\n return resolution;\n });\n\n return instance;\n};\n","import { Resolver } from \"./resolver\";\n\n/**\n * A `Map` of resolvers to their resolutions.\n * Is passed in a resolution context and used by scoped resolvers\n * to retrieve or save resolution within it.\n */\nexport type Scope = Map<Resolver<any>, any>;\n\n/**\n * Creates a `Map` of providers to their instances.\n * Is passed in a resolution context and used by scoped resolvers\n * to retrieve or save resolution 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 * @returns The map.\n */\nexport const createScope = (): Scope => new Map();\n","import { Resolver } from \"./resolver\";\n\ntype PromiseAwarePartial<T> =\n T extends Promise<infer U> ? Promise<Partial<U>> : Partial<T>;\n\ntype Mock<T> =\n | {\n isPartial: false;\n resolver: Resolver<T>;\n }\n | {\n isPartial: true;\n resolver: Resolver<PromiseAwarePartial<T>>;\n };\n\ntype MocksEntries = [Resolver<any>, Mock<any>][];\n\n/**\n * Immutable map that registers and provides mocks.\n * Is passed in a resolution context and used by resolvers\n * to replace or partially replace themselves with a mock if one is defined.\n */\nexport type MockMap = {\n /**\n * Registers a mock for a resolver,\n * creating a new `MockMap` with this registration.\n *\n * @param original - The original resolver.\n * @param mock - The mock resolver.\n */\n mock<T>(original: Resolver<T>, mock: Resolver<T>): MockMap;\n /**\n * Registers a partial mock for a resolver,\n * creating a new `MockMap` with this registration.\n * In this case, the mock resolver's resolution object will be\n * merged with the original resolver's resolution object,\n * overwriting certain fields.\n *\n * @param original - The original resolver.\n * @param mock - The mock resolver.\n */\n mockPartially<T extends object>(\n original: Resolver<T>,\n mock: Resolver<PromiseAwarePartial<T>>,\n ): MockMap;\n /**\n * Returns a mock of a resolver\n * or `undefined` if one is not registered.\n *\n * @param original - The original resolver.\n */\n get<T>(original: Resolver<T>): Mock<T> | undefined;\n};\n\n/**\n * Internal implementation that accepts entries.\n */\nconst createMockMapWithEntries = (entries: MocksEntries = []): MockMap => {\n const set = (key: Resolver<any>, value: Mock<any>) =>\n createMockMapWithEntries([\n ...entries.filter((entry) => entry[0] !== key),\n [key, value],\n ]);\n\n return {\n mock(original, mock) {\n return set(original, {\n isPartial: false,\n resolver: mock,\n });\n },\n mockPartially(original, mock) {\n return set(original, {\n isPartial: true,\n resolver: mock,\n });\n },\n get(original) {\n return entries.find((entry) => entry[0] === original)?.[1] as any;\n },\n };\n};\n\n/**\n * Creates a mock map, an immutable map that registers and provides mocks.\n * Is passed in a resolution context and used by resolvers\n * to replace or partially replace themselves with a mock if one is defined.\n *\n * @example\n * ```ts\n * const mocks = createMockMap()\n * .mock(getDependency, getDependencyMock)\n * .mockPartially(\n * getOtherDepedency,\n * transient(() => ({ someField: \"mock\" }))\n * )\n *\n * const entityWithMocks = getEntity({ mocks })\n * ```\n *\n * @returns The mock map.\n */\nexport const createMockMap = (): MockMap => createMockMapWithEntries();\n","import { ResolutionContext, Resolver } from \"./resolver\";\n\ntype ResolverList = Resolver<any>[];\ntype ResolverRecord = Record<string, Resolver<any>>;\n\ntype InferResolverCollectionResolutions<\n Resolvers extends ResolverList | ResolverRecord,\n> = {\n [K in keyof Resolvers]: Resolvers[K] extends Resolver<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 resolver 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 resolvers:\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 resolver 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 resolvers - The list of resolvers.\n * @param context - The resolution context.\n *\n * @returns The list of resolutions.\n */\nexport const resolveList = <const Resolvers extends ResolverList>(\n resolvers: Resolvers,\n context?: ResolutionContext,\n): AwaitValuesInCollection<InferResolverCollectionResolutions<Resolvers>> => {\n const resolutions = resolvers.map((resolver) => resolver(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 resolver 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 resolvers:\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 resolver 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 resolvers - The map of resolvers.\n * @param context - The resolution context.\n *\n * @returns The map of resolutions.\n */\nexport const resolveMap = <const Resolvers extends ResolverRecord>(\n resolvers: Resolvers,\n context?: ResolutionContext,\n): AwaitValuesInCollection<InferResolverCollectionResolutions<Resolvers>> => {\n const resolutionMapEntries = Object.entries(resolvers).map(\n ([key, resolver]) => [key, resolver(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":"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","names":[]}
package/dist/index.mjs CHANGED
@@ -1,30 +1,37 @@
1
- // src/provider.ts
2
- var createProvider = (resolver) => {
1
+ // src/resolver.ts
2
+ var mockable = (fn) => {
3
3
  const instance = (context) => {
4
4
  var _a;
5
- const maybeMock = (_a = context == null ? void 0 : context.mocks) == null ? void 0 : _a.get(instance);
6
- if (maybeMock) return maybeMock(context);
7
- return resolver(context);
5
+ const mock = (_a = context == null ? void 0 : context.mocks) == null ? void 0 : _a.get(instance);
6
+ if (!mock) return fn(context);
7
+ if (!mock.isPartial) return mock.resolver(context);
8
+ const resolution = fn(context);
9
+ const mockResolution = mock.resolver(context);
10
+ if (resolution instanceof Promise && mockResolution instanceof Promise)
11
+ return Promise.all([resolution, mockResolution]).then(
12
+ ([a, b]) => Object.assign(a, b)
13
+ );
14
+ return Object.assign(resolution, mockResolution);
8
15
  };
9
- return Object.assign(instance, { __brand: "provider" });
16
+ return instance;
10
17
  };
11
- var transient = createProvider;
12
- var singleton = (resolver) => {
18
+ var transient = (fn) => mockable(fn);
19
+ var singleton = (fn) => {
13
20
  let resolved = false;
14
21
  let resolution;
15
- const instance = createProvider((context) => {
22
+ const instance = mockable((context) => {
16
23
  if (resolved) return resolution;
17
- resolution = resolver(context);
24
+ resolution = fn(context);
18
25
  resolved = true;
19
26
  return resolution;
20
27
  });
21
28
  return instance;
22
29
  };
23
- var scoped = (resolver) => {
24
- const singletonFallback = singleton(resolver);
25
- const instance = createProvider((context) => {
30
+ var scoped = (fn) => {
31
+ const singletonFallback = singleton(fn);
32
+ const instance = mockable((context) => {
26
33
  if (!(context == null ? void 0 : context.scope)) return singletonFallback(context);
27
- const resolution = context.scope.has(instance) ? context.scope.get(instance) : resolver(context);
34
+ const resolution = context.scope.has(instance) ? context.scope.get(instance) : fn(context);
28
35
  context.scope.set(instance, resolution);
29
36
  return resolution;
30
37
  });
@@ -35,19 +42,43 @@ var scoped = (resolver) => {
35
42
  var createScope = () => /* @__PURE__ */ new Map();
36
43
 
37
44
  // src/mock-map.ts
38
- var createMockMap = () => /* @__PURE__ */ new Map();
45
+ var createMockMapWithEntries = (entries = []) => {
46
+ const set = (key, value) => createMockMapWithEntries([
47
+ ...entries.filter((entry) => entry[0] !== key),
48
+ [key, value]
49
+ ]);
50
+ return {
51
+ mock(original, mock) {
52
+ return set(original, {
53
+ isPartial: false,
54
+ resolver: mock
55
+ });
56
+ },
57
+ mockPartially(original, mock) {
58
+ return set(original, {
59
+ isPartial: true,
60
+ resolver: mock
61
+ });
62
+ },
63
+ get(original) {
64
+ var _a;
65
+ return (_a = entries.find((entry) => entry[0] === original)) == null ? void 0 : _a[1];
66
+ }
67
+ };
68
+ };
69
+ var createMockMap = () => createMockMapWithEntries();
39
70
 
40
71
  // src/collection-resolution.ts
41
- var resolveList = (providers, context) => {
42
- const resolutions = providers.map((provider) => provider(context));
72
+ var resolveList = (resolvers, context) => {
73
+ const resolutions = resolvers.map((resolver) => resolver(context));
43
74
  const hasPromises = resolutions.some(
44
75
  (resolution) => resolution instanceof Promise
45
76
  );
46
77
  return hasPromises ? Promise.all(resolutions) : resolutions;
47
78
  };
48
- var resolveMap = (providers, context) => {
49
- const resolutionMapEntries = Object.entries(providers).map(
50
- ([key, provider]) => [key, provider(context)]
79
+ var resolveMap = (resolvers, context) => {
80
+ const resolutionMapEntries = Object.entries(resolvers).map(
81
+ ([key, resolver]) => [key, resolver(context)]
51
82
  );
52
83
  const hasPromises = resolutionMapEntries.some(
53
84
  ([, resolution]) => resolution instanceof Promise
@@ -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> & { __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":[]}
1
+ {"version":3,"sources":["../src/resolver.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 takes a resolution context\n * and returns a value of some type.\n */\nexport type ResolverFn<T> = (context?: ResolutionContext) => T;\n\n/**\n * A function that returns a value of some type\n * based on a resolution context.\n */\nexport type Resolver<T> = ResolverFn<T>;\n\n/**\n * A context used by resolvers that defines the behaviour of the resolver\n * with the passed mocks and scope.\n */\nexport type ResolutionContext = {\n scope?: Scope;\n mocks?: MockMap;\n};\n\n/**\n * Makes the resolver function capable of replacing itself\n * with a mock if one is defined in the resolution context.\n */\nconst mockable = <T>(fn: ResolverFn<T>): ResolverFn<T> => {\n const instance = (context?: ResolutionContext) => {\n const mock = context?.mocks?.get(instance);\n if (!mock) return fn(context);\n\n if (!mock.isPartial) return mock.resolver(context);\n\n const resolution = fn(context);\n const mockResolution = mock.resolver(context);\n\n if (resolution instanceof Promise && mockResolution instanceof Promise)\n return Promise.all([resolution, mockResolution]).then(([a, b]) =>\n Object.assign(a as object, b),\n ) as T;\n\n return Object.assign(resolution as object, mockResolution);\n };\n\n return instance;\n};\n\n/**\n * Creates a resolver that creates a new resolution on each call.\n *\n * @example\n * ```ts\n * const getEntity = transient(() => createEntity())\n * getEntity() !== getEntity()\n * ```\n *\n * @param resolver\n * A function that takes a resolution context\n * and returns a value of some type.\n *\n * @returns The transient resolver.\n */\nexport const transient = <T>(fn: ResolverFn<T>): Resolver<T> => mockable(fn);\n\n/**\n * Creates a resolver that creates\n * a resolution once and return it on each call.\n *\n * @example\n * ```ts\n * const getEntity = singleton(() => createEntity())\n * getEntity() === getEntity()\n * ```\n *\n * @param fn\n * A function that takes a resolution context\n * and returns a value of some type.\n *\n * @returns The singleton resolver.\n */\nexport const singleton = <T>(fn: ResolverFn<T>): Resolver<T> => {\n let resolved = false;\n let resolution: T | undefined;\n\n const instance = mockable((context) => {\n if (resolved) return resolution!;\n\n resolution = fn(context);\n resolved = true;\n\n return resolution;\n });\n\n return instance;\n};\n\n/**\n * Creates a resolver that takes its resolution\n * from a scope or create a new one and save it if there is none.\n * If no scope was passed in a resolution context,\n * it will act as a singleton.\n *\n * @example\n * ```ts\n * const getEntity = scoped(() => createEntity())\n * getEntity() === getEntity()\n * ```\n *\n * @example\n * ```ts\n * const getEntity = scoped(() => createEntity())\n * const scope = createScope()\n * getEntity({ scope }) === getEntity({ scope }) !== getEntity()\n * ```\n *\n * @param fn\n * A function that takes a resolution context\n * and returns a value of some type.\n *\n * @returns The scoped resolver.\n */\nexport const scoped = <T>(fn: ResolverFn<T>): Resolver<T> => {\n const singletonFallback = singleton(fn);\n\n const instance = mockable((context) => {\n if (!context?.scope) return singletonFallback(context);\n\n const resolution = context.scope.has(instance)\n ? context.scope.get(instance)\n : fn(context);\n\n context.scope.set(instance, resolution);\n\n return resolution;\n });\n\n return instance;\n};\n","import { Resolver } from \"./resolver\";\n\n/**\n * A `Map` of resolvers to their resolutions.\n * Is passed in a resolution context and used by scoped resolvers\n * to retrieve or save resolution within it.\n */\nexport type Scope = Map<Resolver<any>, any>;\n\n/**\n * Creates a `Map` of providers to their instances.\n * Is passed in a resolution context and used by scoped resolvers\n * to retrieve or save resolution 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 * @returns The map.\n */\nexport const createScope = (): Scope => new Map();\n","import { Resolver } from \"./resolver\";\n\ntype PromiseAwarePartial<T> =\n T extends Promise<infer U> ? Promise<Partial<U>> : Partial<T>;\n\ntype Mock<T> =\n | {\n isPartial: false;\n resolver: Resolver<T>;\n }\n | {\n isPartial: true;\n resolver: Resolver<PromiseAwarePartial<T>>;\n };\n\ntype MocksEntries = [Resolver<any>, Mock<any>][];\n\n/**\n * Immutable map that registers and provides mocks.\n * Is passed in a resolution context and used by resolvers\n * to replace or partially replace themselves with a mock if one is defined.\n */\nexport type MockMap = {\n /**\n * Registers a mock for a resolver,\n * creating a new `MockMap` with this registration.\n *\n * @param original - The original resolver.\n * @param mock - The mock resolver.\n */\n mock<T>(original: Resolver<T>, mock: Resolver<T>): MockMap;\n /**\n * Registers a partial mock for a resolver,\n * creating a new `MockMap` with this registration.\n * In this case, the mock resolver's resolution object will be\n * merged with the original resolver's resolution object,\n * overwriting certain fields.\n *\n * @param original - The original resolver.\n * @param mock - The mock resolver.\n */\n mockPartially<T extends object>(\n original: Resolver<T>,\n mock: Resolver<PromiseAwarePartial<T>>,\n ): MockMap;\n /**\n * Returns a mock of a resolver\n * or `undefined` if one is not registered.\n *\n * @param original - The original resolver.\n */\n get<T>(original: Resolver<T>): Mock<T> | undefined;\n};\n\n/**\n * Internal implementation that accepts entries.\n */\nconst createMockMapWithEntries = (entries: MocksEntries = []): MockMap => {\n const set = (key: Resolver<any>, value: Mock<any>) =>\n createMockMapWithEntries([\n ...entries.filter((entry) => entry[0] !== key),\n [key, value],\n ]);\n\n return {\n mock(original, mock) {\n return set(original, {\n isPartial: false,\n resolver: mock,\n });\n },\n mockPartially(original, mock) {\n return set(original, {\n isPartial: true,\n resolver: mock,\n });\n },\n get(original) {\n return entries.find((entry) => entry[0] === original)?.[1] as any;\n },\n };\n};\n\n/**\n * Creates a mock map, an immutable map that registers and provides mocks.\n * Is passed in a resolution context and used by resolvers\n * to replace or partially replace themselves with a mock if one is defined.\n *\n * @example\n * ```ts\n * const mocks = createMockMap()\n * .mock(getDependency, getDependencyMock)\n * .mockPartially(\n * getOtherDepedency,\n * transient(() => ({ someField: \"mock\" }))\n * )\n *\n * const entityWithMocks = getEntity({ mocks })\n * ```\n *\n * @returns The mock map.\n */\nexport const createMockMap = (): MockMap => createMockMapWithEntries();\n","import { ResolutionContext, Resolver } from \"./resolver\";\n\ntype ResolverList = Resolver<any>[];\ntype ResolverRecord = Record<string, Resolver<any>>;\n\ntype InferResolverCollectionResolutions<\n Resolvers extends ResolverList | ResolverRecord,\n> = {\n [K in keyof Resolvers]: Resolvers[K] extends Resolver<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 resolver 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 resolvers:\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 resolver 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 resolvers - The list of resolvers.\n * @param context - The resolution context.\n *\n * @returns The list of resolutions.\n */\nexport const resolveList = <const Resolvers extends ResolverList>(\n resolvers: Resolvers,\n context?: ResolutionContext,\n): AwaitValuesInCollection<InferResolverCollectionResolutions<Resolvers>> => {\n const resolutions = resolvers.map((resolver) => resolver(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 resolver 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 resolvers:\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 resolver 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 resolvers - The map of resolvers.\n * @param context - The resolution context.\n *\n * @returns The map of resolutions.\n */\nexport const resolveMap = <const Resolvers extends ResolverRecord>(\n resolvers: Resolvers,\n context?: ResolutionContext,\n): AwaitValuesInCollection<InferResolverCollectionResolutions<Resolvers>> => {\n const resolutionMapEntries = Object.entries(resolvers).map(\n ([key, resolver]) => [key, resolver(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":"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","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "atomic-di",
3
- "version": "1.2.0",
4
- "description": "Lifetimes, scopes and mocking for pure dependency injection.",
3
+ "version": "2.0.1",
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",
7
7
  "homepage": "https://github.com/ncor/atomic-di#readme",