atomic-di 1.2.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +262 -224
- package/dist/index.d.mts +101 -78
- package/dist/index.d.ts +101 -78
- package/dist/index.js +51 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +51 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
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/
|
33
|
-
var
|
32
|
+
// src/resolver.ts
|
33
|
+
var mockable = (fn) => {
|
34
34
|
const instance = (context) => {
|
35
35
|
var _a;
|
36
|
-
const
|
37
|
-
if (
|
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
|
47
|
+
return instance;
|
41
48
|
};
|
42
|
-
var transient =
|
43
|
-
var singleton = (
|
49
|
+
var transient = (fn) => mockable(fn);
|
50
|
+
var singleton = (fn) => {
|
44
51
|
let resolved = false;
|
45
52
|
let resolution;
|
46
|
-
const instance =
|
53
|
+
const instance = mockable((context) => {
|
47
54
|
if (resolved) return resolution;
|
48
|
-
resolution =
|
55
|
+
resolution = fn(context);
|
49
56
|
resolved = true;
|
50
57
|
return resolution;
|
51
58
|
});
|
52
59
|
return instance;
|
53
60
|
};
|
54
|
-
var scoped = (
|
55
|
-
const singletonFallback = singleton(
|
56
|
-
const instance =
|
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) :
|
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
|
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 = (
|
73
|
-
const resolutions =
|
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 = (
|
80
|
-
const resolutionMapEntries = Object.entries(
|
81
|
-
([key,
|
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/
|
2
|
-
var
|
1
|
+
// src/resolver.ts
|
2
|
+
var mockable = (fn) => {
|
3
3
|
const instance = (context) => {
|
4
4
|
var _a;
|
5
|
-
const
|
6
|
-
if (
|
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
|
16
|
+
return instance;
|
10
17
|
};
|
11
|
-
var transient =
|
12
|
-
var singleton = (
|
18
|
+
var transient = (fn) => mockable(fn);
|
19
|
+
var singleton = (fn) => {
|
13
20
|
let resolved = false;
|
14
21
|
let resolution;
|
15
|
-
const instance =
|
22
|
+
const instance = mockable((context) => {
|
16
23
|
if (resolved) return resolution;
|
17
|
-
resolution =
|
24
|
+
resolution = fn(context);
|
18
25
|
resolved = true;
|
19
26
|
return resolution;
|
20
27
|
});
|
21
28
|
return instance;
|
22
29
|
};
|
23
|
-
var scoped = (
|
24
|
-
const singletonFallback = singleton(
|
25
|
-
const instance =
|
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) :
|
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
|
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 = (
|
42
|
-
const resolutions =
|
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 = (
|
49
|
-
const resolutionMapEntries = Object.entries(
|
50
|
-
([key,
|
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
|
package/dist/index.mjs.map
CHANGED
@@ -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": "
|
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",
|