ts-ioc-container 56.1.0 → 56.2.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/cjm/ExecutionContext.js +2 -0
- package/cjm/container/AliasMap.js +25 -0
- package/cjm/container/Container.js +175 -0
- package/cjm/container/EmptyContainer.js +71 -0
- package/cjm/container/IContainer.js +6 -0
- package/cjm/errors/CannonSingletonApplyTwiceError.js +17 -0
- package/cjm/errors/ContainerDisposedError.js +12 -0
- package/cjm/errors/ContainerError.js +11 -0
- package/cjm/errors/ContainerNotFoundError.js +12 -0
- package/cjm/errors/DependencyMissingKeyError.js +12 -0
- package/cjm/errors/DependencyNotFoundError.js +12 -0
- package/cjm/errors/MethodNotImplementedError.js +12 -0
- package/cjm/errors/ProviderDisposedError.js +17 -0
- package/cjm/errors/UnexpectedHookResultError.js +12 -0
- package/cjm/errors/UnsupportedTokenTypeError.js +12 -0
- package/cjm/hooks/HookContext.js +42 -0
- package/cjm/hooks/HooksRunner.js +42 -0
- package/cjm/hooks/hook.js +55 -0
- package/cjm/hooks/injectProp.js +6 -0
- package/cjm/hooks/onConstruct.js +28 -0
- package/cjm/hooks/onConstructAsync.js +28 -0
- package/cjm/hooks/onContainerDisposed.js +18 -0
- package/cjm/index.js +141 -0
- package/cjm/injector/IInjector.js +14 -0
- package/cjm/injector/MetadataInjector.js +33 -0
- package/cjm/injector/ProxyInjector.js +18 -0
- package/cjm/injector/SimpleInjector.js +10 -0
- package/cjm/metadata/class.js +21 -0
- package/cjm/metadata/method.js +19 -0
- package/cjm/metadata/parameter.js +34 -0
- package/cjm/provider/IProvider.js +2 -0
- package/cjm/provider/Provider.js +80 -0
- package/cjm/registration/IRegistration.js +50 -0
- package/cjm/registration/Registration.js +85 -0
- package/cjm/select.js +16 -0
- package/cjm/token/BindToken.js +7 -0
- package/cjm/token/ClassToken.js +45 -0
- package/cjm/token/ConstantToken.js +25 -0
- package/cjm/token/FunctionToken.js +42 -0
- package/cjm/token/GroupAliasToken.js +50 -0
- package/cjm/token/GroupInstanceToken.js +33 -0
- package/cjm/token/InjectionToken.js +11 -0
- package/cjm/token/SingleAliasToken.js +50 -0
- package/cjm/token/SingleToken.js +48 -0
- package/cjm/token/toToken.js +29 -0
- package/cjm/utils/array.js +9 -0
- package/cjm/utils/basic.js +13 -0
- package/cjm/utils/debounce.js +16 -0
- package/cjm/utils/errorHandler.js +29 -0
- package/cjm/utils/fp.js +6 -0
- package/cjm/utils/once.js +20 -0
- package/cjm/utils/promise.js +5 -0
- package/cjm/utils/proxy.js +39 -0
- package/cjm/utils/shallowCache.js +23 -0
- package/cjm/utils/throttle.js +18 -0
- package/esm/ExecutionContext.js +1 -0
- package/esm/container/AliasMap.js +21 -0
- package/esm/container/Container.js +171 -0
- package/esm/container/EmptyContainer.js +67 -0
- package/esm/container/IContainer.js +3 -0
- package/esm/errors/CannonSingletonApplyTwiceError.js +13 -0
- package/esm/errors/ContainerDisposedError.js +8 -0
- package/esm/errors/ContainerError.js +7 -0
- package/esm/errors/ContainerNotFoundError.js +8 -0
- package/esm/errors/DependencyMissingKeyError.js +8 -0
- package/esm/errors/DependencyNotFoundError.js +8 -0
- package/esm/errors/MethodNotImplementedError.js +8 -0
- package/esm/errors/ProviderDisposedError.js +13 -0
- package/esm/errors/UnexpectedHookResultError.js +8 -0
- package/esm/errors/UnsupportedTokenTypeError.js +8 -0
- package/esm/hooks/HookContext.js +36 -0
- package/esm/hooks/HooksRunner.js +38 -0
- package/esm/hooks/hook.js +46 -0
- package/esm/hooks/injectProp.js +2 -0
- package/esm/hooks/onConstruct.js +23 -0
- package/esm/hooks/onConstructAsync.js +23 -0
- package/esm/hooks/onContainerDisposed.js +13 -0
- package/esm/index.js +46 -0
- package/esm/injector/IInjector.js +10 -0
- package/esm/injector/MetadataInjector.js +25 -0
- package/esm/injector/ProxyInjector.js +14 -0
- package/esm/injector/SimpleInjector.js +6 -0
- package/esm/metadata/class.js +12 -0
- package/esm/metadata/method.js +10 -0
- package/esm/metadata/parameter.js +25 -0
- package/esm/package.json +3 -0
- package/esm/provider/IProvider.js +1 -0
- package/esm/provider/Provider.js +76 -0
- package/esm/registration/IRegistration.js +35 -0
- package/esm/registration/Registration.js +81 -0
- package/esm/select.js +13 -0
- package/esm/token/BindToken.js +4 -0
- package/esm/token/ClassToken.js +41 -0
- package/esm/token/ConstantToken.js +21 -0
- package/esm/token/FunctionToken.js +38 -0
- package/esm/token/GroupAliasToken.js +45 -0
- package/esm/token/GroupInstanceToken.js +29 -0
- package/esm/token/InjectionToken.js +6 -0
- package/esm/token/SingleAliasToken.js +45 -0
- package/esm/token/SingleToken.js +44 -0
- package/esm/token/toToken.js +24 -0
- package/esm/utils/array.js +6 -0
- package/esm/utils/basic.js +9 -0
- package/esm/utils/debounce.js +12 -0
- package/esm/utils/errorHandler.js +24 -0
- package/esm/utils/fp.js +3 -0
- package/esm/utils/once.js +16 -0
- package/esm/utils/promise.js +1 -0
- package/esm/utils/proxy.js +33 -0
- package/esm/utils/shallowCache.js +19 -0
- package/esm/utils/throttle.js +14 -0
- package/package.json +1 -1
- package/typings/ExecutionContext.d.ts +4 -0
- package/typings/container/AliasMap.d.ts +8 -0
- package/typings/container/Container.d.ts +48 -0
- package/typings/container/EmptyContainer.d.ts +30 -0
- package/typings/container/IContainer.d.ts +56 -0
- package/typings/errors/CannonSingletonApplyTwiceError.d.ts +6 -0
- package/typings/errors/ContainerDisposedError.d.ts +5 -0
- package/typings/errors/ContainerError.d.ts +4 -0
- package/typings/errors/ContainerNotFoundError.d.ts +5 -0
- package/typings/errors/DependencyMissingKeyError.d.ts +5 -0
- package/typings/errors/DependencyNotFoundError.d.ts +5 -0
- package/typings/errors/MethodNotImplementedError.d.ts +5 -0
- package/typings/errors/ProviderDisposedError.d.ts +6 -0
- package/typings/errors/UnexpectedHookResultError.d.ts +5 -0
- package/typings/errors/UnsupportedTokenTypeError.d.ts +5 -0
- package/typings/hooks/HookContext.d.ts +35 -0
- package/typings/hooks/HooksRunner.d.ts +16 -0
- package/typings/hooks/hook.d.ts +20 -0
- package/typings/hooks/injectProp.d.ts +5 -0
- package/typings/hooks/onConstruct.d.ts +14 -0
- package/typings/hooks/onConstructAsync.d.ts +11 -0
- package/typings/hooks/onContainerDisposed.d.ts +9 -0
- package/typings/index.d.ts +48 -0
- package/typings/injector/IInjector.d.ts +17 -0
- package/typings/injector/MetadataInjector.d.ts +13 -0
- package/typings/injector/ProxyInjector.d.ts +6 -0
- package/typings/injector/SimpleInjector.d.ts +6 -0
- package/typings/metadata/class.d.ts +6 -0
- package/typings/metadata/method.d.ts +6 -0
- package/typings/metadata/parameter.d.ts +6 -0
- package/typings/provider/IProvider.d.ts +26 -0
- package/typings/provider/Provider.d.ts +26 -0
- package/typings/registration/IRegistration.d.ts +31 -0
- package/typings/registration/Registration.d.ts +28 -0
- package/typings/select.d.ts +12 -0
- package/typings/token/BindToken.d.ts +5 -0
- package/typings/token/ClassToken.d.ts +18 -0
- package/typings/token/ConstantToken.d.ts +10 -0
- package/typings/token/FunctionToken.d.ts +17 -0
- package/typings/token/GroupAliasToken.d.ts +21 -0
- package/typings/token/GroupInstanceToken.d.ts +15 -0
- package/typings/token/InjectionToken.d.ts +9 -0
- package/typings/token/SingleAliasToken.d.ts +21 -0
- package/typings/token/SingleToken.d.ts +19 -0
- package/typings/token/toToken.d.ts +6 -0
- package/typings/utils/array.d.ts +3 -0
- package/typings/utils/basic.d.ts +11 -0
- package/typings/utils/debounce.d.ts +1 -0
- package/typings/utils/errorHandler.d.ts +6 -0
- package/typings/utils/fp.d.ts +13 -0
- package/typings/utils/once.d.ts +8 -0
- package/typings/utils/promise.d.ts +1 -0
- package/typings/utils/proxy.d.ts +4 -0
- package/typings/utils/shallowCache.d.ts +1 -0
- package/typings/utils/throttle.d.ts +1 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { MethodNotImplementedError } from '../errors/MethodNotImplementedError.js';
|
|
2
|
+
import { DependencyNotFoundError } from '../errors/DependencyNotFoundError.js';
|
|
3
|
+
import { ContainerNotFoundError } from '../errors/ContainerNotFoundError.js';
|
|
4
|
+
export class EmptyContainer {
|
|
5
|
+
get isDisposed() {
|
|
6
|
+
throw new MethodNotImplementedError();
|
|
7
|
+
}
|
|
8
|
+
addInstance(instance) { }
|
|
9
|
+
getParent() {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
getScopes() {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
getScopeByInstanceOrFail(instance) {
|
|
16
|
+
throw new ContainerNotFoundError('Cannot find scope for the given instance');
|
|
17
|
+
}
|
|
18
|
+
getInstances() {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
hasInstance(instance) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
createScope() {
|
|
25
|
+
throw new MethodNotImplementedError();
|
|
26
|
+
}
|
|
27
|
+
dispose() {
|
|
28
|
+
throw new MethodNotImplementedError();
|
|
29
|
+
}
|
|
30
|
+
register(key, value) {
|
|
31
|
+
throw new MethodNotImplementedError();
|
|
32
|
+
}
|
|
33
|
+
hasTag(tag) {
|
|
34
|
+
throw new MethodNotImplementedError();
|
|
35
|
+
}
|
|
36
|
+
addTags(...tags) {
|
|
37
|
+
throw new MethodNotImplementedError();
|
|
38
|
+
}
|
|
39
|
+
getRegistrations() {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
hasRegistration(key) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
removeScope() { }
|
|
46
|
+
useModule(module) {
|
|
47
|
+
throw new MethodNotImplementedError();
|
|
48
|
+
}
|
|
49
|
+
addRegistration(registration) {
|
|
50
|
+
throw new MethodNotImplementedError();
|
|
51
|
+
}
|
|
52
|
+
resolve(key, options) {
|
|
53
|
+
throw new DependencyNotFoundError(`Cannot find ${key.toString()}`);
|
|
54
|
+
}
|
|
55
|
+
resolveByAlias(alias, options) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
resolveOneByAlias(alias, options) {
|
|
59
|
+
throw new DependencyNotFoundError(`Cannot find alias ${alias.toString()}`);
|
|
60
|
+
}
|
|
61
|
+
addOnDisposeHook(...hooks) {
|
|
62
|
+
throw new MethodNotImplementedError();
|
|
63
|
+
}
|
|
64
|
+
addOnConstructHook(...hooks) {
|
|
65
|
+
throw new MethodNotImplementedError();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ContainerError } from './ContainerError.js';
|
|
2
|
+
export class CannonSingletonApplyTwiceError extends ContainerError {
|
|
3
|
+
name = 'CannonSingletonApplyTwiceError';
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
Object.setPrototypeOf(this, CannonSingletonApplyTwiceError.prototype);
|
|
7
|
+
}
|
|
8
|
+
static assert(isTrue, failMessage) {
|
|
9
|
+
if (!isTrue) {
|
|
10
|
+
throw new CannonSingletonApplyTwiceError(failMessage);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ContainerError } from './ContainerError.js';
|
|
2
|
+
export class ContainerDisposedError extends ContainerError {
|
|
3
|
+
name = 'ContainerDisposedError';
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
Object.setPrototypeOf(this, ContainerDisposedError.prototype);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ContainerError } from './ContainerError.js';
|
|
2
|
+
export class ContainerNotFoundError extends ContainerError {
|
|
3
|
+
name = 'ContainerNotFoundError';
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
Object.setPrototypeOf(this, ContainerNotFoundError.prototype);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ContainerError } from './ContainerError.js';
|
|
2
|
+
export class DependencyMissingKeyError extends ContainerError {
|
|
3
|
+
name = 'DependencyMissingKeyError';
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
Object.setPrototypeOf(this, DependencyMissingKeyError.prototype);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ContainerError } from './ContainerError.js';
|
|
2
|
+
export class DependencyNotFoundError extends ContainerError {
|
|
3
|
+
name = 'DependencyNotFoundError';
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
Object.setPrototypeOf(this, DependencyNotFoundError.prototype);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ContainerError } from './ContainerError.js';
|
|
2
|
+
export class MethodNotImplementedError extends ContainerError {
|
|
3
|
+
name = 'MethodNotImplementedError';
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
Object.setPrototypeOf(this, MethodNotImplementedError.prototype);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ContainerError } from './ContainerError.js';
|
|
2
|
+
export class ProviderDisposedError extends ContainerError {
|
|
3
|
+
name = 'ProviderDisposedError';
|
|
4
|
+
static assert(isTrue, failMessage) {
|
|
5
|
+
if (!isTrue) {
|
|
6
|
+
throw new ProviderDisposedError(failMessage);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
Object.setPrototypeOf(this, ProviderDisposedError.prototype);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ContainerError } from './ContainerError.js';
|
|
2
|
+
export class UnexpectedHookResultError extends ContainerError {
|
|
3
|
+
name = 'UnexpectedHookResultError';
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
Object.setPrototypeOf(this, UnexpectedHookResultError.prototype);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ContainerError } from './ContainerError.js';
|
|
2
|
+
export class UnsupportedTokenTypeError extends ContainerError {
|
|
3
|
+
name = 'UnsupportedTokenTypeError';
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
Object.setPrototypeOf(this, UnsupportedTokenTypeError.prototype);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getProxyTarget, isProxy } from '../utils/proxy.js';
|
|
2
|
+
import { resolveArgs } from '../injector/MetadataInjector.js';
|
|
3
|
+
export class HookContext {
|
|
4
|
+
scope;
|
|
5
|
+
methodName;
|
|
6
|
+
instance;
|
|
7
|
+
initialArgs = [];
|
|
8
|
+
constructor(instance, scope, methodName) {
|
|
9
|
+
this.scope = scope;
|
|
10
|
+
this.methodName = methodName;
|
|
11
|
+
this.instance = isProxy(instance) ? getProxyTarget(instance) : instance;
|
|
12
|
+
}
|
|
13
|
+
resolveArgs(...args) {
|
|
14
|
+
return resolveArgs(this.instance.constructor, this.methodName)(this.scope, {
|
|
15
|
+
args: [...this.initialArgs, ...args],
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
invokeMethod({ args = this.resolveArgs() } = {}) {
|
|
19
|
+
return this.instance[this.methodName](...args);
|
|
20
|
+
}
|
|
21
|
+
setProperty(fn) {
|
|
22
|
+
this.instance[this.methodName] = fn.resolve(this.scope);
|
|
23
|
+
}
|
|
24
|
+
getProperty() {
|
|
25
|
+
return this.instance[this.methodName];
|
|
26
|
+
}
|
|
27
|
+
setInitialArgs(...args) {
|
|
28
|
+
this.initialArgs = args;
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
getInitialArgs() {
|
|
32
|
+
return this.initialArgs;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export const createHookContext = (Target, scope, methodName = 'constructor') => new HookContext(Target, scope, methodName);
|
|
36
|
+
export const createHookContextFactory = ({ args = [] } = {}) => (Target, scope, methodName) => createHookContext(Target, scope, methodName).setInitialArgs(...args);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createHookContext } from './HookContext.js';
|
|
2
|
+
import { getHooks, hasHooks, toHookFn } from './hook.js';
|
|
3
|
+
import { UnexpectedHookResultError } from '../errors/UnexpectedHookResultError.js';
|
|
4
|
+
import { promisify } from '../utils/promise.js';
|
|
5
|
+
export class HooksRunner {
|
|
6
|
+
key;
|
|
7
|
+
constructor(key) {
|
|
8
|
+
this.key = key;
|
|
9
|
+
}
|
|
10
|
+
hasHooks(target) {
|
|
11
|
+
return hasHooks(target, this.key);
|
|
12
|
+
}
|
|
13
|
+
execute(target, { scope, createContext = createHookContext, mapContext = (context) => context, predicate = () => true, }) {
|
|
14
|
+
const hooks = Array.from(getHooks(target, this.key).entries()).filter(([methodName]) => predicate(methodName));
|
|
15
|
+
const runMethodHooks = (methodName, executions) => {
|
|
16
|
+
const context = mapContext(createContext(target, scope, methodName));
|
|
17
|
+
for (const execute of executions) {
|
|
18
|
+
const result = execute(context);
|
|
19
|
+
if (result instanceof Promise) {
|
|
20
|
+
throw new UnexpectedHookResultError(`Hook ${methodName} returned a promise, use runHooksAsync instead`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
for (const [methodName, executions] of hooks) {
|
|
25
|
+
runMethodHooks(methodName, executions.map(toHookFn));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async executeAsync(target, { scope, createContext = createHookContext, mapContext = (context) => context, predicate = () => true, }) {
|
|
29
|
+
const hooks = Array.from(getHooks(target, this.key).entries()).filter(([methodName]) => predicate(methodName));
|
|
30
|
+
const runMethodHooks = async (methodName, executions) => {
|
|
31
|
+
const context = mapContext(createContext(target, scope, methodName));
|
|
32
|
+
for (const execute of executions) {
|
|
33
|
+
await promisify(execute(context));
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
return Promise.all(hooks.map(([methodName, executions]) => runMethodHooks(methodName, executions.map(toHookFn))));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Is } from '../utils/basic.js';
|
|
2
|
+
import { getProxyTarget, isProxy } from '../utils/proxy.js';
|
|
3
|
+
export const appendHooks = (...fns) => (...prev) => [...prev, ...fns];
|
|
4
|
+
export const prependHooks = (...fns) => (...prev) => [...fns, ...prev];
|
|
5
|
+
export const append = appendHooks;
|
|
6
|
+
export const prepend = prependHooks;
|
|
7
|
+
const isHookClassConstructor = (execute) => {
|
|
8
|
+
return Is.constructor(execute) && execute.prototype.execute;
|
|
9
|
+
};
|
|
10
|
+
export const toHookFn = (execute) => isHookClassConstructor(execute) ? (context) => context.scope.resolve(execute).execute(context) : execute;
|
|
11
|
+
const getReflectionTarget = (target) => {
|
|
12
|
+
return isProxy(target) ? getProxyTarget(target) : target;
|
|
13
|
+
};
|
|
14
|
+
const getConstructorChain = (ctor) => {
|
|
15
|
+
const chain = [];
|
|
16
|
+
let current = ctor;
|
|
17
|
+
while (typeof current === 'function' && current !== Function.prototype) {
|
|
18
|
+
chain.push(current);
|
|
19
|
+
current = Object.getPrototypeOf(current);
|
|
20
|
+
}
|
|
21
|
+
return chain;
|
|
22
|
+
};
|
|
23
|
+
export function getHooks(target, key) {
|
|
24
|
+
const reflectionTarget = getReflectionTarget(target);
|
|
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;
|
|
35
|
+
}
|
|
36
|
+
export function hasHooks(target, key) {
|
|
37
|
+
const reflectionTarget = getReflectionTarget(target);
|
|
38
|
+
return getConstructorChain(reflectionTarget.constructor).some((ctor) => Reflect.hasOwnMetadata(key, ctor));
|
|
39
|
+
}
|
|
40
|
+
export const hook = (key, mapFn) => (target, propertyKey) => {
|
|
41
|
+
const hooks = Reflect.hasOwnMetadata(key, target.constructor)
|
|
42
|
+
? Reflect.getOwnMetadata(key, target.constructor)
|
|
43
|
+
: new Map();
|
|
44
|
+
hooks.set(propertyKey, mapFn(...(hooks.get(propertyKey) ?? [])));
|
|
45
|
+
Reflect.defineMetadata(key, hooks, target.constructor);
|
|
46
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { hook, prependHooks } from './hook.js';
|
|
2
|
+
import { HooksRunner } from './HooksRunner.js';
|
|
3
|
+
export const onConstructHooksRunner = new HooksRunner('onConstruct');
|
|
4
|
+
export const onConstruct = (...fns) => hook('onConstruct', prependHooks(...fns));
|
|
5
|
+
export class AddOnConstructHookModule {
|
|
6
|
+
onException;
|
|
7
|
+
constructor(onException) {
|
|
8
|
+
this.onException = onException;
|
|
9
|
+
}
|
|
10
|
+
applyTo(container) {
|
|
11
|
+
container.addOnConstructHook((instance, scope) => {
|
|
12
|
+
try {
|
|
13
|
+
onConstructHooksRunner.execute(instance, { scope });
|
|
14
|
+
}
|
|
15
|
+
catch (ex) {
|
|
16
|
+
if (!this.onException) {
|
|
17
|
+
throw ex;
|
|
18
|
+
}
|
|
19
|
+
this.onException(ex, { scope });
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { hook, prependHooks } from './hook.js';
|
|
2
|
+
import { HooksRunner } from './HooksRunner.js';
|
|
3
|
+
export const onConstructAsyncHooksRunner = new HooksRunner('onConstructAsync');
|
|
4
|
+
export const onConstructAsync = (...fns) => hook('onConstructAsync', prependHooks(...fns));
|
|
5
|
+
export class AddOnConstructAsyncHookModule {
|
|
6
|
+
onException;
|
|
7
|
+
constructor(onException) {
|
|
8
|
+
this.onException = onException;
|
|
9
|
+
}
|
|
10
|
+
applyTo(container) {
|
|
11
|
+
container.addOnConstructHook((instance, scope) => {
|
|
12
|
+
if (!onConstructAsyncHooksRunner.hasHooks(instance)) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
onConstructAsyncHooksRunner.executeAsync(instance, { scope }).catch((ex) => {
|
|
16
|
+
if (!this.onException) {
|
|
17
|
+
throw ex;
|
|
18
|
+
}
|
|
19
|
+
this.onException(ex, { scope });
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { hook, prependHooks } from './hook.js';
|
|
2
|
+
import { HooksRunner } from './HooksRunner.js';
|
|
3
|
+
export const onContainerDisposedHooksRunner = new HooksRunner('onContainerDisposed');
|
|
4
|
+
export const onContainerDisposed = (...fns) => hook('onContainerDisposed', prependHooks(...fns));
|
|
5
|
+
export class AddOnDisposeHookModule {
|
|
6
|
+
applyTo(container) {
|
|
7
|
+
container.addOnDisposeHook((scope) => {
|
|
8
|
+
for (const instance of scope.getInstances()) {
|
|
9
|
+
onContainerDisposedHooksRunner.execute(instance, { scope });
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
package/esm/index.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export { isDependencyKey, } from './container/IContainer.js';
|
|
2
|
+
export { Container } from './container/Container.js';
|
|
3
|
+
export { EmptyContainer } from './container/EmptyContainer.js';
|
|
4
|
+
export { Injector } from './injector/IInjector.js';
|
|
5
|
+
export { MetadataInjector, inject, args, argsFn, resolveArgs } from './injector/MetadataInjector.js';
|
|
6
|
+
export { SimpleInjector } from './injector/SimpleInjector.js';
|
|
7
|
+
export { ProxyInjector } from './injector/ProxyInjector.js';
|
|
8
|
+
export { Provider } from './provider/Provider.js';
|
|
9
|
+
export { register, bindTo, scope, scopeAccess, lazy, singleton, decorate, appendArgs, appendArgsFn, } from './registration/IRegistration.js';
|
|
10
|
+
export { Registration } from './registration/Registration.js';
|
|
11
|
+
export { ContainerError } from './errors/ContainerError.js';
|
|
12
|
+
export { DependencyNotFoundError } from './errors/DependencyNotFoundError.js';
|
|
13
|
+
export { ContainerNotFoundError } from './errors/ContainerNotFoundError.js';
|
|
14
|
+
export { DependencyMissingKeyError } from './errors/DependencyMissingKeyError.js';
|
|
15
|
+
export { MethodNotImplementedError } from './errors/MethodNotImplementedError.js';
|
|
16
|
+
export { ContainerDisposedError } from './errors/ContainerDisposedError.js';
|
|
17
|
+
export { ProviderDisposedError } from './errors/ProviderDisposedError.js';
|
|
18
|
+
export { UnexpectedHookResultError } from './errors/UnexpectedHookResultError.js';
|
|
19
|
+
export { CannonSingletonApplyTwiceError } from './errors/CannonSingletonApplyTwiceError.js';
|
|
20
|
+
export { UnsupportedTokenTypeError } from './errors/UnsupportedTokenTypeError.js';
|
|
21
|
+
export { getHooks, hook, hasHooks, appendHooks, prependHooks, append, prepend, } from './hooks/hook.js';
|
|
22
|
+
export { HookContext, createHookContextFactory, createHookContext } from './hooks/HookContext.js';
|
|
23
|
+
export { injectProp } from './hooks/injectProp.js';
|
|
24
|
+
export { onConstructHooksRunner, onConstruct, AddOnConstructHookModule, } from './hooks/onConstruct.js';
|
|
25
|
+
export { onConstructAsyncHooksRunner, onConstructAsync, AddOnConstructAsyncHookModule } from './hooks/onConstructAsync.js';
|
|
26
|
+
export { onContainerDisposedHooksRunner, onContainerDisposed, AddOnDisposeHookModule, } from './hooks/onContainerDisposed.js';
|
|
27
|
+
export { HooksRunner } from './hooks/HooksRunner.js';
|
|
28
|
+
export { InjectionToken } from './token/InjectionToken.js';
|
|
29
|
+
export { GroupAliasToken, toGroupAlias } from './token/GroupAliasToken.js';
|
|
30
|
+
export { SingleAliasToken, toSingleAlias } from './token/SingleAliasToken.js';
|
|
31
|
+
export { ClassToken } from './token/ClassToken.js';
|
|
32
|
+
export { SingleToken } from './token/SingleToken.js';
|
|
33
|
+
export { FunctionToken } from './token/FunctionToken.js';
|
|
34
|
+
export { ConstantToken } from './token/ConstantToken.js';
|
|
35
|
+
export { GroupInstanceToken } from './token/GroupInstanceToken.js';
|
|
36
|
+
export { addClassMeta, getClassMeta, addClassLabel, getClassLabels, addClassTag, getClassTags } from './metadata/class.js';
|
|
37
|
+
export { addParamMeta, getParamMeta, addParamLabel, getParamLabels, addParamTag, getParamTags, } from './metadata/parameter.js';
|
|
38
|
+
export { addMethodMeta, getMethodMeta, addMethodLabel, getMethodLabels, addMethodTag, getMethodTags, } from './metadata/method.js';
|
|
39
|
+
export { handleError, handleAsyncError } from './utils/errorHandler.js';
|
|
40
|
+
export { throttle } from './utils/throttle.js';
|
|
41
|
+
export { debounce } from './utils/debounce.js';
|
|
42
|
+
export { shallowCache } from './utils/shallowCache.js';
|
|
43
|
+
export { once } from './utils/once.js';
|
|
44
|
+
export { select } from './select.js';
|
|
45
|
+
export { pipe } from './utils/fp.js';
|
|
46
|
+
export { Is, resolveConstructor } from './utils/basic.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { toLazyIf } from '../utils/proxy.js';
|
|
2
|
+
export class Injector {
|
|
3
|
+
resolve(scope, Target, { args, lazy } = {}) {
|
|
4
|
+
return toLazyIf(() => {
|
|
5
|
+
const instance = this.createInstance(scope, Target, { args });
|
|
6
|
+
scope.addInstance(instance);
|
|
7
|
+
return instance;
|
|
8
|
+
}, lazy);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Injector } from './IInjector.js';
|
|
2
|
+
import { Is } from '../utils/basic.js';
|
|
3
|
+
import { getParamMeta, addParamMeta } from '../metadata/parameter.js';
|
|
4
|
+
import { argToToken, toToken } from '../token/toToken.js';
|
|
5
|
+
import { FunctionToken } from '../token/FunctionToken.js';
|
|
6
|
+
export class MetadataInjector extends Injector {
|
|
7
|
+
createInstance(scope, Target, { args: deps = [] } = {}) {
|
|
8
|
+
const args = resolveArgs(Target)(scope, { args: deps });
|
|
9
|
+
return new Target(...args);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
const hookMetaKey = (methodName = 'constructor') => `inject:${methodName}`;
|
|
13
|
+
export const inject = (fn, selectFn = (instance) => instance) => (target, propertyKey, parameterIndex) => {
|
|
14
|
+
const toSelectedToken = () => {
|
|
15
|
+
const token = toToken(fn);
|
|
16
|
+
return new FunctionToken((scope, options) => selectFn(token.resolve(scope, options)));
|
|
17
|
+
};
|
|
18
|
+
addParamMeta(hookMetaKey(propertyKey), toSelectedToken)(Is.instance(target) ? target.constructor : target, propertyKey, parameterIndex);
|
|
19
|
+
};
|
|
20
|
+
export const argsFn = (predicate) => (c, { args = [] }) => args.find((value, index) => predicate(value, index));
|
|
21
|
+
export const args = (index) => argsFn((value, i) => i === index);
|
|
22
|
+
export const resolveArgs = (Target, methodName) => {
|
|
23
|
+
const tokens = getParamMeta(hookMetaKey(methodName), Target);
|
|
24
|
+
return (scope, { args = [], lazy }) => tokens.map((fn) => fn.resolve(scope, { args: args.map(argToToken).map((t) => t.resolve(scope)), lazy }));
|
|
25
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Injector } from './IInjector.js';
|
|
2
|
+
export class ProxyInjector extends Injector {
|
|
3
|
+
createInstance(scope, Target, { args = [] } = {}) {
|
|
4
|
+
const proxy = new Proxy({}, {
|
|
5
|
+
get(target, prop) {
|
|
6
|
+
if (prop === 'args') {
|
|
7
|
+
return args;
|
|
8
|
+
}
|
|
9
|
+
return prop.toString().search(/alias/gi) >= 0 ? scope.resolveByAlias(prop) : scope.resolve(prop);
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
return new Target(proxy);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { resolveConstructor } from '../utils/basic.js';
|
|
2
|
+
export const addClassMeta = (key, mapFn) => (target) => {
|
|
3
|
+
const value = Reflect.getOwnMetadata(key, target);
|
|
4
|
+
Reflect.defineMetadata(key, mapFn(value), target);
|
|
5
|
+
};
|
|
6
|
+
export function getClassMeta(target, key) {
|
|
7
|
+
return Reflect.getOwnMetadata(key, resolveConstructor(target));
|
|
8
|
+
}
|
|
9
|
+
export const addClassLabel = (key, label) => addClassMeta('label', (prev = new Map()) => prev.set(key, label));
|
|
10
|
+
export const getClassLabels = (target) => getClassMeta(target, 'label') ?? new Map();
|
|
11
|
+
export const addClassTag = (tag) => addClassMeta('tag', (prev = new Set()) => prev.add(tag));
|
|
12
|
+
export const getClassTags = (target) => getClassMeta(target, 'tag') ?? new Set();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { resolveConstructor } from '../utils/basic.js';
|
|
2
|
+
export const addMethodMeta = (key, mapFn) => (target, propertyKey) => {
|
|
3
|
+
const metadata = Reflect.getMetadata(key, target.constructor, propertyKey);
|
|
4
|
+
Reflect.defineMetadata(key, mapFn(metadata), target.constructor, propertyKey);
|
|
5
|
+
};
|
|
6
|
+
export const getMethodMeta = (key, target, propertyKey) => Reflect.getMetadata(key, resolveConstructor(target), propertyKey);
|
|
7
|
+
export const addMethodLabel = (key, label) => addMethodMeta('label', (prev = new Map()) => prev.set(key, label));
|
|
8
|
+
export const getMethodLabels = (target, propertyKey) => getMethodMeta('label', target, propertyKey) ?? new Map();
|
|
9
|
+
export const addMethodTag = (tag) => addMethodMeta('tag', (prev = new Set()) => prev.add(tag));
|
|
10
|
+
export const getMethodTags = (target, propertyKey) => getMethodMeta('tag', target, propertyKey) ?? new Set();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { resolveConstructor } from '../utils/basic.js';
|
|
2
|
+
export const addParamMeta = (key, mapFn) => (target, _, parameterIndex) => {
|
|
3
|
+
const metadata = Reflect.getOwnMetadata(key, target) ?? [];
|
|
4
|
+
metadata[parameterIndex] = mapFn(metadata[parameterIndex]);
|
|
5
|
+
Reflect.defineMetadata(key, metadata, target);
|
|
6
|
+
};
|
|
7
|
+
export const getParamMeta = (key, target) => {
|
|
8
|
+
return Reflect.getOwnMetadata(key, resolveConstructor(target)) ?? [];
|
|
9
|
+
};
|
|
10
|
+
export const addParamLabel = (key, label) => addParamMeta('label', (prev) => {
|
|
11
|
+
const map = prev ?? new Map();
|
|
12
|
+
return map.set(key, label);
|
|
13
|
+
});
|
|
14
|
+
export const getParamLabels = (target, parameterIndex) => {
|
|
15
|
+
const all = getParamMeta('label', target);
|
|
16
|
+
return all[parameterIndex] ?? new Map();
|
|
17
|
+
};
|
|
18
|
+
export const addParamTag = (tag) => addParamMeta('tag', (prev) => {
|
|
19
|
+
const set = prev ?? new Set();
|
|
20
|
+
return set.add(tag);
|
|
21
|
+
});
|
|
22
|
+
export const getParamTags = (target, parameterIndex) => {
|
|
23
|
+
const all = getParamMeta('tag', target);
|
|
24
|
+
return all[parameterIndex] ?? new Set();
|
|
25
|
+
};
|
package/esm/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { CannonSingletonApplyTwiceError } from '../errors/CannonSingletonApplyTwiceError.js';
|
|
2
|
+
import { ProviderDisposedError } from '../errors/ProviderDisposedError.js';
|
|
3
|
+
export class Provider {
|
|
4
|
+
resolveDependency;
|
|
5
|
+
static fromClass(Target) {
|
|
6
|
+
return new Provider((container, options) => container.resolve(Target, options));
|
|
7
|
+
}
|
|
8
|
+
static fromValue(value) {
|
|
9
|
+
return new Provider(() => value);
|
|
10
|
+
}
|
|
11
|
+
static fromKey(key) {
|
|
12
|
+
return new Provider((c) => c.resolve(key));
|
|
13
|
+
}
|
|
14
|
+
argsFnList = [];
|
|
15
|
+
accessRules = [];
|
|
16
|
+
mappers = [];
|
|
17
|
+
isLazy = false;
|
|
18
|
+
cache = new Map();
|
|
19
|
+
getKey;
|
|
20
|
+
isDisposed = false;
|
|
21
|
+
constructor(resolveDependency) {
|
|
22
|
+
this.resolveDependency = resolveDependency;
|
|
23
|
+
}
|
|
24
|
+
resolve(scope, options) {
|
|
25
|
+
ProviderDisposedError.assert(!this.isDisposed, 'Provider is already disposed');
|
|
26
|
+
if (!this.getKey) {
|
|
27
|
+
return this.resolveDep(scope, options);
|
|
28
|
+
}
|
|
29
|
+
const key = this.getKey(...(options.args ?? []));
|
|
30
|
+
if (!this.cache.has(key)) {
|
|
31
|
+
this.cache.set(key, this.resolveDep(scope, options));
|
|
32
|
+
}
|
|
33
|
+
return this.cache.get(key);
|
|
34
|
+
}
|
|
35
|
+
resolveDep(scope, { args = [], lazy } = {}) {
|
|
36
|
+
const dependency = this.resolveDependency(scope, {
|
|
37
|
+
args: this.argsFnList.reduce((acc, current) => current(scope, { args: acc }), args),
|
|
38
|
+
lazy: lazy ?? this.isLazy,
|
|
39
|
+
});
|
|
40
|
+
return this.mappers.reduce((acc, current) => current(acc, scope), dependency);
|
|
41
|
+
}
|
|
42
|
+
map(...mappers) {
|
|
43
|
+
this.mappers.push(...mappers);
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
addAccessRule(...rules) {
|
|
47
|
+
this.accessRules.push(...rules);
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
lazy() {
|
|
51
|
+
this.isLazy = true;
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
addArgsFn(...fns) {
|
|
55
|
+
this.argsFnList.push(...fns);
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
hasAccess(options) {
|
|
59
|
+
ProviderDisposedError.assert(!this.isDisposed, 'Provider is already disposed');
|
|
60
|
+
return this.accessRules.reduce((acc, rule) => rule(options, acc), true);
|
|
61
|
+
}
|
|
62
|
+
singleton(getCacheKey = () => '1') {
|
|
63
|
+
CannonSingletonApplyTwiceError.assert(!this.getKey, 'Provider is already singleton');
|
|
64
|
+
this.getKey = getCacheKey;
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
dispose() {
|
|
68
|
+
ProviderDisposedError.assert(!this.isDisposed, 'Provider is already disposed');
|
|
69
|
+
this.isDisposed = true;
|
|
70
|
+
this.getKey = undefined;
|
|
71
|
+
this.cache.clear();
|
|
72
|
+
this.accessRules.splice(0, this.accessRules.length);
|
|
73
|
+
this.mappers.splice(0, this.mappers.length);
|
|
74
|
+
this.argsFnList.splice(0, this.argsFnList.length);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { isDependencyKey } from '../container/IContainer.js';
|
|
2
|
+
import { SingleToken } from '../token/SingleToken.js';
|
|
3
|
+
import { isBindToken } from '../token/BindToken.js';
|
|
4
|
+
import { addClassMeta, getClassMeta } from '../metadata/class.js';
|
|
5
|
+
export const isProviderPipe = (obj) => obj !== null && typeof obj === 'object' && 'mapProvider' in obj;
|
|
6
|
+
export const registerPipe = (mapProvider) => ({
|
|
7
|
+
mapProvider,
|
|
8
|
+
mapRegistration: (r) => r.pipe(mapProvider),
|
|
9
|
+
});
|
|
10
|
+
const METADATA_KEY = 'registration';
|
|
11
|
+
export const getTransformers = (Target) => getClassMeta(Target, METADATA_KEY) ?? [];
|
|
12
|
+
export const register = (...mappers) => addClassMeta(METADATA_KEY, (acc) => {
|
|
13
|
+
const result = mappers.map((m) => {
|
|
14
|
+
if (isProviderPipe(m))
|
|
15
|
+
return (r) => m.mapRegistration(r);
|
|
16
|
+
if (isBindToken(m) || isDependencyKey(m))
|
|
17
|
+
return bindTo(m);
|
|
18
|
+
return m;
|
|
19
|
+
});
|
|
20
|
+
return acc ? [...result, ...acc] : result;
|
|
21
|
+
});
|
|
22
|
+
export const bindTo = (...tokens) => (r) => {
|
|
23
|
+
for (const token of tokens) {
|
|
24
|
+
const targetToken = isBindToken(token) ? token : new SingleToken(token);
|
|
25
|
+
targetToken.bindTo(r);
|
|
26
|
+
}
|
|
27
|
+
return r;
|
|
28
|
+
};
|
|
29
|
+
export const scope = (...rules) => (r) => r.when(...rules);
|
|
30
|
+
export const appendArgs = (...extraArgs) => registerPipe((p) => p.addArgsFn((_, { args = [] } = {}) => [...args, ...extraArgs]));
|
|
31
|
+
export const appendArgsFn = (fn) => registerPipe((p) => p.addArgsFn((scope, options) => [...(options?.args ?? []), ...fn(scope, options)]));
|
|
32
|
+
export const scopeAccess = (rule) => registerPipe((p) => p.addAccessRule(rule));
|
|
33
|
+
export const lazy = () => registerPipe((p) => p.lazy());
|
|
34
|
+
export const decorate = (...fns) => registerPipe((p) => p.map(...fns));
|
|
35
|
+
export const singleton = (getCacheKey) => registerPipe((p) => p.singleton(getCacheKey));
|