@composed-di/core 0.5.0-alpha
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/dist/errors.d.ts +17 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +26 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/serviceEventListener.d.ts +120 -0
- package/dist/serviceEventListener.d.ts.map +1 -0
- package/dist/serviceEventListener.js +3 -0
- package/dist/serviceEventListener.js.map +1 -0
- package/dist/serviceFactory.d.ts +36 -0
- package/dist/serviceFactory.d.ts.map +1 -0
- package/dist/serviceFactory.js +68 -0
- package/dist/serviceFactory.js.map +1 -0
- package/dist/serviceKey.d.ts +90 -0
- package/dist/serviceKey.d.ts.map +1 -0
- package/dist/serviceKey.js +93 -0
- package/dist/serviceKey.js.map +1 -0
- package/dist/serviceModule.d.ts +71 -0
- package/dist/serviceModule.d.ts.map +1 -0
- package/dist/serviceModule.js +329 -0
- package/dist/serviceModule.js.map +1 -0
- package/dist/serviceScope.d.ts +6 -0
- package/dist/serviceScope.d.ts.map +1 -0
- package/dist/serviceScope.js +11 -0
- package/dist/serviceScope.js.map +1 -0
- package/dist/serviceSelector.d.ts +64 -0
- package/dist/serviceSelector.d.ts.map +1 -0
- package/dist/serviceSelector.js +69 -0
- package/dist/serviceSelector.js.map +1 -0
- package/dist/utils.d.ts +61 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +209 -0
- package/dist/utils.js.map +1 -0
- package/package.json +39 -0
- package/src/errors.ts +23 -0
- package/src/index.ts +8 -0
- package/src/serviceEventListener.ts +130 -0
- package/src/serviceFactory.ts +104 -0
- package/src/serviceKey.ts +95 -0
- package/src/serviceModule.ts +372 -0
- package/src/serviceScope.ts +7 -0
- package/src/serviceSelector.ts +68 -0
- package/src/utils.ts +277 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { ServiceKey, ServiceSelectorKey } from './serviceKey';
|
|
2
|
+
import { ServiceFactory } from './serviceFactory';
|
|
3
|
+
import { ServiceScope } from './serviceScope';
|
|
4
|
+
import { ServiceSelector } from './serviceSelector';
|
|
5
|
+
import { ServiceFactoryNotFoundError, ServiceModuleInitError } from './errors';
|
|
6
|
+
import type { ServiceEventListener } from './serviceEventListener';
|
|
7
|
+
|
|
8
|
+
type GenericFactory = ServiceFactory<unknown, readonly ServiceKey<any>[]>;
|
|
9
|
+
type GenericKey = ServiceKey<any>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* ServiceModule is a container for service factories and manages dependency resolution.
|
|
13
|
+
*
|
|
14
|
+
* It provides a way to retrieve service instances based on their ServiceKey,
|
|
15
|
+
* ensuring that all dependencies are resolved and initialized in the correct order.
|
|
16
|
+
* It also handles circular dependency detection and missing dependency validation
|
|
17
|
+
* at the time of module creation.
|
|
18
|
+
*/
|
|
19
|
+
export class ServiceModule {
|
|
20
|
+
/**
|
|
21
|
+
* Private constructor to enforce module creation through the `static from` method.
|
|
22
|
+
*
|
|
23
|
+
* @param factories An array of service factories that this module will manage.
|
|
24
|
+
*/
|
|
25
|
+
private constructor(readonly factories: GenericFactory[]) {
|
|
26
|
+
checkCircularDependencies(this.factories);
|
|
27
|
+
factories.forEach((factory) => {
|
|
28
|
+
checkMissingDependencies(factory, this.factories);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Retrieves an instance for the given ServiceKey.
|
|
34
|
+
*
|
|
35
|
+
* @param key - The key of the service to retrieve.
|
|
36
|
+
* @return A promise that resolves to the service instance.
|
|
37
|
+
* @throws {ServiceFactoryNotFoundError} If no suitable factory is found for the given key.
|
|
38
|
+
*/
|
|
39
|
+
public async get<T>(key: ServiceKey<T>): Promise<T> {
|
|
40
|
+
const factory = this.factories.find((factory: GenericFactory) => {
|
|
41
|
+
return isSuitable(key, factory);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Check if a factory to supply the requested key was not found
|
|
45
|
+
if (!factory) {
|
|
46
|
+
throw new ServiceFactoryNotFoundError(
|
|
47
|
+
`Could not find a suitable factory for ${key.name}`,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Resolve all dependencies first
|
|
52
|
+
const dependencies = await Promise.all(
|
|
53
|
+
factory.dependsOn.map((dependencyKey: ServiceKey<unknown>) => {
|
|
54
|
+
// If the dependency is a ServiceSelectorKey, create a ServiceSelector instance
|
|
55
|
+
if (dependencyKey instanceof ServiceSelectorKey) {
|
|
56
|
+
return new ServiceSelector(this, dependencyKey);
|
|
57
|
+
}
|
|
58
|
+
return this.get(dependencyKey);
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// Call the factory to retrieve the dependency
|
|
63
|
+
return factory.initialize(...dependencies);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Retrieves the value associated with the given service key or returns null if the service is not found.
|
|
68
|
+
*
|
|
69
|
+
* @param key - The key used to retrieve the associated service.
|
|
70
|
+
* @return A promise that resolves to the service value if found, or null if the service is not found.
|
|
71
|
+
*/
|
|
72
|
+
public async getOrNull<T>(key: ServiceKey<T>): Promise<T | null> {
|
|
73
|
+
try {
|
|
74
|
+
return await this.get(key);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
if (error instanceof ServiceFactoryNotFoundError) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Disposes of service factories within the specified scope or all factories if no scope is provided.
|
|
85
|
+
*
|
|
86
|
+
* This method is useful for cleaning up resources and instances held by service factories,
|
|
87
|
+
* such as singleton factories, as they may hold database connections or other resources that need to be released.
|
|
88
|
+
*
|
|
89
|
+
* @param scope The scope to filter the factories to be disposed.
|
|
90
|
+
* If not provided, all factories are disposed of.
|
|
91
|
+
* @return No return value.
|
|
92
|
+
*/
|
|
93
|
+
public dispose(scope?: ServiceScope) {
|
|
94
|
+
const factories = scope
|
|
95
|
+
? this.factories.filter((f) => f.scope === scope)
|
|
96
|
+
: this.factories;
|
|
97
|
+
|
|
98
|
+
factories.forEach((factory) => factory.dispose?.());
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Creates a new ServiceModule instance by aggregating and deduplicating a list of
|
|
103
|
+
* ServiceModule or GenericFactory instances.
|
|
104
|
+
* If multiple factories provide the same
|
|
105
|
+
* ServiceKey, the last one in the list takes precedence.
|
|
106
|
+
*
|
|
107
|
+
* When a listener is provided, every factory is wrapped with instrumentation:
|
|
108
|
+
* the listener is notified when a service is initialized or disposed and when
|
|
109
|
+
* a method is called on a service instance, and may return an EventSpan per
|
|
110
|
+
* operation to observe its completion. Service instances are wrapped in a
|
|
111
|
+
* Proxy to observe method calls, and errors are rethrown after being
|
|
112
|
+
* reported, so behavior is otherwise unchanged.
|
|
113
|
+
*
|
|
114
|
+
* @param entries - An array of ServiceModule or GenericFactory
|
|
115
|
+
* instances to be processed into a single ServiceModule.
|
|
116
|
+
* @param listener - An optional ServiceEventListener notified of service
|
|
117
|
+
* lifecycle events and method calls in the resulting module.
|
|
118
|
+
* @return A new ServiceModule containing the deduplicated factories.
|
|
119
|
+
* @throws {ServiceModuleInitError} If circular or missing dependencies are detected during module creation.
|
|
120
|
+
*/
|
|
121
|
+
static from(
|
|
122
|
+
entries: (ServiceModule | GenericFactory)[],
|
|
123
|
+
listener?: ServiceEventListener,
|
|
124
|
+
): ServiceModule {
|
|
125
|
+
// Flatten entries and keep only the last factory for each ServiceKey
|
|
126
|
+
const flattened = entries.flatMap((e) =>
|
|
127
|
+
e instanceof ServiceModule ? e.factories : [e],
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const byKey = new Map<symbol, GenericFactory>();
|
|
131
|
+
// Later factories overwrite earlier ones (last-wins)
|
|
132
|
+
for (const f of flattened) {
|
|
133
|
+
byKey.set(f.provides.symbol, f);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (listener) {
|
|
137
|
+
return new ServiceModule(
|
|
138
|
+
Array.from(byKey.values()).map((factory) => {
|
|
139
|
+
return makeObservable(listener, factory);
|
|
140
|
+
}),
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return new ServiceModule(Array.from(byKey.values()));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Validates that there are no circular dependencies among the provided factories.
|
|
150
|
+
*
|
|
151
|
+
* @param factories The list of factories to check for cycles.
|
|
152
|
+
* @throws {ServiceModuleInitError} If a circular dependency is detected.
|
|
153
|
+
*/
|
|
154
|
+
function checkCircularDependencies(factories: GenericFactory[]) {
|
|
155
|
+
const factoryMap = new Map<symbol, GenericFactory>();
|
|
156
|
+
for (const f of factories) {
|
|
157
|
+
factoryMap.set(f.provides.symbol, f);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const visited = new Set<symbol>();
|
|
161
|
+
const stack = new Set<symbol>();
|
|
162
|
+
|
|
163
|
+
function walk(factory: GenericFactory, path: string[]) {
|
|
164
|
+
const symbol = factory.provides.symbol;
|
|
165
|
+
|
|
166
|
+
if (stack.has(symbol)) {
|
|
167
|
+
const cyclePath = [...path, factory.provides.name].join(' -> ');
|
|
168
|
+
throw new ServiceModuleInitError(
|
|
169
|
+
`Circular dependency detected: ${cyclePath}`,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (visited.has(symbol)) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
visited.add(symbol);
|
|
178
|
+
stack.add(symbol);
|
|
179
|
+
|
|
180
|
+
for (const depKey of factory.dependsOn) {
|
|
181
|
+
const keysToCheck =
|
|
182
|
+
depKey instanceof ServiceSelectorKey ? depKey.values : [depKey];
|
|
183
|
+
|
|
184
|
+
for (const key of keysToCheck) {
|
|
185
|
+
const depFactory = factoryMap.get(key.symbol);
|
|
186
|
+
if (depFactory) {
|
|
187
|
+
walk(depFactory, [...path, factory.provides.name]);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
stack.delete(symbol);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
for (const factory of factories) {
|
|
196
|
+
walk(factory, []);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Validates that all dependencies of a given factory are present in the list of factories.
|
|
202
|
+
*
|
|
203
|
+
* @param factory The factory whose dependencies are to be checked.
|
|
204
|
+
* @param factories The list of available factories in the module.
|
|
205
|
+
* @throws {ServiceModuleInitError} If any dependency is missing.
|
|
206
|
+
*/
|
|
207
|
+
function checkMissingDependencies(
|
|
208
|
+
factory: GenericFactory,
|
|
209
|
+
factories: GenericFactory[],
|
|
210
|
+
) {
|
|
211
|
+
const missingDependencies: GenericKey[] = [];
|
|
212
|
+
|
|
213
|
+
factory.dependsOn.forEach((dependencyKey: GenericKey) => {
|
|
214
|
+
// For ServiceSelectorKey, check all contained keys are registered
|
|
215
|
+
if (dependencyKey instanceof ServiceSelectorKey) {
|
|
216
|
+
dependencyKey.values.forEach((key) => {
|
|
217
|
+
if (!isRegistered(key, factories)) {
|
|
218
|
+
missingDependencies.push(key);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
} else if (!isRegistered(dependencyKey, factories)) {
|
|
222
|
+
missingDependencies.push(dependencyKey);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
if (missingDependencies.length === 0) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const dependencyList = missingDependencies
|
|
231
|
+
.map((dependencyKey) => ` -> ${dependencyKey.name}`)
|
|
232
|
+
.join('\n');
|
|
233
|
+
throw new ServiceModuleInitError(
|
|
234
|
+
`${factory.provides.name} will fail because it depends on:\n ${dependencyList}`,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Checks if a ServiceKey is registered among the provided factories.
|
|
240
|
+
*
|
|
241
|
+
* @param key The ServiceKey to look for.
|
|
242
|
+
* @param factories The list of factories to search in.
|
|
243
|
+
* @returns True if a factory provides the given key, false otherwise.
|
|
244
|
+
*/
|
|
245
|
+
function isRegistered(key: GenericKey, factories: GenericFactory[]) {
|
|
246
|
+
return factories.some((factory) => factory.provides?.symbol === key?.symbol);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Determines if a factory is suitable for providing a specific ServiceKey.
|
|
251
|
+
*
|
|
252
|
+
* @param key The ServiceKey being requested.
|
|
253
|
+
* @param factory The factory to check.
|
|
254
|
+
* @returns True if the factory provides the key, false otherwise.
|
|
255
|
+
*/
|
|
256
|
+
function isSuitable<T, D extends readonly ServiceKey<any>[]>(
|
|
257
|
+
key: ServiceKey<T>,
|
|
258
|
+
factory: ServiceFactory<any, D>,
|
|
259
|
+
): factory is ServiceFactory<T, D> {
|
|
260
|
+
return factory?.provides?.symbol === key?.symbol;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Wraps a given service factory with instrumentation to notify a listener of
|
|
265
|
+
* lifecycle events and method calls.
|
|
266
|
+
*
|
|
267
|
+
* For each of initialize, dispose, and method calls, the listener is invoked
|
|
268
|
+
* at the start of the operation and may return an EventSpan whose `end` or
|
|
269
|
+
* `error` is called when the operation finishes. Errors are rethrown after
|
|
270
|
+
* being reported.
|
|
271
|
+
*
|
|
272
|
+
* @param listener The listener notified of lifecycle and method call events.
|
|
273
|
+
* @param delegate The original service factory to be instrumented.
|
|
274
|
+
* @return A new service factory that provides the same dependencies but includes event notification logic.
|
|
275
|
+
*/
|
|
276
|
+
function makeObservable<T, D extends readonly ServiceKey<any>[]>(
|
|
277
|
+
listener: ServiceEventListener,
|
|
278
|
+
delegate: ServiceFactory<any, D>,
|
|
279
|
+
): ServiceFactory<T, D> {
|
|
280
|
+
const key = delegate.provides;
|
|
281
|
+
|
|
282
|
+
return ServiceFactory.singleton({
|
|
283
|
+
scope: delegate.scope,
|
|
284
|
+
provides: delegate.provides,
|
|
285
|
+
dependsOn: delegate.dependsOn,
|
|
286
|
+
dispose: () => {
|
|
287
|
+
const dispose = delegate.dispose;
|
|
288
|
+
if (dispose) {
|
|
289
|
+
const span = listener.onDispose?.({ key });
|
|
290
|
+
try {
|
|
291
|
+
dispose();
|
|
292
|
+
} catch (error) {
|
|
293
|
+
span?.error?.(error);
|
|
294
|
+
throw error;
|
|
295
|
+
}
|
|
296
|
+
span?.end?.();
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
initialize: async (...args) => {
|
|
300
|
+
const span = listener.onInitialize?.({ key });
|
|
301
|
+
try {
|
|
302
|
+
const instance = observeMethodCalls(
|
|
303
|
+
await delegate.initialize(...args),
|
|
304
|
+
listener,
|
|
305
|
+
key,
|
|
306
|
+
);
|
|
307
|
+
span?.end?.({ result: instance });
|
|
308
|
+
return instance;
|
|
309
|
+
} catch (error) {
|
|
310
|
+
span?.error?.(error);
|
|
311
|
+
throw error;
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Wraps an object with a Proxy to notify the listener of method calls.
|
|
319
|
+
*
|
|
320
|
+
* Methods returning a promise report end/error when the promise settles,
|
|
321
|
+
* not when the method returns.
|
|
322
|
+
*
|
|
323
|
+
* @param thing The object whose method calls need to be observed.
|
|
324
|
+
* @param listener The listener notified of method call events.
|
|
325
|
+
* @param key The service key used to identify the service in events.
|
|
326
|
+
* @return A Proxy wrapping the input object, with all method calls being reported.
|
|
327
|
+
*/
|
|
328
|
+
function observeMethodCalls(
|
|
329
|
+
thing: any,
|
|
330
|
+
listener: ServiceEventListener,
|
|
331
|
+
key: ServiceKey<unknown>,
|
|
332
|
+
): any {
|
|
333
|
+
if (typeof thing !== 'object' || thing === null) {
|
|
334
|
+
return thing;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return new Proxy(thing, {
|
|
338
|
+
get(target, prop) {
|
|
339
|
+
const value = Reflect.get(target, prop);
|
|
340
|
+
if (typeof value === 'function' && typeof prop === 'string') {
|
|
341
|
+
return (...args: unknown[]) => {
|
|
342
|
+
const span = listener.onMethodCall?.({
|
|
343
|
+
key,
|
|
344
|
+
methodName: prop,
|
|
345
|
+
args,
|
|
346
|
+
});
|
|
347
|
+
try {
|
|
348
|
+
const result = value.apply(target, args);
|
|
349
|
+
if (result instanceof Promise) {
|
|
350
|
+
return result.then(
|
|
351
|
+
(resolved) => {
|
|
352
|
+
span?.end?.({ result: resolved });
|
|
353
|
+
return resolved;
|
|
354
|
+
},
|
|
355
|
+
(error) => {
|
|
356
|
+
span?.error?.(error);
|
|
357
|
+
throw error;
|
|
358
|
+
},
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
span?.end?.({ result });
|
|
362
|
+
return result;
|
|
363
|
+
} catch (error) {
|
|
364
|
+
span?.error?.(error);
|
|
365
|
+
throw error;
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
return value;
|
|
370
|
+
},
|
|
371
|
+
});
|
|
372
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ServiceKey, ServiceSelectorKey } from './serviceKey';
|
|
2
|
+
import { ServiceModule } from './serviceModule';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A runtime selector that provides access to multiple service implementations of the same type.
|
|
6
|
+
*
|
|
7
|
+
* ServiceSelector is automatically created and injected when a factory depends on a
|
|
8
|
+
* `ServiceSelectorKey<T>`. It allows the dependent service to dynamically choose which
|
|
9
|
+
* implementation to use at runtime, rather than being bound to a single implementation
|
|
10
|
+
* at configuration time.
|
|
11
|
+
*
|
|
12
|
+
* @template T The common type shared by all services accessible through this selector.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* // In a factory that depends on ServiceSelectorKey
|
|
17
|
+
* const appFactory = ServiceFactory.singleton({
|
|
18
|
+
* provides: AppKey,
|
|
19
|
+
* dependsOn: [LoggerSelectorKey] as const,
|
|
20
|
+
* initialize: (loggerSelector: ServiceSelector<Logger>) => {
|
|
21
|
+
* return {
|
|
22
|
+
* logWithConsole: async () => {
|
|
23
|
+
* const logger = await loggerSelector.get(ConsoleLoggerKey);
|
|
24
|
+
* logger.log('Using console logger');
|
|
25
|
+
* },
|
|
26
|
+
* logWithFile: async () => {
|
|
27
|
+
* const logger = await loggerSelector.get(FileLoggerKey);
|
|
28
|
+
* logger.log('Using file logger');
|
|
29
|
+
* },
|
|
30
|
+
* };
|
|
31
|
+
* },
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export class ServiceSelector<T> {
|
|
36
|
+
/**
|
|
37
|
+
* Creates a new ServiceSelector instance.
|
|
38
|
+
*
|
|
39
|
+
* Note: ServiceSelector instances are created automatically by ServiceModule
|
|
40
|
+
* when resolving dependencies. You typically don't need to create them manually.
|
|
41
|
+
*
|
|
42
|
+
* @param serviceModule The ServiceModule used to resolve the selected service.
|
|
43
|
+
* @param selectorKey The ServiceSelectorKey that defines which services can be selected.
|
|
44
|
+
*/
|
|
45
|
+
constructor(
|
|
46
|
+
readonly serviceModule: ServiceModule,
|
|
47
|
+
readonly selectorKey: ServiceSelectorKey<T>,
|
|
48
|
+
) {}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Retrieves a service instance by its key from the available services in this selector.
|
|
52
|
+
*
|
|
53
|
+
* The key must be one of the keys that were included in the `ServiceSelectorKey`
|
|
54
|
+
* used to create this selector.
|
|
55
|
+
*
|
|
56
|
+
* @param key The ServiceKey identifying which service implementation to retrieve.
|
|
57
|
+
* @returns A Promise that resolves to the requested service instance.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* const logger = await loggerSelector.get(ConsoleLoggerKey);
|
|
62
|
+
* logger.log('Hello!');
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
get(key: ServiceKey<T>): Promise<T> {
|
|
66
|
+
return this.serviceModule.get(key);
|
|
67
|
+
}
|
|
68
|
+
}
|