navi-di 1.1.0 → 1.1.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 +87 -4
- package/dist/container/container.d.ts +114 -1
- package/dist/container/container.js +203 -5
- package/dist/container/registry.d.ts +41 -0
- package/dist/container/registry.js +51 -0
- package/dist/decorators/inject.d.ts +11 -0
- package/dist/decorators/inject.js +11 -0
- package/dist/decorators/service.d.ts +11 -0
- package/dist/errors/circular-dependency-error.d.ts +8 -0
- package/dist/errors/circular-dependency-error.js +8 -0
- package/dist/errors/container-disposed-error.d.ts +11 -0
- package/dist/errors/container-disposed-error.js +12 -0
- package/dist/errors/container-duplicated-error.d.ts +8 -0
- package/dist/errors/container-duplicated-error.js +8 -0
- package/dist/errors/default-container-id-error.d.ts +5 -0
- package/dist/errors/default-container-id-error.js +6 -1
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/errors/service-not-found-error.d.ts +8 -0
- package/dist/errors/service-not-found-error.js +8 -0
- package/dist/index.d.ts +1 -0
- package/dist/tokens/token.d.ts +22 -0
- package/dist/tokens/token.js +22 -0
- package/dist/types/constructable.d.ts +6 -0
- package/dist/types/container.d.ts +82 -1
- package/dist/types/container.js +3 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/injection.d.ts +12 -0
- package/dist/types/injection.js +3 -0
- package/dist/types/service.d.ts +15 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,6 +55,9 @@ Runtime exports:
|
|
|
55
55
|
- `Inject`
|
|
56
56
|
- `Token`
|
|
57
57
|
|
|
58
|
+
`Container` exposes the runtime API for resolution and low-level registration,
|
|
59
|
+
including `of()`, `get()`, `set()`, `has()`, `remove()`, and `reset()`.
|
|
60
|
+
|
|
58
61
|
Type-only exports:
|
|
59
62
|
|
|
60
63
|
- `Constructable` / `AbstractConstructable`
|
|
@@ -209,6 +212,9 @@ handler.logger.log('hello from token injection');
|
|
|
209
212
|
|
|
210
213
|
Returns the default container or a named container.
|
|
211
214
|
|
|
215
|
+
Calling `Container.of(id)` with the same identifier reuses the same container
|
|
216
|
+
instance until that instance is disposed.
|
|
217
|
+
|
|
212
218
|
### `container.get(id)`
|
|
213
219
|
|
|
214
220
|
Resolves a service by class or service identifier.
|
|
@@ -218,6 +224,15 @@ Throws:
|
|
|
218
224
|
- `ServiceNotFoundError` when no registration exists;
|
|
219
225
|
- `CircularDependencyError` when the current resolution path loops back to an in-progress dependency.
|
|
220
226
|
|
|
227
|
+
### `container.tryGet(id)`
|
|
228
|
+
|
|
229
|
+
Resolves a service by class or service identifier and returns `undefined` when
|
|
230
|
+
no registration exists.
|
|
231
|
+
|
|
232
|
+
Unlike `get()`, this only returns `undefined` when the requested identifier is
|
|
233
|
+
not registered anywhere in the current resolution path. Other failures, such as
|
|
234
|
+
circular dependencies or missing nested dependencies, still throw.
|
|
235
|
+
|
|
221
236
|
### `container.has(id)`
|
|
222
237
|
|
|
223
238
|
Checks whether the current container has a local registration.
|
|
@@ -235,12 +250,80 @@ Supported strategies:
|
|
|
235
250
|
|
|
236
251
|
This is especially useful in tests.
|
|
237
252
|
|
|
238
|
-
### `container.
|
|
253
|
+
### `await container.dispose()`
|
|
254
|
+
|
|
255
|
+
Disposes the current container instance explicitly.
|
|
256
|
+
|
|
257
|
+
- clears local registrations, bindings, and cached service instances;
|
|
258
|
+
- awaits any bound value or cached service instance that exposes a `dispose()` method;
|
|
259
|
+
- makes the disposed container instance unusable for future operations.
|
|
260
|
+
|
|
261
|
+
After disposal, calling `Container.of(id)` again creates a fresh container for
|
|
262
|
+
that identifier. The same applies to the default container: after disposal, the
|
|
263
|
+
next `Container.of()` or named-container fallback access recreates a fresh
|
|
264
|
+
default container with no previous registrations or cached instances.
|
|
265
|
+
|
|
266
|
+
### `container.set(id, valueOrProvider)`
|
|
267
|
+
|
|
268
|
+
Registers or replaces a bound value or provider for a service identifier.
|
|
269
|
+
|
|
270
|
+
Use `set()` when you want manual registration without decorating a class.
|
|
271
|
+
This is the supported low-level API for values, classes, and factories.
|
|
272
|
+
|
|
273
|
+
Supported forms today:
|
|
274
|
+
|
|
275
|
+
- `container.set(id, value)`
|
|
276
|
+
- `container.set(id, { useValue: value })`
|
|
277
|
+
- `container.set(id, { useClass: Class, scope?, injections? })`
|
|
278
|
+
- `container.set(id, { useFactory: (container) => value, scope? })`
|
|
279
|
+
|
|
280
|
+
This is the low-level API for manual value, class, and factory registration.
|
|
281
|
+
For decorator-driven classes, prefer `@Service()`.
|
|
282
|
+
|
|
283
|
+
Value example:
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
const REQUEST_ID = 'request-id';
|
|
287
|
+
|
|
288
|
+
Container.of().set(REQUEST_ID, { useValue: crypto.randomUUID() });
|
|
289
|
+
|
|
290
|
+
const requestId = Container.of().get<string>(REQUEST_ID);
|
|
291
|
+
```
|
|
239
292
|
|
|
240
|
-
|
|
293
|
+
Class example:
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
interface Logger {
|
|
297
|
+
log(message: string): void;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
class ConsoleLogger implements Logger {
|
|
301
|
+
public log(message: string) {
|
|
302
|
+
console.log(message);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
Container.of().set<Logger>('logger', { useClass: ConsoleLogger, scope: 'singleton' });
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Factory example:
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
class Connection {
|
|
313
|
+
constructor(public readonly requestId: string) {}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
Container.of().set('request-id', { useValue: 'request-1' });
|
|
317
|
+
Container.of().set(Connection, {
|
|
318
|
+
useFactory: (container) => new Connection(container.get('request-id')),
|
|
319
|
+
scope: 'container',
|
|
320
|
+
});
|
|
321
|
+
```
|
|
241
322
|
|
|
242
|
-
|
|
243
|
-
|
|
323
|
+
For named containers, values bound in the default container are available through
|
|
324
|
+
fallback. Provider objects are detected structurally, so plain object values that
|
|
325
|
+
contain `useValue`, `useClass`, or `useFactory` should be wrapped with
|
|
326
|
+
`{ useValue: value }` if you want them treated as values.
|
|
244
327
|
|
|
245
328
|
## Internal architecture
|
|
246
329
|
|
|
@@ -1,19 +1,132 @@
|
|
|
1
|
-
import type { ContainerIdentifier, Metadata, ServiceIdentifier } from '../types';
|
|
1
|
+
import type { ContainerIdentifier, Metadata, ServiceIdentifier, ServiceProvider } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves services and stores container-local bindings.
|
|
4
|
+
*
|
|
5
|
+
* Containers let you resolve registered services, override values for the
|
|
6
|
+
* current context, and isolate container-scoped services by container.
|
|
7
|
+
*/
|
|
2
8
|
export declare class Container {
|
|
9
|
+
/**
|
|
10
|
+
* The identifier of this container.
|
|
11
|
+
*
|
|
12
|
+
* The default container uses `'default'`, and named containers use the
|
|
13
|
+
* identifier they were created with.
|
|
14
|
+
*/
|
|
3
15
|
readonly id: ContainerIdentifier;
|
|
4
16
|
private metadataMap;
|
|
5
17
|
private bindingMap;
|
|
6
18
|
private resolving;
|
|
7
19
|
private resolvingPath;
|
|
20
|
+
private disposed;
|
|
8
21
|
constructor(id: ContainerIdentifier);
|
|
22
|
+
private ensureNotDisposed;
|
|
23
|
+
private isDisposable;
|
|
24
|
+
private canResolveLocally;
|
|
25
|
+
private canResolve;
|
|
26
|
+
private isValueProvider;
|
|
27
|
+
private isClassProvider;
|
|
28
|
+
private isFactoryProvider;
|
|
29
|
+
private getClassProviderMetadata;
|
|
30
|
+
private getFactoryProviderMetadata;
|
|
31
|
+
/**
|
|
32
|
+
* Returns the default container or a named container.
|
|
33
|
+
*
|
|
34
|
+
* Calling this method with the same identifier returns the same container
|
|
35
|
+
* instance until that instance is disposed.
|
|
36
|
+
*
|
|
37
|
+
* @param id The container identifier. Omit this to use the default container.
|
|
38
|
+
* @returns The matching container instance.
|
|
39
|
+
*/
|
|
9
40
|
static of(id?: ContainerIdentifier): Container;
|
|
41
|
+
/**
|
|
42
|
+
* Registers service metadata in this container.
|
|
43
|
+
*
|
|
44
|
+
* This is a low-level API for manual registration when you are not using
|
|
45
|
+
* `@Service()`. Registering a `singleton` on a named container stores it in
|
|
46
|
+
* the default container so it can be shared across containers.
|
|
47
|
+
*
|
|
48
|
+
* @param metadata The service metadata to register.
|
|
49
|
+
* @returns The current container.
|
|
50
|
+
*/
|
|
10
51
|
register<T>(metadata: Metadata<T>): this;
|
|
52
|
+
/**
|
|
53
|
+
* Returns whether this container has a local registration for an identifier.
|
|
54
|
+
*
|
|
55
|
+
* This only checks registrations stored on the current container and does
|
|
56
|
+
* not indicate whether the identifier can be resolved through fallback.
|
|
57
|
+
*
|
|
58
|
+
* @param id The service identifier to check.
|
|
59
|
+
* @returns `true` when the current container has a local registration.
|
|
60
|
+
*/
|
|
11
61
|
has(id: ServiceIdentifier): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Registers a value or provider for a service identifier.
|
|
64
|
+
*
|
|
65
|
+
* Use a plain value to bind an explicit instance, or a provider object to
|
|
66
|
+
* resolve by class or factory.
|
|
67
|
+
*
|
|
68
|
+
* @param id The service identifier to register.
|
|
69
|
+
* @param valueOrProvider The bound value or provider definition.
|
|
70
|
+
* @returns The current container.
|
|
71
|
+
*/
|
|
12
72
|
set<T>(id: ServiceIdentifier<T>, value: T): this;
|
|
73
|
+
set<T>(id: ServiceIdentifier<T>, provider: ServiceProvider<T>): this;
|
|
74
|
+
/**
|
|
75
|
+
* Removes a local binding or registration from this container.
|
|
76
|
+
*
|
|
77
|
+
* @param id The service identifier to remove.
|
|
78
|
+
* @returns The current container.
|
|
79
|
+
*/
|
|
13
80
|
remove(id: ServiceIdentifier): this;
|
|
81
|
+
/**
|
|
82
|
+
* Resets services stored in this container.
|
|
83
|
+
*
|
|
84
|
+
* Use `'value'` to clear cached instances while keeping registrations, or
|
|
85
|
+
* `'service'` to remove local registrations and bindings entirely.
|
|
86
|
+
*
|
|
87
|
+
* @param strategy The reset strategy to apply.
|
|
88
|
+
* @returns The current container.
|
|
89
|
+
*/
|
|
14
90
|
reset(strategy?: 'value' | 'service'): this;
|
|
91
|
+
/**
|
|
92
|
+
* Resolves a service from this container.
|
|
93
|
+
*
|
|
94
|
+
* If the current container does not have a local registration, named
|
|
95
|
+
* containers can continue resolution through the default container according
|
|
96
|
+
* to the service scope. Services are instantiated with `new Class()` and do
|
|
97
|
+
* not support constructor arguments.
|
|
98
|
+
*
|
|
99
|
+
* @param id The service identifier to resolve.
|
|
100
|
+
* @returns The resolved service instance or bound value.
|
|
101
|
+
* @throws {ServiceNotFoundError} If no service is registered for the identifier.
|
|
102
|
+
* @throws {CircularDependencyError} If the dependency graph contains a cycle.
|
|
103
|
+
*/
|
|
15
104
|
get<T>(id: ServiceIdentifier<T>): T;
|
|
105
|
+
/**
|
|
106
|
+
* Resolves a service if it exists, otherwise returns `undefined`.
|
|
107
|
+
*
|
|
108
|
+
* Unlike `get()`, this only suppresses `ServiceNotFoundError`. Other errors,
|
|
109
|
+
* such as circular dependencies or disposed-container access, still surface.
|
|
110
|
+
*
|
|
111
|
+
* @param id The service identifier to resolve.
|
|
112
|
+
* @returns The resolved value, or `undefined` when the service is missing.
|
|
113
|
+
*/
|
|
114
|
+
tryGet<T>(id: ServiceIdentifier<T>): T | undefined;
|
|
115
|
+
/**
|
|
116
|
+
* Disposes this container instance and clears all local registrations.
|
|
117
|
+
*
|
|
118
|
+
* The container becomes unusable after disposal. Cached service instances and
|
|
119
|
+
* bound values that expose an async or sync `dispose()` method are awaited in
|
|
120
|
+
* the order they were discovered.
|
|
121
|
+
*/
|
|
122
|
+
dispose(): Promise<void>;
|
|
16
123
|
private isDefault;
|
|
124
|
+
/**
|
|
125
|
+
* Resets a container by its identifier.
|
|
126
|
+
*
|
|
127
|
+
* @param containerId The container to reset.
|
|
128
|
+
* @param options Reset options.
|
|
129
|
+
*/
|
|
17
130
|
static reset(containerId: ContainerIdentifier, options?: {
|
|
18
131
|
strategy?: 'value' | 'service';
|
|
19
132
|
}): void;
|
|
@@ -1,15 +1,89 @@
|
|
|
1
|
-
import { CircularDependencyError, ServiceNotFoundError } from '../errors';
|
|
1
|
+
import { CircularDependencyError, ContainerDisposedError, ServiceNotFoundError } from '../errors';
|
|
2
2
|
import { EMPTY_VALUE } from '../types';
|
|
3
3
|
import { ContainerRegistry } from './registry';
|
|
4
|
+
/**
|
|
5
|
+
* Resolves services and stores container-local bindings.
|
|
6
|
+
*
|
|
7
|
+
* Containers let you resolve registered services, override values for the
|
|
8
|
+
* current context, and isolate container-scoped services by container.
|
|
9
|
+
*/
|
|
4
10
|
export class Container {
|
|
11
|
+
/**
|
|
12
|
+
* The identifier of this container.
|
|
13
|
+
*
|
|
14
|
+
* The default container uses `'default'`, and named containers use the
|
|
15
|
+
* identifier they were created with.
|
|
16
|
+
*/
|
|
5
17
|
id;
|
|
6
18
|
metadataMap = new Map();
|
|
7
19
|
bindingMap = new Map();
|
|
8
20
|
resolving = new Set();
|
|
9
21
|
resolvingPath = [];
|
|
22
|
+
disposed = false;
|
|
10
23
|
constructor(id) {
|
|
11
24
|
this.id = id;
|
|
12
25
|
}
|
|
26
|
+
ensureNotDisposed() {
|
|
27
|
+
if (this.disposed) {
|
|
28
|
+
throw new ContainerDisposedError(this.id);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
isDisposable(value) {
|
|
32
|
+
return typeof value === 'object' && value !== null && 'dispose' in value && typeof value.dispose === 'function';
|
|
33
|
+
}
|
|
34
|
+
canResolveLocally(id) {
|
|
35
|
+
return this.bindingMap.has(id) || this.metadataMap.has(id);
|
|
36
|
+
}
|
|
37
|
+
canResolve(id) {
|
|
38
|
+
if (this.canResolveLocally(id)) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
if (this.isDefault()) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
return ContainerRegistry.defaultContainer.canResolveLocally(id);
|
|
45
|
+
}
|
|
46
|
+
isValueProvider(provider) {
|
|
47
|
+
return typeof provider === 'object' && provider !== null && 'useValue' in provider;
|
|
48
|
+
}
|
|
49
|
+
isClassProvider(provider) {
|
|
50
|
+
return typeof provider === 'object' && provider !== null && 'useClass' in provider;
|
|
51
|
+
}
|
|
52
|
+
isFactoryProvider(provider) {
|
|
53
|
+
return typeof provider === 'object' && provider !== null && 'useFactory' in provider;
|
|
54
|
+
}
|
|
55
|
+
getClassProviderMetadata(id, provider) {
|
|
56
|
+
const existingMetadata = this.metadataMap.get(provider.useClass) ?? ContainerRegistry.defaultContainer.metadataMap.get(provider.useClass);
|
|
57
|
+
const inheritedInjections = existingMetadata?.Class === provider.useClass ? [...existingMetadata.injections] : [];
|
|
58
|
+
const inheritedScope = existingMetadata?.Class === provider.useClass ? existingMetadata.scope : 'container';
|
|
59
|
+
return {
|
|
60
|
+
id,
|
|
61
|
+
Class: provider.useClass,
|
|
62
|
+
name: provider.useClass.name || String(id),
|
|
63
|
+
injections: provider.injections ?? inheritedInjections,
|
|
64
|
+
scope: provider.scope ?? inheritedScope,
|
|
65
|
+
value: EMPTY_VALUE,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
getFactoryProviderMetadata(id, provider) {
|
|
69
|
+
return {
|
|
70
|
+
id,
|
|
71
|
+
name: typeof id === 'function' ? id.name : String(id),
|
|
72
|
+
injections: [],
|
|
73
|
+
scope: provider.scope ?? 'container',
|
|
74
|
+
value: EMPTY_VALUE,
|
|
75
|
+
factory: provider.useFactory,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Returns the default container or a named container.
|
|
80
|
+
*
|
|
81
|
+
* Calling this method with the same identifier returns the same container
|
|
82
|
+
* instance until that instance is disposed.
|
|
83
|
+
*
|
|
84
|
+
* @param id The container identifier. Omit this to use the default container.
|
|
85
|
+
* @returns The matching container instance.
|
|
86
|
+
*/
|
|
13
87
|
static of(id = 'default') {
|
|
14
88
|
if (id === 'default') {
|
|
15
89
|
return ContainerRegistry.defaultContainer;
|
|
@@ -21,7 +95,18 @@ export class Container {
|
|
|
21
95
|
ContainerRegistry.registerContainer(container);
|
|
22
96
|
return container;
|
|
23
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Registers service metadata in this container.
|
|
100
|
+
*
|
|
101
|
+
* This is a low-level API for manual registration when you are not using
|
|
102
|
+
* `@Service()`. Registering a `singleton` on a named container stores it in
|
|
103
|
+
* the default container so it can be shared across containers.
|
|
104
|
+
*
|
|
105
|
+
* @param metadata The service metadata to register.
|
|
106
|
+
* @returns The current container.
|
|
107
|
+
*/
|
|
24
108
|
register(metadata) {
|
|
109
|
+
this.ensureNotDisposed();
|
|
25
110
|
if (metadata.scope === 'singleton' && !this.isDefault()) {
|
|
26
111
|
ContainerRegistry.defaultContainer.register(metadata);
|
|
27
112
|
this.metadataMap.delete(metadata.id);
|
|
@@ -39,19 +124,61 @@ export class Container {
|
|
|
39
124
|
}
|
|
40
125
|
return this;
|
|
41
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Returns whether this container has a local registration for an identifier.
|
|
129
|
+
*
|
|
130
|
+
* This only checks registrations stored on the current container and does
|
|
131
|
+
* not indicate whether the identifier can be resolved through fallback.
|
|
132
|
+
*
|
|
133
|
+
* @param id The service identifier to check.
|
|
134
|
+
* @returns `true` when the current container has a local registration.
|
|
135
|
+
*/
|
|
42
136
|
has(id) {
|
|
43
|
-
|
|
137
|
+
this.ensureNotDisposed();
|
|
138
|
+
return this.bindingMap.has(id) || this.metadataMap.has(id);
|
|
44
139
|
}
|
|
45
|
-
set(id,
|
|
46
|
-
this.
|
|
140
|
+
set(id, valueOrProvider) {
|
|
141
|
+
this.ensureNotDisposed();
|
|
142
|
+
if (this.isValueProvider(valueOrProvider)) {
|
|
143
|
+
this.bindingMap.set(id, valueOrProvider.useValue);
|
|
144
|
+
this.metadataMap.delete(id);
|
|
145
|
+
return this;
|
|
146
|
+
}
|
|
147
|
+
if (this.isClassProvider(valueOrProvider)) {
|
|
148
|
+
this.bindingMap.delete(id);
|
|
149
|
+
return this.register(this.getClassProviderMetadata(id, valueOrProvider));
|
|
150
|
+
}
|
|
151
|
+
if (this.isFactoryProvider(valueOrProvider)) {
|
|
152
|
+
this.bindingMap.delete(id);
|
|
153
|
+
return this.register(this.getFactoryProviderMetadata(id, valueOrProvider));
|
|
154
|
+
}
|
|
155
|
+
this.bindingMap.set(id, valueOrProvider);
|
|
156
|
+
this.metadataMap.delete(id);
|
|
47
157
|
return this;
|
|
48
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Removes a local binding or registration from this container.
|
|
161
|
+
*
|
|
162
|
+
* @param id The service identifier to remove.
|
|
163
|
+
* @returns The current container.
|
|
164
|
+
*/
|
|
49
165
|
remove(id) {
|
|
166
|
+
this.ensureNotDisposed();
|
|
50
167
|
this.bindingMap.delete(id);
|
|
51
168
|
this.metadataMap.delete(id);
|
|
52
169
|
return this;
|
|
53
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Resets services stored in this container.
|
|
173
|
+
*
|
|
174
|
+
* Use `'value'` to clear cached instances while keeping registrations, or
|
|
175
|
+
* `'service'` to remove local registrations and bindings entirely.
|
|
176
|
+
*
|
|
177
|
+
* @param strategy The reset strategy to apply.
|
|
178
|
+
* @returns The current container.
|
|
179
|
+
*/
|
|
54
180
|
reset(strategy = 'value') {
|
|
181
|
+
this.ensureNotDisposed();
|
|
55
182
|
if (strategy === 'value') {
|
|
56
183
|
this.metadataMap.forEach((metadata) => {
|
|
57
184
|
metadata.value = EMPTY_VALUE;
|
|
@@ -64,12 +191,29 @@ export class Container {
|
|
|
64
191
|
return this;
|
|
65
192
|
}
|
|
66
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Resolves a service from this container.
|
|
196
|
+
*
|
|
197
|
+
* If the current container does not have a local registration, named
|
|
198
|
+
* containers can continue resolution through the default container according
|
|
199
|
+
* to the service scope. Services are instantiated with `new Class()` and do
|
|
200
|
+
* not support constructor arguments.
|
|
201
|
+
*
|
|
202
|
+
* @param id The service identifier to resolve.
|
|
203
|
+
* @returns The resolved service instance or bound value.
|
|
204
|
+
* @throws {ServiceNotFoundError} If no service is registered for the identifier.
|
|
205
|
+
* @throws {CircularDependencyError} If the dependency graph contains a cycle.
|
|
206
|
+
*/
|
|
67
207
|
get(id) {
|
|
208
|
+
this.ensureNotDisposed();
|
|
68
209
|
if (this.bindingMap.has(id)) {
|
|
69
210
|
return this.bindingMap.get(id);
|
|
70
211
|
}
|
|
71
212
|
let metadata = this.metadataMap.get(id);
|
|
72
213
|
if (!metadata && !this.isDefault()) {
|
|
214
|
+
if (ContainerRegistry.defaultContainer.bindingMap.has(id)) {
|
|
215
|
+
return ContainerRegistry.defaultContainer.bindingMap.get(id);
|
|
216
|
+
}
|
|
73
217
|
const defaultMetadata = ContainerRegistry.defaultContainer.metadataMap.get(id);
|
|
74
218
|
if (!defaultMetadata) {
|
|
75
219
|
throw new ServiceNotFoundError(id);
|
|
@@ -101,7 +245,7 @@ export class Container {
|
|
|
101
245
|
this.resolving.add(id);
|
|
102
246
|
this.resolvingPath.push(id);
|
|
103
247
|
try {
|
|
104
|
-
const instance = new metadata.Class();
|
|
248
|
+
const instance = metadata.factory ? metadata.factory(this) : new metadata.Class();
|
|
105
249
|
for (const injection of metadata.injections) {
|
|
106
250
|
Object.defineProperty(instance, injection.name, {
|
|
107
251
|
value: this.get(injection.id),
|
|
@@ -119,9 +263,63 @@ export class Container {
|
|
|
119
263
|
this.resolvingPath.pop();
|
|
120
264
|
}
|
|
121
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* Resolves a service if it exists, otherwise returns `undefined`.
|
|
268
|
+
*
|
|
269
|
+
* Unlike `get()`, this only suppresses `ServiceNotFoundError`. Other errors,
|
|
270
|
+
* such as circular dependencies or disposed-container access, still surface.
|
|
271
|
+
*
|
|
272
|
+
* @param id The service identifier to resolve.
|
|
273
|
+
* @returns The resolved value, or `undefined` when the service is missing.
|
|
274
|
+
*/
|
|
275
|
+
tryGet(id) {
|
|
276
|
+
this.ensureNotDisposed();
|
|
277
|
+
if (!this.canResolve(id)) {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
return this.get(id);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Disposes this container instance and clears all local registrations.
|
|
284
|
+
*
|
|
285
|
+
* The container becomes unusable after disposal. Cached service instances and
|
|
286
|
+
* bound values that expose an async or sync `dispose()` method are awaited in
|
|
287
|
+
* the order they were discovered.
|
|
288
|
+
*/
|
|
289
|
+
async dispose() {
|
|
290
|
+
if (this.disposed) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const ownedValues = new Set();
|
|
294
|
+
this.bindingMap.forEach((value) => {
|
|
295
|
+
ownedValues.add(value);
|
|
296
|
+
});
|
|
297
|
+
this.metadataMap.forEach((metadata) => {
|
|
298
|
+
if (metadata.value !== EMPTY_VALUE) {
|
|
299
|
+
ownedValues.add(metadata.value);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
this.disposed = true;
|
|
303
|
+
ContainerRegistry.disposeContainer(this);
|
|
304
|
+
this.bindingMap.clear();
|
|
305
|
+
this.metadataMap.clear();
|
|
306
|
+
this.resolving.clear();
|
|
307
|
+
this.resolvingPath = [];
|
|
308
|
+
for (const value of ownedValues) {
|
|
309
|
+
if (this.isDisposable(value)) {
|
|
310
|
+
await value.dispose();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
122
314
|
isDefault() {
|
|
123
315
|
return this === ContainerRegistry.defaultContainer;
|
|
124
316
|
}
|
|
317
|
+
/**
|
|
318
|
+
* Resets a container by its identifier.
|
|
319
|
+
*
|
|
320
|
+
* @param containerId The container to reset.
|
|
321
|
+
* @param options Reset options.
|
|
322
|
+
*/
|
|
125
323
|
static reset(containerId, options) {
|
|
126
324
|
const container = ContainerRegistry.getContainer(containerId);
|
|
127
325
|
container?.reset(options?.strategy);
|
|
@@ -1,11 +1,52 @@
|
|
|
1
1
|
import type { ContainerIdentifier } from '../types';
|
|
2
2
|
import { Container } from './container';
|
|
3
|
+
/**
|
|
4
|
+
* Stores the default container and all named containers.
|
|
5
|
+
*
|
|
6
|
+
* This registry is the shared source of truth behind `Container.of()` and
|
|
7
|
+
* decorator-based service registration.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
3
11
|
export declare class ContainerRegistry {
|
|
4
12
|
private static defaultContainerInstance?;
|
|
5
13
|
private static readonly containerMap;
|
|
14
|
+
/**
|
|
15
|
+
* Returns the canonical default container instance.
|
|
16
|
+
*
|
|
17
|
+
* The default container is created lazily the first time it is requested.
|
|
18
|
+
*/
|
|
6
19
|
static get defaultContainer(): Container;
|
|
20
|
+
/**
|
|
21
|
+
* Registers a named container in the shared registry.
|
|
22
|
+
*
|
|
23
|
+
* @param container The container to register.
|
|
24
|
+
* @throws {DefaultContainerIdError} If the container uses the reserved `default` id.
|
|
25
|
+
* @throws {ContainerDuplicatedError} If another container already uses the same id.
|
|
26
|
+
*/
|
|
7
27
|
static registerContainer(container: Container): void;
|
|
28
|
+
/**
|
|
29
|
+
* Returns the default container or a named container by identifier.
|
|
30
|
+
*
|
|
31
|
+
* @param id The container identifier to resolve.
|
|
32
|
+
* @returns The default container for `'default'`, or the matching named container if one exists.
|
|
33
|
+
*/
|
|
8
34
|
static getContainer(id: ContainerIdentifier): Container | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Returns whether a container exists for the given identifier.
|
|
37
|
+
*
|
|
38
|
+
* The reserved `default` identifier is always treated as present.
|
|
39
|
+
*
|
|
40
|
+
* @param id The container identifier to check.
|
|
41
|
+
* @returns `true` when the container exists in the registry.
|
|
42
|
+
*/
|
|
9
43
|
static hasContainer(id: ContainerIdentifier): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Removes a named container from the registry.
|
|
46
|
+
*
|
|
47
|
+
* @param id The named container identifier to remove.
|
|
48
|
+
* @throws {DefaultContainerIdError} If the reserved `default` id is used.
|
|
49
|
+
*/
|
|
10
50
|
static removeContainer(id: ContainerIdentifier): void;
|
|
51
|
+
static disposeContainer(container: Container): void;
|
|
11
52
|
}
|
|
@@ -1,12 +1,32 @@
|
|
|
1
1
|
import { ContainerDuplicatedError, DefaultContainerIdError } from '../errors';
|
|
2
2
|
import { Container } from './container';
|
|
3
|
+
/**
|
|
4
|
+
* Stores the default container and all named containers.
|
|
5
|
+
*
|
|
6
|
+
* This registry is the shared source of truth behind `Container.of()` and
|
|
7
|
+
* decorator-based service registration.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
3
11
|
export class ContainerRegistry {
|
|
4
12
|
static defaultContainerInstance;
|
|
5
13
|
static containerMap = new Map();
|
|
14
|
+
/**
|
|
15
|
+
* Returns the canonical default container instance.
|
|
16
|
+
*
|
|
17
|
+
* The default container is created lazily the first time it is requested.
|
|
18
|
+
*/
|
|
6
19
|
static get defaultContainer() {
|
|
7
20
|
this.defaultContainerInstance ??= new Container('default');
|
|
8
21
|
return this.defaultContainerInstance;
|
|
9
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Registers a named container in the shared registry.
|
|
25
|
+
*
|
|
26
|
+
* @param container The container to register.
|
|
27
|
+
* @throws {DefaultContainerIdError} If the container uses the reserved `default` id.
|
|
28
|
+
* @throws {ContainerDuplicatedError} If another container already uses the same id.
|
|
29
|
+
*/
|
|
10
30
|
static registerContainer(container) {
|
|
11
31
|
if (container.id === 'default') {
|
|
12
32
|
throw new DefaultContainerIdError();
|
|
@@ -16,22 +36,53 @@ export class ContainerRegistry {
|
|
|
16
36
|
}
|
|
17
37
|
this.containerMap.set(container.id, container);
|
|
18
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Returns the default container or a named container by identifier.
|
|
41
|
+
*
|
|
42
|
+
* @param id The container identifier to resolve.
|
|
43
|
+
* @returns The default container for `'default'`, or the matching named container if one exists.
|
|
44
|
+
*/
|
|
19
45
|
static getContainer(id) {
|
|
20
46
|
if (id === 'default') {
|
|
21
47
|
return this.defaultContainer;
|
|
22
48
|
}
|
|
23
49
|
return this.containerMap.get(id);
|
|
24
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Returns whether a container exists for the given identifier.
|
|
53
|
+
*
|
|
54
|
+
* The reserved `default` identifier is always treated as present.
|
|
55
|
+
*
|
|
56
|
+
* @param id The container identifier to check.
|
|
57
|
+
* @returns `true` when the container exists in the registry.
|
|
58
|
+
*/
|
|
25
59
|
static hasContainer(id) {
|
|
26
60
|
if (id === 'default') {
|
|
27
61
|
return true;
|
|
28
62
|
}
|
|
29
63
|
return this.containerMap.has(id);
|
|
30
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Removes a named container from the registry.
|
|
67
|
+
*
|
|
68
|
+
* @param id The named container identifier to remove.
|
|
69
|
+
* @throws {DefaultContainerIdError} If the reserved `default` id is used.
|
|
70
|
+
*/
|
|
31
71
|
static removeContainer(id) {
|
|
32
72
|
if (id === 'default') {
|
|
33
73
|
throw new DefaultContainerIdError();
|
|
34
74
|
}
|
|
35
75
|
this.containerMap.delete(id);
|
|
36
76
|
}
|
|
77
|
+
static disposeContainer(container) {
|
|
78
|
+
if (container.id === 'default') {
|
|
79
|
+
if (this.defaultContainerInstance === container) {
|
|
80
|
+
this.defaultContainerInstance = undefined;
|
|
81
|
+
}
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (this.containerMap.get(container.id) === container) {
|
|
85
|
+
this.containerMap.delete(container.id);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
37
88
|
}
|
|
@@ -1,2 +1,13 @@
|
|
|
1
1
|
import { type ServiceIdentifier } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Injects a dependency into a class field when the service is resolved.
|
|
4
|
+
*
|
|
5
|
+
* Use this field decorator on service properties that should be resolved from
|
|
6
|
+
* the container that creates the service instance.
|
|
7
|
+
*
|
|
8
|
+
* The dependency is assigned after the instance is created, so it is not
|
|
9
|
+
* available in constructors or field initializers.
|
|
10
|
+
*
|
|
11
|
+
* @param dependency The service identifier to inject into the decorated field.
|
|
12
|
+
*/
|
|
2
13
|
export declare function Inject<T>(dependency: ServiceIdentifier<T>): (_: undefined, context: ClassFieldDecoratorContext) => void;
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { INJECTION_KEY } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Injects a dependency into a class field when the service is resolved.
|
|
4
|
+
*
|
|
5
|
+
* Use this field decorator on service properties that should be resolved from
|
|
6
|
+
* the container that creates the service instance.
|
|
7
|
+
*
|
|
8
|
+
* The dependency is assigned after the instance is created, so it is not
|
|
9
|
+
* available in constructors or field initializers.
|
|
10
|
+
*
|
|
11
|
+
* @param dependency The service identifier to inject into the decorated field.
|
|
12
|
+
*/
|
|
2
13
|
export function Inject(dependency) {
|
|
3
14
|
return function (_, context) {
|
|
4
15
|
const injections = (context.metadata[INJECTION_KEY] ??= []);
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import type { ServiceIdentifier, ServiceOption } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Registers a class as a service that can be resolved from a container.
|
|
4
|
+
*
|
|
5
|
+
* By default, the decorated class is registered in the default container,
|
|
6
|
+
* uses its own class as the service identifier, and uses the `container`
|
|
7
|
+
* scope.
|
|
8
|
+
*
|
|
9
|
+
* Use this decorator with a custom identifier or `scope` option when you need
|
|
10
|
+
* to change how the service is registered or reused. When you provide a
|
|
11
|
+
* custom identifier, resolve the service by that identifier.
|
|
12
|
+
*/
|
|
2
13
|
export declare function Service(): Function;
|
|
3
14
|
export declare function Service(id: ServiceIdentifier): Function;
|
|
4
15
|
export declare function Service(options?: ServiceOption): Function;
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import type { ServiceIdentifier } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when service resolution detects a circular dependency path.
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
2
7
|
export declare class CircularDependencyError extends Error {
|
|
3
8
|
name: string;
|
|
9
|
+
/**
|
|
10
|
+
* @param path The dependency chain that produced the circular reference.
|
|
11
|
+
*/
|
|
4
12
|
constructor(path: ServiceIdentifier[]);
|
|
5
13
|
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when service resolution detects a circular dependency path.
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
1
6
|
export class CircularDependencyError extends Error {
|
|
2
7
|
name = 'CircularDependencyError';
|
|
8
|
+
/**
|
|
9
|
+
* @param path The dependency chain that produced the circular reference.
|
|
10
|
+
*/
|
|
3
11
|
constructor(path) {
|
|
4
12
|
super(`Circular dependency detected: ${path.map(String).join(' -> ')}`);
|
|
5
13
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ContainerIdentifier } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when an operation is attempted on a disposed container instance.
|
|
4
|
+
*/
|
|
5
|
+
export declare class ContainerDisposedError extends Error {
|
|
6
|
+
name: string;
|
|
7
|
+
/**
|
|
8
|
+
* @param id The identifier of the disposed container.
|
|
9
|
+
*/
|
|
10
|
+
constructor(id: ContainerIdentifier);
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when an operation is attempted on a disposed container instance.
|
|
3
|
+
*/
|
|
4
|
+
export class ContainerDisposedError extends Error {
|
|
5
|
+
name = 'ContainerDisposedError';
|
|
6
|
+
/**
|
|
7
|
+
* @param id The identifier of the disposed container.
|
|
8
|
+
*/
|
|
9
|
+
constructor(id) {
|
|
10
|
+
super(`Container has been disposed: ${String(id)}`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import type { ContainerIdentifier } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when a named container is registered with an identifier that already exists.
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
2
7
|
export declare class ContainerDuplicatedError extends Error {
|
|
3
8
|
name: string;
|
|
9
|
+
/**
|
|
10
|
+
* @param id The duplicated container identifier.
|
|
11
|
+
*/
|
|
4
12
|
constructor(id: ContainerIdentifier);
|
|
5
13
|
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when a named container is registered with an identifier that already exists.
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
1
6
|
export class ContainerDuplicatedError extends Error {
|
|
2
7
|
name = 'ContainerDuplicatedError';
|
|
8
|
+
/**
|
|
9
|
+
* @param id The duplicated container identifier.
|
|
10
|
+
*/
|
|
3
11
|
constructor(id) {
|
|
4
12
|
super(`Cannot register container with same ID(${String(id)})`);
|
|
5
13
|
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when code attempts to use the reserved `default` identifier for a named container operation.
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
1
6
|
export class DefaultContainerIdError extends Error {
|
|
2
7
|
name = 'DefaultContainerIdError';
|
|
3
8
|
constructor() {
|
|
4
|
-
super(`
|
|
9
|
+
super('The `default` identifier is reserved for the default container.');
|
|
5
10
|
}
|
|
6
11
|
}
|
package/dist/errors/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { CircularDependencyError } from './circular-dependency-error';
|
|
2
2
|
export { ContainerDuplicatedError } from './container-duplicated-error';
|
|
3
|
+
export { ContainerDisposedError } from './container-disposed-error';
|
|
3
4
|
export { DefaultContainerIdError } from './default-container-id-error';
|
|
4
5
|
export { ServiceNotFoundError } from './service-not-found-error';
|
package/dist/errors/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { CircularDependencyError } from './circular-dependency-error';
|
|
2
2
|
export { ContainerDuplicatedError } from './container-duplicated-error';
|
|
3
|
+
export { ContainerDisposedError } from './container-disposed-error';
|
|
3
4
|
export { DefaultContainerIdError } from './default-container-id-error';
|
|
4
5
|
export { ServiceNotFoundError } from './service-not-found-error';
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import type { ServiceIdentifier } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when a service cannot be resolved for the requested identifier.
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
2
7
|
export declare class ServiceNotFoundError extends Error {
|
|
3
8
|
name: string;
|
|
9
|
+
/**
|
|
10
|
+
* @param id The service identifier that could not be resolved.
|
|
11
|
+
*/
|
|
4
12
|
constructor(id: ServiceIdentifier);
|
|
5
13
|
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when a service cannot be resolved for the requested identifier.
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
1
6
|
export class ServiceNotFoundError extends Error {
|
|
2
7
|
name = 'ServiceNotFoundError';
|
|
8
|
+
/**
|
|
9
|
+
* @param id The service identifier that could not be resolved.
|
|
10
|
+
*/
|
|
3
11
|
constructor(id) {
|
|
4
12
|
super(`Service not found: ${String(id)}`);
|
|
5
13
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,3 +3,4 @@ export { Service, Inject } from './decorators';
|
|
|
3
3
|
export { Token } from './tokens';
|
|
4
4
|
export type { AbstractConstructable, Constructable } from './types/constructable.ts';
|
|
5
5
|
export type { ServiceIdentifier } from './types/service.ts';
|
|
6
|
+
export type { ClassProvider, FactoryProvider, ServiceFactory, ServiceProvider, ValueProvider, } from './types/container.ts';
|
package/dist/tokens/token.d.ts
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a typed service identifier.
|
|
3
|
+
*
|
|
4
|
+
* Tokens are useful when a dependency should be resolved by an explicit key
|
|
5
|
+
* instead of by its class constructor.
|
|
6
|
+
*/
|
|
1
7
|
export declare class Token<T> {
|
|
8
|
+
/**
|
|
9
|
+
* A human-readable name for this token.
|
|
10
|
+
*
|
|
11
|
+
* This value is useful for debugging and display, but token resolution is
|
|
12
|
+
* based on the token instance itself.
|
|
13
|
+
*/
|
|
2
14
|
name?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Creates a token with an optional display name.
|
|
17
|
+
*
|
|
18
|
+
* @param name A human-readable name used for debugging and display.
|
|
19
|
+
*/
|
|
3
20
|
constructor(name?: string);
|
|
21
|
+
/**
|
|
22
|
+
* Returns a readable string representation of this token.
|
|
23
|
+
*
|
|
24
|
+
* @returns A display string for this token.
|
|
25
|
+
*/
|
|
4
26
|
toString(): string;
|
|
5
27
|
}
|
package/dist/tokens/token.js
CHANGED
|
@@ -1,9 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a typed service identifier.
|
|
3
|
+
*
|
|
4
|
+
* Tokens are useful when a dependency should be resolved by an explicit key
|
|
5
|
+
* instead of by its class constructor.
|
|
6
|
+
*/
|
|
1
7
|
// oxlint-disable-next-line no-unused-vars
|
|
2
8
|
export class Token {
|
|
9
|
+
/**
|
|
10
|
+
* A human-readable name for this token.
|
|
11
|
+
*
|
|
12
|
+
* This value is useful for debugging and display, but token resolution is
|
|
13
|
+
* based on the token instance itself.
|
|
14
|
+
*/
|
|
3
15
|
name;
|
|
16
|
+
/**
|
|
17
|
+
* Creates a token with an optional display name.
|
|
18
|
+
*
|
|
19
|
+
* @param name A human-readable name used for debugging and display.
|
|
20
|
+
*/
|
|
4
21
|
constructor(name) {
|
|
5
22
|
this.name = name;
|
|
6
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Returns a readable string representation of this token.
|
|
26
|
+
*
|
|
27
|
+
* @returns A display string for this token.
|
|
28
|
+
*/
|
|
7
29
|
toString() {
|
|
8
30
|
return this.name ? `Token(${this.name})` : `Token()`;
|
|
9
31
|
}
|
|
@@ -1,2 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A class constructor that can be instantiated with `new`.
|
|
3
|
+
*/
|
|
1
4
|
export type Constructable<T = object, Args extends unknown[] = never[]> = new (...args: Args) => T;
|
|
5
|
+
/**
|
|
6
|
+
* An abstract class constructor used for typing class-based service identifiers.
|
|
7
|
+
*/
|
|
2
8
|
export type AbstractConstructable<T = object, Args extends unknown[] = never[]> = abstract new (...args: Args) => T;
|
|
@@ -1,13 +1,94 @@
|
|
|
1
1
|
import type { Constructable } from './constructable';
|
|
2
2
|
import type { InjectionMetadata } from './injection';
|
|
3
3
|
import type { ServiceIdentifier, ServiceScope } from './service';
|
|
4
|
+
import type { Container } from '../container';
|
|
5
|
+
/**
|
|
6
|
+
* A container identifier used to select the default container or a named container.
|
|
7
|
+
*/
|
|
4
8
|
export type ContainerIdentifier = string | symbol;
|
|
9
|
+
/**
|
|
10
|
+
* Sentinel value used internally to mark services that have not been instantiated yet.
|
|
11
|
+
*/
|
|
5
12
|
export declare const EMPTY_VALUE: unique symbol;
|
|
13
|
+
/**
|
|
14
|
+
* A factory function that creates a service using the current container.
|
|
15
|
+
*/
|
|
16
|
+
export type ServiceFactory<T> = (container: Container) => T;
|
|
17
|
+
/**
|
|
18
|
+
* A provider that always resolves to the same explicit value.
|
|
19
|
+
*/
|
|
20
|
+
export interface ValueProvider<T> {
|
|
21
|
+
/**
|
|
22
|
+
* The value returned for the registered identifier.
|
|
23
|
+
*/
|
|
24
|
+
useValue: T;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A provider that resolves by instantiating a class.
|
|
28
|
+
*/
|
|
29
|
+
export interface ClassProvider<T> {
|
|
30
|
+
/**
|
|
31
|
+
* The class instantiated when the service is resolved.
|
|
32
|
+
*/
|
|
33
|
+
useClass: Constructable<T>;
|
|
34
|
+
/**
|
|
35
|
+
* Optional property injection definitions for the registered class.
|
|
36
|
+
*/
|
|
37
|
+
injections?: InjectionMetadata[];
|
|
38
|
+
/**
|
|
39
|
+
* The lifetime used when the service is resolved.
|
|
40
|
+
*/
|
|
41
|
+
scope?: ServiceScope;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A provider that resolves by calling a factory function.
|
|
45
|
+
*/
|
|
46
|
+
export interface FactoryProvider<T> {
|
|
47
|
+
/**
|
|
48
|
+
* The factory used to create the resolved value.
|
|
49
|
+
*/
|
|
50
|
+
useFactory: ServiceFactory<T>;
|
|
51
|
+
/**
|
|
52
|
+
* The lifetime used when the service is resolved.
|
|
53
|
+
*/
|
|
54
|
+
scope?: ServiceScope;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* A provider object accepted by low-level container registration APIs.
|
|
58
|
+
*/
|
|
59
|
+
export type ServiceProvider<T> = ValueProvider<T> | ClassProvider<T> | FactoryProvider<T>;
|
|
60
|
+
/**
|
|
61
|
+
* Service registration metadata stored by a container.
|
|
62
|
+
*
|
|
63
|
+
* This is primarily used by low-level registration APIs and internal container logic.
|
|
64
|
+
*/
|
|
6
65
|
export interface Metadata<T = unknown> {
|
|
66
|
+
/**
|
|
67
|
+
* The identifier used to resolve the service.
|
|
68
|
+
*/
|
|
7
69
|
id: ServiceIdentifier;
|
|
8
|
-
|
|
70
|
+
/**
|
|
71
|
+
* The class instantiated for this service.
|
|
72
|
+
*/
|
|
73
|
+
Class?: Constructable<T>;
|
|
74
|
+
/**
|
|
75
|
+
* The original class or member name, when available.
|
|
76
|
+
*/
|
|
9
77
|
name?: string | symbol;
|
|
78
|
+
/**
|
|
79
|
+
* Property injection definitions collected for the service.
|
|
80
|
+
*/
|
|
10
81
|
injections: InjectionMetadata[];
|
|
82
|
+
/**
|
|
83
|
+
* The lifetime used when resolving the service.
|
|
84
|
+
*/
|
|
11
85
|
scope: ServiceScope;
|
|
86
|
+
/**
|
|
87
|
+
* The cached service instance, or `EMPTY_VALUE` when no instance is cached.
|
|
88
|
+
*/
|
|
12
89
|
value: T | typeof EMPTY_VALUE;
|
|
90
|
+
/**
|
|
91
|
+
* An optional factory used to create the resolved value.
|
|
92
|
+
*/
|
|
93
|
+
factory?: ServiceFactory<T>;
|
|
13
94
|
}
|
package/dist/types/container.js
CHANGED
package/dist/types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export type { AbstractConstructable, Constructable } from './constructable';
|
|
2
2
|
export type { ServiceIdentifier, ServiceOption } from './service';
|
|
3
|
-
export { type ContainerIdentifier, type Metadata,
|
|
3
|
+
export { type ClassProvider, type ContainerIdentifier, EMPTY_VALUE, type FactoryProvider, type Metadata, type ServiceFactory, type ServiceProvider, type ValueProvider, } from './container';
|
|
4
4
|
export { type InjectionMetadata, INJECTION_KEY } from './injection';
|
package/dist/types/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { EMPTY_VALUE } from './container';
|
|
1
|
+
export { EMPTY_VALUE, } from './container';
|
|
2
2
|
export { INJECTION_KEY } from './injection';
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import type { ServiceIdentifier } from './service';
|
|
2
|
+
/**
|
|
3
|
+
* Property injection metadata collected for a service.
|
|
4
|
+
*/
|
|
2
5
|
export interface InjectionMetadata {
|
|
6
|
+
/**
|
|
7
|
+
* The identifier of the dependency to inject.
|
|
8
|
+
*/
|
|
3
9
|
id: ServiceIdentifier;
|
|
10
|
+
/**
|
|
11
|
+
* The class field that receives the resolved dependency.
|
|
12
|
+
*/
|
|
4
13
|
name: string | symbol;
|
|
5
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Metadata key used internally to store property injection definitions.
|
|
17
|
+
*/
|
|
6
18
|
export declare const INJECTION_KEY: unique symbol;
|
package/dist/types/injection.js
CHANGED
package/dist/types/service.d.ts
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import type { Token } from '../tokens';
|
|
2
2
|
import type { AbstractConstructable, Constructable } from './constructable';
|
|
3
|
+
/**
|
|
4
|
+
* An identifier that can be used to register or resolve a service.
|
|
5
|
+
*/
|
|
3
6
|
export type ServiceIdentifier<T = unknown, Args extends unknown[] = never[]> = Constructable<T, Args> | AbstractConstructable<T, Args> | CallableFunction | string | Token<T>;
|
|
7
|
+
/**
|
|
8
|
+
* The lifetime used when a service is resolved from a container.
|
|
9
|
+
*/
|
|
4
10
|
export type ServiceScope = 'singleton' | 'container' | 'transient';
|
|
11
|
+
/**
|
|
12
|
+
* Options for registering a service with `@Service()`.
|
|
13
|
+
*/
|
|
5
14
|
export interface ServiceOption {
|
|
15
|
+
/**
|
|
16
|
+
* A custom identifier used to resolve the service.
|
|
17
|
+
*/
|
|
6
18
|
id?: ServiceIdentifier;
|
|
19
|
+
/**
|
|
20
|
+
* The lifetime used when the service is resolved.
|
|
21
|
+
*/
|
|
7
22
|
scope?: ServiceScope;
|
|
8
23
|
}
|