proxydi 0.0.7 → 0.0.9
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 +21 -21
- package/README.md +296 -206
- package/dist/ProxyDiContainer.d.ts +24 -14
- package/dist/autoInjectable.d.ts +3 -3
- package/dist/index.cjs +74 -26
- package/dist/index.d.ts +2 -1
- package/dist/index.js +72 -27
- package/dist/index.umd.js +74 -26
- package/dist/presets.d.ts +0 -1
- package/dist/resolveAll.d.ts +8 -0
- package/dist/types.d.ts +16 -8
- package/package.json +60 -60
- package/dist/ProxyDI.d.ts +0 -27
- package/dist/ProxyFactory.d.ts +0 -8
- package/dist/autoInjectableService.d.ts +0 -13
- package/dist/injectable.d.ts +0 -3
- package/doc/assets/Logo.png +0 -0
- package/doc/doc.css +0 -3
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 proxy-di
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 proxy-di
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,206 +1,296 @@
|
|
|
1
|
-
# ProxyDi
|
|
2
|
-
|
|
3
|
-
[](https://coveralls.io/github/proxy-di/proxydi)
|
|
4
|
-
|
|
5
|
-
A typed hierarchical DI container that resolves circular dependencies via Proxy.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
1
|
+
# ProxyDi
|
|
2
|
+
|
|
3
|
+
[](https://coveralls.io/github/proxy-di/proxydi)
|
|
4
|
+
|
|
5
|
+
A typed hierarchical DI container that resolves circular dependencies via Proxy.
|
|
6
|
+
|
|
7
|
+
<img src="https://github.com/proxy-di/proxydi/blob/main/assets/ProxyDiLogo.png?raw=true" width="196">
|
|
8
|
+
|
|
9
|
+
Core features:
|
|
10
|
+
|
|
11
|
+
- Uses Stage 3 decorators, supported in TypeScript 5.x ([examples repository](https://github.com/proxy-di/node-ts-examples)) and Babel via babel-plugin-proposal-decorators ([examples repository](https://github.com/proxy-di/node-babel-examples))
|
|
12
|
+
- Automatically resolves circular dependencies with no performance impact
|
|
13
|
+
- Resolves dependencies in the context of a particular container
|
|
14
|
+
- Supports hierarchical containers with the ability to resolve dependencies in both directions
|
|
15
|
+
- Matches dependencies by unique identifiers or automatically using class names and property names
|
|
16
|
+
- Currently under active development, the API may change until version 0.1.0
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
19
|
+
|
|
20
|
+
> If you are using React, you should use the React wrapper for ProxyDi instead of this library directly: [@proxydi/react](https://github.com/proxy-di/proxydi-react)
|
|
21
|
+
|
|
22
|
+
Install the `proxydi` package in your JavaScript or TypeScript project:
|
|
23
|
+
|
|
24
|
+
```shell
|
|
25
|
+
npm i proxydi
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Configure TypeScript
|
|
29
|
+
|
|
30
|
+
If you are using TypeScript, ensure that `experimentalDecorators` is set to `false` in your `tsconfig.json`. This enables support for Stage 3 decorators:
|
|
31
|
+
|
|
32
|
+
```jsonc
|
|
33
|
+
// tsconfig.json
|
|
34
|
+
{
|
|
35
|
+
"compilerOptions": {
|
|
36
|
+
// ...
|
|
37
|
+
"experimentalDecorators": false,
|
|
38
|
+
"strictPropertyInitialization": false,
|
|
39
|
+
},
|
|
40
|
+
//...
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Changing `strictPropertyInitialization` is not necessary, but if you leave it at the default value, you will need to slightly modify the examples. More about this later.
|
|
45
|
+
|
|
46
|
+
### Configure Babel
|
|
47
|
+
|
|
48
|
+
For Babel projects, ensure that @babel/plugin-proposal-decorators is configured exactly as follows:
|
|
49
|
+
|
|
50
|
+
```jsonc
|
|
51
|
+
// .babelrc
|
|
52
|
+
{
|
|
53
|
+
// ...
|
|
54
|
+
"plugins": [
|
|
55
|
+
// other plugins
|
|
56
|
+
["@babel/plugin-proposal-decorators", { "version": "2023-11" }],
|
|
57
|
+
],
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Usage
|
|
62
|
+
|
|
63
|
+
> We will use TypeScript for all examples, because it is easier to remove typing than add it
|
|
64
|
+
|
|
65
|
+
The process of using ProxyDi consists of 3 stages:
|
|
66
|
+
|
|
67
|
+
1. Use the [@inject()](https://proxy-di.github.io/proxydi/functions/inject.html) decorator to define the dependencies to be resolved by the ProxyDi container. In this example, we define an interface for characters and ask ProxyDi to resolve the `Role` dependency for actors.
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
interface Character {
|
|
71
|
+
greet(): string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
class Actor {
|
|
75
|
+
@inject('Role') role: Character;
|
|
76
|
+
|
|
77
|
+
play = () => this.role.greet();
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
2. Next, create the [ProxyDiContainer](https://proxy-di.github.io/proxydi/classes/ProxyDiContainer.html)
|
|
82
|
+
and fill it with dependencies using [register()](https://proxy-di.github.io/proxydi/classes/ProxyDiContainer.html#register) method. For example, let's define an agent 007 role and prepare our first container:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
class Agent007 implements Character {
|
|
86
|
+
greet = () => 'Bond... James Bond';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const container = new ProxyDiContainer();
|
|
90
|
+
container.register(Agent007, 'Role');
|
|
91
|
+
container.register(Actor, 'Actor');
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
3. At the last stage, take dependencies from the ProxyDi container by [resolve()](https://proxy-di.github.io/proxydi/classes/ProxyDiContainer.html#resolve) method and just use them. To continue our example, let our actor play its role:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
const actor = container.resolve<Actor>('Actor');
|
|
98
|
+
console.log(actor.play());
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
And the result is:
|
|
102
|
+
|
|
103
|
+
```shell
|
|
104
|
+
> Bond... James Bond
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## The Reason
|
|
108
|
+
|
|
109
|
+
To illustrate why we should use a container for this simple purpose, let's create a container for another character and ask the actor to play the new role:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
class M implements Character {
|
|
113
|
+
greet = () => '007, I have a new mission for you';
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const container = new ProxyDiContainer();
|
|
117
|
+
container.register(M, 'Role');
|
|
118
|
+
container.register(Actor, 'Actor');
|
|
119
|
+
|
|
120
|
+
const actor = container.resolve<Actor>('Actor');
|
|
121
|
+
console.log(actor.play());
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```shell
|
|
125
|
+
> 007, I have a new mission for you
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
In this example, we changed the behavior of the actor by changing the role dependency in the ProxyDi container. This is exactly the goal of the [Dependency inversion principle](https://en.wikipedia.org/wiki/Dependency_inversion_principle) in the SOLID approach to software design. Continuing our metaphor, the actor can play any role, but he is not the one who decides which role he will play. This is a film director's decision, and here we just cosplay him by setting up our containers.
|
|
129
|
+
|
|
130
|
+
> So, ProxyDi is just a tool to link dependencies, allowing them to freely communicate with each other without worrying about which specific dependency they're dealing with. And nothing more.
|
|
131
|
+
|
|
132
|
+
## Circular dependencies
|
|
133
|
+
|
|
134
|
+
There are several rough edges in traditional DI container implementations that ProxyDi addresses. The first of these is circular dependencies.
|
|
135
|
+
|
|
136
|
+
Let's illustrate this with a movie set metaphor. During filming, the actor and director work together in a continuous feedback loop:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
class Director {
|
|
140
|
+
@inject('Actor') private actor: Actor;
|
|
141
|
+
private passionLevel = 1;
|
|
142
|
+
|
|
143
|
+
direct(line: string) {
|
|
144
|
+
return this.actor.perform(line, this.passionLevel);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
class Actor {
|
|
149
|
+
@inject('Role') private role: Character;
|
|
150
|
+
@inject('Director') private director: Director;
|
|
151
|
+
|
|
152
|
+
play() {
|
|
153
|
+
const line = this.role.greet();
|
|
154
|
+
// Here, the actor asks the director how to perform the line.
|
|
155
|
+
return this.director.direct(line);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
perform(line: string, loudness: number = 0) {
|
|
159
|
+
return line + '!'.repeat(loudness);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const container = new ProxyDiContainer();
|
|
164
|
+
container.register(Actor, 'Actor');
|
|
165
|
+
container.register(Director, 'Director');
|
|
166
|
+
container.register(Agent007, 'Role');
|
|
167
|
+
|
|
168
|
+
const actor = container.resolve<Actor>('Actor');
|
|
169
|
+
console.log(actor.play());
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
```shell
|
|
173
|
+
> Bond... James Bond!
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
In traditional DI containers, this scene would be tricky to shoot - the Director calls Actor's methods while Actor simultaneously needs Director's guidance.
|
|
177
|
+
|
|
178
|
+
But take a look, our approach is still the same - we just link dependencies with @inject and use them freely without any worries. ProxyDi handles this tricky issue as elegantly as possible. It does this using JavaScript Proxies, more about Proxies and their impact on performance [later](#injection-proxy-performance).
|
|
179
|
+
|
|
180
|
+
## Hierarchy of containers
|
|
181
|
+
|
|
182
|
+
Another tricky part of DI containers is the ability to create multiple instances of the same class. ProxyDi solves this problem in the simplest way possible - it just does not allow it. When you register a class as a dependency, there is only one instance of this class in the container.
|
|
183
|
+
|
|
184
|
+
Instead, it suggests you to use a hierarchy of containers by using [createChildContainer()](https://proxy-di.github.io/proxydi/classes/ProxyDiContainer.html#createchildcontainer) method. Child container inherits all parent settings and can resolve exactly the same dependencies as their parent (but parent container does not have access to dependencies registered in its children).
|
|
185
|
+
|
|
186
|
+
For example, imagine you are working on a game level, there are many characters on this level, and each character could have many perks.
|
|
187
|
+
|
|
188
|
+
With ProxyDi we can present all these stuff in a hierarchy of containers. The most top container holds information about game level, the most bottom ones hold information about perks:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
const tutorialContainer = new ProxyDiContainer();
|
|
192
|
+
tutorialContainer.register(new GameLevel({ undewater: true }), 'level');
|
|
193
|
+
|
|
194
|
+
const heroContainer = tutorialContainer.createChildContainer();
|
|
195
|
+
const hero = heroContainer.register<Character>(Character, 'character');
|
|
196
|
+
|
|
197
|
+
const perksContainer = heroContainer.createChildContainer();
|
|
198
|
+
const perk = perksContainer.register(new UnderwaterShield(10), 'perk');
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
This is not how I propose to design games, ECS pattern does it better, but the goal was to demonstrate that with this approach instead of creating bunches of instances to represent your project entities, you can create bunches of containers each of them containing instances related to each other.
|
|
202
|
+
|
|
203
|
+
### Resolving dependencies from parents
|
|
204
|
+
|
|
205
|
+
As a bonus, each bottom level dependency is free to use any dependency from the top just as if they were registered in its own container:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
class UnderwaterShield {
|
|
209
|
+
@inject('level') private level: GameLevel;
|
|
210
|
+
@inject('character') private character: Character;
|
|
211
|
+
|
|
212
|
+
constructor(private amount: number) {}
|
|
213
|
+
|
|
214
|
+
activate = () =>
|
|
215
|
+
this.level.isUnderwater && (this.character.health += this.amount);
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
In this example, the shield perk increases character health if it is underwater. The perk receives both level and character using the @inject() decorator, the same way as we have seen so far.
|
|
220
|
+
|
|
221
|
+
## Resolving dependencies from children
|
|
222
|
+
|
|
223
|
+
Backward bonus of containers hierarchy, each top level dependency is free to use all dependency from the bottom:
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
class Character {
|
|
227
|
+
public health = 100;
|
|
228
|
+
|
|
229
|
+
hit(abount: number) {
|
|
230
|
+
this.health -= abount;
|
|
231
|
+
|
|
232
|
+
const perks = resolveAll<Perk>(this, 'perk');
|
|
233
|
+
perks.forEach((perk) => perk.activate());
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
In this example, the character activates all its perks, which are registered in all children containers. The way it do this job need a little bit more explanation.
|
|
239
|
+
|
|
240
|
+
### Reference to the container
|
|
241
|
+
|
|
242
|
+
Here you should be wondering, how [resolveAll()](https://proxy-di.github.io/proxydi/functions/resolveAll.html.html) function knows about the container, to which character belongs. The answer - each time when dependency is registered in the ProxyDiContainer, it saves a reference to itself in this dependency instance. So, when you call resolveAll() function, it just takes this reference from the instance and then recursively resolves all asked dependencies from this container and all its children and children of children and so on.
|
|
243
|
+
|
|
244
|
+
Despite this explanation is a little bit complicated, the example is still simple, the character just activates all its perks.
|
|
245
|
+
|
|
246
|
+
## Rewriting dependencies
|
|
247
|
+
|
|
248
|
+
By default, ProxyDi doesn't allow rewriting dependencies in the container. After a dependency becomes known to the container, any attempt to register a new dependency with the same dependency ID will throw an Error:
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
const container = new ProxyDiContainer();
|
|
252
|
+
container.register(Actor, 'Actor');
|
|
253
|
+
container.register(Actor, 'Actor'); // !!! Error here
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
However, there is an option that allows you to do these kinds of things:
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
const container = new ProxyDiContainer({ allowRewriteDependencies: true });
|
|
260
|
+
container.register(Actor, 'Actor');
|
|
261
|
+
|
|
262
|
+
const actor = container.resolve<Actor>('Actor');
|
|
263
|
+
const wrapper = new ActorWrapper(actor);
|
|
264
|
+
|
|
265
|
+
container.register(wrapper, 'Actor'); // No error is thrown here now
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Injection proxy performance
|
|
269
|
+
|
|
270
|
+
As mentioned before, ProxyDi uses `Proxy` for each field marked by the @inject() decorator. This makes it possible to resolve circular dependencies. By default, these proxies are replaced by the actual dependency instances from the container during their first use, so the performance impact on your application is minimal.
|
|
271
|
+
|
|
272
|
+
However, if you allow rewriting dependencies in the container, these proxies remain in use to keep injections updated. As a result, every time you access a dependency field, there is a significant performance impact. In our tests, property access via Proxy is up to 100 times slower. For this reason, we recommend not allowing rewriting dependencies in production and keeping the container’s default behavior.
|
|
273
|
+
|
|
274
|
+
### Baking injections
|
|
275
|
+
|
|
276
|
+
There is a container method [bakeInjections()](https://proxy-di.github.io/proxydi/classes/ProxyDiContainer.html#bakeinjections) that bakes all injections and freezes the current container’s dependencies. After calling this method, the container restores behaviour by default and denies any attempts to rewrite dependencies. It also bakes the dependencies in all its children.
|
|
277
|
+
|
|
278
|
+
Therefore, after the container has been baked, the performance impact becomes zero. So, you could use this method even for containers with default settings, ensuring that your application doesn't have to wait for the first use of each injection before they are baked.
|
|
279
|
+
|
|
280
|
+
To be continued...
|
|
281
|
+
|
|
282
|
+
## Motivation
|
|
283
|
+
|
|
284
|
+
The world and software changes, they become more complex over time. But the main imperative in software development stays the same - managing the complexity. Despite the tendency that software is written more often by artificial intelligence than humans, complexity stays complexity. The less complex conceptions any kind of intelligence should operate, the more efficient it will be.
|
|
285
|
+
|
|
286
|
+
The main goal of ProxyDi is to decrease complexity in the very small field of connecting different entities of software code between each other, no matter by whom this code was written. This is my attempt to make the linking of dependencies as transparent and simple as possible for developers.
|
|
287
|
+
|
|
288
|
+
Also, I'm tired of moving this-like code from one project to another. I hope that ProxyDi will be a good enough solution for this problem not only for me. To be honest, this is the 4th attempt to create an "ideal" DI container and my 1st that uses Stage 3 decorators. I hope, this one will be the last one. With your help :) For TS/JS technology stack, of course!
|
|
289
|
+
|
|
290
|
+
## Contributing
|
|
291
|
+
|
|
292
|
+
Any reviews, comments, ideas, issues, and pull requests are welcome. Contribution documentation is not ready yet but is planned. Feel free to contribute even now though! :)
|
|
293
|
+
|
|
294
|
+
## License
|
|
295
|
+
|
|
296
|
+
This project is licensed under the terms of the MIT License. See the [LICENSE](./LICENSE) file for details.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IProxyDiContainer as IProxyDiContainer, ContainerizedDependency as ContainerizedDependency,
|
|
1
|
+
import { IProxyDiContainer as IProxyDiContainer, ContainerizedDependency as ContainerizedDependency, DependencyClass } from './types';
|
|
2
2
|
import { ContainerSettings as ContainerSettings, DependencyId } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* A dependency injection container
|
|
@@ -9,16 +9,16 @@ export declare class ProxyDiContainer implements IProxyDiContainer {
|
|
|
9
9
|
*/
|
|
10
10
|
private static idCounter;
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Just unique number identifier for this container, nothing more
|
|
13
13
|
*/
|
|
14
14
|
readonly id: number;
|
|
15
15
|
/**
|
|
16
16
|
* Optional parent container from which this container can inherit dependencies.
|
|
17
17
|
*/
|
|
18
18
|
readonly parent?: ProxyDiContainer;
|
|
19
|
-
private
|
|
19
|
+
private _children;
|
|
20
20
|
/**
|
|
21
|
-
* Holds
|
|
21
|
+
* Holds dependency instances registered particular in this container.
|
|
22
22
|
*/
|
|
23
23
|
private dependencies;
|
|
24
24
|
/**
|
|
@@ -36,22 +36,22 @@ export declare class ProxyDiContainer implements IProxyDiContainer {
|
|
|
36
36
|
*/
|
|
37
37
|
constructor(settings?: ContainerSettings, parent?: ProxyDiContainer);
|
|
38
38
|
/**
|
|
39
|
-
* Registers a dependency in the container.
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* @param param The dependency instance or dependency class.
|
|
39
|
+
* Registers a dependency in the container. Could register eacher class or instance.
|
|
40
|
+
* In case of class, it will be instantiated without any parameters.
|
|
41
|
+
*
|
|
42
|
+
* @param dependency The dependency instance or dependency class.
|
|
44
43
|
* @param dependencyId The unique identifier for the dependency in this container.
|
|
45
44
|
* @throws Error if dependency is already registered and rewriting is not allowed or if invalid dependency (not object) is provided and this it now allowed.
|
|
45
|
+
* @returns Dependency instance, registered in container
|
|
46
46
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
register<T>(DependencyClass: DependencyClass<T>, dependencyId: DependencyId): T & ContainerizedDependency;
|
|
48
|
+
register<T>(dependency: T extends new (...args: any[]) => any ? never : T, dependencyId: DependencyId): T & ContainerizedDependency;
|
|
49
49
|
/**
|
|
50
50
|
* Internal method that implements registeration of dependency and prepare it for injection.
|
|
51
51
|
* @param dependencyId The unique identifier of the dependency.
|
|
52
52
|
* @param dependency The dependency instance.
|
|
53
53
|
*/
|
|
54
|
-
private
|
|
54
|
+
private registerImpl;
|
|
55
55
|
/**
|
|
56
56
|
* Checks if a dependency with the given ID is known to the container or its ancestors which means that it can be resolved by this container
|
|
57
57
|
* @param dependencyId The identifier of the dependency.
|
|
@@ -65,7 +65,7 @@ export declare class ProxyDiContainer implements IProxyDiContainer {
|
|
|
65
65
|
* @throws Error if the dependency cannot be found or is not auto injectable.
|
|
66
66
|
*/
|
|
67
67
|
resolve<T>(dependencyId: DependencyId): T & ContainerizedDependency;
|
|
68
|
-
resolve<T extends new (...args: any[]) => any>(SomeClass: T): InstanceType<T
|
|
68
|
+
resolve<T extends new (...args: any[]) => any>(SomeClass: T): InstanceType<T> & ContainerizedDependency;
|
|
69
69
|
/**
|
|
70
70
|
* Injects dependencies to the given object based on its defined injections metadata. Does not affect the container.
|
|
71
71
|
* @param injectionsOwner The object to inject dependencies into.
|
|
@@ -85,7 +85,7 @@ export declare class ProxyDiContainer implements IProxyDiContainer {
|
|
|
85
85
|
* Removes a given dependency from the container using either the dependency instance or its ID.
|
|
86
86
|
* @param dependencyOrId The dependency instance or dependency identifier to remove.
|
|
87
87
|
*/
|
|
88
|
-
|
|
88
|
+
remove(dependencyOrId: DependencyId | ContainerizedDependency): void;
|
|
89
89
|
/**
|
|
90
90
|
* Destroys the container by removing all dependencies,
|
|
91
91
|
* recursively destroying child containers and removing itself from its parent.
|
|
@@ -97,6 +97,16 @@ export declare class ProxyDiContainer implements IProxyDiContainer {
|
|
|
97
97
|
* @returns The dependency if found, otherwise undefined.
|
|
98
98
|
*/
|
|
99
99
|
private findDependency;
|
|
100
|
+
/**
|
|
101
|
+
* All direct descendants of this container
|
|
102
|
+
*/
|
|
103
|
+
get children(): ProxyDiContainer[];
|
|
104
|
+
/**
|
|
105
|
+
*
|
|
106
|
+
* @param id Unique identifier of container
|
|
107
|
+
* @returns
|
|
108
|
+
*/
|
|
109
|
+
getChild(id: number): ProxyDiContainer;
|
|
100
110
|
/**
|
|
101
111
|
* Registers a child container to this container.
|
|
102
112
|
* @param child The child container to add.
|