atomic-di 2.0.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -233,22 +233,13 @@ IoC containers implement this by defining copies of a container in different par
233
233
  ### Creating a scope
234
234
 
235
235
  There are two ways to create a scope:
236
- - Create a map with the correct type manually.
237
- ```ts
238
- const scope = new Map<Resolver<any>, any>()
239
- ```
240
- - Use `createScope` function.
236
+ - By calling `createScope` function.
241
237
  ```ts
242
238
  const scope = createScope()
243
239
  ```
244
-
245
- It is important to note that a scope must be created **once** during an entire lifecycle of a program.
240
+ - By creating a `Map` with the correct type manually.
246
241
  ```ts
247
- const requestScope = createScope()
248
-
249
- app.use(() => {
250
- // use the scope here
251
- })
242
+ const scope = new Map<Resolver<any>, any>()
252
243
  ```
253
244
 
254
245
  ### Resolving with a scope
package/dist/index.d.mts CHANGED
@@ -1,50 +1,75 @@
1
+ type PromiseAwarePartial<T> = T extends Promise<infer U> ? Promise<Partial<U>> : Partial<T>;
2
+ type Mock<T> = {
3
+ isPartial: false;
4
+ resolver: Resolver<T>;
5
+ } | {
6
+ isPartial: true;
7
+ resolver: Resolver<PromiseAwarePartial<T>>;
8
+ };
1
9
  /**
2
- * A `Map` of providers to providers of the same type
3
- * which is then passed to a provider call in a resolution context object
4
- * in order to replace providers with their mocks.
10
+ * Immutable map that registers and provides mocks.
11
+ * Is passed in a resolution context and used by resolvers
12
+ * to replace or partially replace themselves with a mock if one is defined.
5
13
  */
