@praxisjs/di 0.2.3 → 1.1.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/CHANGELOG.md +65 -0
- package/dist/__tests__/container.test.js +108 -3
- package/dist/__tests__/container.test.js.map +1 -1
- package/dist/__tests__/decorators.test.js +322 -100
- package/dist/__tests__/decorators.test.js.map +1 -1
- package/dist/container.d.ts +2 -2
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +43 -24
- package/dist/container.js.map +1 -1
- package/dist/decorators.d.ts +16 -4
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +83 -45
- package/dist/decorators.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/__tests__/container.test.ts +106 -1
- package/src/__tests__/decorators.test.ts +204 -114
- package/src/container.ts +59 -25
- package/src/decorators.ts +99 -55
- package/src/index.ts +2 -3
package/dist/container.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.js","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"container.js","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"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"}
|
package/dist/decorators.d.ts
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import { type Container, Token, type Constructor, type InjectableOptions } from "./container";
|
|
2
2
|
export declare function Injectable(options?: InjectableOptions): (value: Constructor, _context: ClassDecoratorContext) => void;
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Creates a child DI container scoped to each instance of the decorated class.
|
|
5
|
+
* Services registered in the configure callback are available within the scope,
|
|
6
|
+
* and @Inject / @InjectContainer will resolve from it automatically.
|
|
7
|
+
*
|
|
8
|
+
* @Scope((c) => {
|
|
9
|
+
* c.register(UserRepository);
|
|
10
|
+
* c.registerValue(AuthToken, 'abc');
|
|
11
|
+
* })
|
|
12
|
+
* class UserModule extends StatefulComponent {
|
|
13
|
+
* @Inject(UserRepository) repo!: UserRepository;
|
|
14
|
+
* }
|
|
15
|
+
*/
|
|
16
|
+
export declare function Scope(configure?: (c: Container) => void): (cls: new (...args: any[]) => any, _ctx: ClassDecoratorContext) => any;
|
|
17
|
+
export declare function Inject<T>(dep: Constructor<T> | Token<T>): (_value: undefined, context: ClassFieldDecoratorContext<any>) => void;
|
|
18
|
+
export declare function InjectContainer(): (_value: undefined, context: ClassFieldDecoratorContext<any>) => void;
|
|
7
19
|
//# sourceMappingURL=decorators.d.ts.map
|
package/dist/decorators.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,KAAK,SAAS,EAAE,KAAK,EAAE,KAAK,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAWzG,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,IACvC,OAAO,WAAW,EAAE,UAAU,qBAAqB,KAAG,IAAI,CAG5E;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,IAEtC,KAAK,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAAE,MAAM,qBAAqB,KAAG,GAAG,CAapF;AAID,wBAAgB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAsCrC,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,CAAC,GAAG,CAAC,KAAK,IAAI,CACvF;AAID,wBAAgB,eAAe,IAkBZ,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,CAAC,GAAG,CAAC,KAAK,IAAI,CACvF"}
|
package/dist/decorators.js
CHANGED
|
@@ -1,60 +1,98 @@
|
|
|
1
|
+
import { createFieldDecorator } from "@praxisjs/decorators";
|
|
1
2
|
import { container, Token } from "./container";
|
|
3
|
+
// Maps instances to their scoped child containers (set by @Scope).
|
|
4
|
+
const scopedContainers = new WeakMap();
|
|
5
|
+
function resolveFrom(instance) {
|
|
6
|
+
return scopedContainers.get(instance) ?? container;
|
|
7
|
+
}
|
|
8
|
+
// ── @Injectable ───────────────────────────────────────────────────────────────
|
|
2
9
|
export function Injectable(options = {}) {
|
|
3
10
|
return function (value, _context) {
|
|
4
11
|
container.register(value, options);
|
|
5
12
|
};
|
|
6
13
|
}
|
|
14
|
+
// ── @Scope ────────────────────────────────────────────────────────────────────
|
|
15
|
+
/**
|
|
16
|
+
* Creates a child DI container scoped to each instance of the decorated class.
|
|
17
|
+
* Services registered in the configure callback are available within the scope,
|
|
18
|
+
* and @Inject / @InjectContainer will resolve from it automatically.
|
|
19
|
+
*
|
|
20
|
+
* @Scope((c) => {
|
|
21
|
+
* c.register(UserRepository);
|
|
22
|
+
* c.registerValue(AuthToken, 'abc');
|
|
23
|
+
* })
|
|
24
|
+
* class UserModule extends StatefulComponent {
|
|
25
|
+
* @Inject(UserRepository) repo!: UserRepository;
|
|
26
|
+
* }
|
|
27
|
+
*/
|
|
28
|
+
export function Scope(configure) {
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
return function (cls, _ctx) {
|
|
31
|
+
return class extends cls {
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
|
+
constructor(...args) {
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
35
|
+
super(...args);
|
|
36
|
+
const child = container.createChild();
|
|
37
|
+
configure?.(child);
|
|
38
|
+
scopedContainers.set(this, child);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// ── @Inject ───────────────────────────────────────────────────────────────────
|
|
7
44
|
export function Inject(dep) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
45
|
+
const cache = new WeakMap();
|
|
46
|
+
return createFieldDecorator({
|
|
47
|
+
bind(_instance, name, _initialValue) {
|
|
48
|
+
return {
|
|
49
|
+
descriptor: {
|
|
50
|
+
get() {
|
|
51
|
+
if (!cache.has(this)) {
|
|
52
|
+
const c = resolveFrom(this);
|
|
53
|
+
let resolved;
|
|
54
|
+
try {
|
|
55
|
+
resolved = c.resolve(dep);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
throw new Error(`[Inject] Failed to resolve "${dep instanceof Token
|
|
59
|
+
? dep.toString()
|
|
60
|
+
: dep.name}" in "${this.constructor.name}.${name}": ${err.message}`);
|
|
61
|
+
}
|
|
62
|
+
cache.set(this, resolved);
|
|
18
63
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
64
|
+
return cache.get(this);
|
|
65
|
+
},
|
|
66
|
+
set(_value) {
|
|
67
|
+
if (process.env.NODE_ENV !== "production") {
|
|
68
|
+
console.warn(`[Inject] "${name}" is managed by the DI container and cannot be assigned directly.`);
|
|
23
69
|
}
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
return cache.get(this);
|
|
70
|
+
},
|
|
27
71
|
},
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
enumerable: true,
|
|
34
|
-
configurable: true,
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
};
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
// DI decorators work on any class, not just StatefulComponent
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
|
+
});
|
|
38
77
|
}
|
|
78
|
+
// ── @InjectContainer ──────────────────────────────────────────────────────────
|
|
39
79
|
export function InjectContainer() {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
80
|
+
const cache = new WeakMap();
|
|
81
|
+
return createFieldDecorator({
|
|
82
|
+
bind(_instance, _name, _initialValue) {
|
|
83
|
+
return {
|
|
84
|
+
descriptor: {
|
|
85
|
+
get() {
|
|
86
|
+
if (!cache.has(this)) {
|
|
87
|
+
cache.set(this, resolveFrom(this));
|
|
88
|
+
}
|
|
89
|
+
return cache.get(this);
|
|
90
|
+
},
|
|
45
91
|
},
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
export function useService(dep) {
|
|
53
|
-
return container.resolve(dep);
|
|
54
|
-
}
|
|
55
|
-
export function createScope(configure) {
|
|
56
|
-
const child = container.createChild();
|
|
57
|
-
configure?.(child);
|
|
58
|
-
return child;
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
// DI decorators work on any class, not just StatefulComponent
|
|
95
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
96
|
+
});
|
|
59
97
|
}
|
|
60
98
|
//# sourceMappingURL=decorators.js.map
|
package/dist/decorators.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAkB,KAAK,
|
|
1
|
+
{"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
|
-
export { Injectable, Inject, InjectContainer,
|
|
2
|
+
export { Injectable, Inject, InjectContainer, Scope, } from "./decorators";
|
|
3
3
|
export { Container, container, Token, token } from "./container";
|
|
4
|
-
export type { Scope, InjectableOptions, Constructor, ServiceDescriptor, } from "./container";
|
|
4
|
+
export type { Scope as ScopeType, InjectableOptions, Constructor, ServiceDescriptor, } from "./container";
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EACL,UAAU,EACV,MAAM,EACN,eAAe,EACf,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EACL,UAAU,EACV,MAAM,EACN,eAAe,EACf,KAAK,GACN,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACjE,YAAY,EACV,KAAK,IAAI,SAAS,EAClB,iBAAiB,EACjB,WAAW,EACX,iBAAiB,GAClB,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
|
-
export { Injectable, Inject, InjectContainer,
|
|
2
|
+
export { Injectable, Inject, InjectContainer, Scope, } from "./decorators";
|
|
3
3
|
export { Container, container, Token, token } from "./container";
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EACL,UAAU,EACV,MAAM,EACN,eAAe,EACf,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EACL,UAAU,EACV,MAAM,EACN,eAAe,EACf,KAAK,GACN,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisjs/di",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"reflect-metadata": "^0.2.2",
|
|
15
|
-
"@praxisjs/core": "
|
|
15
|
+
"@praxisjs/core": "1.1.0",
|
|
16
|
+
"@praxisjs/decorators": "0.6.0"
|
|
16
17
|
},
|
|
17
18
|
"devDependencies": {
|
|
18
19
|
"@types/node": "^25.3.0",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
2
|
import { describe, it, expect } from "vitest";
|
|
3
3
|
|
|
4
|
-
import { Container, Token, token } from "../container";
|
|
4
|
+
import { Container, Token, token, type Constructor } from "../container";
|
|
5
5
|
|
|
6
6
|
describe("Token", () => {
|
|
7
7
|
it("stores description", () => {
|
|
@@ -133,6 +133,26 @@ describe("Container", () => {
|
|
|
133
133
|
expect(grandchild.resolve(URL_TOKEN)).toBe("https://grandparent.com");
|
|
134
134
|
});
|
|
135
135
|
|
|
136
|
+
it("resolves constructor injection from di:inject metadata", () => {
|
|
137
|
+
class Engine {}
|
|
138
|
+
class Car {
|
|
139
|
+
constructor(public engine: Engine) {}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const c = new Container();
|
|
143
|
+
c.register(Engine);
|
|
144
|
+
c.register(Car);
|
|
145
|
+
|
|
146
|
+
// Simulate what a @Inject constructor param decorator would set
|
|
147
|
+
Reflect.defineMetadata("di:inject", [Engine], Car);
|
|
148
|
+
|
|
149
|
+
const car = c.resolve(Car);
|
|
150
|
+
expect(car).toBeInstanceOf(Car);
|
|
151
|
+
expect(car.engine).toBeInstanceOf(Engine);
|
|
152
|
+
|
|
153
|
+
Reflect.deleteMetadata("di:inject", Car);
|
|
154
|
+
});
|
|
155
|
+
|
|
136
156
|
it("registering the same class twice replaces the previous registration", () => {
|
|
137
157
|
class Counter {
|
|
138
158
|
value = Math.random();
|
|
@@ -149,4 +169,89 @@ describe("Container", () => {
|
|
|
149
169
|
expect(second).toBeInstanceOf(Counter);
|
|
150
170
|
expect(second).not.toBe(first);
|
|
151
171
|
});
|
|
172
|
+
|
|
173
|
+
it("circular dependency A → B → A throws a descriptive error", () => {
|
|
174
|
+
class A {
|
|
175
|
+
constructor(public b: unknown) {}
|
|
176
|
+
}
|
|
177
|
+
class B {
|
|
178
|
+
constructor(public a: unknown) {}
|
|
179
|
+
}
|
|
180
|
+
const c = new Container();
|
|
181
|
+
c.register(A);
|
|
182
|
+
c.register(B);
|
|
183
|
+
Reflect.defineMetadata("di:inject", [B], A);
|
|
184
|
+
Reflect.defineMetadata("di:inject", [A], B);
|
|
185
|
+
|
|
186
|
+
expect(() => c.resolve(A)).toThrow("[DI] Circular dependency detected");
|
|
187
|
+
|
|
188
|
+
Reflect.deleteMetadata("di:inject", A);
|
|
189
|
+
Reflect.deleteMetadata("di:inject", B);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("factory function that throws — error message includes which service failed", () => {
|
|
193
|
+
class Broken {
|
|
194
|
+
constructor() {
|
|
195
|
+
throw new Error("boom");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const c = new Container();
|
|
199
|
+
c.register(Broken);
|
|
200
|
+
expect(() => c.resolve(Broken)).toThrow("Broken");
|
|
201
|
+
expect(() => c.resolve(Broken)).toThrow("[DI] Failed to instantiate");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("resolve() called concurrently on the same uninitialized singleton — returns same instance or does not crash", async () => {
|
|
205
|
+
class SlowService {}
|
|
206
|
+
const c = new Container();
|
|
207
|
+
c.register(SlowService, { scope: "singleton" });
|
|
208
|
+
|
|
209
|
+
const [a, b] = await Promise.all([
|
|
210
|
+
Promise.resolve(c.resolve(SlowService)),
|
|
211
|
+
Promise.resolve(c.resolve(SlowService)),
|
|
212
|
+
]);
|
|
213
|
+
|
|
214
|
+
expect(a).toBeInstanceOf(SlowService);
|
|
215
|
+
expect(a).toBe(b);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("re-registering a service clears the previous singleton instance", () => {
|
|
219
|
+
class Svc {
|
|
220
|
+
id = Math.random();
|
|
221
|
+
}
|
|
222
|
+
const c = new Container();
|
|
223
|
+
c.register(Svc, { scope: "singleton" });
|
|
224
|
+
const first = c.resolve(Svc);
|
|
225
|
+
|
|
226
|
+
c.register(Svc, { scope: "singleton" });
|
|
227
|
+
const second = c.resolve(Svc);
|
|
228
|
+
|
|
229
|
+
expect(second).not.toBe(first);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("circular dependency chain including a Token shows the Token name in the error", () => {
|
|
233
|
+
const MY_TOKEN = token<unknown>("MyToken");
|
|
234
|
+
const c = new Container();
|
|
235
|
+
c.registerValue(MY_TOKEN, "value");
|
|
236
|
+
|
|
237
|
+
// Pre-populate the stack as if we are already in the middle of resolving MY_TOKEN
|
|
238
|
+
const stack = new Set<Constructor | Token<unknown>>([MY_TOKEN]);
|
|
239
|
+
expect(() => c.resolve(MY_TOKEN, stack)).toThrow("Token(MyToken)");
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it("instantiate rethrows DI errors thrown inside a constructor without wrapping", () => {
|
|
243
|
+
const c = new Container();
|
|
244
|
+
class Unregistered {}
|
|
245
|
+
class Outer {
|
|
246
|
+
constructor() {
|
|
247
|
+
// Manually triggers a [DI] error inside the constructor body
|
|
248
|
+
c.resolve(Unregistered);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
c.register(Outer);
|
|
252
|
+
|
|
253
|
+
const err = (() => { try { c.resolve(Outer); } catch (e) { return e as Error; } })()!;
|
|
254
|
+
expect(err.message).toMatch(/\[DI\] Service not registered/);
|
|
255
|
+
expect(err.message).not.toMatch(/Failed to instantiate/);
|
|
256
|
+
});
|
|
152
257
|
});
|