@praxisjs/di 0.2.2 → 1.0.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 +56 -0
- package/dist/__tests__/container.test.js +70 -1
- package/dist/__tests__/container.test.js.map +1 -1
- package/dist/__tests__/decorators.test.js +247 -85
- package/dist/__tests__/decorators.test.js.map +1 -1
- package/dist/container.d.ts +1 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +1 -3
- 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 +79 -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 +76 -0
- package/src/__tests__/decorators.test.ts +157 -102
- package/src/container.ts +2 -1
- package/src/decorators.ts +94 -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,IAaZ,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,CAAC,GAAG,CAAC,KAAK,IAAI,CACvF"}
|
package/dist/decorators.js
CHANGED
|
@@ -1,60 +1,94 @@
|
|
|
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);
|
|
27
|
-
},
|
|
28
|
-
set(_value) {
|
|
29
|
-
if (process.env.NODE_ENV !== "production") {
|
|
30
|
-
console.warn(`[Inject] "${propertyKey}" is managed by the DI container and cannot be assigned directly.`);
|
|
31
|
-
}
|
|
70
|
+
},
|
|
32
71
|
},
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
return
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
80
|
+
return createFieldDecorator({
|
|
81
|
+
bind(_instance, _name, _initialValue) {
|
|
82
|
+
return {
|
|
83
|
+
descriptor: {
|
|
84
|
+
get() {
|
|
85
|
+
return resolveFrom(this);
|
|
86
|
+
},
|
|
45
87
|
},
|
|
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;
|
|
88
|
+
};
|
|
89
|
+
},
|
|
90
|
+
// DI decorators work on any class, not just StatefulComponent
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
92
|
+
});
|
|
59
93
|
}
|
|
60
94
|
//# 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": "0.
|
|
3
|
+
"version": "1.0.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": "0.
|
|
15
|
+
"@praxisjs/core": "1.0.0",
|
|
16
|
+
"@praxisjs/decorators": "0.5.0"
|
|
16
17
|
},
|
|
17
18
|
"devDependencies": {
|
|
18
19
|
"@types/node": "^25.3.0",
|
|
@@ -93,4 +93,80 @@ describe("Container", () => {
|
|
|
93
93
|
const c = new Container();
|
|
94
94
|
expect(c.register(A).register(B)).toBe(c);
|
|
95
95
|
});
|
|
96
|
+
|
|
97
|
+
it("resolves prop injections from di:props metadata", () => {
|
|
98
|
+
class Logger {}
|
|
99
|
+
class Service {
|
|
100
|
+
logger!: Logger;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const c = new Container();
|
|
104
|
+
c.register(Logger);
|
|
105
|
+
c.register(Service);
|
|
106
|
+
|
|
107
|
+
// Simulate what the @Inject decorator does — set di:props metadata
|
|
108
|
+
const propsMap = new Map<string, unknown>();
|
|
109
|
+
propsMap.set("logger", Logger);
|
|
110
|
+
Reflect.defineMetadata("di:props", propsMap, Service.prototype);
|
|
111
|
+
|
|
112
|
+
const instance = c.resolve(Service);
|
|
113
|
+
expect(instance.logger).toBeInstanceOf(Logger);
|
|
114
|
+
|
|
115
|
+
// Cleanup
|
|
116
|
+
Reflect.deleteMetadata("di:props", Service.prototype);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("child container falls back to parent for unregistered service", () => {
|
|
120
|
+
class MyDep {}
|
|
121
|
+
const parent = new Container();
|
|
122
|
+
parent.register(MyDep);
|
|
123
|
+
const child = parent.createChild();
|
|
124
|
+
expect(child.resolve(MyDep)).toBeInstanceOf(MyDep);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("grandchild resolves from grandparent (three-level chain)", () => {
|
|
128
|
+
const URL_TOKEN = token<string>("GRANDPARENT_URL");
|
|
129
|
+
const grandparent = new Container();
|
|
130
|
+
grandparent.registerValue(URL_TOKEN, "https://grandparent.com");
|
|
131
|
+
const child = grandparent.createChild();
|
|
132
|
+
const grandchild = child.createChild();
|
|
133
|
+
expect(grandchild.resolve(URL_TOKEN)).toBe("https://grandparent.com");
|
|
134
|
+
});
|
|
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
|
+
|
|
156
|
+
it("registering the same class twice replaces the previous registration", () => {
|
|
157
|
+
class Counter {
|
|
158
|
+
value = Math.random();
|
|
159
|
+
}
|
|
160
|
+
const c = new Container();
|
|
161
|
+
c.register(Counter, { scope: "singleton" });
|
|
162
|
+
const first = c.resolve(Counter);
|
|
163
|
+
|
|
164
|
+
// Re-register resets the descriptor (no cached instance)
|
|
165
|
+
c.register(Counter, { scope: "singleton" });
|
|
166
|
+
const second = c.resolve(Counter);
|
|
167
|
+
|
|
168
|
+
// After re-registration, a new singleton is created
|
|
169
|
+
expect(second).toBeInstanceOf(Counter);
|
|
170
|
+
expect(second).not.toBe(first);
|
|
171
|
+
});
|
|
96
172
|
});
|