6
- type MockMap = Omit<Map<Resolver<any>, Resolver<any>>, "set" | "get"> & {
14
+ type MockMap = {
15
+ /**
16
+ * Registers a mock for a resolver,
17
+ * creating a new `MockMap` with this registration.
18
+ *
19
+ * @param original - The original resolver.
20
+ * @param mock - The mock resolver.
21
+ */
22
+ mock<T>(original: Resolver<T>, mock: Resolver<T>): MockMap;
7
23
  /**
8
- * Sets a mock for a provider.
24
+ * Registers a partial mock for a resolver,
25
+ * creating a new `MockMap` with this registration.
26
+ * In this case, the mock resolver's resolution object will be
27
+ * merged with the original resolver's resolution object,
28
+ * overwriting certain fields.
9
29
  *
10
- * @param provider - The original provider.
11
- * @param mock - The mock provider.
30
+ * @param original - The original resolver.
31
+ * @param mock - The mock resolver.
12
32
  */
13
- set<T>(provider: Resolver<T>, mock: Resolver<T>): MockMap;
33
+ mockPartially<T extends object>(original: Resolver<T>, mock: Resolver<PromiseAwarePartial<T>>): MockMap;
14
34
  /**
15
- * Retrieves a mock of a provider. Returns undefined if there's none.
35
+ * Returns a mock of a resolver
36
+ * or `undefined` if one is not registered.
16
37
  *
17
- * @param provider - The provider.
38
+ * @param original - The original resolver.
18
39
  */
19
- get<T>(provider: Resolver<T>): Resolver<T> | undefined;
40
+ get<T>(original: Resolver<T>): Mock<T> | undefined;
20
41
  };
21
42
  /**
22
- * Creates a `Map` of providers to providers of the same type
23
- * which is then passed to a provider call in a resolution context object
24
- * in order to replace providers with their mocks.
43
+ * Creates a mock map, an immutable map that registers and provides mocks.
44
+ * Is passed in a resolution context and used by resolvers
45
+ * to replace or partially replace themselves with a mock if one is defined.
25
46
  *
26
47
  * @example
27
48
  * ```ts
28
49
  * const mocks = createMockMap()
29
- * .set(getConfig, getTestConfig)
50
+ * .mock(getDependency, getDependencyMock)
51
+ * .mockPartially(
52
+ * getOtherDepedency,
53
+ * transient(() => ({ someField: "mock" }))
54
+ * )
30
55
  *
31
- * getThing({ mocks })
56
+ * const entityWithMocks = getEntity({ mocks })
32
57
  * ```
33
58
  *
34
- * @returns The map instance.
59
+ * @returns The mock map.
35
60
  */
36
61
  declare const createMockMap: () => MockMap;
37
62
 
38
63
  /**
39
- * A `Map` of providers to their instances
40
- * that is then passed to a provider call in a resolution context object
41
- * to resolve instances of scoped providers within it.
64
+ * A `Map` of resolvers to their resolutions.
65
+ * Is passed in a resolution context and used by scoped resolvers
66
+ * to retrieve or save resolution within it.
42
67
  */
43
68
  type Scope = Map<Resolver<any>, any>;
44
69
  /**
45
- * Creates a `Map` of providers to their instances
46
- * that is then passed to a provider call in a resolution context object
47
- * to resolve instances of scoped providers within it.
70
+ * Creates a `Map` of providers to their instances.
71
+ * Is passed in a resolution context and used by scoped resolvers
72
+ * to retrieve or save resolution within it.
48
73
  *
49
74
  * @example
50
75
  * ```ts
@@ -52,97 +77,95 @@ type Scope = Map<Resolver<any>, any>;
52
77
  *
53
78
  * app.use(() => {
54
79
  * const db = getDb({ scope: requestScope })
55
- * // ...
56
80
  * })
57
81
  * ```
58
82
  *
59
- * @returns The map instance.
83
+ * @returns The map.
60
84
  */
61
85
  declare const createScope: () => Scope;
62
86
 
63
87
  /**
64
- * A function that returns a value of a particular type
65
- * with a resolution context being passed to it.
88
+ * A function that takes a resolution context
89
+ * and returns a value of some type.
66
90
  */
67
- type Resolver<T> = (context?: ResolutionContext) => T;
91
+ type ResolverFn<T> = (context?: ResolutionContext) => T;
68
92
  /**
69
- * A function that resolves an instance or a `Promise` of a particular type
70
- * based on a resolution context passed to it.
93
+ * A function that returns a value of some type
94
+ * based on a resolution context.
71
95
  */
72
- type Provider<T> = Resolver<T> & {
73
- __brand: "provider";
74
- };
96
+ type Resolver<T> = ResolverFn<T>;
75
97
  /**
76
- * A context used by providers to resolve instances
77
- * based on current scope and mocks.
98
+ * A context used by resolvers that defines the behaviour of the resolver
99
+ * with the passed mocks and scope.
78
100
  */
79
101
  type ResolutionContext = {
80
102
  scope?: Scope;
81
103
  mocks?: MockMap;
82
104
  };
83
105
  /**
84
- * Creates a transient provider that will resolve a new instance on each call.
106
+ * Creates a resolver that creates a new resolution on each call.
85
107
  *
86
108
  * @example
87
109
  * ```ts
88
- * const getThing = transient(() => createThing())
89
- * getThing() !== getThing()
110
+ * const getEntity = transient(() => createEntity())
111
+ * getEntity() !== getEntity()
90
112
  * ```
91
113
  *
92
114
  * @param resolver
93
- * A function that returns a value of a particular type
94
- * with a resolution context being passed to it.
115
+ * A function that takes a resolution context
116
+ * and returns a value of some type.
95
117
  *
96
- * @returns The transient provider.
118
+ * @returns The transient resolver.
97
119
  */
98
- declare const transient: <T>(resolver: Resolver<T>) => Provider<T>;
120
+ declare const transient: <T>(fn: ResolverFn<T>) => Resolver<T>;
99
121
  /**
100
- * Creates a singleton provider that will resolve an instance once
101
- * and return it on every call.
122
+ * Creates a resolver that creates
123
+ * a resolution once and return it on each call.
102
124
  *
103
125
  * @example
104
126
  * ```ts
105
- * const getThing = singleton(() => createThing())
106
- * getThing() === getThing()
127
+ * const getEntity = singleton(() => createEntity())
128
+ * getEntity() === getEntity()
107
129
  * ```
108
130
  *
109
- * @param resolver
110
- * A function that returns a value of a particular type
111
- * with a resolution context being passed to it.
131
+ * @param fn
132
+ * A function that takes a resolution context
133
+ * and returns a value of some type.
112
134
  *
113
- * @returns The singleton provider.
135
+ * @returns The singleton resolver.
114
136
  */
115
- declare const singleton: <T>(resolver: Resolver<T>) => Provider<T>;
137
+ declare const singleton: <T>(fn: ResolverFn<T>) => Resolver<T>;
116
138
  /**
117
- * Creates a scoped provider that will take its resolution from a passed scope
118
- * or create a new one and save it if there is none.
119
- * If no scope is passed, it will act as a singleton.
139
+ * Creates a resolver that takes its resolution
140
+ * from a scope or create a new one and save it if there is none.
141
+ * If no scope was passed in a resolution context,
142
+ * it will act as a singleton.
120
143
  *
121
144
  * @example
122
145
  * ```ts
123
- * const getThing = scoped(() => createThing())
124
- * getThing() === getThing()
146
+ * const getEntity = scoped(() => createEntity())
147
+ * getEntity() === getEntity()
125
148
  * ```
126
149
  *
127
150
  * @example
128
151
  * ```ts
129
- * const getThing = scoped(() => createThing())
152
+ * const getEntity = scoped(() => createEntity())
130
153
  * const scope = createScope()
131
- * getThing({ scope }) === getThing({ scope }) !== getThing()
154
+ * getEntity({ scope }) === getEntity({ scope }) !== getEntity()
132
155
  * ```
133
156
  *
134
- * @param resolver
135
- * A function that returns a value of a particular type
136
- * with a resolution context being passed to it.
157
+ * @param fn
158
+ * A function that takes a resolution context
159
+ * and returns a value of some type.
137
160
  *
138
- * @returns The scoped provider.
161
+ * @returns The scoped resolver.
139
162
  */
140
- declare const scoped: <T>(resolver: Resolver<T>) => Provider<T>;
163
+ declare const scoped: <T>(fn: ResolverFn<T>) => Resolver<T>;
141
164
 
142
- type ProviderList = Provider<any>[];
143
- type ProviderRecord = Record<string, Provider<any>>;
144
- type InferProviderCollectionResolutions<Providers extends ProviderList | ProviderRecord> = {
145
- [K in keyof Providers]: Providers[K] extends Provider<infer T> ? T : never;
165
+ type ResolverList = Resolver<any>[];
166
+ type ResolverRecord = Record<string, Resolver<any>>;
167
+ type InferResolverCollectionResolutions<Resolvers extends ResolverList | ResolverRecord> = {
168
+ [K in keyof Resolvers]: Resolvers[K] extends Resolver<infer T> ? T : never;
146
169
  };
147
170
  /**
148
171
  * Awaits all promises and wraps the collection in a promise
@@ -153,12 +176,12 @@ type AwaitValuesInCollection<T extends any[] | Record<any, any>> = Promise<any>
153
176
  [I in keyof T]: T[I] extends Promise<infer T> ? T : T[I];
154
177
  }> : T;
155
178
  /**
156
- * Calls every provider in a list with a provided resolution context
179
+ * Calls every resolver in a list with a provided resolution context
157
180
  * and returns a list of resolutions. Returns a `Promise` of a list
158
181
  * of awaited resolutions if there's at least one `Promise` in the resolutions.
159
182
  *
160
183
  * @example
161
- * Only sync providers:
184
+ * Only sync resolvers:
162
185
  * ```ts
163
186
  * const getA = scoped(() => createA())
164
187
  * const getB = scoped(() => createB())
@@ -178,7 +201,7 @@ type AwaitValuesInCollection<T extends any[] | Record<any, any>> = Promise<any>
178
201
  * ```
179
202
  *
180
203
  * @example
181
- * Some provider is async:
204
+ * Some resolver is async:
182
205
  * ```ts
183
206
  * const getA = scoped(() => createA())
184
207
  * const getB = scoped(async () => await createB())
@@ -197,20 +220,20 @@ type AwaitValuesInCollection<T extends any[] | Record<any, any>> = Promise<any>
197
220
  * ]
198
221
  * ```
199
222
  *
200
- * @param providers - The list of providers.
223
+ * @param resolvers - The list of resolvers.
201
224
  * @param context - The resolution context.
202
225
  *
203
226
  * @returns The list of resolutions.
204
227
  */
205
- declare const resolveList: <const Providers extends ProviderList>(providers: Providers, context?: ResolutionContext) => AwaitValuesInCollection<InferProviderCollectionResolutions<Providers>>;
228
+ declare const resolveList: <const Resolvers extends ResolverList>(resolvers: Resolvers, context?: ResolutionContext) => AwaitValuesInCollection<InferResolverCollectionResolutions<Resolvers>>;
206
229
  /**
207
- * Calls every provider in a map with a provided resolution context
230
+ * Calls every resolver in a map with a provided resolution context
208
231
  * and returns a map with identical keys but with resolutions in values instead.
209
232
  * Returns a `Promise` of a map of awaited resolutions if there's at least one
210
233
  * `Promise` in the resolutions.
211
234
  *
212
235
  * @example
213
- * Only sync providers:
236
+ * Only sync resolvers:
214
237
  * ```ts
215
238
  * const getA = scoped(() => createA())
216
239
  * const getB = scoped(() => createB())
@@ -230,7 +253,7 @@ declare const resolveList: <const Providers extends ProviderList>(providers: Pro
230
253
  * ```
231
254
  *
232
255
  * @example
233
- * Some provider is async:
256
+ * Some resolver is async:
234
257
  * ```ts
235
258
  * const getA = scoped(() => createA())
236
259
  * const getB = scoped(async () => await createB())
@@ -249,11 +272,11 @@ declare const resolveList: <const Providers extends ProviderList>(providers: Pro
249
272
  * }
250
273
  * ```
251
274
  *
252
- * @param providers - The map of providers.
275
+ * @param resolvers - The map of resolvers.
253
276
  * @param context - The resolution context.
254
277
  *
255
278
  * @returns The map of resolutions.
256
279
  */
257
- declare const resolveMap: <const Providers extends ProviderRecord>(providers: Providers, context?: ResolutionContext) => AwaitValuesInCollection<InferProviderCollectionResolutions<Providers>>;
280
+ declare const resolveMap: <const Resolvers extends ResolverRecord>(resolvers: Resolvers, context?: ResolutionContext) => AwaitValuesInCollection<InferResolverCollectionResolutions<Resolvers>>;
258
281
 
259
- export { type MockMap, type Provider, type ResolutionContext, type Resolver, type Scope, createMockMap, createScope, resolveList, resolveMap, scoped, singleton, transient };
282
+ export { type MockMap, type ResolutionContext, type Resolver, type ResolverFn, type Scope, createMockMap, createScope, resolveList, resolveMap, scoped, singleton, transient };
package/dist/index.d.ts CHANGED
@@ -1,50 +1,75 @@
1
+ type PromiseAwarePartial<T> = T extends Promise<infer U> ? Promise<Partial<U>> : Partial<T>;
2
+ type Mock<T> = {
3
+ isPartial: false;
4
+ resolver: Resolver<T>;
5
+ } | {
6
+ isPartial: true;
7
+ resolver: Resolver<PromiseAwarePartial<T>>;
8
+ };
1
9
  /**
2
- * A `Map` of providers to providers of the same type
3
- * which is then passed to a provider call in a resolution context object
4
- * in order to replace providers with their mocks.
10
+ * Immutable map that registers and provides mocks.
11
+ * Is passed in a resolution context and used by resolvers
12
+ * to replace or partially replace themselves with a mock if one is defined.
5
13
  */
6
- type MockMap = Omit<Map<Resolver<any>, Resolver<any>>, "set" | "get"> & {
14
+ type MockMap = {
15
+ /**
16
+ * Registers a mock for a resolver,
17
+ * creating a new `MockMap` with this registration.
18
+ *
19
+ * @param original - The original resolver.
20
+ * @param mock - The mock resolver.
21
+ */
22
+ mock<T>(original: Resolver<T>, mock: Resolver<T>): MockMap;
7
23
  /**
8
- * Sets a mock for a provider.
24
+ * Registers a partial mock for a resolver,
25
+ * creating a new `MockMap` with this registration.
26
+ * In this case, the mock resolver's resolution object will be
27
+ * merged with the original resolver's resolution object,
28
+ * overwriting certain fields.
9
29
  *
10
- * @param provider - The original provider.
11
- * @param mock - The mock provider.
30
+ * @param original - The original resolver.
31
+ * @param mock - The mock resolver.
12
32
  */
13
- set<T>(provider: Resolver<T>, mock: Resolver<T>): MockMap;
33
+ mockPartially<T extends object>(original: Resolver<T>, mock: Resolver<PromiseAwarePartial<T>>): MockMap;
14
34
  /**
15
- * Retrieves a mock of a provider. Returns undefined if there's none.
35
+ * Returns a mock of a resolver
36
+ * or `undefined` if one is not registered.
16
37
  *
17
- * @param provider - The provider.
38
+ * @param original - The original resolver.
18
39
  */
19
- get<T>(provider: Resolver<T>): Resolver<T> | undefined;
40
+ get<T>(original: Resolver<T>): Mock<T> | undefined;
20
41
  };
21
42
  /**
22
- * Creates a `Map` of providers to providers of the same type
23
- * which is then passed to a provider call in a resolution context object
24
- * in order to replace providers with their mocks.
43
+ * Creates a mock map, an immutable map that registers and provides mocks.
44
+ * Is passed in a resolution context and used by resolvers
45
+ * to replace or partially replace themselves with a mock if one is defined.
25
46
  *
26
47
  * @example
27
48
  * ```ts
28
49
  * const mocks = createMockMap()
29
- * .set(getConfig, getTestConfig)
50
+ * .mock(getDependency, getDependencyMock)
51
+ * .mockPartially(
52
+ * getOtherDepedency,
53
+ * transient(() => ({ someField: "mock" }))
54
+ * )
30
55
  *
31
- * getThing({ mocks })
56
+ * const entityWithMocks = getEntity({ mocks })
32
57
  * ```
33
58
  *
34
- * @returns The map instance.
59
+ * @returns The mock map.
35
60
  */
36
61
  declare const createMockMap: () => MockMap;
37
62
 
38
63
  /**
39
- * A `Map` of providers to their instances
40
- * that is then passed to a provider call in a resolution context object
41
- * to resolve instances of scoped providers within it.
64
+ * A `Map` of resolvers to their resolutions.
65
+ * Is passed in a resolution context and used by scoped resolvers
66
+ * to retrieve or save resolution within it.
42
67
  */
43
68
  type Scope = Map<Resolver<any>, any>;
44
69
  /**
45
- * Creates a `Map` of providers to their instances
46
- * that is then passed to a provider call in a resolution context object
47
- * to resolve instances of scoped providers within it.
70
+ * Creates a `Map` of providers to their instances.
71
+ * Is passed in a resolution context and used by scoped resolvers
72
+ * to retrieve or save resolution within it.
48
73
  *
49
74
  * @example
50
75
  * ```ts
@@ -52,97 +77,95 @@ type Scope = Map<Resolver<any>, any>;
52
77
  *
53
78
  * app.use(() => {
54
79
  * const db = getDb({ scope: requestScope })
55
- * // ...
56
80
  * })
57
81
  * ```
58
82
  *
59
- * @returns The map instance.
83
+ * @returns The map.
60
84
  */
61
85
  declare const createScope: () => Scope;
62
86
 
63
87
  /**
64
- * A function that returns a value of a particular type
65
- * with a resolution context being passed to it.
88
+ * A function that takes a resolution context
89
+ * and returns a value of some type.
66
90
  */
67
- type Resolver<T> = (context?: ResolutionContext) => T;
91
+ type ResolverFn<T> = (context?: ResolutionContext) => T;
68
92
  /**
69
- * A function that resolves an instance or a `Promise` of a particular type
70
- * based on a resolution context passed to it.
93
+ * A function that returns a value of some type
94
+ * based on a resolution context.
71
95
  */
72
- type Provider<T> = Resolver<T> & {
73
- __brand: "provider";
74
- };
96
+ type Resolver<T> = ResolverFn<T>;
75
97
  /**
76
- * A context used by providers to resolve instances
77
- * based on current scope and mocks.
98
+ * A context used by resolvers that defines the behaviour of the resolver
99
+ * with the passed mocks and scope.
78
100
  */
79
101
  type ResolutionContext = {
80
102
  scope?: Scope;
81
103
  mocks?: MockMap;
82
104
  };
83
105
  /**
84
- * Creates a transient provider that will resolve a new instance on each call.
106
+ * Creates a resolver that creates a new resolution on each call.
85
107
  *
86
108
  * @example
87
109
  * ```ts
88
- * const getThing = transient(() => createThing())
89
- * getThing() !== getThing()
110
+ * const getEntity = transient(() => createEntity())
111
+ * getEntity() !== getEntity()
90
112
  * ```
91
113
  *
92
114
  * @param resolver
93
- * A function that returns a value of a particular type
94
- * with a resolution context being passed to it.
115
+ * A function that takes a resolution context
116
+ * and returns a value of some type.
95
117
  *
96
- * @returns The transient provider.
118
+ * @returns The transient resolver.
97
119
  */
98
- declare const transient: <T>(resolver: Resolver<T>) => Provider<T>;
120
+ declare const transient: <T>(fn: ResolverFn<T>) => Resolver<T>;
99
121
  /**
100
- * Creates a singleton provider that will resolve an instance once
101
- * and return it on every call.
122
+ * Creates a resolver that creates
123
+ * a resolution once and return it on each call.
102
124
  *
103
125
  * @example
104
126
  * ```ts
105
- * const getThing = singleton(() => createThing())
106
- * getThing() === getThing()
127
+ * const getEntity = singleton(() => createEntity())
128
+ * getEntity() === getEntity()
107
129
  * ```
108
130
  *
109
- * @param resolver
110
- * A function that returns a value of a particular type
111
- * with a resolution context being passed to it.
131
+ * @param fn
132
+ * A function that takes a resolution context
133
+ * and returns a value of some type.
112
134
  *
113
- * @returns The singleton provider.
135
+ * @returns The singleton resolver.
114
136
  */
115
- declare const singleton: <T>(resolver: Resolver<T>) => Provider<T>;
137
+ declare const singleton: <T>(fn: ResolverFn<T>) => Resolver<T>;
116
138
  /**
117
- * Creates a scoped provider that will take its resolution from a passed scope
118
- * or create a new one and save it if there is none.
119
- * If no scope is passed, it will act as a singleton.
139
+ * Creates a resolver that takes its resolution
140
+ * from a scope or create a new one and save it if there is none.
141
+ * If no scope was passed in a resolution context,
142
+ * it will act as a singleton.
120
143
  *
121
144
  * @example
122
145
  * ```ts
123
- * const getThing = scoped(() => createThing())
124
- * getThing() === getThing()
146
+ * const getEntity = scoped(() => createEntity())
147
+ * getEntity() === getEntity()
125
148
  * ```
126
149
  *
127
150
  * @example
128
151
  * ```ts
129
- * const getThing = scoped(() => createThing())
152
+ * const getEntity = scoped(() => createEntity())
130
153
  * const scope = createScope()
131
- * getThing({ scope }) === getThing({ scope }) !== getThing()
154
+ * getEntity({ scope }) === getEntity({ scope }) !== getEntity()
132
155
  * ```
133
156
  *
134
- * @param resolver
135
- * A function that returns a value of a particular type
136
- * with a resolution context being passed to it.
157
+ * @param fn
158
+ * A function that takes a resolution context
159
+ * and returns a value of some type.
137
160
  *
138
- * @returns The scoped provider.
161
+ * @returns The scoped resolver.
139
162
  */
140
- declare const scoped: <T>(resolver: Resolver<T>) => Provider<T>;
163
+ declare const scoped: <T>(fn: ResolverFn<T>) => Resolver<T>;
141
164
 
142
- type ProviderList = Provider<any>[];
143
- type ProviderRecord = Record<string, Provider<any>>;
144
- type InferProviderCollectionResolutions<Providers extends ProviderList | ProviderRecord> = {
145
- [K in keyof Providers]: Providers[K] extends Provider<infer T> ? T : never;
165
+ type ResolverList = Resolver<any>[];
166
+ type ResolverRecord = Record<string, Resolver<any>>;
167
+ type InferResolverCollectionResolutions<Resolvers extends ResolverList | ResolverRecord> = {
168
+ [K in keyof Resolvers]: Resolvers[K] extends Resolver<infer T> ? T : never;
146
169
  };
147
170
  /**
148
171
  * Awaits all promises and wraps the collection in a promise
@@ -153,12 +176,12 @@ type AwaitValuesInCollection<T extends any[] | Record<any, any>> = Promise<any>
153
176
  [I in keyof T]: T[I] extends Promise<infer T> ? T : T[I];
154
177
  }> : T;
155
178
  /**
156
- * Calls every provider in a list with a provided resolution context
179
+ * Calls every resolver in a list with a provided resolution context
157
180
  * and returns a list of resolutions. Returns a `Promise` of a list
158
181
  * of awaited resolutions if there's at least one `Promise` in the resolutions.
159
182
  *
160
183
  * @example
161
- * Only sync providers:
184
+ * Only sync resolvers:
162
185
  * ```ts
163
186
  * const getA = scoped(() => createA())
164
187
  * const getB = scoped(() => createB())
@@ -178,7 +201,7 @@ type AwaitValuesInCollection<T extends any[] | Record<any, any>> = Promise<any>
178
201
  * ```
179
202
  *
180
203
  * @example
181
- * Some provider is async:
204
+ * Some resolver is async:
182
205
  * ```ts
183
206
  * const getA = scoped(() => createA())
184
207
  * const getB = scoped(async () => await createB())
@@ -197,20 +220,20 @@ type AwaitValuesInCollection<T extends any[] | Record<any, any>> = Promise<any>
197
220
  * ]
198
221
  * ```
199
222
  *
200
- * @param providers - The list of providers.
223
+ * @param resolvers - The list of resolvers.
201
224
  * @param context - The resolution context.
202
225
  *
203
226
  * @returns The list of resolutions.
204
227
  */
205
- declare const resolveList: <const Providers extends ProviderList>(providers: Providers, context?: ResolutionContext) => AwaitValuesInCollection<InferProviderCollectionResolutions<Providers>>;
228
+ declare const resolveList: <const Resolvers extends ResolverList>(resolvers: Resolvers, context?: ResolutionContext) => AwaitValuesInCollection<InferResolverCollectionResolutions<Resolvers>>;
206
229
  /**
207
- * Calls every provider in a map with a provided resolution context
230
+ * Calls every resolver in a map with a provided resolution context
208
231
  * and returns a map with identical keys but with resolutions in values instead.
209
232
  * Returns a `Promise` of a map of awaited resolutions if there's at least one
210
233
  * `Promise` in the resolutions.
211
234
  *
212
235
  * @example
213
- * Only sync providers:
236
+ * Only sync resolvers:
214
237
  * ```ts
215
238
  * const getA = scoped(() => createA())
216
239
  * const getB = scoped(() => createB())
@@ -230,7 +253,7 @@ declare const resolveList: <const Providers extends ProviderList>(providers: Pro
230
253
  * ```
231
254
  *
232
255
  * @example
233
- * Some provider is async:
256
+ * Some resolver is async:
234
257
  * ```ts
235
258
  * const getA = scoped(() => createA())
236
259
  * const getB = scoped(async () => await createB())
@@ -249,11 +272,11 @@ declare const resolveList: <const Providers extends ProviderList>(providers: Pro
249
272
  * }
250
273
  * ```
251
274
  *
252
- * @param providers - The map of providers.
275
+ * @param resolvers - The map of resolvers.
253
276
  * @param context - The resolution context.
254
277
  *
255
278
  * @returns The map of resolutions.
256
279
  */
257
- declare const resolveMap: <const Providers extends ProviderRecord>(providers: Providers, context?: ResolutionContext) => AwaitValuesInCollection<InferProviderCollectionResolutions<Providers>>;
280
+ declare const resolveMap: <const Resolvers extends ResolverRecord>(resolvers: Resolvers, context?: ResolutionContext) => AwaitValuesInCollection<InferResolverCollectionResolutions<Resolvers>>;
258
281
 
259
- export { type MockMap, type Provider, type ResolutionContext, type Resolver, type Scope, createMockMap, createScope, resolveList, resolveMap, scoped, singleton, transient };
282
+ export { type MockMap, type ResolutionContext, type Resolver, type ResolverFn, type Scope, createMockMap, createScope, resolveList, resolveMap, scoped, singleton, transient };
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": "2.0.0",
4
- "description": "Lifetimes, scopes and mocking for pure dependency injection.",
3
+ "version": "2.0.2",
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",