@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.
@@ -1 +1 @@
1
- {"version":3,"file":"container.js","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"container.js","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"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"}
@@ -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
- export declare function Inject<T>(dep: Constructor<T> | Token<T>): (_value: undefined, context: ClassFieldDecoratorContext) => void;
4
- export declare function InjectContainer(): (_value: undefined, context: ClassFieldDecoratorContext) => void;
5
- export declare function useService<T>(dep: Constructor<T> | Token<T>): T;
6
- export declare function createScope(configure?: (c: Container) => void): Container;
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
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,SAAS,EAAE,KAAK,EAAE,KAAK,WAAW,EAAE,KAAK,iBAAiB,EAAG,MAAM,aAAa,CAAC;AAE1G,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,IACvC,OAAO,WAAW,EAAE,UAAU,qBAAqB,KAAG,IAAI,CAG5E;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IACrC,QAAQ,SAAS,EAAE,SAAS,0BAA0B,KAAG,IAAI,CAsC/E;AAED,wBAAgB,eAAe,KACZ,QAAQ,SAAS,EAAE,SAAS,0BAA0B,KAAG,IAAI,CAW/E;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAE/D;AAED,wBAAgB,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,GAAG,SAAS,CAIzE"}
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"}
@@ -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
- return function (_value, context) {
9
- const cache = new WeakMap();
10
- const propertyKey = String(context.name);
11
- context.addInitializer(function () {
12
- Object.defineProperty(this, context.name, {
13
- get() {
14
- if (!cache.has(this)) {
15
- let resolved;
16
- try {
17
- resolved = container.resolve(dep);
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
- catch (err) {
20
- throw new Error(`[Inject] Failed to resolve "${dep instanceof Token
21
- ? dep.toString()
22
- : dep.name}" in "${this.constructor.name}.${propertyKey}": ${err.message}`);
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
- cache.set(this, resolved);
25
- }
26
- return cache.get(this);
70
+ },
27
71
  },
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
- }
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
- return function (_value, context) {
41
- context.addInitializer(function () {
42
- Object.defineProperty(this, context.name, {
43
- get() {
44
- return container;
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
- enumerable: true,
47
- configurable: true,
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
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAkB,KAAK,EAA6C,MAAM,aAAa,CAAC;AAE1G,MAAM,UAAU,UAAU,CAAC,UAA6B,EAAE;IACxD,OAAO,UAAU,KAAkB,EAAE,QAA+B;QAClE,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,MAAM,CAAI,GAA8B;IACtD,OAAO,UAAU,MAAiB,EAAE,OAAmC;QACrE,MAAM,KAAK,GAAG,IAAI,OAAO,EAAa,CAAC;QACvC,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAEzC,OAAO,CAAC,cAAc,CAAC;YACrB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE;gBACxC,GAAG;oBACD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;wBACrB,IAAI,QAAW,CAAC;wBAChB,IAAI,CAAC;4BACH,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,GAAqB,CAAC,CAAC;wBACtD,CAAC;wBAAC,OAAO,GAAG,EAAE,CAAC;4BACb,MAAM,IAAI,KAAK,CACb,+BACE,GAAG,YAAY,KAAK;gCAClB,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE;gCAChB,CAAC,CAAE,GAAmB,CAAC,IAC3B,SAAU,IAA0C,CAAC,WAAW,CAAC,IAAI,IAAI,WAAW,MAAO,GAAa,CAAC,OAAO,EAAE,CACnH,CAAC;wBACJ,CAAC;wBACD,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;oBAC5B,CAAC;oBACD,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAM,CAAC;gBAC9B,CAAC;gBAED,GAAG,CAAC,MAAe;oBACjB,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;wBAC1C,OAAO,CAAC,IAAI,CACV,aAAa,WAAW,mEAAmE,CAC5F,CAAC;oBACJ,CAAC;gBACH,CAAC;gBAED,UAAU,EAAE,IAAI;gBAChB,YAAY,EAAE,IAAI;aACnB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,UAAU,MAAiB,EAAE,OAAmC;QACrE,OAAO,CAAC,cAAc,CAAC;YACrB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE;gBACxC,GAAG;oBACD,OAAO,SAAS,CAAC;gBACnB,CAAC;gBACD,UAAU,EAAE,IAAI;gBAChB,YAAY,EAAE,IAAI;aACnB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAI,GAA8B;IAC1D,OAAO,SAAS,CAAC,OAAO,CAAC,GAAqB,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,SAAkC;IAC5D,MAAM,KAAK,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;IACtC,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC;IACnB,OAAO,KAAK,CAAC;AACf,CAAC"}
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, useService, createScope, } from "./decorators";
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
@@ -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,UAAU,EACV,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACjE,YAAY,EACV,KAAK,EACL,iBAAiB,EACjB,WAAW,EACX,iBAAiB,GAClB,MAAM,aAAa,CAAC"}
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, useService, createScope, } from "./decorators";
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,UAAU,EACV,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC"}
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.2.3",
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": "0.4.2"
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
  });