ts-ioc-container 54.0.0 → 54.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.
@@ -6,6 +6,7 @@ const ContainerDisposedError_1 = require("../errors/ContainerDisposedError");
6
6
  const MetadataInjector_1 = require("../injector/MetadataInjector");
7
7
  const AliasMap_1 = require("./AliasMap");
8
8
  const DependencyNotFoundError_1 = require("../errors/DependencyNotFoundError");
9
+ const ContainerNotFoundError_1 = require("../errors/ContainerNotFoundError");
9
10
  const basic_1 = require("../utils/basic");
10
11
  const array_1 = require("../utils/array");
11
12
  class Container {
@@ -131,6 +132,21 @@ class Container {
131
132
  getScopes() {
132
133
  return [...this.scopes];
133
134
  }
135
+ hasInstance(instance) {
136
+ return this.instances.includes(instance);
137
+ }
138
+ getScopeByInstanceOrFail(instance) {
139
+ this.validateContainer();
140
+ const queue = [this];
141
+ while (queue.length > 0) {
142
+ const scope = queue.shift();
143
+ if (scope.hasInstance(instance)) {
144
+ return scope;
145
+ }
146
+ queue.push(...scope.getScopes());
147
+ }
148
+ throw new ContainerNotFoundError_1.ContainerNotFoundError('Cannot find scope for the given instance');
149
+ }
134
150
  removeScope(child) {
135
151
  this.scopes = this.scopes.filter((s) => s !== child);
136
152
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.EmptyContainer = void 0;
4
4
  const MethodNotImplementedError_1 = require("../errors/MethodNotImplementedError");
5
5
  const DependencyNotFoundError_1 = require("../errors/DependencyNotFoundError");
6
+ const ContainerNotFoundError_1 = require("../errors/ContainerNotFoundError");
6
7
  class EmptyContainer {
7
8
  get isDisposed() {
8
9
  throw new MethodNotImplementedError_1.MethodNotImplementedError();
@@ -14,9 +15,15 @@ class EmptyContainer {
14
15
  getScopes() {
15
16
  return [];
16
17
  }
18
+ getScopeByInstanceOrFail(instance) {
19
+ throw new ContainerNotFoundError_1.ContainerNotFoundError('Cannot find scope for the given instance');
20
+ }
17
21
  getInstances() {
18
22
  return [];
19
23
  }
24
+ hasInstance(instance) {
25
+ return false;
26
+ }
20
27
  createScope() {
21
28
  throw new MethodNotImplementedError_1.MethodNotImplementedError();
22
29
  }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ContainerNotFoundError = void 0;
4
+ const ContainerError_1 = require("./ContainerError");
5
+ class ContainerNotFoundError extends ContainerError_1.ContainerError {
6
+ name = 'ContainerNotFoundError';
7
+ constructor(message) {
8
+ super(message);
9
+ Object.setPrototypeOf(this, ContainerNotFoundError.prototype);
10
+ }
11
+ }
12
+ exports.ContainerNotFoundError = ContainerNotFoundError;
package/cjm/hooks/hook.js CHANGED
@@ -13,21 +13,40 @@ exports.toHookFn = toHookFn;
13
13
  const getReflectionTarget = (target) => {
14
14
  return (0, proxy_1.isProxy)(target) ? (0, proxy_1.getProxyTarget)(target) : target;
15
15
  };
16
- // Get hooks metadata
16
+ // Walk the constructor's prototype chain (most-derived first) collecting each class.
17
+ const getConstructorChain = (ctor) => {
18
+ const chain = [];
19
+ let current = ctor;
20
+ while (typeof current === 'function' && current !== Function.prototype) {
21
+ chain.push(current);
22
+ current = Object.getPrototypeOf(current);
23
+ }
24
+ return chain;
25
+ };
26
+ // Get hooks metadata, merging hooks declared on parent (extended-from) classes.
27
+ // Hooks are collected from base to derived so a derived class's hooks for the same
28
+ // method name take precedence over (replace) the parent's.
17
29
  function getHooks(target, key) {
18
30
  const reflectionTarget = getReflectionTarget(target);
19
- return Reflect.hasMetadata(key, reflectionTarget.constructor)
20
- ? Reflect.getMetadata(key, reflectionTarget.constructor)
21
- : new Map();
31
+ const merged = new Map();
32
+ for (const ctor of getConstructorChain(reflectionTarget.constructor).reverse()) {
33
+ const ownHooks = Reflect.getOwnMetadata(key, ctor);
34
+ if (ownHooks) {
35
+ for (const [methodName, fns] of ownHooks) {
36
+ merged.set(methodName, fns);
37
+ }
38
+ }
39
+ }
40
+ return merged;
22
41
  }
23
42
  function hasHooks(target, key) {
24
43
  const reflectionTarget = getReflectionTarget(target);
25
- return Reflect.hasMetadata(key, reflectionTarget.constructor);
44
+ return getConstructorChain(reflectionTarget.constructor).some((ctor) => Reflect.hasOwnMetadata(key, ctor));
26
45
  }
27
46
  // Hook decorator
28
47
  const hook = (key, ...fns) => (target, propertyKey) => {
29
- const hooks = Reflect.hasMetadata(key, target.constructor)
30
- ? Reflect.getMetadata(key, target.constructor)
48
+ const hooks = Reflect.hasOwnMetadata(key, target.constructor)
49
+ ? Reflect.getOwnMetadata(key, target.constructor)
31
50
  : new Map();
32
51
  hooks.set(propertyKey, (hooks.get(propertyKey) ?? []).concat(fns));
33
52
  Reflect.defineMetadata(key, hooks, target.constructor);
package/cjm/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SingleToken = exports.ClassToken = exports.toSingleAlias = exports.SingleAliasToken = exports.toGroupAlias = exports.GroupAliasToken = exports.InjectionToken = exports.HooksRunner = exports.AddOnDisposeHookModule = exports.onDispose = exports.onDisposeHooksRunner = exports.AddOnConstructHookModule = exports.onConstruct = exports.onConstructHooksRunner = exports.injectProp = exports.createHookContext = exports.createHookContextFactory = exports.HookContext = exports.hasHooks = exports.hook = exports.getHooks = exports.CannonSingletonApplyTwiceError = exports.UnexpectedHookResultError = exports.ProviderDisposedError = exports.ContainerDisposedError = exports.MethodNotImplementedError = exports.DependencyMissingKeyError = exports.DependencyNotFoundError = exports.Registration = exports.appendArgsFn = exports.appendArgs = exports.decorate = exports.singleton = exports.lazy = exports.scopeAccess = exports.scope = exports.bindTo = exports.register = exports.Provider = exports.ProxyInjector = exports.SimpleInjector = exports.resolveArgs = exports.argsFn = exports.args = exports.inject = exports.MetadataInjector = exports.Injector = exports.EmptyContainer = exports.Container = exports.isDependencyKey = void 0;
4
- exports.resolveConstructor = exports.Is = exports.pipe = exports.select = exports.once = exports.shallowCache = exports.debounce = exports.throttle = exports.handleAsyncError = exports.handleError = exports.getMethodTags = exports.methodTag = exports.getMethodLabels = exports.methodLabel = exports.getMethodMeta = exports.methodMeta = exports.getParamTags = exports.paramTag = exports.getParamLabels = exports.paramLabel = exports.getParamMeta = exports.paramMeta = exports.getClassTags = exports.classTag = exports.getClassLabels = exports.classLabel = exports.getClassMeta = exports.classMeta = exports.GroupInstanceToken = exports.ConstantToken = exports.FunctionToken = void 0;
3
+ exports.ClassToken = exports.toSingleAlias = exports.SingleAliasToken = exports.toGroupAlias = exports.GroupAliasToken = exports.InjectionToken = exports.HooksRunner = exports.AddOnDisposeHookModule = exports.onDispose = exports.onDisposeHooksRunner = exports.AddOnConstructHookModule = exports.onConstruct = exports.onConstructHooksRunner = exports.injectProp = exports.createHookContext = exports.createHookContextFactory = exports.HookContext = exports.hasHooks = exports.hook = exports.getHooks = exports.CannonSingletonApplyTwiceError = exports.UnexpectedHookResultError = exports.ProviderDisposedError = exports.ContainerDisposedError = exports.MethodNotImplementedError = exports.DependencyMissingKeyError = exports.ContainerNotFoundError = exports.DependencyNotFoundError = exports.Registration = exports.appendArgsFn = exports.appendArgs = exports.decorate = exports.singleton = exports.lazy = exports.scopeAccess = exports.scope = exports.bindTo = exports.register = exports.Provider = exports.ProxyInjector = exports.SimpleInjector = exports.resolveArgs = exports.argsFn = exports.args = exports.inject = exports.MetadataInjector = exports.Injector = exports.EmptyContainer = exports.Container = exports.isDependencyKey = void 0;
4
+ exports.resolveConstructor = exports.Is = exports.pipe = exports.select = exports.once = exports.shallowCache = exports.debounce = exports.throttle = exports.handleAsyncError = exports.handleError = exports.getMethodTags = exports.methodTag = exports.getMethodLabels = exports.methodLabel = exports.getMethodMeta = exports.methodMeta = exports.getParamTags = exports.paramTag = exports.getParamLabels = exports.paramLabel = exports.getParamMeta = exports.paramMeta = exports.getClassTags = exports.classTag = exports.getClassLabels = exports.classLabel = exports.getClassMeta = exports.classMeta = exports.GroupInstanceToken = exports.ConstantToken = exports.FunctionToken = exports.SingleToken = void 0;
5
5
  // Containers
6
6
  var IContainer_1 = require("./container/IContainer");
7
7
  Object.defineProperty(exports, "isDependencyKey", { enumerable: true, get: function () { return IContainer_1.isDependencyKey; } });
@@ -40,6 +40,8 @@ Object.defineProperty(exports, "Registration", { enumerable: true, get: function
40
40
  // Errors
41
41
  var DependencyNotFoundError_1 = require("./errors/DependencyNotFoundError");
42
42
  Object.defineProperty(exports, "DependencyNotFoundError", { enumerable: true, get: function () { return DependencyNotFoundError_1.DependencyNotFoundError; } });
43
+ var ContainerNotFoundError_1 = require("./errors/ContainerNotFoundError");
44
+ Object.defineProperty(exports, "ContainerNotFoundError", { enumerable: true, get: function () { return ContainerNotFoundError_1.ContainerNotFoundError; } });
43
45
  var DependencyMissingKeyError_1 = require("./errors/DependencyMissingKeyError");
44
46
  Object.defineProperty(exports, "DependencyMissingKeyError", { enumerable: true, get: function () { return DependencyMissingKeyError_1.DependencyMissingKeyError; } });
45
47
  var MethodNotImplementedError_1 = require("./errors/MethodNotImplementedError");
@@ -3,6 +3,7 @@ import { ContainerDisposedError } from '../errors/ContainerDisposedError';
3
3
  import { MetadataInjector } from '../injector/MetadataInjector';
4
4
  import { AliasMap } from './AliasMap';
5
5
  import { DependencyNotFoundError } from '../errors/DependencyNotFoundError';
6
+ import { ContainerNotFoundError } from '../errors/ContainerNotFoundError';
6
7
  import { Is } from '../utils/basic';
7
8
  import { Filter as F } from '../utils/array';
8
9
  export class Container {
@@ -128,6 +129,21 @@ export class Container {
128
129
  getScopes() {
129
130
  return [...this.scopes];
130
131
  }
132
+ hasInstance(instance) {
133
+ return this.instances.includes(instance);
134
+ }
135
+ getScopeByInstanceOrFail(instance) {
136
+ this.validateContainer();
137
+ const queue = [this];
138
+ while (queue.length > 0) {
139
+ const scope = queue.shift();
140
+ if (scope.hasInstance(instance)) {
141
+ return scope;
142
+ }
143
+ queue.push(...scope.getScopes());
144
+ }
145
+ throw new ContainerNotFoundError('Cannot find scope for the given instance');
146
+ }
131
147
  removeScope(child) {
132
148
  this.scopes = this.scopes.filter((s) => s !== child);
133
149
  }
@@ -1,5 +1,6 @@
1
1
  import { MethodNotImplementedError } from '../errors/MethodNotImplementedError';
2
2
  import { DependencyNotFoundError } from '../errors/DependencyNotFoundError';
3
+ import { ContainerNotFoundError } from '../errors/ContainerNotFoundError';
3
4
  export class EmptyContainer {
4
5
  get isDisposed() {
5
6
  throw new MethodNotImplementedError();
@@ -11,9 +12,15 @@ export class EmptyContainer {
11
12
  getScopes() {
12
13
  return [];
13
14
  }
15
+ getScopeByInstanceOrFail(instance) {
16
+ throw new ContainerNotFoundError('Cannot find scope for the given instance');
17
+ }
14
18
  getInstances() {
15
19
  return [];
16
20
  }
21
+ hasInstance(instance) {
22
+ return false;
23
+ }
17
24
  createScope() {
18
25
  throw new MethodNotImplementedError();
19
26
  }
@@ -0,0 +1,8 @@
1
+ import { ContainerError } from './ContainerError';
2
+ export class ContainerNotFoundError extends ContainerError {
3
+ name = 'ContainerNotFoundError';
4
+ constructor(message) {
5
+ super(message);
6
+ Object.setPrototypeOf(this, ContainerNotFoundError.prototype);
7
+ }
8
+ }
package/esm/hooks/hook.js CHANGED
@@ -7,21 +7,40 @@ export const toHookFn = (execute) => isHookClassConstructor(execute) ? (context)
7
7
  const getReflectionTarget = (target) => {
8
8
  return isProxy(target) ? getProxyTarget(target) : target;
9
9
  };
10
- // Get hooks metadata
10
+ // Walk the constructor's prototype chain (most-derived first) collecting each class.
11
+ const getConstructorChain = (ctor) => {
12
+ const chain = [];
13
+ let current = ctor;
14
+ while (typeof current === 'function' && current !== Function.prototype) {
15
+ chain.push(current);
16
+ current = Object.getPrototypeOf(current);
17
+ }
18
+ return chain;
19
+ };
20
+ // Get hooks metadata, merging hooks declared on parent (extended-from) classes.
21
+ // Hooks are collected from base to derived so a derived class's hooks for the same
22
+ // method name take precedence over (replace) the parent's.
11
23
  export function getHooks(target, key) {
12
24
  const reflectionTarget = getReflectionTarget(target);
13
- return Reflect.hasMetadata(key, reflectionTarget.constructor)
14
- ? Reflect.getMetadata(key, reflectionTarget.constructor)
15
- : new Map();
25
+ const merged = new Map();
26
+ for (const ctor of getConstructorChain(reflectionTarget.constructor).reverse()) {
27
+ const ownHooks = Reflect.getOwnMetadata(key, ctor);
28
+ if (ownHooks) {
29
+ for (const [methodName, fns] of ownHooks) {
30
+ merged.set(methodName, fns);
31
+ }
32
+ }
33
+ }
34
+ return merged;
16
35
  }
17
36
  export function hasHooks(target, key) {
18
37
  const reflectionTarget = getReflectionTarget(target);
19
- return Reflect.hasMetadata(key, reflectionTarget.constructor);
38
+ return getConstructorChain(reflectionTarget.constructor).some((ctor) => Reflect.hasOwnMetadata(key, ctor));
20
39
  }
21
40
  // Hook decorator
22
41
  export const hook = (key, ...fns) => (target, propertyKey) => {
23
- const hooks = Reflect.hasMetadata(key, target.constructor)
24
- ? Reflect.getMetadata(key, target.constructor)
42
+ const hooks = Reflect.hasOwnMetadata(key, target.constructor)
43
+ ? Reflect.getOwnMetadata(key, target.constructor)
25
44
  : new Map();
26
45
  hooks.set(propertyKey, (hooks.get(propertyKey) ?? []).concat(fns));
27
46
  Reflect.defineMetadata(key, hooks, target.constructor);
package/esm/index.js CHANGED
@@ -13,6 +13,7 @@ export { register, bindTo, scope, scopeAccess, lazy, singleton, decorate, append
13
13
  export { Registration } from './registration/Registration';
14
14
  // Errors
15
15
  export { DependencyNotFoundError } from './errors/DependencyNotFoundError';
16
+ export { ContainerNotFoundError } from './errors/ContainerNotFoundError';
16
17
  export { DependencyMissingKeyError } from './errors/DependencyMissingKeyError';
17
18
  export { MethodNotImplementedError } from './errors/MethodNotImplementedError';
18
19
  export { ContainerDisposedError } from './errors/ContainerDisposedError';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-ioc-container",
3
- "version": "54.0.0",
3
+ "version": "54.1.0",
4
4
  "description": "Fast, lightweight TypeScript dependency injection container with a clean API, scoped lifecycles, decorators, tokens, hooks, lazy injection, customizable providers, and no global container objects.",
5
5
  "workspaces": [
6
6
  "docs"
@@ -35,6 +35,8 @@ export declare class Container implements IContainer {
35
35
  addOnDisposeHook(...hooks: OnDisposeHook[]): this;
36
36
  addInstance(instance: Instance): void;
37
37
  getScopes(): IContainer[];
38
+ hasInstance(instance: object): boolean;
39
+ getScopeByInstanceOrFail(instance: object): IContainer;
38
40
  removeScope(child: IContainer): void;
39
41
  useModule(module: IContainerModule): this;
40
42
  getParent(): IContainer;
@@ -9,7 +9,9 @@ export declare class EmptyContainer implements IContainer {
9
9
  addInstance(instance: Instance): void;
10
10
  getParent(): undefined;
11
11
  getScopes(): never[];
12
+ getScopeByInstanceOrFail(instance: object): IContainer;
12
13
  getInstances(): never[];
14
+ hasInstance(instance: object): boolean;
13
15
  createScope(): IContainer;
14
16
  dispose(): void;
15
17
  register(key: DependencyKey, value: IProvider): this;
@@ -44,10 +44,12 @@ export interface IContainer extends Tagged {
44
44
  resolveOneByAlias<T>(alias: DependencyKey, options?: ResolveOneOptions): T;
45
45
  createScope(options?: CreateScopeOptions): IContainer;
46
46
  getScopes(): IContainer[];
47
+ getScopeByInstanceOrFail(instance: object): IContainer;
47
48
  removeScope(child: IContainer): void;
48
49
  useModule(module: IContainerModule): this;
49
50
  getParent(): IContainer | undefined;
50
51
  getInstances(cascade?: boolean): Instance[];
52
+ hasInstance(instance: object): boolean;
51
53
  dispose(): void;
52
54
  addInstance(instance: Instance): void;
53
55
  }
@@ -0,0 +1,5 @@
1
+ import { ContainerError } from './ContainerError';
2
+ export declare class ContainerNotFoundError extends ContainerError {
3
+ name: string;
4
+ constructor(message: string);
5
+ }
@@ -10,6 +10,7 @@ export { Provider } from './provider/Provider';
10
10
  export { type IRegistration, type ReturnTypeOfRegistration, type ScopeMatchRule, type ProviderPipe, register, bindTo, scope, scopeAccess, lazy, singleton, decorate, appendArgs, appendArgsFn, } from './registration/IRegistration';
11
11
  export { Registration } from './registration/Registration';
12
12
  export { DependencyNotFoundError } from './errors/DependencyNotFoundError';
13
+ export { ContainerNotFoundError } from './errors/ContainerNotFoundError';
13
14
  export { DependencyMissingKeyError } from './errors/DependencyMissingKeyError';
14
15
  export { MethodNotImplementedError } from './errors/MethodNotImplementedError';
15
16
  export { ContainerDisposedError } from './errors/ContainerDisposedError';