@stone-js/service-container 0.1.2 → 0.8.0
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/LICENSE +1 -1
- package/README.md +1 -1
- package/dist/Container.d.ts +181 -0
- package/dist/Proxiable.d.ts +16 -0
- package/dist/declarations.d.ts +144 -0
- package/dist/errors/ContainerError.d.ts +62 -0
- package/dist/index.d.ts +9 -528
- package/dist/index.js +85 -8
- package/dist/models/Binding.d.ts +48 -0
- package/dist/models/Factory.d.ts +24 -0
- package/dist/models/Instance.d.ts +20 -0
- package/dist/models/ResolverBinding.d.ts +26 -0
- package/dist/models/Singleton.d.ts +24 -0
- package/package.json +7 -7
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://opensource.org/licenses/MIT)
|
|
4
4
|
[](https://www.npmjs.com/package/@stone-js/service-container)
|
|
5
5
|
[](https://www.npmjs.com/package/@stone-js/service-container)
|
|
6
|
-

|
|
7
7
|
[](https://github.com/stone-foundation/stone-js-service-container/actions/workflows/main.yml)
|
|
8
8
|
[](https://github.com/stone-foundation/stone-js-service-container/actions/workflows/release.yml)
|
|
9
9
|
[](https://sonarcloud.io/summary/new_code?id=stone-foundation_stone-js-service-container)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { Proxiable } from './Proxiable';
|
|
2
|
+
import { Binding } from './models/Binding';
|
|
3
|
+
import { BindingKey, BindingValue, Resolver, IContainer } from './declarations';
|
|
4
|
+
/**
|
|
5
|
+
* Class representing a Container.
|
|
6
|
+
*
|
|
7
|
+
* The Container class acts as a dependency injection container, managing bindings and resolving instances.
|
|
8
|
+
* It supports different types of bindings, such as singletons, factories, and instances, and allows the use of aliases for bindings.
|
|
9
|
+
* This makes it easier to manage and resolve complex dependency trees in an application.
|
|
10
|
+
*
|
|
11
|
+
* @author Mr. Stone <evensstone@gmail.com>
|
|
12
|
+
*/
|
|
13
|
+
export declare class Container extends Proxiable implements IContainer {
|
|
14
|
+
private readonly aliases;
|
|
15
|
+
private readonly resolvingKeys;
|
|
16
|
+
private readonly bindings;
|
|
17
|
+
/**
|
|
18
|
+
* Create a Container.
|
|
19
|
+
*
|
|
20
|
+
* @returns A new Container instance.
|
|
21
|
+
*/
|
|
22
|
+
static create(): Container;
|
|
23
|
+
/**
|
|
24
|
+
* Create a ProxyHandler for the container.
|
|
25
|
+
*
|
|
26
|
+
* @returns A new ProxyHandler instance.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Well-known property names that must never trigger service resolution. Accessing them
|
|
30
|
+
* (via `await container`, `JSON.stringify`, `console.log`, React element checks, spread,
|
|
31
|
+
* etc.) returns `undefined` so the container is safe to inspect and pass around, while
|
|
32
|
+
* genuine unknown-service access still fails fast through `make()`.
|
|
33
|
+
*/
|
|
34
|
+
private static readonly SYSTEM_PROPS;
|
|
35
|
+
private static Proxyhandler;
|
|
36
|
+
/**
|
|
37
|
+
* Create a container.
|
|
38
|
+
*
|
|
39
|
+
* Initializes the container with empty alias and binding maps.
|
|
40
|
+
*/
|
|
41
|
+
protected constructor();
|
|
42
|
+
/**
|
|
43
|
+
* Retrieve the value of the bindings property.
|
|
44
|
+
*
|
|
45
|
+
* @returns A map of all bindings registered in the container.
|
|
46
|
+
*/
|
|
47
|
+
getBindings(): Map<BindingKey, Binding<BindingValue>>;
|
|
48
|
+
/**
|
|
49
|
+
* Retrieve the value of the aliases property.
|
|
50
|
+
*
|
|
51
|
+
* @returns A map of all aliases registered in the container.
|
|
52
|
+
*/
|
|
53
|
+
getAliases(): Map<string, BindingKey>;
|
|
54
|
+
/**
|
|
55
|
+
* Set a binding as alias.
|
|
56
|
+
*
|
|
57
|
+
* Adds one or more aliases for a given binding key.
|
|
58
|
+
*
|
|
59
|
+
* @param key - The binding value.
|
|
60
|
+
* @param aliases - One or more strings representing the aliases.
|
|
61
|
+
* @returns The container instance.
|
|
62
|
+
*/
|
|
63
|
+
alias(key: BindingKey, aliases: string | string[]): this;
|
|
64
|
+
/**
|
|
65
|
+
* Check if an alias exists in the container.
|
|
66
|
+
*
|
|
67
|
+
* @param alias - The alias to check.
|
|
68
|
+
* @returns True if the alias exists, false otherwise.
|
|
69
|
+
*/
|
|
70
|
+
isAlias(alias: BindingKey): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Get a binding key by its alias.
|
|
73
|
+
*
|
|
74
|
+
* @param alias - The alias name.
|
|
75
|
+
* @returns The binding key associated with the alias, or undefined if not found.
|
|
76
|
+
*/
|
|
77
|
+
getAliasKey(alias: BindingKey): BindingKey | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* Bind a single instance or value into the container under the provided key.
|
|
80
|
+
*
|
|
81
|
+
* @param key - The key to associate with the value.
|
|
82
|
+
* @param value - The value to be bound.
|
|
83
|
+
* @returns The container instance.
|
|
84
|
+
*/
|
|
85
|
+
instance(key: BindingKey, value: BindingValue): this;
|
|
86
|
+
/**
|
|
87
|
+
* Bind a single instance or value into the container under the provided key if not already bound.
|
|
88
|
+
*
|
|
89
|
+
* @param key - The key to associate with the value.
|
|
90
|
+
* @param value - The value to be bound.
|
|
91
|
+
* @returns The container instance.
|
|
92
|
+
*/
|
|
93
|
+
instanceIf(key: BindingKey, value: BindingValue): this;
|
|
94
|
+
/**
|
|
95
|
+
* Bind a resolver function into the container under the provided key as a singleton.
|
|
96
|
+
*
|
|
97
|
+
* The resolver function will be called once, and the resulting value will be cached for future use.
|
|
98
|
+
*
|
|
99
|
+
* @param key - The key to associate with the singleton value.
|
|
100
|
+
* @param resolver - The resolver function to provide the value.
|
|
101
|
+
* @returns The container instance.
|
|
102
|
+
*/
|
|
103
|
+
singleton<V extends BindingValue>(key: BindingKey, resolver: Resolver<V>): this;
|
|
104
|
+
/**
|
|
105
|
+
* Bind a resolver function into the container under the provided key as a singleton if not already bound.
|
|
106
|
+
*
|
|
107
|
+
* @param key - The key to associate with the singleton value.
|
|
108
|
+
* @param resolver - The resolver function to provide the value.
|
|
109
|
+
* @returns The container instance.
|
|
110
|
+
*/
|
|
111
|
+
singletonIf<V extends BindingValue>(key: BindingKey, resolver: Resolver<V>): this;
|
|
112
|
+
/**
|
|
113
|
+
* Bind a resolver function into the container under the provided key, returning a new instance each time.
|
|
114
|
+
*
|
|
115
|
+
* @param key - The key to associate with the value.
|
|
116
|
+
* @param resolver - The resolver function to provide the value.
|
|
117
|
+
* @returns The container instance.
|
|
118
|
+
*/
|
|
119
|
+
binding<V extends BindingValue>(key: BindingKey, resolver: Resolver<V>): this;
|
|
120
|
+
/**
|
|
121
|
+
* Bind a resolver function into the container under the provided key, returning a new instance each time if not already bound.
|
|
122
|
+
*
|
|
123
|
+
* @param key - The key to associate with the value.
|
|
124
|
+
* @param resolver - The resolver function to provide the value.
|
|
125
|
+
* @returns The container instance.
|
|
126
|
+
*/
|
|
127
|
+
bindingIf<V extends BindingValue>(key: BindingKey, resolver: Resolver<V>): this;
|
|
128
|
+
/**
|
|
129
|
+
* Resolve a registered value from the container by its key.
|
|
130
|
+
*
|
|
131
|
+
* @param key - The key to resolve.
|
|
132
|
+
* @returns The resolved value.
|
|
133
|
+
* @throws ContainerError if the key cannot be resolved.
|
|
134
|
+
*/
|
|
135
|
+
make<V extends BindingValue>(key: BindingKey): V;
|
|
136
|
+
/**
|
|
137
|
+
* Resolve a value from the container by its key, binding it if necessary.
|
|
138
|
+
*
|
|
139
|
+
* @param key - The key to resolve.
|
|
140
|
+
* @param singleton - Whether to bind as a singleton if not already bound.
|
|
141
|
+
* @returns The resolved value.
|
|
142
|
+
*/
|
|
143
|
+
resolve<V extends BindingValue>(key: BindingKey, singleton?: boolean): V;
|
|
144
|
+
/**
|
|
145
|
+
* Resolve a value from the container by its key and return it in a factory function.
|
|
146
|
+
*
|
|
147
|
+
* @param key - The key to resolve.
|
|
148
|
+
* @returns A factory function that returns the resolved value.
|
|
149
|
+
*/
|
|
150
|
+
factory<V extends BindingValue>(key: BindingKey): () => V;
|
|
151
|
+
/**
|
|
152
|
+
* Check if a value is already bound in the container by its key.
|
|
153
|
+
*
|
|
154
|
+
* @param key - The key to check.
|
|
155
|
+
* @returns True if the key is bound, false otherwise.
|
|
156
|
+
*/
|
|
157
|
+
bound(key: BindingKey): boolean;
|
|
158
|
+
/**
|
|
159
|
+
* Check if a value is already bound in the container by its key.
|
|
160
|
+
*
|
|
161
|
+
* @param key - The key to check.
|
|
162
|
+
* @returns True if the key is bound, false otherwise.
|
|
163
|
+
*/
|
|
164
|
+
has(key: BindingKey): boolean;
|
|
165
|
+
/**
|
|
166
|
+
* Reset the container so that all bindings are removed.
|
|
167
|
+
*
|
|
168
|
+
* @returns The container instance.
|
|
169
|
+
*/
|
|
170
|
+
clear(): this;
|
|
171
|
+
/**
|
|
172
|
+
* AutoBind value to the service container.
|
|
173
|
+
*
|
|
174
|
+
* @param name - A key to make the binding. Can be anything.
|
|
175
|
+
* @param item - The item to bind.
|
|
176
|
+
* @param singleton - Bind as singleton when true.
|
|
177
|
+
* @param alias - Key binding aliases.
|
|
178
|
+
* @returns The container instance.
|
|
179
|
+
*/
|
|
180
|
+
autoBinding<V extends BindingValue>(name: BindingKey, item?: V, singleton?: boolean, alias?: string | string[]): this;
|
|
181
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Class representing a Proxiable.
|
|
3
|
+
*
|
|
4
|
+
* This class allows instances to be wrapped in a Proxy, enabling custom behaviors for property access, assignment, etc.
|
|
5
|
+
*
|
|
6
|
+
* @author Mr. Stone <evensstone@gmail.com>
|
|
7
|
+
*/
|
|
8
|
+
export declare abstract class Proxiable {
|
|
9
|
+
/**
|
|
10
|
+
* Creates a Proxiable instance wrapped in a Proxy.
|
|
11
|
+
*
|
|
12
|
+
* @param handler - A trap object for the proxy, which defines custom behavior for fundamental operations (e.g., property lookup, assignment, etc.).
|
|
13
|
+
* @returns A new proxy object for this instance.
|
|
14
|
+
*/
|
|
15
|
+
constructor(handler: ProxyHandler<Proxiable>);
|
|
16
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A resolver function that takes a container and returns a value of type V.
|
|
3
|
+
*
|
|
4
|
+
* @template V - The type of value that the resolver returns.
|
|
5
|
+
* @param container - The container used to resolve dependencies.
|
|
6
|
+
* @returns The resolved value of type V.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const myResolver: Resolver<number> = (container: IContainer) => {
|
|
11
|
+
* // Use the container to resolve dependencies and return a number.
|
|
12
|
+
* return 42;
|
|
13
|
+
* };
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export type Resolver<V> = (container: IContainer) => V;
|
|
17
|
+
/**
|
|
18
|
+
* A union type representing the possible keys that can be used to bind values in the container.
|
|
19
|
+
*
|
|
20
|
+
* Binding keys can be of various types, such as numbers, booleans, strings, functions, objects, or symbols.
|
|
21
|
+
* These types are used because they provide a broad range of ways to uniquely identify a binding.
|
|
22
|
+
*
|
|
23
|
+
* - `number`, `boolean`, `string`: These are basic types that are easy to use and uniquely identify a binding.
|
|
24
|
+
* - `Function`: Useful for identifying bindings by constructor or other functions.
|
|
25
|
+
* - `object`: Allows more complex key types, like instances of classes.
|
|
26
|
+
* - `symbol`: Guarantees a unique identifier, which can prevent conflicts.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const key1: BindingKey = 42; // Using a number as a key
|
|
31
|
+
* const key2: BindingKey = 'serviceName'; // Using a string as a key
|
|
32
|
+
* const key3: BindingKey = Symbol('uniqueKey'); // Using a symbol for uniqueness
|
|
33
|
+
* const key4: BindingKey = MyServiceClass; // Using a function (constructor) as a key
|
|
34
|
+
* const key5: BindingKey = { custom: 'objectKey' }; // Using an object as a key
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export type BindingKey = number | boolean | string | Function | object | symbol;
|
|
38
|
+
/**
|
|
39
|
+
* A union type representing the possible values that can be bound in the container.
|
|
40
|
+
*
|
|
41
|
+
* Binding values can be of various types, including numbers, booleans, strings, functions, objects, or symbols.
|
|
42
|
+
* Unlike `BindingKey`, `BindingValue` represents the actual data or instance being bound, while `BindingKey` represents the identifier used to access that data.
|
|
43
|
+
*/
|
|
44
|
+
export type BindingValue = number | boolean | string | Function | object | symbol;
|
|
45
|
+
/**
|
|
46
|
+
* Interface representing a Binding.
|
|
47
|
+
*
|
|
48
|
+
* This interface defines the contract for all types of bindings in the service container.
|
|
49
|
+
* Bindings are used to manage dependencies and control how objects are instantiated within the container.
|
|
50
|
+
*
|
|
51
|
+
* @template V - The type of value that this binding holds.
|
|
52
|
+
* @author Mr. Stone <evensstone@gmail.com>
|
|
53
|
+
*/
|
|
54
|
+
export interface IBinding<V extends BindingValue> {
|
|
55
|
+
/**
|
|
56
|
+
* Resolve and return the value of the binding.
|
|
57
|
+
*
|
|
58
|
+
* @param container - The container to resolve dependencies from.
|
|
59
|
+
* @returns The resolved value of the binding.
|
|
60
|
+
*/
|
|
61
|
+
resolve: (container: IContainer) => V | undefined;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Interface representing a Container.
|
|
65
|
+
*
|
|
66
|
+
* This interface defines the public contract for dependency injection containers,
|
|
67
|
+
* allowing for better testability and preventing circular dependencies.
|
|
68
|
+
*
|
|
69
|
+
* @author Mr. Stone <evensstone@gmail.com>
|
|
70
|
+
*/
|
|
71
|
+
export interface IContainer {
|
|
72
|
+
/**
|
|
73
|
+
* Retrieve the value of the bindings property.
|
|
74
|
+
*/
|
|
75
|
+
getBindings: () => Map<BindingKey, IBinding<BindingValue>>;
|
|
76
|
+
/**
|
|
77
|
+
* Retrieve the value of the aliases property.
|
|
78
|
+
*/
|
|
79
|
+
getAliases: () => Map<string, BindingKey>;
|
|
80
|
+
/**
|
|
81
|
+
* Set a binding as alias.
|
|
82
|
+
*/
|
|
83
|
+
alias: (key: BindingKey, aliases: string | string[]) => this;
|
|
84
|
+
/**
|
|
85
|
+
* Check if an alias exists in the container.
|
|
86
|
+
*/
|
|
87
|
+
isAlias: (alias: BindingKey) => boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Get a binding key by its alias.
|
|
90
|
+
*/
|
|
91
|
+
getAliasKey: (alias: BindingKey) => BindingKey | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* Bind a single instance or value into the container under the provided key.
|
|
94
|
+
*/
|
|
95
|
+
instance: (key: BindingKey, value: BindingValue) => this;
|
|
96
|
+
/**
|
|
97
|
+
* Bind a single instance or value into the container under the provided key if not already bound.
|
|
98
|
+
*/
|
|
99
|
+
instanceIf: (key: BindingKey, value: BindingValue) => this;
|
|
100
|
+
/**
|
|
101
|
+
* Bind a resolver function into the container under the provided key as a singleton.
|
|
102
|
+
*/
|
|
103
|
+
singleton: <V extends BindingValue>(key: BindingKey, resolver: Resolver<V>) => this;
|
|
104
|
+
/**
|
|
105
|
+
* Bind a resolver function into the container under the provided key as a singleton if not already bound.
|
|
106
|
+
*/
|
|
107
|
+
singletonIf: <V extends BindingValue>(key: BindingKey, resolver: Resolver<V>) => this;
|
|
108
|
+
/**
|
|
109
|
+
* Bind a resolver function into the container under the provided key, returning a new instance each time.
|
|
110
|
+
*/
|
|
111
|
+
binding: <V extends BindingValue>(key: BindingKey, resolver: Resolver<V>) => this;
|
|
112
|
+
/**
|
|
113
|
+
* Bind a resolver function into the container under the provided key, returning a new instance each time if not already bound.
|
|
114
|
+
*/
|
|
115
|
+
bindingIf: <V extends BindingValue>(key: BindingKey, resolver: Resolver<V>) => this;
|
|
116
|
+
/**
|
|
117
|
+
* Resolve a registered value from the container by its key.
|
|
118
|
+
*/
|
|
119
|
+
make: <V extends BindingValue>(key: BindingKey) => V;
|
|
120
|
+
/**
|
|
121
|
+
* Resolve a value from the container by its key, binding it if necessary.
|
|
122
|
+
*/
|
|
123
|
+
resolve: <V extends BindingValue>(key: BindingKey, singleton?: boolean) => V;
|
|
124
|
+
/**
|
|
125
|
+
* Resolve a value from the container by its key and return it in a factory function.
|
|
126
|
+
*/
|
|
127
|
+
factory: <V extends BindingValue>(key: BindingKey) => () => V;
|
|
128
|
+
/**
|
|
129
|
+
* Check if a value is already bound in the container by its key.
|
|
130
|
+
*/
|
|
131
|
+
bound: (key: BindingKey) => boolean;
|
|
132
|
+
/**
|
|
133
|
+
* Check if a value is already bound in the container by its key.
|
|
134
|
+
*/
|
|
135
|
+
has: (key: BindingKey) => boolean;
|
|
136
|
+
/**
|
|
137
|
+
* Reset the container so that all bindings are removed.
|
|
138
|
+
*/
|
|
139
|
+
clear: () => this;
|
|
140
|
+
/**
|
|
141
|
+
* AutoBind value to the service container.
|
|
142
|
+
*/
|
|
143
|
+
autoBinding: <V extends BindingValue>(name: BindingKey, item?: V, singleton?: boolean, alias?: string | string[]) => this;
|
|
144
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { BindingKey } from '../declarations';
|
|
2
|
+
/**
|
|
3
|
+
* Class representing a ContainerError.
|
|
4
|
+
*
|
|
5
|
+
* @author Mr. Stone <evensstone@gmail.com>
|
|
6
|
+
*/
|
|
7
|
+
export declare class ContainerError extends Error {
|
|
8
|
+
/**
|
|
9
|
+
* Error type indicating an alias conflict.
|
|
10
|
+
*/
|
|
11
|
+
static readonly ALIAS_TYPE = "alias";
|
|
12
|
+
/**
|
|
13
|
+
* Error type indicating that the resolver is not a function.
|
|
14
|
+
*/
|
|
15
|
+
static readonly RESOLVER_TYPE = "resolver";
|
|
16
|
+
/**
|
|
17
|
+
* Error type indicating a resolution failure.
|
|
18
|
+
*/
|
|
19
|
+
static readonly RESOLUTION_TYPE = "resolution";
|
|
20
|
+
/**
|
|
21
|
+
* Error type indicating an attempt to alias an unbound value.
|
|
22
|
+
*/
|
|
23
|
+
static readonly ALIAS_UNBOUND_TYPE = "alias_unbound";
|
|
24
|
+
/**
|
|
25
|
+
* Error type indicating that a value is not a service.
|
|
26
|
+
*/
|
|
27
|
+
static readonly NOT_A_SERVICE_TYPE = "not_a_service";
|
|
28
|
+
/**
|
|
29
|
+
* Error type indicating an error thrown by the resolver function.
|
|
30
|
+
*/
|
|
31
|
+
static readonly CANNOT_RESOLVE_TYPE = "cannot_resolve";
|
|
32
|
+
/**
|
|
33
|
+
* Error type indicating a circular dependency.
|
|
34
|
+
*/
|
|
35
|
+
static readonly CIRCULAR_DEPENDENCY_TYPE = "circular_dependency";
|
|
36
|
+
/**
|
|
37
|
+
* The type of the error.
|
|
38
|
+
*/
|
|
39
|
+
private readonly type;
|
|
40
|
+
/**
|
|
41
|
+
* Create a ContainerError.
|
|
42
|
+
*
|
|
43
|
+
* @param type - The type of the error.
|
|
44
|
+
* @param message - The error message or key related to the error.
|
|
45
|
+
*/
|
|
46
|
+
constructor(type: string, message: BindingKey);
|
|
47
|
+
/**
|
|
48
|
+
* Retrieve the error message based on the type and provided message.
|
|
49
|
+
*
|
|
50
|
+
* @param type - The type of the error.
|
|
51
|
+
* @param message - The error message or key related to the error.
|
|
52
|
+
* @returns The formatted error message.
|
|
53
|
+
*/
|
|
54
|
+
private getMessage;
|
|
55
|
+
/**
|
|
56
|
+
* Retrieve the resolution message based on the key.
|
|
57
|
+
*
|
|
58
|
+
* @param key - The key for which the resolution failed.
|
|
59
|
+
* @returns The formatted resolution error message.
|
|
60
|
+
*/
|
|
61
|
+
private getResolutionMessage;
|
|
62
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,528 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* Creates a Proxiable instance wrapped in a Proxy.
|
|
11
|
-
*
|
|
12
|
-
* @param handler - A trap object for the proxy, which defines custom behavior for fundamental operations (e.g., property lookup, assignment, etc.).
|
|
13
|
-
* @returns A new proxy object for this instance.
|
|
14
|
-
*/
|
|
15
|
-
constructor(handler: ProxyHandler<Proxiable>);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* A resolver function that takes a container and returns a value of type V.
|
|
20
|
-
*
|
|
21
|
-
* @template V - The type of value that the resolver returns.
|
|
22
|
-
* @param container - The container used to resolve dependencies.
|
|
23
|
-
* @returns The resolved value of type V.
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```typescript
|
|
27
|
-
* const myResolver: Resolver<number> = (container: IContainer) => {
|
|
28
|
-
* // Use the container to resolve dependencies and return a number.
|
|
29
|
-
* return 42;
|
|
30
|
-
* };
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
type Resolver<V> = (container: IContainer) => V;
|
|
34
|
-
/**
|
|
35
|
-
* A union type representing the possible keys that can be used to bind values in the container.
|
|
36
|
-
*
|
|
37
|
-
* Binding keys can be of various types, such as numbers, booleans, strings, functions, objects, or symbols.
|
|
38
|
-
* These types are used because they provide a broad range of ways to uniquely identify a binding.
|
|
39
|
-
*
|
|
40
|
-
* - `number`, `boolean`, `string`: These are basic types that are easy to use and uniquely identify a binding.
|
|
41
|
-
* - `Function`: Useful for identifying bindings by constructor or other functions.
|
|
42
|
-
* - `object`: Allows more complex key types, like instances of classes.
|
|
43
|
-
* - `symbol`: Guarantees a unique identifier, which can prevent conflicts.
|
|
44
|
-
*
|
|
45
|
-
* @example
|
|
46
|
-
* ```typescript
|
|
47
|
-
* const key1: BindingKey = 42; // Using a number as a key
|
|
48
|
-
* const key2: BindingKey = 'serviceName'; // Using a string as a key
|
|
49
|
-
* const key3: BindingKey = Symbol('uniqueKey'); // Using a symbol for uniqueness
|
|
50
|
-
* const key4: BindingKey = MyServiceClass; // Using a function (constructor) as a key
|
|
51
|
-
* const key5: BindingKey = { custom: 'objectKey' }; // Using an object as a key
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
|
-
type BindingKey = number | boolean | string | Function | object | symbol;
|
|
55
|
-
/**
|
|
56
|
-
* A union type representing the possible values that can be bound in the container.
|
|
57
|
-
*
|
|
58
|
-
* Binding values can be of various types, including numbers, booleans, strings, functions, objects, or symbols.
|
|
59
|
-
* Unlike `BindingKey`, `BindingValue` represents the actual data or instance being bound, while `BindingKey` represents the identifier used to access that data.
|
|
60
|
-
*/
|
|
61
|
-
type BindingValue = number | boolean | string | Function | object | symbol;
|
|
62
|
-
/**
|
|
63
|
-
* Interface representing a Binding.
|
|
64
|
-
*
|
|
65
|
-
* This interface defines the contract for all types of bindings in the service container.
|
|
66
|
-
* Bindings are used to manage dependencies and control how objects are instantiated within the container.
|
|
67
|
-
*
|
|
68
|
-
* @template V - The type of value that this binding holds.
|
|
69
|
-
* @author Mr. Stone <evensstone@gmail.com>
|
|
70
|
-
*/
|
|
71
|
-
interface IBinding<V extends BindingValue> {
|
|
72
|
-
/**
|
|
73
|
-
* Resolve and return the value of the binding.
|
|
74
|
-
*
|
|
75
|
-
* @param container - The container to resolve dependencies from.
|
|
76
|
-
* @returns The resolved value of the binding.
|
|
77
|
-
*/
|
|
78
|
-
resolve: (container: IContainer) => V | undefined;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Interface representing a Container.
|
|
82
|
-
*
|
|
83
|
-
* This interface defines the public contract for dependency injection containers,
|
|
84
|
-
* allowing for better testability and preventing circular dependencies.
|
|
85
|
-
*
|
|
86
|
-
* @author Mr. Stone <evensstone@gmail.com>
|
|
87
|
-
*/
|
|
88
|
-
interface IContainer {
|
|
89
|
-
/**
|
|
90
|
-
* Retrieve the value of the bindings property.
|
|
91
|
-
*/
|
|
92
|
-
getBindings: () => Map<BindingKey, IBinding<BindingValue>>;
|
|
93
|
-
/**
|
|
94
|
-
* Retrieve the value of the aliases property.
|
|
95
|
-
*/
|
|
96
|
-
getAliases: () => Map<string, BindingKey>;
|
|
97
|
-
/**
|
|
98
|
-
* Set a binding as alias.
|
|
99
|
-
*/
|
|
100
|
-
alias: (key: BindingKey, aliases: string | string[]) => this;
|
|
101
|
-
/**
|
|
102
|
-
* Check if an alias exists in the container.
|
|
103
|
-
*/
|
|
104
|
-
isAlias: (alias: BindingKey) => boolean;
|
|
105
|
-
/**
|
|
106
|
-
* Get a binding key by its alias.
|
|
107
|
-
*/
|
|
108
|
-
getAliasKey: (alias: BindingKey) => BindingKey | undefined;
|
|
109
|
-
/**
|
|
110
|
-
* Bind a single instance or value into the container under the provided key.
|
|
111
|
-
*/
|
|
112
|
-
instance: (key: BindingKey, value: BindingValue) => this;
|
|
113
|
-
/**
|
|
114
|
-
* Bind a single instance or value into the container under the provided key if not already bound.
|
|
115
|
-
*/
|
|
116
|
-
instanceIf: (key: BindingKey, value: BindingValue) => this;
|
|
117
|
-
/**
|
|
118
|
-
* Bind a resolver function into the container under the provided key as a singleton.
|
|
119
|
-
*/
|
|
120
|
-
singleton: <V extends BindingValue>(key: BindingKey, resolver: Resolver<V>) => this;
|
|
121
|
-
/**
|
|
122
|
-
* Bind a resolver function into the container under the provided key as a singleton if not already bound.
|
|
123
|
-
*/
|
|
124
|
-
singletonIf: <V extends BindingValue>(key: BindingKey, resolver: Resolver<V>) => this;
|
|
125
|
-
/**
|
|
126
|
-
* Bind a resolver function into the container under the provided key, returning a new instance each time.
|
|
127
|
-
*/
|
|
128
|
-
binding: <V extends BindingValue>(key: BindingKey, resolver: Resolver<V>) => this;
|
|
129
|
-
/**
|
|
130
|
-
* Bind a resolver function into the container under the provided key, returning a new instance each time if not already bound.
|
|
131
|
-
*/
|
|
132
|
-
bindingIf: <V extends BindingValue>(key: BindingKey, resolver: Resolver<V>) => this;
|
|
133
|
-
/**
|
|
134
|
-
* Resolve a registered value from the container by its key.
|
|
135
|
-
*/
|
|
136
|
-
make: <V extends BindingValue>(key: BindingKey) => V;
|
|
137
|
-
/**
|
|
138
|
-
* Resolve a value from the container by its key, binding it if necessary.
|
|
139
|
-
*/
|
|
140
|
-
resolve: <V extends BindingValue>(key: BindingKey, singleton?: boolean) => V;
|
|
141
|
-
/**
|
|
142
|
-
* Resolve a value from the container by its key and return it in a factory function.
|
|
143
|
-
*/
|
|
144
|
-
factory: <V extends BindingValue>(key: BindingKey) => () => V;
|
|
145
|
-
/**
|
|
146
|
-
* Check if a value is already bound in the container by its key.
|
|
147
|
-
*/
|
|
148
|
-
bound: (key: BindingKey) => boolean;
|
|
149
|
-
/**
|
|
150
|
-
* Check if a value is already bound in the container by its key.
|
|
151
|
-
*/
|
|
152
|
-
has: (key: BindingKey) => boolean;
|
|
153
|
-
/**
|
|
154
|
-
* Reset the container so that all bindings are removed.
|
|
155
|
-
*/
|
|
156
|
-
clear: () => this;
|
|
157
|
-
/**
|
|
158
|
-
* AutoBind value to the service container.
|
|
159
|
-
*/
|
|
160
|
-
autoBinding: <V extends BindingValue>(name: BindingKey, item?: V, singleton?: boolean, alias?: string | string[]) => this;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Abstract class representing a Binding.
|
|
165
|
-
*
|
|
166
|
-
* This abstract class serves as the base class for all types of bindings in the service container. It holds a value and provides an abstract method
|
|
167
|
-
* to resolve and return that value, allowing different subclasses to implement their own resolution logic. Bindings are used to manage dependencies
|
|
168
|
-
* and control how objects are instantiated within the container.
|
|
169
|
-
*
|
|
170
|
-
* @template V - The type of value that this binding holds.
|
|
171
|
-
* @author Mr. Stone <evensstone@gmail.com>
|
|
172
|
-
*/
|
|
173
|
-
declare abstract class Binding<V extends BindingValue> implements IBinding<V> {
|
|
174
|
-
/**
|
|
175
|
-
* The value held by the binding.
|
|
176
|
-
*
|
|
177
|
-
* This value is resolved at runtime, either directly or through a resolver function.
|
|
178
|
-
*/
|
|
179
|
-
protected value?: V;
|
|
180
|
-
/**
|
|
181
|
-
* Create a new instance of Binding.
|
|
182
|
-
*
|
|
183
|
-
* @param value - The value to be held by the binding.
|
|
184
|
-
*/
|
|
185
|
-
constructor(value?: V);
|
|
186
|
-
/**
|
|
187
|
-
* Check if the value has been resolved.
|
|
188
|
-
*
|
|
189
|
-
* @returns A boolean indicating whether the value has been resolved.
|
|
190
|
-
*/
|
|
191
|
-
protected isResolved(): boolean;
|
|
192
|
-
/**
|
|
193
|
-
* Resolve and return the value of the binding.
|
|
194
|
-
*
|
|
195
|
-
* This abstract method must be implemented by subclasses to provide specific resolution logic.
|
|
196
|
-
*
|
|
197
|
-
* @param container - The container to resolve dependencies from.
|
|
198
|
-
* @returns The resolved value of the binding.
|
|
199
|
-
*/
|
|
200
|
-
abstract resolve(container: IContainer): V | undefined;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Class representing a Container.
|
|
205
|
-
*
|
|
206
|
-
* The Container class acts as a dependency injection container, managing bindings and resolving instances.
|
|
207
|
-
* It supports different types of bindings, such as singletons, factories, and instances, and allows the use of aliases for bindings.
|
|
208
|
-
* This makes it easier to manage and resolve complex dependency trees in an application.
|
|
209
|
-
*
|
|
210
|
-
* @author Mr. Stone <evensstone@gmail.com>
|
|
211
|
-
*/
|
|
212
|
-
declare class Container extends Proxiable implements IContainer {
|
|
213
|
-
private readonly aliases;
|
|
214
|
-
private readonly resolvingKeys;
|
|
215
|
-
private readonly bindings;
|
|
216
|
-
/**
|
|
217
|
-
* Create a Container.
|
|
218
|
-
*
|
|
219
|
-
* @returns A new Container instance.
|
|
220
|
-
*/
|
|
221
|
-
static create(): Container;
|
|
222
|
-
/**
|
|
223
|
-
* Create a ProxyHandler for the container.
|
|
224
|
-
*
|
|
225
|
-
* @returns A new ProxyHandler instance.
|
|
226
|
-
*/
|
|
227
|
-
private static Proxyhandler;
|
|
228
|
-
/**
|
|
229
|
-
* Create a container.
|
|
230
|
-
*
|
|
231
|
-
* Initializes the container with empty alias and binding maps.
|
|
232
|
-
*/
|
|
233
|
-
protected constructor();
|
|
234
|
-
/**
|
|
235
|
-
* Retrieve the value of the bindings property.
|
|
236
|
-
*
|
|
237
|
-
* @returns A map of all bindings registered in the container.
|
|
238
|
-
*/
|
|
239
|
-
getBindings(): Map<BindingKey, Binding<BindingValue>>;
|
|
240
|
-
/**
|
|
241
|
-
* Retrieve the value of the aliases property.
|
|
242
|
-
*
|
|
243
|
-
* @returns A map of all aliases registered in the container.
|
|
244
|
-
*/
|
|
245
|
-
getAliases(): Map<string, BindingKey>;
|
|
246
|
-
/**
|
|
247
|
-
* Set a binding as alias.
|
|
248
|
-
*
|
|
249
|
-
* Adds one or more aliases for a given binding key.
|
|
250
|
-
*
|
|
251
|
-
* @param key - The binding value.
|
|
252
|
-
* @param aliases - One or more strings representing the aliases.
|
|
253
|
-
* @returns The container instance.
|
|
254
|
-
*/
|
|
255
|
-
alias(key: BindingKey, aliases: string | string[]): this;
|
|
256
|
-
/**
|
|
257
|
-
* Check if an alias exists in the container.
|
|
258
|
-
*
|
|
259
|
-
* @param alias - The alias to check.
|
|
260
|
-
* @returns True if the alias exists, false otherwise.
|
|
261
|
-
*/
|
|
262
|
-
isAlias(alias: BindingKey): boolean;
|
|
263
|
-
/**
|
|
264
|
-
* Get a binding key by its alias.
|
|
265
|
-
*
|
|
266
|
-
* @param alias - The alias name.
|
|
267
|
-
* @returns The binding key associated with the alias, or undefined if not found.
|
|
268
|
-
*/
|
|
269
|
-
getAliasKey(alias: BindingKey): BindingKey | undefined;
|
|
270
|
-
/**
|
|
271
|
-
* Bind a single instance or value into the container under the provided key.
|
|
272
|
-
*
|
|
273
|
-
* @param key - The key to associate with the value.
|
|
274
|
-
* @param value - The value to be bound.
|
|
275
|
-
* @returns The container instance.
|
|
276
|
-
*/
|
|
277
|
-
instance(key: BindingKey, value: BindingValue): this;
|
|
278
|
-
/**
|
|
279
|
-
* Bind a single instance or value into the container under the provided key if not already bound.
|
|
280
|
-
*
|
|
281
|
-
* @param key - The key to associate with the value.
|
|
282
|
-
* @param value - The value to be bound.
|
|
283
|
-
* @returns The container instance.
|
|
284
|
-
*/
|
|
285
|
-
instanceIf(key: BindingKey, value: BindingValue): this;
|
|
286
|
-
/**
|
|
287
|
-
* Bind a resolver function into the container under the provided key as a singleton.
|
|
288
|
-
*
|
|
289
|
-
* The resolver function will be called once, and the resulting value will be cached for future use.
|
|
290
|
-
*
|
|
291
|
-
* @param key - The key to associate with the singleton value.
|
|
292
|
-
* @param resolver - The resolver function to provide the value.
|
|
293
|
-
* @returns The container instance.
|
|
294
|
-
*/
|
|
295
|
-
singleton<V extends BindingValue>(key: BindingKey, resolver: Resolver<V>): this;
|
|
296
|
-
/**
|
|
297
|
-
* Bind a resolver function into the container under the provided key as a singleton if not already bound.
|
|
298
|
-
*
|
|
299
|
-
* @param key - The key to associate with the singleton value.
|
|
300
|
-
* @param resolver - The resolver function to provide the value.
|
|
301
|
-
* @returns The container instance.
|
|
302
|
-
*/
|
|
303
|
-
singletonIf<V extends BindingValue>(key: BindingKey, resolver: Resolver<V>): this;
|
|
304
|
-
/**
|
|
305
|
-
* Bind a resolver function into the container under the provided key, returning a new instance each time.
|
|
306
|
-
*
|
|
307
|
-
* @param key - The key to associate with the value.
|
|
308
|
-
* @param resolver - The resolver function to provide the value.
|
|
309
|
-
* @returns The container instance.
|
|
310
|
-
*/
|
|
311
|
-
binding<V extends BindingValue>(key: BindingKey, resolver: Resolver<V>): this;
|
|
312
|
-
/**
|
|
313
|
-
* Bind a resolver function into the container under the provided key, returning a new instance each time if not already bound.
|
|
314
|
-
*
|
|
315
|
-
* @param key - The key to associate with the value.
|
|
316
|
-
* @param resolver - The resolver function to provide the value.
|
|
317
|
-
* @returns The container instance.
|
|
318
|
-
*/
|
|
319
|
-
bindingIf<V extends BindingValue>(key: BindingKey, resolver: Resolver<V>): this;
|
|
320
|
-
/**
|
|
321
|
-
* Resolve a registered value from the container by its key.
|
|
322
|
-
*
|
|
323
|
-
* @param key - The key to resolve.
|
|
324
|
-
* @returns The resolved value.
|
|
325
|
-
* @throws ContainerError if the key cannot be resolved.
|
|
326
|
-
*/
|
|
327
|
-
make<V extends BindingValue>(key: BindingKey): V;
|
|
328
|
-
/**
|
|
329
|
-
* Resolve a value from the container by its key, binding it if necessary.
|
|
330
|
-
*
|
|
331
|
-
* @param key - The key to resolve.
|
|
332
|
-
* @param singleton - Whether to bind as a singleton if not already bound.
|
|
333
|
-
* @returns The resolved value.
|
|
334
|
-
*/
|
|
335
|
-
resolve<V extends BindingValue>(key: BindingKey, singleton?: boolean): V;
|
|
336
|
-
/**
|
|
337
|
-
* Resolve a value from the container by its key and return it in a factory function.
|
|
338
|
-
*
|
|
339
|
-
* @param key - The key to resolve.
|
|
340
|
-
* @returns A factory function that returns the resolved value.
|
|
341
|
-
*/
|
|
342
|
-
factory<V extends BindingValue>(key: BindingKey): () => V;
|
|
343
|
-
/**
|
|
344
|
-
* Check if a value is already bound in the container by its key.
|
|
345
|
-
*
|
|
346
|
-
* @param key - The key to check.
|
|
347
|
-
* @returns True if the key is bound, false otherwise.
|
|
348
|
-
*/
|
|
349
|
-
bound(key: BindingKey): boolean;
|
|
350
|
-
/**
|
|
351
|
-
* Check if a value is already bound in the container by its key.
|
|
352
|
-
*
|
|
353
|
-
* @param key - The key to check.
|
|
354
|
-
* @returns True if the key is bound, false otherwise.
|
|
355
|
-
*/
|
|
356
|
-
has(key: BindingKey): boolean;
|
|
357
|
-
/**
|
|
358
|
-
* Reset the container so that all bindings are removed.
|
|
359
|
-
*
|
|
360
|
-
* @returns The container instance.
|
|
361
|
-
*/
|
|
362
|
-
clear(): this;
|
|
363
|
-
/**
|
|
364
|
-
* AutoBind value to the service container.
|
|
365
|
-
*
|
|
366
|
-
* @param name - A key to make the binding. Can be anything.
|
|
367
|
-
* @param item - The item to bind.
|
|
368
|
-
* @param singleton - Bind as singleton when true.
|
|
369
|
-
* @param alias - Key binding aliases.
|
|
370
|
-
* @returns The container instance.
|
|
371
|
-
*/
|
|
372
|
-
autoBinding<V extends BindingValue>(name: BindingKey, item?: V, singleton?: boolean, alias?: string | string[]): this;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Class representing a ContainerError.
|
|
377
|
-
*
|
|
378
|
-
* @author Mr. Stone <evensstone@gmail.com>
|
|
379
|
-
*/
|
|
380
|
-
declare class ContainerError extends Error {
|
|
381
|
-
/**
|
|
382
|
-
* Error type indicating an alias conflict.
|
|
383
|
-
*/
|
|
384
|
-
static readonly ALIAS_TYPE = "alias";
|
|
385
|
-
/**
|
|
386
|
-
* Error type indicating that the resolver is not a function.
|
|
387
|
-
*/
|
|
388
|
-
static readonly RESOLVER_TYPE = "resolver";
|
|
389
|
-
/**
|
|
390
|
-
* Error type indicating a resolution failure.
|
|
391
|
-
*/
|
|
392
|
-
static readonly RESOLUTION_TYPE = "resolution";
|
|
393
|
-
/**
|
|
394
|
-
* Error type indicating an attempt to alias an unbound value.
|
|
395
|
-
*/
|
|
396
|
-
static readonly ALIAS_UNBOUND_TYPE = "alias_unbound";
|
|
397
|
-
/**
|
|
398
|
-
* Error type indicating that a value is not a service.
|
|
399
|
-
*/
|
|
400
|
-
static readonly NOT_A_SERVICE_TYPE = "not_a_service";
|
|
401
|
-
/**
|
|
402
|
-
* Error type indicating an error thrown by the resolver function.
|
|
403
|
-
*/
|
|
404
|
-
static readonly CANNOT_RESOLVE_TYPE = "cannot_resolve";
|
|
405
|
-
/**
|
|
406
|
-
* Error type indicating a circular dependency.
|
|
407
|
-
*/
|
|
408
|
-
static readonly CIRCULAR_DEPENDENCY_TYPE = "circular_dependency";
|
|
409
|
-
/**
|
|
410
|
-
* The type of the error.
|
|
411
|
-
*/
|
|
412
|
-
private readonly type;
|
|
413
|
-
/**
|
|
414
|
-
* Create a ContainerError.
|
|
415
|
-
*
|
|
416
|
-
* @param type - The type of the error.
|
|
417
|
-
* @param message - The error message or key related to the error.
|
|
418
|
-
*/
|
|
419
|
-
constructor(type: string, message: BindingKey);
|
|
420
|
-
/**
|
|
421
|
-
* Retrieve the error message based on the type and provided message.
|
|
422
|
-
*
|
|
423
|
-
* @param type - The type of the error.
|
|
424
|
-
* @param message - The error message or key related to the error.
|
|
425
|
-
* @returns The formatted error message.
|
|
426
|
-
*/
|
|
427
|
-
private getMessage;
|
|
428
|
-
/**
|
|
429
|
-
* Retrieve the resolution message based on the key.
|
|
430
|
-
*
|
|
431
|
-
* @param key - The key for which the resolution failed.
|
|
432
|
-
* @returns The formatted resolution error message.
|
|
433
|
-
*/
|
|
434
|
-
private getResolutionMessage;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* Class representing a ResolverBinding.
|
|
439
|
-
*
|
|
440
|
-
* This class extends the Binding class, using a resolver function to lazily resolve the value when needed.
|
|
441
|
-
*
|
|
442
|
-
* @template V - The type of value that this binding holds.
|
|
443
|
-
* @author Mr. Stone <evensstone@gmail.com>
|
|
444
|
-
*/
|
|
445
|
-
declare abstract class ResolverBinding<V extends BindingValue> extends Binding<V> {
|
|
446
|
-
/**
|
|
447
|
-
* The resolver function used to provide the binding value.
|
|
448
|
-
*
|
|
449
|
-
* This function will be called when the value is needed, allowing for lazy instantiation
|
|
450
|
-
* and dependency resolution. It should return an instance of type `V`.
|
|
451
|
-
*/
|
|
452
|
-
protected readonly resolver: Resolver<V>;
|
|
453
|
-
/**
|
|
454
|
-
* Create a new instance of ResolverBinding.
|
|
455
|
-
*
|
|
456
|
-
* @param resolver - The resolver function to provide the binding value.
|
|
457
|
-
* @throws ContainerError if the resolver is not a function.
|
|
458
|
-
*/
|
|
459
|
-
constructor(resolver: Resolver<V>);
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* Class representing a Factory.
|
|
464
|
-
*
|
|
465
|
-
* The Factory class extends the ResolverBinding class, providing a mechanism to resolve a new instance each time the binding is resolved.
|
|
466
|
-
* This ensures that a fresh instance is created with each call to the `resolve` method.
|
|
467
|
-
*
|
|
468
|
-
* @template V - The type of value that this binding holds.
|
|
469
|
-
* @author Mr. Stone <evensstone@gmail.com>
|
|
470
|
-
*/
|
|
471
|
-
declare class Factory<V extends BindingValue> extends ResolverBinding<V> {
|
|
472
|
-
/**
|
|
473
|
-
* Resolve and return the value of the binding.
|
|
474
|
-
*
|
|
475
|
-
* Each time this method is called, a new value is resolved using the resolver function.
|
|
476
|
-
* This is intended for cases where a fresh instance is required for each resolution, such as factories or transient dependencies.
|
|
477
|
-
*
|
|
478
|
-
* @param container - The container to resolve dependencies from.
|
|
479
|
-
* @returns The resolved value of the binding.
|
|
480
|
-
* @throws ContainerError if the value cannot be resolved.
|
|
481
|
-
*/
|
|
482
|
-
resolve(container: IContainer): V;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* Class representing an Instance.
|
|
487
|
-
*
|
|
488
|
-
* This class extends the Binding class and directly holds an instance value.
|
|
489
|
-
* It provides a straightforward resolution mechanism that simply returns the stored value.
|
|
490
|
-
*
|
|
491
|
-
* @template V - The type of value that this binding holds.
|
|
492
|
-
* @author Mr. Stone <evensstone@gmail.com>
|
|
493
|
-
*/
|
|
494
|
-
declare class Instance<V extends BindingValue> extends Binding<V> {
|
|
495
|
-
/**
|
|
496
|
-
* Resolve and return the value of the binding.
|
|
497
|
-
*
|
|
498
|
-
* @param _container - Container to resolve dependencies (not used in this implementation).
|
|
499
|
-
* @returns The resolved value of the binding.
|
|
500
|
-
*/
|
|
501
|
-
resolve(_container: IContainer): V | undefined;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
/**
|
|
505
|
-
* Class representing a Singleton.
|
|
506
|
-
*
|
|
507
|
-
* The Singleton class extends the ResolverBinding class, ensuring that the value is only resolved once.
|
|
508
|
-
* Subsequent calls to the `resolve` method will return the previously resolved value, making it behave as a singleton.
|
|
509
|
-
*
|
|
510
|
-
* @template V - The type of value that this binding holds.
|
|
511
|
-
* @author Mr. Stone <evensstone@gmail.com>
|
|
512
|
-
*/
|
|
513
|
-
declare class Singleton<V extends BindingValue> extends ResolverBinding<V> {
|
|
514
|
-
/**
|
|
515
|
-
* Resolve and return the value of the binding.
|
|
516
|
-
*
|
|
517
|
-
* If the value has already been resolved, return the cached value. Otherwise, use the resolver function
|
|
518
|
-
* to resolve the value, store it, and return it.
|
|
519
|
-
*
|
|
520
|
-
* @param container - The container to resolve dependencies from.
|
|
521
|
-
* @returns The resolved value of the binding.
|
|
522
|
-
* @throws ContainerError if the value cannot be resolved.
|
|
523
|
-
*/
|
|
524
|
-
resolve(container: IContainer): V | undefined;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
export { Binding, Container, ContainerError, Factory, Instance, Proxiable, ResolverBinding, Singleton };
|
|
528
|
-
export type { BindingKey, BindingValue, IBinding, IContainer, Resolver };
|
|
1
|
+
export * from './Container';
|
|
2
|
+
export * from './Proxiable';
|
|
3
|
+
export * from './declarations';
|
|
4
|
+
export * from './errors/ContainerError';
|
|
5
|
+
export * from './models/Binding';
|
|
6
|
+
export * from './models/Factory';
|
|
7
|
+
export * from './models/Instance';
|
|
8
|
+
export * from './models/ResolverBinding';
|
|
9
|
+
export * from './models/Singleton';
|
package/dist/index.js
CHANGED
|
@@ -35,6 +35,14 @@ class Binding {
|
|
|
35
35
|
* This value is resolved at runtime, either directly or through a resolver function.
|
|
36
36
|
*/
|
|
37
37
|
value;
|
|
38
|
+
/**
|
|
39
|
+
* Whether the value has been resolved at least once.
|
|
40
|
+
*
|
|
41
|
+
* Tracked explicitly (not inferred from `value !== undefined`) so a binding whose resolved
|
|
42
|
+
* value is legitimately `undefined` is still considered resolved — preserving the singleton
|
|
43
|
+
* guarantee and avoiding repeated resolver side effects.
|
|
44
|
+
*/
|
|
45
|
+
resolved;
|
|
38
46
|
/**
|
|
39
47
|
* Create a new instance of Binding.
|
|
40
48
|
*
|
|
@@ -42,6 +50,7 @@ class Binding {
|
|
|
42
50
|
*/
|
|
43
51
|
constructor(value) {
|
|
44
52
|
this.value = value;
|
|
53
|
+
this.resolved = value !== undefined;
|
|
45
54
|
}
|
|
46
55
|
/**
|
|
47
56
|
* Check if the value has been resolved.
|
|
@@ -49,7 +58,7 @@ class Binding {
|
|
|
49
58
|
* @returns A boolean indicating whether the value has been resolved.
|
|
50
59
|
*/
|
|
51
60
|
isResolved() {
|
|
52
|
-
return this.
|
|
61
|
+
return this.resolved;
|
|
53
62
|
}
|
|
54
63
|
}
|
|
55
64
|
|
|
@@ -116,7 +125,7 @@ class ContainerError extends Error {
|
|
|
116
125
|
[ContainerError.ALIAS_TYPE]: `${String(message)} is aliased to itself`,
|
|
117
126
|
[ContainerError.CANNOT_RESOLVE_TYPE]: `Failed to resolve binding: ${String(message)}`,
|
|
118
127
|
[ContainerError.ALIAS_UNBOUND_TYPE]: `Cannot alias an unbound value : ${String(message)}`,
|
|
119
|
-
[ContainerError.CIRCULAR_DEPENDENCY_TYPE]: `Circular dependency detected
|
|
128
|
+
[ContainerError.CIRCULAR_DEPENDENCY_TYPE]: `Circular dependency detected: ${String(message)}`,
|
|
120
129
|
[ContainerError.RESOLVER_TYPE]: `Invalid resolver: Expected a function but received ${typeof message}`,
|
|
121
130
|
[ContainerError.NOT_A_SERVICE_TYPE]: `This (${String(message)}) is not a service. Must contain $$metadata$$ static property or must use @Service decorator.`
|
|
122
131
|
};
|
|
@@ -262,6 +271,7 @@ class Singleton extends ResolverBinding {
|
|
|
262
271
|
if (!this.isResolved()) {
|
|
263
272
|
try {
|
|
264
273
|
this.value = this.resolver(container);
|
|
274
|
+
this.resolved = true;
|
|
265
275
|
}
|
|
266
276
|
catch (error) {
|
|
267
277
|
throw new ContainerError(ContainerError.CANNOT_RESOLVE_TYPE, error.message);
|
|
@@ -297,15 +307,30 @@ class Container extends Proxiable {
|
|
|
297
307
|
*
|
|
298
308
|
* @returns A new ProxyHandler instance.
|
|
299
309
|
*/
|
|
310
|
+
/**
|
|
311
|
+
* Well-known property names that must never trigger service resolution. Accessing them
|
|
312
|
+
* (via `await container`, `JSON.stringify`, `console.log`, React element checks, spread,
|
|
313
|
+
* etc.) returns `undefined` so the container is safe to inspect and pass around, while
|
|
314
|
+
* genuine unknown-service access still fails fast through `make()`.
|
|
315
|
+
*/
|
|
316
|
+
static SYSTEM_PROPS = new Set([
|
|
317
|
+
'then', 'catch', 'finally',
|
|
318
|
+
'toJSON', 'toString', 'valueOf', 'inspect',
|
|
319
|
+
'constructor', 'prototype', '$$typeof', 'nodeType'
|
|
320
|
+
]);
|
|
300
321
|
static Proxyhandler() {
|
|
301
322
|
return {
|
|
302
323
|
get: (target, prop, receiver) => {
|
|
303
324
|
if (Reflect.has(target, prop)) {
|
|
304
325
|
return Reflect.get(target, prop, receiver);
|
|
305
326
|
}
|
|
306
|
-
|
|
307
|
-
|
|
327
|
+
// Symbols (Symbol.toPrimitive, Symbol.iterator, inspect.custom…) and well-known
|
|
328
|
+
// system props are inspection/coercion hooks, never services: return undefined.
|
|
329
|
+
if (typeof prop === 'symbol' || Container.SYSTEM_PROPS.has(prop)) {
|
|
330
|
+
return undefined;
|
|
308
331
|
}
|
|
332
|
+
// Otherwise resolve as a bound service (make throws ContainerError if unbound).
|
|
333
|
+
return target.make(prop);
|
|
309
334
|
}
|
|
310
335
|
};
|
|
311
336
|
}
|
|
@@ -458,7 +483,10 @@ class Container extends Proxiable {
|
|
|
458
483
|
make(key) {
|
|
459
484
|
key = this.getAliasKey(key) ?? key;
|
|
460
485
|
if (this.resolvingKeys.has(key)) {
|
|
461
|
-
|
|
486
|
+
// Surface the full resolution chain (A → B → C → A) so the cycle is diagnosable
|
|
487
|
+
// at a glance, not just the offending key.
|
|
488
|
+
const chain = [...this.resolvingKeys, key].map(containerKeyName).join(' → ');
|
|
489
|
+
throw new ContainerError(ContainerError.CIRCULAR_DEPENDENCY_TYPE, chain);
|
|
462
490
|
}
|
|
463
491
|
this.resolvingKeys.add(key);
|
|
464
492
|
try {
|
|
@@ -539,18 +567,67 @@ class Container extends Proxiable {
|
|
|
539
567
|
if (!this.bound(key)) {
|
|
540
568
|
if (typeof value === 'function') {
|
|
541
569
|
const callable = value;
|
|
542
|
-
|
|
543
|
-
|
|
570
|
+
// Only real ES classes are instantiated with `new`; ordinary/arrow factory
|
|
571
|
+
// functions are called. `hasOwnProperty('prototype')` was too loose (every
|
|
572
|
+
// non-arrow function has a prototype), breaking `function` factories.
|
|
573
|
+
const resolver = isClassConstructor(callable)
|
|
574
|
+
? (container) => new callable(container)
|
|
544
575
|
: (container) => callable(container);
|
|
545
576
|
singleton ? this.singleton(key, resolver) : this.binding(key, resolver);
|
|
546
577
|
}
|
|
547
578
|
else {
|
|
548
579
|
this.instance(key, value);
|
|
549
580
|
}
|
|
550
|
-
this.alias(key, alias);
|
|
551
581
|
}
|
|
582
|
+
// Apply aliases even when the key is already bound (aliasing was previously dropped).
|
|
583
|
+
this.alias(key, alias);
|
|
552
584
|
return this;
|
|
553
585
|
}
|
|
554
586
|
}
|
|
587
|
+
/**
|
|
588
|
+
* Detect a class constructor (as opposed to an ordinary or factory function).
|
|
589
|
+
*
|
|
590
|
+
* Uses two complementary signals so it survives down-level (ES5) bundling:
|
|
591
|
+
* 1. Native/modern classes stringify with the `class` keyword.
|
|
592
|
+
* 2. Transpiled classes lose the keyword but keep their methods on the prototype, whereas a
|
|
593
|
+
* plain/factory function's prototype has only `constructor`.
|
|
594
|
+
*
|
|
595
|
+
* For ambiguous cases, callers should pass an explicit `isClass`/`isFactory` flag.
|
|
596
|
+
*
|
|
597
|
+
* @param value - The value to test.
|
|
598
|
+
* @returns True if the value is (very likely) a class constructor.
|
|
599
|
+
*/
|
|
600
|
+
function isClassConstructor(value) {
|
|
601
|
+
// Callers already narrow to `typeof value === 'function'`, so no redundant guard here.
|
|
602
|
+
// A function's source starts with `class` only for real ES classes.
|
|
603
|
+
if (Function.prototype.toString.call(value).startsWith('class')) {
|
|
604
|
+
return true;
|
|
605
|
+
}
|
|
606
|
+
// Otherwise fall back to the ES5-safe heuristic: a transpiled class carries own methods on
|
|
607
|
+
// its prototype (a bare/arrow factory does not).
|
|
608
|
+
const proto = value.prototype;
|
|
609
|
+
if (proto === undefined || proto === null) {
|
|
610
|
+
return false;
|
|
611
|
+
}
|
|
612
|
+
return Object.getOwnPropertyNames(proto).length > 1;
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Produce a readable name for a binding key (used in circular-dependency chains).
|
|
616
|
+
*
|
|
617
|
+
* @param key - The binding key.
|
|
618
|
+
* @returns A human-readable label.
|
|
619
|
+
*/
|
|
620
|
+
function containerKeyName(key) {
|
|
621
|
+
if (typeof key === 'function') {
|
|
622
|
+
return key.name.length > 0 ? key.name : 'anonymous';
|
|
623
|
+
}
|
|
624
|
+
if (typeof key === 'symbol') {
|
|
625
|
+
return key.toString();
|
|
626
|
+
}
|
|
627
|
+
if (typeof key === 'object' && key !== null) {
|
|
628
|
+
return key.constructor?.name ?? 'Object';
|
|
629
|
+
}
|
|
630
|
+
return String(key);
|
|
631
|
+
}
|
|
555
632
|
|
|
556
633
|
export { Binding, Container, ContainerError, Factory, Instance, Proxiable, ResolverBinding, Singleton };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { BindingValue, IContainer, IBinding } from '../declarations';
|
|
2
|
+
/**
|
|
3
|
+
* Abstract class representing a Binding.
|
|
4
|
+
*
|
|
5
|
+
* This abstract class serves as the base class for all types of bindings in the service container. It holds a value and provides an abstract method
|
|
6
|
+
* to resolve and return that value, allowing different subclasses to implement their own resolution logic. Bindings are used to manage dependencies
|
|
7
|
+
* and control how objects are instantiated within the container.
|
|
8
|
+
*
|
|
9
|
+
* @template V - The type of value that this binding holds.
|
|
10
|
+
* @author Mr. Stone <evensstone@gmail.com>
|
|
11
|
+
*/
|
|
12
|
+
export declare abstract class Binding<V extends BindingValue> implements IBinding<V> {
|
|
13
|
+
/**
|
|
14
|
+
* The value held by the binding.
|
|
15
|
+
*
|
|
16
|
+
* This value is resolved at runtime, either directly or through a resolver function.
|
|
17
|
+
*/
|
|
18
|
+
protected value?: V;
|
|
19
|
+
/**
|
|
20
|
+
* Whether the value has been resolved at least once.
|
|
21
|
+
*
|
|
22
|
+
* Tracked explicitly (not inferred from `value !== undefined`) so a binding whose resolved
|
|
23
|
+
* value is legitimately `undefined` is still considered resolved — preserving the singleton
|
|
24
|
+
* guarantee and avoiding repeated resolver side effects.
|
|
25
|
+
*/
|
|
26
|
+
protected resolved: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Create a new instance of Binding.
|
|
29
|
+
*
|
|
30
|
+
* @param value - The value to be held by the binding.
|
|
31
|
+
*/
|
|
32
|
+
constructor(value?: V);
|
|
33
|
+
/**
|
|
34
|
+
* Check if the value has been resolved.
|
|
35
|
+
*
|
|
36
|
+
* @returns A boolean indicating whether the value has been resolved.
|
|
37
|
+
*/
|
|
38
|
+
protected isResolved(): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Resolve and return the value of the binding.
|
|
41
|
+
*
|
|
42
|
+
* This abstract method must be implemented by subclasses to provide specific resolution logic.
|
|
43
|
+
*
|
|
44
|
+
* @param container - The container to resolve dependencies from.
|
|
45
|
+
* @returns The resolved value of the binding.
|
|
46
|
+
*/
|
|
47
|
+
abstract resolve(container: IContainer): V | undefined;
|
|
48
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ResolverBinding } from './ResolverBinding';
|
|
2
|
+
import { BindingValue, IContainer } from '../declarations';
|
|
3
|
+
/**
|
|
4
|
+
* Class representing a Factory.
|
|
5
|
+
*
|
|
6
|
+
* The Factory class extends the ResolverBinding class, providing a mechanism to resolve a new instance each time the binding is resolved.
|
|
7
|
+
* This ensures that a fresh instance is created with each call to the `resolve` method.
|
|
8
|
+
*
|
|
9
|
+
* @template V - The type of value that this binding holds.
|
|
10
|
+
* @author Mr. Stone <evensstone@gmail.com>
|
|
11
|
+
*/
|
|
12
|
+
export declare class Factory<V extends BindingValue> extends ResolverBinding<V> {
|
|
13
|
+
/**
|
|
14
|
+
* Resolve and return the value of the binding.
|
|
15
|
+
*
|
|
16
|
+
* Each time this method is called, a new value is resolved using the resolver function.
|
|
17
|
+
* This is intended for cases where a fresh instance is required for each resolution, such as factories or transient dependencies.
|
|
18
|
+
*
|
|
19
|
+
* @param container - The container to resolve dependencies from.
|
|
20
|
+
* @returns The resolved value of the binding.
|
|
21
|
+
* @throws ContainerError if the value cannot be resolved.
|
|
22
|
+
*/
|
|
23
|
+
resolve(container: IContainer): V;
|
|
24
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Binding } from './Binding';
|
|
2
|
+
import { BindingValue, IContainer } from '../declarations';
|
|
3
|
+
/**
|
|
4
|
+
* Class representing an Instance.
|
|
5
|
+
*
|
|
6
|
+
* This class extends the Binding class and directly holds an instance value.
|
|
7
|
+
* It provides a straightforward resolution mechanism that simply returns the stored value.
|
|
8
|
+
*
|
|
9
|
+
* @template V - The type of value that this binding holds.
|
|
10
|
+
* @author Mr. Stone <evensstone@gmail.com>
|
|
11
|
+
*/
|
|
12
|
+
export declare class Instance<V extends BindingValue> extends Binding<V> {
|
|
13
|
+
/**
|
|
14
|
+
* Resolve and return the value of the binding.
|
|
15
|
+
*
|
|
16
|
+
* @param _container - Container to resolve dependencies (not used in this implementation).
|
|
17
|
+
* @returns The resolved value of the binding.
|
|
18
|
+
*/
|
|
19
|
+
resolve(_container: IContainer): V | undefined;
|
|
20
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Binding } from './Binding';
|
|
2
|
+
import { BindingValue, Resolver } from '../declarations';
|
|
3
|
+
/**
|
|
4
|
+
* Class representing a ResolverBinding.
|
|
5
|
+
*
|
|
6
|
+
* This class extends the Binding class, using a resolver function to lazily resolve the value when needed.
|
|
7
|
+
*
|
|
8
|
+
* @template V - The type of value that this binding holds.
|
|
9
|
+
* @author Mr. Stone <evensstone@gmail.com>
|
|
10
|
+
*/
|
|
11
|
+
export declare abstract class ResolverBinding<V extends BindingValue> extends Binding<V> {
|
|
12
|
+
/**
|
|
13
|
+
* The resolver function used to provide the binding value.
|
|
14
|
+
*
|
|
15
|
+
* This function will be called when the value is needed, allowing for lazy instantiation
|
|
16
|
+
* and dependency resolution. It should return an instance of type `V`.
|
|
17
|
+
*/
|
|
18
|
+
protected readonly resolver: Resolver<V>;
|
|
19
|
+
/**
|
|
20
|
+
* Create a new instance of ResolverBinding.
|
|
21
|
+
*
|
|
22
|
+
* @param resolver - The resolver function to provide the binding value.
|
|
23
|
+
* @throws ContainerError if the resolver is not a function.
|
|
24
|
+
*/
|
|
25
|
+
constructor(resolver: Resolver<V>);
|
|
26
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ResolverBinding } from './ResolverBinding';
|
|
2
|
+
import { BindingValue, IContainer } from '../declarations';
|
|
3
|
+
/**
|
|
4
|
+
* Class representing a Singleton.
|
|
5
|
+
*
|
|
6
|
+
* The Singleton class extends the ResolverBinding class, ensuring that the value is only resolved once.
|
|
7
|
+
* Subsequent calls to the `resolve` method will return the previously resolved value, making it behave as a singleton.
|
|
8
|
+
*
|
|
9
|
+
* @template V - The type of value that this binding holds.
|
|
10
|
+
* @author Mr. Stone <evensstone@gmail.com>
|
|
11
|
+
*/
|
|
12
|
+
export declare class Singleton<V extends BindingValue> extends ResolverBinding<V> {
|
|
13
|
+
/**
|
|
14
|
+
* Resolve and return the value of the binding.
|
|
15
|
+
*
|
|
16
|
+
* If the value has already been resolved, return the cached value. Otherwise, use the resolver function
|
|
17
|
+
* to resolve the value, store it, and return it.
|
|
18
|
+
*
|
|
19
|
+
* @param container - The container to resolve dependencies from.
|
|
20
|
+
* @returns The resolved value of the binding.
|
|
21
|
+
* @throws ContainerError if the value cannot be resolved.
|
|
22
|
+
*/
|
|
23
|
+
resolve(container: IContainer): V | undefined;
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stone-js/service-container",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Javascript/Typescript IoC Service Container with proxy resolver and destructuring injection",
|
|
5
5
|
"author": "Mr. Stone <evensstone@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "git+
|
|
9
|
+
"url": "git+https://github.com/stone-foundation/stone-js-framework.git",
|
|
10
|
+
"directory": "stone-js-service-container"
|
|
10
11
|
},
|
|
11
12
|
"homepage": "https://stonejs.dev",
|
|
12
13
|
"bugs": {
|
|
13
|
-
"url": "https://github.com/stone-foundation/stone-js-
|
|
14
|
+
"url": "https://github.com/stone-foundation/stone-js-framework/issues"
|
|
14
15
|
},
|
|
15
16
|
"keywords": [
|
|
16
17
|
"DI",
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
"lint:fix": "ts-standard --fix src tests",
|
|
44
45
|
"predoc": "rimraf docs",
|
|
45
46
|
"doc": "typedoc",
|
|
46
|
-
"
|
|
47
|
+
"clean": "rimraf dist",
|
|
47
48
|
"build": "rollup -c",
|
|
48
49
|
"test": "vitest run",
|
|
49
50
|
"test:cvg": "npm run test -- --coverage",
|
|
@@ -64,8 +65,6 @@
|
|
|
64
65
|
"husky": "^9.1.7",
|
|
65
66
|
"rimraf": "^6.0.1",
|
|
66
67
|
"rollup": "^4.44.1",
|
|
67
|
-
"rollup-plugin-delete": "^3.0.1",
|
|
68
|
-
"rollup-plugin-dts": "^6.2.1",
|
|
69
68
|
"rollup-plugin-node-externals": "^8.0.1",
|
|
70
69
|
"ts-standard": "^12.0.2",
|
|
71
70
|
"tslib": "^2.8.1",
|
|
@@ -82,5 +81,6 @@
|
|
|
82
81
|
"describe",
|
|
83
82
|
"beforeEach"
|
|
84
83
|
]
|
|
85
|
-
}
|
|
84
|
+
},
|
|
85
|
+
"sideEffects": false
|
|
86
86
|
}
|