@praxisjs/di 1.3.13 → 1.3.14

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 CHANGED
@@ -1,5 +1,36 @@
1
1
  # @praxisjs/di
2
2
 
3
+ ## 1.3.14
4
+
5
+ ### Patch Changes
6
+
7
+ - 75115a9: Decorated fields are defined by their innermost decorator. With `@praxisjs/vite-plugin`, `@State()`, `@Prop()`, every built-in field decorator and `createFieldDecorator` bindings install their property directly with the field's initial value instead of replacing a plain value, which kept every instance in V8's slow dictionary mode. New `addClaimableInitializer(context, init)` export for field decorators written against the TC39 API.
8
+ - 75115a9: Field decorators throw a `TypeError` when the class is defined if they decorate an `accessor`, naming the member to fix. Covers `@State()`, `@Prop()`, every built-in field decorator and `createFieldDecorator` bindings. New `assertFieldContext(context, label?)` export for field decorators written against the TC39 API directly.
9
+ - 75115a9: Published packages no longer contain their tests: package builds compile `src` without `src/**/__tests__` (through a `tsconfig.build.json`), and each package's `files` leaves test sources and stale compiled tests out of the tarball. `pnpm typecheck` still type-checks the tests.
10
+ - 75115a9: Performance pass and bug fixes across the feature packages.
11
+ - `@praxisjs/router`: static paths are matched through an index and dynamic ones with a single regex run; `RouterView` keeps a shared layout mounted across navigations; new `router.isActive(path)` (used by `Link`, so a navigation re-evaluates only the links whose state changed) and `router.destroy()`. Fixes regex characters in route paths (`.`, `(`, `+`...) being treated as patterns, and a router replaced by `createRouter()` still reacting to `popstate`. `@Route` no longer wraps the class, and the field decorators share one accessor.
12
+ - `@praxisjs/store`: plugin-wrapped actions keep their identity across reads, extensions are bound once, and writing a field inside an effect no longer subscribes the effect to it. `constructor` and inherited `Object.prototype` members are no longer reported as actions. `@Store` caches the singleton.
13
+ - `@praxisjs/motion`: `@Tween` and `@Spring` share one accessor per field and no longer start a frame loop at construction; `tween()`/`spring()` react to target writes without an effect. Fixes `reset()` leaving a tween marked as playing.
14
+ - `@praxisjs/di`: cached singletons and tokens resolve without allocating, `@Injectable` registers the class itself instead of a subclass, and a dependency cycle through `inject()` calls in constructors now reports `[DI] Circular dependency detected` instead of overflowing the stack.
15
+ - `@praxisjs/fsm`: `send()` and `reset()` no longer subscribe the calling effect to the machine state, transitions allocate nothing per send, and `@StateMachine` shares one accessor.
16
+ - `@praxisjs/head`: tag diffing without `JSON.stringify` or a full `<head>` query per update; updating or removing an entry below the top one leaves the document alone, and removing an unknown id no longer resets the title.
17
+ - `@praxisjs/content`: each entry is loaded, parsed and rendered once and shared by `getCollection`, `getPage` and `getEntry`; `getEntry` looks the slug up in an index; schema defaults are built once per class.
18
+
19
+ - 75115a9: Smaller production bundles. The framework and feature packages are marked `"sideEffects": false`, so bundlers leave out the modules an app does not use (`For`/`Index` and `selector()` were kept in every bundle by their shape keepers). `@praxisjs/core`'s graph gates its observer instrumentation on a boolean that minifiers fold away when no observer is ever installed. `@praxisjs/runtime` passes `Index`'s positional update to the list view, so `For`-only apps leave it out, and builds its event prop table from camel-case names. `@praxisjs/devtools` is built one file per module and its decorators check `process.env.NODE_ENV` inline, so `@Trace`/`@Debug` in production code no longer bundle the registry and the panel. `@praxisjs/vite-plugin` serves TypeScript's decorator helpers and the claimed fields' helper from one virtual module (`virtual:praxisjs/decorators`) instead of writing them into every decorated file, and replaces the identical copies `tsc` wrote into ES module libraries.
20
+ - Updated dependencies [75115a9]
21
+ - Updated dependencies [75115a9]
22
+ - Updated dependencies [75115a9]
23
+ - Updated dependencies [75115a9]
24
+ - Updated dependencies [75115a9]
25
+ - Updated dependencies [75115a9]
26
+ - Updated dependencies [75115a9]
27
+ - Updated dependencies [75115a9]
28
+ - Updated dependencies [75115a9]
29
+ - Updated dependencies [75115a9]
30
+ - Updated dependencies [75115a9]
31
+ - @praxisjs/decorators@2.0.0
32
+ - @praxisjs/core@2.3.0
33
+
3
34
  ## 1.3.13
4
35
 
5
36
  ### Patch Changes
@@ -21,7 +21,7 @@ export declare class Container {
21
21
  register<T>(target: Constructor<T>, options?: InjectableOptions): this;
22
22
  registerValue<T>(token: Token<T>, value: T): this;
23
23
  registerFactory<T>(token: Token<T>, factory: (container: Container) => T): this;
24
- resolve<T>(target: Constructor<T> | Token<T>, resolutionStack?: Set<Constructor<unknown> | Token<unknown>>): T;
24
+ resolve<T>(target: Constructor<T> | Token<T>, resolutionStack?: Set<Constructor | Token<unknown>>): T;
25
25
  private instantiate;
26
26
  createChild(): Container;
27
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GAAG,WAAW,GAAG,WAAW,CAAC;AAE9C,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAGD,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,OAAO,IAAI,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AAEjE,qBAAa,KAAK,CAAC,EAAE;IACnB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBACjB,WAAW,EAAE,MAAM;IAG/B,QAAQ;CAGT;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAEtD;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoD;IAC7E,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAY;gBAExB,MAAM,CAAC,EAAE,SAAS;IAI9B,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,iBAAsB,GAAG,IAAI;IAQ1E,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAKjD,eAAe,CAAC,CAAC,EACf,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,OAAO,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,CAAC,GACnC,IAAI;IAKP,OAAO,CAAC,CAAC,EACP,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EACjC,eAAe,6CAA0C,GACxD,CAAC;IAwCJ,OAAO,CAAC,WAAW;IAWnB,WAAW,IAAI,SAAS;CAGzB;AAED,eAAO,MAAM,SAAS,WAAkB,CAAC"}
1
+ {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GAAG,WAAW,GAAG,WAAW,CAAC;AAE9C,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAGD,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,OAAO,IAAI,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AAEjE,qBAAa,KAAK,CAAC,EAAE;IACnB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBACjB,WAAW,EAAE,MAAM;IAG/B,QAAQ;CAGT;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAEtD;AAMD,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoD;IAC7E,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAY;gBAExB,MAAM,CAAC,EAAE,SAAS;IAI9B,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,iBAAsB,GAAG,IAAI;IAQ1E,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAKjD,eAAe,CAAC,CAAC,EACf,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,OAAO,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,CAAC,GACnC,IAAI;IAKP,OAAO,CAAC,CAAC,EACP,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EACjC,eAAe,GAAE,GAAG,CAAC,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,CAAa,GAC7D,CAAC;IAyCJ,OAAO,CAAC,WAAW;IAWnB,WAAW,IAAI,SAAS;CAGzB;AAED,eAAO,MAAM,SAAS,WAAkB,CAAC"}
package/dist/container.js CHANGED
@@ -10,6 +10,9 @@ export class Token {
10
10
  export function token(description) {
11
11
  return new Token(description);
12
12
  }
13
+ // Shared by every resolution: a constructor that calls `inject()` starts a new resolve() call, and a
14
+ // cycle running through it must still be caught instead of overflowing the stack.
15
+ const resolving = new Set();
13
16
  export class Container {
14
17
  services = new Map();
15
18
  parent;
@@ -31,35 +34,39 @@ export class Container {
31
34
  this.services.set(token, factory(this));
32
35
  return this;
33
36
  }
34
- resolve(target, resolutionStack = new Set()) {
37
+ resolve(target, resolutionStack = resolving) {
35
38
  const key = target;
36
- if (resolutionStack.has(key)) {
39
+ if (resolutionStack.size !== 0 && resolutionStack.has(key)) {
37
40
  const chain = [...resolutionStack, key]
38
41
  .map((k) => (k instanceof Token ? k.toString() : k.name))
39
42
  .join(" → ");
40
43
  throw new Error(`[DI] Circular dependency detected: ${chain}`);
41
44
  }
45
+ const entry = this.services.get(key);
46
+ if (target instanceof Token) {
47
+ if (entry !== undefined)
48
+ return entry;
49
+ if (this.parent)
50
+ return this.parent.resolve(target, resolutionStack);
51
+ throw new Error(`[DI] Token not registered: ${target.toString()}`);
52
+ }
53
+ if (!entry) {
54
+ if (this.parent)
55
+ return this.parent.resolve(target, resolutionStack);
56
+ throw new Error(`[DI] Service not registered: ${target.name}`);
57
+ }
58
+ const descriptor = entry;
59
+ const singleton = descriptor.scope === "singleton";
60
+ if (singleton && descriptor.instance !== undefined)
61
+ return descriptor.instance;
62
+ // Only a key that is being constructed goes on the stack, so delegating to a parent is never
63
+ // mistaken for a cycle.
42
64
  resolutionStack.add(key);
43
65
  try {
44
- const entry = this.services.get(key);
45
- if (target instanceof Token) {
46
- if (entry !== undefined)
47
- return entry;
48
- if (this.parent)
49
- return this.parent.resolve(target);
50
- throw new Error(`[DI] Token not registered: ${target.toString()}`);
51
- }
52
- if (!entry) {
53
- if (this.parent)
54
- return this.parent.resolve(target);
55
- throw new Error(`[DI] Service not registered: ${target.name}`);
56
- }
57
- const descriptor = entry;
58
- if (descriptor.scope === "singleton") {
59
- descriptor.instance ??= this.instantiate(descriptor.target);
60
- return descriptor.instance;
61
- }
62
- return this.instantiate(descriptor.target);
66
+ const instance = this.instantiate(descriptor.target);
67
+ if (singleton)
68
+ descriptor.instance = instance;
69
+ return instance;
63
70
  }
64
71
  finally {
65
72
  resolutionStack.delete(key);
@@ -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,9 @@
1
1
  import { type Container, Token, type Constructor, type InjectableOptions } from "./container";
2
+ type DIFieldDecorator = (_value: undefined, context: ClassFieldDecoratorContext<any>) => void;
2
3
  export declare function Injectable(options?: InjectableOptions): (value: Constructor, _context: ClassDecoratorContext) => void;
3
4
  export declare function Scope(configure?: (c: Container) => void): (cls: new (...args: any[]) => any, _ctx: ClassDecoratorContext) => any;
4
- export declare function Inject<T>(dep: Constructor<T> | Token<T>): (_value: undefined, context: ClassFieldDecoratorContext<any>) => void;
5
+ export declare function Inject<T>(dep: Constructor<T> | Token<T>): DIFieldDecorator;
5
6
  export declare function inject<T>(dep: Constructor<T> | Token<T>): T;
6
- export declare function InjectContainer(): (_value: undefined, context: ClassFieldDecoratorContext<any>) => void;
7
+ export declare function InjectContainer(): DIFieldDecorator;
8
+ export {};
7
9
  //# sourceMappingURL=decorators.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAGA,OAAO,EAAa,KAAK,SAAS,EAAE,KAAK,EAAE,KAAK,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AA0BzG,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAEmB,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,qBAAqB,KAAK,IAAI,CACzI;AA8BD,wBAAgB,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,GAEkB,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAAE,IAAI,EAAE,qBAAqB,KAAK,GAAG,CAC/I;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,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAE3D;AAID,wBAAgB,eAAe,IAkBZ,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,CAAC,GAAG,CAAC,KAAK,IAAI,CACvF"}
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAGA,OAAO,EAAa,KAAK,SAAS,EAAE,KAAK,EAAE,KAAK,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAKzG,KAAK,gBAAgB,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;AAY9F,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,IACvC,OAAO,WAAW,EAAE,UAAU,qBAAqB,KAAG,IAAI,CAG5E;AA8BD,wBAAgB,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,GAEkB,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAAE,IAAI,EAAE,qBAAqB,KAAK,GAAG,CAC/I;AAQD,wBAAgB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAqC1E;AAID,wBAAgB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAE3D;AAYD,wBAAgB,eAAe,IAAI,gBAAgB,CAQlD"}
@@ -1,27 +1,16 @@
1
- import { createFieldDecorator, createClassDecorator, ClassBehavior } from "@praxisjs/decorators";
1
+ import { createClassDecorator, ClassBehavior, addClaimableInitializer, assertFieldContext } from "@praxisjs/decorators";
2
2
  import { container, Token } from "./container";
3
- // Maps instances to their scoped child containers (set by @Scope).
4
- const scopedContainers = new WeakMap();
3
+ // The child container @Scope creates for an instance.
4
+ const SCOPE = Symbol("praxis.di.scope");
5
5
  function resolveFrom(instance) {
6
- return scopedContainers.get(instance) ?? container;
6
+ return instance[SCOPE] ?? container;
7
7
  }
8
8
  // ── @Injectable ───────────────────────────────────────────────────────────────
9
- class InjectableBehavior extends ClassBehavior {
10
- options;
11
- constructor(options) {
12
- super();
13
- this.options = options;
14
- }
15
- create(_instance) {
16
- return {};
17
- }
18
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
- initialize(Enhanced, _original) {
20
- container.register(Enhanced, this.options);
21
- }
22
- }
9
+ // Registration is all it does, so the class itself is registered rather than a per-instance subclass.
23
10
  export function Injectable(options = {}) {
24
- return createClassDecorator(new InjectableBehavior(options));
11
+ return function (value, _context) {
12
+ container.register(value, options);
13
+ };
25
14
  }
26
15
  // ── @Scope ────────────────────────────────────────────────────────────────────
27
16
  /**
@@ -46,7 +35,7 @@ class ScopeBehavior extends ClassBehavior {
46
35
  create(instance) {
47
36
  const child = container.createChild();
48
37
  this.configure?.(child);
49
- scopedContainers.set(instance, child);
38
+ instance[SCOPE] = child;
50
39
  return {};
51
40
  }
52
41
  }
@@ -55,62 +44,64 @@ export function Scope(configure) {
55
44
  return createClassDecorator(new ScopeBehavior(configure));
56
45
  }
57
46
  // ── @Inject ───────────────────────────────────────────────────────────────────
47
+ function depName(dep) {
48
+ return dep instanceof Token ? dep.toString() : dep.name;
49
+ }
58
50
  export function Inject(dep) {
59
- const cache = new WeakMap();
60
- return createFieldDecorator({
61
- bind(_instance, name, _initialValue) {
62
- return {
63
- descriptor: {
64
- get() {
65
- if (!cache.has(this)) {
66
- const c = resolveFrom(this);
67
- let resolved;
68
- try {
69
- resolved = c.resolve(dep);
70
- }
71
- catch (err) {
72
- throw new Error(`[Inject] Failed to resolve "${dep instanceof Token
73
- ? dep.toString()
74
- : dep.name}" in "${this.constructor.name}.${name}": ${err.message}`);
75
- }
76
- cache.set(this, resolved);
77
- }
78
- return cache.get(this);
79
- },
80
- set(_value) {
81
- if (process.env.NODE_ENV !== "production") {
82
- console.warn(`[Inject] "${name}" is managed by the DI container and cannot be assigned directly.`);
83
- }
84
- },
85
- },
86
- };
87
- },
88
- // DI decorators work on any class, not just StatefulComponent
89
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
90
- });
51
+ return function (_value, context) {
52
+ assertFieldContext(context);
53
+ const name = String(context.name);
54
+ // The resolved service lives on the instance under this symbol; the accessor is shared.
55
+ const slot = Symbol(`praxis.inject.${name}`);
56
+ const descriptor = {
57
+ get() {
58
+ const cached = this[slot];
59
+ if (cached !== undefined)
60
+ return cached;
61
+ let resolved;
62
+ try {
63
+ resolved = resolveFrom(this).resolve(dep);
64
+ }
65
+ catch (err) {
66
+ throw new Error(`[Inject] Failed to resolve "${depName(dep)}" in "${this.constructor.name}.${name}": ${err.message}`);
67
+ }
68
+ this[slot] = resolved;
69
+ return resolved;
70
+ },
71
+ set(_value) {
72
+ if (process.env.NODE_ENV !== "production") {
73
+ console.warn(`[Inject] "${name}" is managed by the DI container and cannot be assigned directly.`);
74
+ }
75
+ },
76
+ enumerable: true,
77
+ configurable: true,
78
+ };
79
+ addClaimableInitializer(context, function () {
80
+ // Reserving the slot up front keeps every instance of the class on the same shape.
81
+ this[slot] = undefined;
82
+ Object.defineProperty(this, context.name, descriptor);
83
+ });
84
+ };
91
85
  }
92
86
  // ── inject() ──────────────────────────────────────────────────────────────────
93
87
  export function inject(dep) {
94
88
  return container.resolve(dep);
95
89
  }
96
90
  // ── @InjectContainer ──────────────────────────────────────────────────────────
91
+ const containerDescriptor = {
92
+ get() {
93
+ return resolveFrom(this);
94
+ },
95
+ enumerable: true,
96
+ configurable: true,
97
+ };
97
98
  export function InjectContainer() {
98
- const cache = new WeakMap();
99
- return createFieldDecorator({
100
- bind(_instance, _name, _initialValue) {
101
- return {
102
- descriptor: {
103
- get() {
104
- if (!cache.has(this)) {
105
- cache.set(this, resolveFrom(this));
106
- }
107
- return cache.get(this);
108
- },
109
- },
110
- };
111
- },
112
- // DI decorators work on any class, not just StatefulComponent
113
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
114
- });
99
+ return function (_value, context) {
100
+ assertFieldContext(context);
101
+ const name = context.name;
102
+ addClaimableInitializer(context, function () {
103
+ Object.defineProperty(this, name, containerDescriptor);
104
+ });
105
+ };
115
106
  }
116
107
  //# sourceMappingURL=decorators.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,aAAa,EAA4C,MAAM,sBAAsB,CAAC;AAE3I,OAAO,EAAE,SAAS,EAAkB,KAAK,EAA4C,MAAM,aAAa,CAAC;AAEzG,mEAAmE;AACnE,MAAM,gBAAgB,GAAG,IAAI,OAAO,EAAqB,CAAC;AAE1D,SAAS,WAAW,CAAC,QAAgB;IACnC,OAAO,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC;AACrD,CAAC;AAED,iFAAiF;AAEjF,MAAM,kBAAmB,SAAQ,aAAa;IACf;IAA7B,YAA6B,OAA0B;QACrD,KAAK,EAAE,CAAC;QADmB,YAAO,GAAP,OAAO,CAAmB;IAEvD,CAAC;IAED,MAAM,CAAC,SAAwB;QAC7B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,8DAA8D;IAC9D,UAAU,CAAC,QAAyC,EAAE,SAA0C;QAC9F,SAAS,CAAC,QAAQ,CAAC,QAAuB,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5D,CAAC;CACF;AAED,MAAM,UAAU,UAAU,CAAC,UAA6B,EAAE;IAExD,OAAO,oBAAoB,CAAC,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAA6E,CAAC;AAC3I,CAAC;AAED,iFAAiF;AAEjF;;;;;;;;;;;;GAYG;AACH,MAAM,aAAc,SAAQ,aAAa;IACV;IAA7B,YAA6B,SAAkC;QAC7D,KAAK,EAAE,CAAC;QADmB,cAAS,GAAT,SAAS,CAAyB;IAE/D,CAAC;IAED,MAAM,CAAC,QAAuB;QAC5B,MAAM,KAAK,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;QACtC,IAAI,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC;QACxB,gBAAgB,CAAC,GAAG,CAAC,QAAkB,EAAE,KAAK,CAAC,CAAC;QAChD,OAAO,EAAE,CAAC;IACZ,CAAC;CACF;AAED,MAAM,UAAU,KAAK,CAAC,SAAkC;IACtD,8DAA8D;IAC9D,OAAO,oBAAoB,CAAC,IAAI,aAAa,CAAC,SAAS,CAAC,CAAsF,CAAC;AACjJ,CAAC;AAED,iFAAiF;AAEjF,MAAM,UAAU,MAAM,CAAI,GAA8B;IACtD,MAAM,KAAK,GAAG,IAAI,OAAO,EAAa,CAAC;IAEvC,OAAO,oBAAoB,CAAC;QAC1B,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,aAAa;YACjC,OAAO;gBACL,UAAU,EAAE;oBACV,GAAG;wBACD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;4BACrB,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;4BAC5B,IAAI,QAAW,CAAC;4BAChB,IAAI,CAAC;gCACH,QAAQ,GAAG,CAAC,CAAC,OAAO,CAAC,GAAqB,CAAC,CAAC;4BAC9C,CAAC;4BAAC,OAAO,GAAG,EAAE,CAAC;gCACb,MAAM,IAAI,KAAK,CACb,+BACE,GAAG,YAAY,KAAK;oCAClB,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE;oCAChB,CAAC,CAAE,GAAmB,CAAC,IAC3B,SAAU,IAA0C,CAAC,WAAW,CAAC,IAAI,IAAI,IAAI,MAAO,GAAa,CAAC,OAAO,EAAE,CAC5G,CAAC;4BACJ,CAAC;4BACD,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;wBAC5B,CAAC;wBACD,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAM,CAAC;oBAC9B,CAAC;oBACD,GAAG,CAAC,MAAe;wBACjB,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;4BAC1C,OAAO,CAAC,IAAI,CACV,aAAa,IAAI,mEAAmE,CACrF,CAAC;wBACJ,CAAC;oBACH,CAAC;iBACF;aACF,CAAC;QACJ,CAAC;QACH,8DAA8D;QAC9D,8DAA8D;KAC7D,CAAqF,CAAC;AACzF,CAAC;AAED,iFAAiF;AAEjF,MAAM,UAAU,MAAM,CAAI,GAA8B;IACtD,OAAO,SAAS,CAAC,OAAO,CAAC,GAAqB,CAAC,CAAC;AAClD,CAAC;AAED,iFAAiF;AAEjF,MAAM,UAAU,eAAe;IAC7B,MAAM,KAAK,GAAG,IAAI,OAAO,EAAqB,CAAC;IAE/C,OAAO,oBAAoB,CAAC;QAC1B,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,aAAa;YAClC,OAAO;gBACL,UAAU,EAAE;oBACV,GAAG;wBACD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;4BACrB,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;wBACrC,CAAC;wBACD,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBACzB,CAAC;iBACF;aACF,CAAC;QACJ,CAAC;QACH,8DAA8D;QAC9D,8DAA8D;KAC7D,CAAqF,CAAC;AACzF,CAAC"}
1
+ {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@praxisjs/di",
3
- "version": "1.3.13",
3
+ "version": "1.3.14",
4
4
  "type": "module",
5
+ "sideEffects": false,
5
6
  "main": "./dist/index.js",
6
7
  "types": "./dist/index.d.ts",
7
8
  "exports": {
@@ -10,16 +11,24 @@
10
11
  "types": "./dist/index.d.ts"
11
12
  }
12
13
  },
14
+ "files": [
15
+ "dist",
16
+ "src",
17
+ "!dist/**/__tests__",
18
+ "!src/**/__tests__",
19
+ "CHANGELOG.md"
20
+ ],
13
21
  "dependencies": {
14
- "@praxisjs/core": "2.2.0",
15
- "@praxisjs/decorators": "1.7.0"
22
+ "@praxisjs/core": "2.3.0",
23
+ "@praxisjs/decorators": "2.0.0"
16
24
  },
17
25
  "devDependencies": {
18
26
  "@types/node": "^25.9.1",
19
27
  "typescript": "^6.0.3"
20
28
  },
21
29
  "scripts": {
22
- "build": "tsc",
23
- "dev": "tsc --watch"
30
+ "build": "tsc -p tsconfig.build.json",
31
+ "dev": "tsc -p tsconfig.build.json --watch",
32
+ "typecheck": "tsc --noEmit"
24
33
  }
25
34
  }
package/src/container.ts CHANGED
@@ -27,6 +27,10 @@ export function token<T>(description: string): Token<T> {
27
27
  return new Token<T>(description);
28
28
  }
29
29
 
30
+ // Shared by every resolution: a constructor that calls `inject()` starts a new resolve() call, and a
31
+ // cycle running through it must still be caught instead of overflowing the stack.
32
+ const resolving = new Set<Constructor | Token<unknown>>();
33
+
30
34
  export class Container {
31
35
  private readonly services = new Map<Constructor | Token<unknown>, unknown>();
32
36
  private readonly parent?: Container;
@@ -58,42 +62,43 @@ export class Container {
58
62
 
59
63
  resolve<T>(
60
64
  target: Constructor<T> | Token<T>,
61
- resolutionStack = new Set<Constructor | Token<unknown>>(),
65
+ resolutionStack: Set<Constructor | Token<unknown>> = resolving,
62
66
  ): T {
63
67
  const key = target as Constructor | Token<unknown>;
64
68
 
65
- if (resolutionStack.has(key)) {
69
+ if (resolutionStack.size !== 0 && resolutionStack.has(key)) {
66
70
  const chain = [...resolutionStack, key]
67
71
  .map((k) => (k instanceof Token ? k.toString() : k.name))
68
72
  .join(" → ");
69
73
  throw new Error(`[DI] Circular dependency detected: ${chain}`);
70
74
  }
71
75
 
72
- resolutionStack.add(key);
73
- try {
74
- const entry = this.services.get(key);
76
+ const entry = this.services.get(key);
75
77
 
76
- if (target instanceof Token) {
77
- if (entry !== undefined) return entry as T;
78
- if (this.parent) return this.parent.resolve(target);
79
- throw new Error(`[DI] Token not registered: ${target.toString()}`);
80
- }
81
-
82
- if (!entry) {
83
- if (this.parent) return this.parent.resolve(target);
84
- throw new Error(
85
- `[DI] Service not registered: ${(target as Constructor).name}`,
86
- );
87
- }
78
+ if (target instanceof Token) {
79
+ if (entry !== undefined) return entry as T;
80
+ if (this.parent) return this.parent.resolve(target, resolutionStack);
81
+ throw new Error(`[DI] Token not registered: ${target.toString()}`);
82
+ }
88
83
 
89
- const descriptor = entry as ServiceDescriptor;
84
+ if (!entry) {
85
+ if (this.parent) return this.parent.resolve(target, resolutionStack);
86
+ throw new Error(
87
+ `[DI] Service not registered: ${(target as Constructor).name}`,
88
+ );
89
+ }
90
90
 
91
- if (descriptor.scope === "singleton") {
92
- descriptor.instance ??= this.instantiate(descriptor.target);
93
- return descriptor.instance as T;
94
- }
91
+ const descriptor = entry as ServiceDescriptor;
92
+ const singleton = descriptor.scope === "singleton";
93
+ if (singleton && descriptor.instance !== undefined) return descriptor.instance as T;
95
94
 
96
- return this.instantiate(descriptor.target) as T;
95
+ // Only a key that is being constructed goes on the stack, so delegating to a parent is never
96
+ // mistaken for a cycle.
97
+ resolutionStack.add(key);
98
+ try {
99
+ const instance = this.instantiate(descriptor.target);
100
+ if (singleton) descriptor.instance = instance;
101
+ return instance as T;
97
102
  } finally {
98
103
  resolutionStack.delete(key);
99
104
  }