awilixify 2.2.1 → 3.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.
- package/README.md +15 -6
- package/dist/index.d.ts +19 -0
- package/dist/index.js +15 -0
- package/dist/lib/decorators/decorator-state.d.ts +15 -0
- package/dist/lib/decorators/decorator-state.js +72 -0
- package/dist/lib/decorators/decorator-state.types.d.ts +35 -0
- package/dist/lib/decorators/decorator-state.types.js +1 -0
- package/dist/lib/devtools/devtools.types.d.ts +63 -0
- package/dist/lib/devtools/devtools.types.js +1 -0
- package/dist/lib/devtools/helpers.d.ts +2 -0
- package/dist/lib/devtools/helpers.js +18 -0
- package/dist/lib/devtools/index.d.ts +4 -0
- package/dist/lib/devtools/index.js +3 -0
- package/dist/lib/di/common.types.d.ts +6 -0
- package/dist/lib/di/common.types.js +1 -0
- package/dist/lib/di/contexts/container-context-base.d.ts +63 -0
- package/dist/lib/di/contexts/container-context-base.js +232 -0
- package/dist/lib/di/contexts/di-context-async.d.ts +14 -0
- package/dist/lib/di/contexts/di-context-async.js +118 -0
- package/dist/lib/di/contexts/di-context-base.d.ts +19 -0
- package/dist/lib/di/contexts/di-context-base.js +33 -0
- package/dist/lib/di/contexts/di-context.d.ts +7 -0
- package/dist/lib/di/contexts/di-context.js +9 -0
- package/dist/lib/di/contexts/module-graph-ensurer.d.ts +32 -0
- package/dist/lib/di/contexts/module-graph-ensurer.js +50 -0
- package/dist/lib/di/errors.d.ts +69 -0
- package/dist/lib/di/errors.js +154 -0
- package/dist/lib/di/modules/module-def.types.d.ts +117 -0
- package/dist/lib/di/modules/module-def.types.js +1 -0
- package/dist/lib/di/modules/module-factories.d.ts +16 -0
- package/dist/lib/di/modules/module-factories.js +54 -0
- package/dist/lib/di/modules/module-overrides.d.ts +70 -0
- package/dist/lib/di/modules/module-overrides.js +3 -0
- package/dist/lib/di/modules/module-ref.types.d.ts +13 -0
- package/dist/lib/di/modules/module-ref.types.js +1 -0
- package/dist/lib/di/modules/module.types.d.ts +178 -0
- package/dist/lib/di/modules/module.types.js +1 -0
- package/dist/lib/di/modules/runtime-module.types.d.ts +32 -0
- package/dist/lib/di/modules/runtime-module.types.js +1 -0
- package/dist/lib/di/processors/controller-processor.d.ts +13 -0
- package/dist/lib/di/processors/controller-processor.js +76 -0
- package/dist/lib/di/processors/handler-processor.d.ts +19 -0
- package/dist/lib/di/processors/handler-processor.js +103 -0
- package/dist/lib/di/processors/initializer-processor.d.ts +20 -0
- package/dist/lib/di/processors/initializer-processor.js +95 -0
- package/dist/lib/di/processors/interceptor-processor.d.ts +21 -0
- package/dist/lib/di/processors/interceptor-processor.js +117 -0
- package/dist/lib/di/processors/keyed-feature-registrar.d.ts +19 -0
- package/dist/lib/di/processors/keyed-feature-registrar.js +112 -0
- package/dist/lib/di/processors/lifecycle-processor.d.ts +37 -0
- package/dist/lib/di/processors/lifecycle-processor.js +175 -0
- package/dist/lib/di/processors/overrides-processor.d.ts +24 -0
- package/dist/lib/di/processors/overrides-processor.js +69 -0
- package/dist/lib/di/providers/provider-dependency-sorter.d.ts +9 -0
- package/dist/lib/di/providers/provider-dependency-sorter.js +76 -0
- package/dist/lib/di/providers/provider-resolver.d.ts +35 -0
- package/dist/lib/di/providers/provider-resolver.js +168 -0
- package/dist/lib/di/providers/provider.types.d.ts +1 -0
- package/dist/lib/di/providers/provider.types.js +1 -0
- package/dist/lib/di/providers/types/controller.types.d.ts +9 -0
- package/dist/lib/di/providers/types/controller.types.js +1 -0
- package/dist/lib/di/providers/types/decorator-context.types.d.ts +9 -0
- package/dist/lib/di/providers/types/decorator-context.types.js +1 -0
- package/dist/lib/di/providers/types/handler.types.d.ts +9 -0
- package/dist/lib/di/providers/types/handler.types.js +1 -0
- package/dist/lib/di/providers/types/index.d.ts +7 -0
- package/dist/lib/di/providers/types/index.js +7 -0
- package/dist/lib/di/providers/types/initializer.types.d.ts +16 -0
- package/dist/lib/di/providers/types/initializer.types.js +3 -0
- package/dist/lib/di/providers/types/interceptor.types.d.ts +18 -0
- package/dist/lib/di/providers/types/interceptor.types.js +1 -0
- package/dist/lib/di/providers/types/middleware.types.d.ts +10 -0
- package/dist/lib/di/providers/types/middleware.types.js +1 -0
- package/dist/lib/di/providers/types/provider.types.d.ts +26 -0
- package/dist/lib/di/providers/types/provider.types.js +1 -0
- package/dist/lib/di/request-scope-context.d.ts +5 -0
- package/dist/lib/di/request-scope-context.js +28 -0
- package/dist/lib/di/type-guards.d.ts +35 -0
- package/dist/lib/di/type-guards.js +84 -0
- package/dist/lib/http/decorators.d.ts +33 -0
- package/dist/lib/http/decorators.js +151 -0
- package/dist/lib/http/exceptions.d.ts +58 -0
- package/dist/lib/http/exceptions.js +71 -0
- package/dist/lib/http/http-verbs.d.ts +12 -0
- package/dist/lib/http/http-verbs.js +11 -0
- package/dist/lib/http/index.d.ts +3 -0
- package/dist/lib/http/index.js +3 -0
- package/dist/lib/http/openapi-builder.d.ts +27 -0
- package/dist/lib/http/openapi-builder.js +83 -0
- package/dist/lib/mediator/contract.types.d.ts +159 -0
- package/dist/lib/mediator/contract.types.js +1 -0
- package/dist/lib/mediator/errors.d.ts +15 -0
- package/dist/lib/mediator/errors.js +33 -0
- package/dist/lib/mediator/global-middlewares.types.d.ts +13 -0
- package/dist/lib/mediator/global-middlewares.types.js +1 -0
- package/dist/lib/mediator/handler.types.d.ts +16 -0
- package/dist/lib/mediator/handler.types.js +1 -0
- package/dist/lib/mediator/mediator.d.ts +24 -0
- package/dist/lib/mediator/mediator.js +134 -0
- package/dist/lib/mediator/mediator.types.d.ts +64 -0
- package/dist/lib/mediator/mediator.types.js +1 -0
- package/dist/lib/mediator/middleware.types.d.ts +41 -0
- package/dist/lib/mediator/middleware.types.js +1 -0
- package/dist/lib/mediator/result.d.ts +18 -0
- package/dist/lib/mediator/result.js +24 -0
- package/dist/lib/react/errors.d.ts +12 -0
- package/dist/lib/react/errors.js +24 -0
- package/dist/lib/react/index.d.ts +4 -0
- package/dist/lib/react/index.js +4 -0
- package/dist/lib/react/react-component-processor.d.ts +10 -0
- package/dist/lib/react/react-component-processor.js +62 -0
- package/dist/lib/react/react-di-context.d.ts +16 -0
- package/dist/lib/react/react-di-context.js +52 -0
- package/dist/lib/react/react-module-factories.d.ts +6 -0
- package/dist/lib/react/react-module-factories.js +34 -0
- package/dist/lib/react/react-module.types.d.ts +169 -0
- package/dist/lib/react/react-module.types.js +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +33 -3
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { resolveDecoratorState } from "../../decorators/decorator-state.js";
|
|
2
|
+
import { getControllerMethodNames } from "../../devtools/helpers.js";
|
|
3
|
+
import * as ERRORS from "../errors.js";
|
|
4
|
+
import { runInRequestScopeContext } from "../request-scope-context.js";
|
|
5
|
+
import { KeyedFeatureRegistrar } from "./keyed-feature-registrar.js";
|
|
6
|
+
export class InitializerProcessor {
|
|
7
|
+
devtoolsProcessorRef;
|
|
8
|
+
keyedFeatureRegistrar = new KeyedFeatureRegistrar({});
|
|
9
|
+
resolversByModule = new WeakMap();
|
|
10
|
+
constructor(devtoolsProcessorRef) {
|
|
11
|
+
this.devtoolsProcessorRef = devtoolsProcessorRef;
|
|
12
|
+
}
|
|
13
|
+
collectInitializers(m, scope, importedModulesWithScope, controllers) {
|
|
14
|
+
this.resolversByModule.set(m, this.keyedFeatureRegistrar.register({
|
|
15
|
+
featureKind: "initializers",
|
|
16
|
+
module: m,
|
|
17
|
+
scope,
|
|
18
|
+
importedModulesWithScope,
|
|
19
|
+
}));
|
|
20
|
+
const initializers = [
|
|
21
|
+
...(this.resolversByModule.get(m) ??
|
|
22
|
+
new Map()).entries(),
|
|
23
|
+
];
|
|
24
|
+
if (initializers.length === 0 || controllers.length === 0)
|
|
25
|
+
return null;
|
|
26
|
+
this.devtoolsProcessorRef.current?.graphCollector.collectModuleRoutes({
|
|
27
|
+
module: m,
|
|
28
|
+
controllers,
|
|
29
|
+
initializers,
|
|
30
|
+
});
|
|
31
|
+
return async (options) => {
|
|
32
|
+
const activeInitializers = this.filterActiveInitializers(initializers, options);
|
|
33
|
+
if (activeInitializers.length === 0)
|
|
34
|
+
return;
|
|
35
|
+
for (const controller of controllers) {
|
|
36
|
+
for (const methodName of getControllerMethodNames(controller.controllerClass)) {
|
|
37
|
+
const invoke = (...args) => this.devtoolsProcessorRef.current?.tracer.traceInitializer({
|
|
38
|
+
args,
|
|
39
|
+
callback: () => runInRequestScopeContext(() => controller.resolve()[methodName](...args)),
|
|
40
|
+
controllerName: controller.controllerClass.name,
|
|
41
|
+
getStatusCode: () => getStatusCode(args),
|
|
42
|
+
methodName: String(methodName),
|
|
43
|
+
moduleName: m.name,
|
|
44
|
+
}) ??
|
|
45
|
+
runInRequestScopeContext(() => controller.resolve()[methodName](...args));
|
|
46
|
+
this.ensureNoMultiInvokableInitializersPerMethod(m.name, methodName, controller, activeInitializers);
|
|
47
|
+
for (const [, resolveInitializer] of activeInitializers) {
|
|
48
|
+
const initializer = resolveInitializer();
|
|
49
|
+
const decoratorState = resolveDecoratorState(controller.controllerClass, initializer.token);
|
|
50
|
+
if (decoratorState === null)
|
|
51
|
+
continue;
|
|
52
|
+
const metadata = decoratorState.methods.get(methodName);
|
|
53
|
+
if (metadata === undefined)
|
|
54
|
+
continue;
|
|
55
|
+
await initializer.initialize({
|
|
56
|
+
moduleName: m.name,
|
|
57
|
+
target: controller.controllerClass,
|
|
58
|
+
methodName,
|
|
59
|
+
metadata,
|
|
60
|
+
decoratorState,
|
|
61
|
+
...(initializer.usesInvoke ? { invoke } : {}),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
ensureNoMultiInvokableInitializersPerMethod(moduleName, methodName, controller, initializers) {
|
|
69
|
+
const invokableInitializers = initializers
|
|
70
|
+
.map(([, resolver]) => resolver())
|
|
71
|
+
.filter((initializer) => {
|
|
72
|
+
const decoratorState = resolveDecoratorState(controller.controllerClass, initializer.token);
|
|
73
|
+
return (decoratorState?.methods.has(methodName) === true &&
|
|
74
|
+
initializer.usesInvoke === true);
|
|
75
|
+
});
|
|
76
|
+
if (invokableInitializers.length > 1) {
|
|
77
|
+
throw new ERRORS.MultipleInvokeInitializersPerMethodError(moduleName, controller.controllerClass.name, String(methodName), invokableInitializers.map((initializer) => initializer.constructor.name));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
filterActiveInitializers(initializers, options) {
|
|
81
|
+
if (options?.excludeInitializers === true)
|
|
82
|
+
return [];
|
|
83
|
+
if (!Array.isArray(options?.excludeInitializers))
|
|
84
|
+
return initializers;
|
|
85
|
+
const excludedKeys = new Set(options.excludeInitializers);
|
|
86
|
+
return initializers.filter(([key]) => !excludedKeys.has(key));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function getStatusCode(args) {
|
|
90
|
+
const reply = args[1];
|
|
91
|
+
if (!reply || typeof reply !== "object")
|
|
92
|
+
return undefined;
|
|
93
|
+
const statusCode = reply.statusCode;
|
|
94
|
+
return typeof statusCode === "number" ? statusCode : undefined;
|
|
95
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as Awilix from "awilix";
|
|
2
|
+
import type { DevtoolsProcessorRef } from "../../devtools/devtools.types.js";
|
|
3
|
+
import type { RegisteredModuleScope } from "../contexts/container-context-base.js";
|
|
4
|
+
import type { DiContextOptions } from "../contexts/di-context-base.js";
|
|
5
|
+
import type { InternalModuleLike as M } from "../modules/runtime-module.types.js";
|
|
6
|
+
export declare class InterceptorProcessor {
|
|
7
|
+
private readonly devtoolsProcessorRef;
|
|
8
|
+
private readonly keyedFeatureRegistrar;
|
|
9
|
+
private readonly resolversByModule;
|
|
10
|
+
constructor(providerOptions: DiContextOptions["providerOptions"], devtoolsProcessorRef: DevtoolsProcessorRef);
|
|
11
|
+
processInterceptors(m: M, scope: Awilix.AwilixContainer, importedModulesWithScope: RegisteredModuleScope[]): void;
|
|
12
|
+
createInterceptedProviderResolver({ module, useClass, options, resolver, }: {
|
|
13
|
+
module: M;
|
|
14
|
+
useClass: new (...args: any[]) => object;
|
|
15
|
+
options: Awilix.BuildResolverOptions<any>;
|
|
16
|
+
resolver?: Awilix.Resolver<any>;
|
|
17
|
+
}): Awilix.Resolver<any>;
|
|
18
|
+
private createInterceptedProviderInstance;
|
|
19
|
+
private callWithInterceptorChain;
|
|
20
|
+
private get devtoolsTracer();
|
|
21
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import * as Awilix from "awilix";
|
|
2
|
+
import { hasDecoratorMethodMetadata, resolveDecoratorState, } from "../../decorators/decorator-state.js";
|
|
3
|
+
import { KeyedFeatureRegistrar } from "./keyed-feature-registrar.js";
|
|
4
|
+
export class InterceptorProcessor {
|
|
5
|
+
devtoolsProcessorRef;
|
|
6
|
+
keyedFeatureRegistrar;
|
|
7
|
+
resolversByModule = new WeakMap();
|
|
8
|
+
constructor(providerOptions, devtoolsProcessorRef) {
|
|
9
|
+
this.devtoolsProcessorRef = devtoolsProcessorRef;
|
|
10
|
+
this.keyedFeatureRegistrar = new KeyedFeatureRegistrar(providerOptions ?? {});
|
|
11
|
+
}
|
|
12
|
+
processInterceptors(m, scope, importedModulesWithScope) {
|
|
13
|
+
const resolverMap = this.keyedFeatureRegistrar.register({
|
|
14
|
+
featureKind: "interceptors",
|
|
15
|
+
module: m,
|
|
16
|
+
scope,
|
|
17
|
+
importedModulesWithScope,
|
|
18
|
+
});
|
|
19
|
+
this.resolversByModule.set(m, resolverMap);
|
|
20
|
+
}
|
|
21
|
+
createInterceptedProviderResolver({ module, useClass, options, resolver, }) {
|
|
22
|
+
const baseResolver = resolver || Awilix.asClass(useClass, options);
|
|
23
|
+
const interceptors = [
|
|
24
|
+
...(this.resolversByModule.get(module)?.values() ?? []),
|
|
25
|
+
];
|
|
26
|
+
if (interceptors.length === 0) {
|
|
27
|
+
return baseResolver;
|
|
28
|
+
}
|
|
29
|
+
if (!hasDecoratorMethodMetadata(useClass)) {
|
|
30
|
+
return baseResolver;
|
|
31
|
+
}
|
|
32
|
+
return Awilix.createBuildResolver({
|
|
33
|
+
...options,
|
|
34
|
+
resolve: (container) => this.createInterceptedProviderInstance(baseResolver.resolve(container), useClass, interceptors, module.name),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
createInterceptedProviderInstance(instance, metadataTarget, resolveInterceptors, moduleName) {
|
|
38
|
+
const wrappers = new Map();
|
|
39
|
+
const self = this;
|
|
40
|
+
return new Proxy(instance, {
|
|
41
|
+
get(target, propertyKey, proxyReceiver) {
|
|
42
|
+
const value = Reflect.get(target, propertyKey, proxyReceiver);
|
|
43
|
+
if (typeof value !== "function")
|
|
44
|
+
return value;
|
|
45
|
+
const existing = wrappers.get(propertyKey);
|
|
46
|
+
if (existing)
|
|
47
|
+
return existing;
|
|
48
|
+
const wrapped = (...args) => {
|
|
49
|
+
const interceptors = resolveInterceptors.map((resolve) => resolve());
|
|
50
|
+
const metadataByToken = new Map();
|
|
51
|
+
for (const interceptor of interceptors) {
|
|
52
|
+
const state = resolveDecoratorState(metadataTarget, interceptor.token);
|
|
53
|
+
if (state === null)
|
|
54
|
+
continue;
|
|
55
|
+
const method = state.methods.get(propertyKey);
|
|
56
|
+
if (method !== undefined) {
|
|
57
|
+
metadataByToken.set(interceptor.token.stateSymbol, {
|
|
58
|
+
state,
|
|
59
|
+
method,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (metadataByToken.size === 0)
|
|
64
|
+
return value.apply(target, args);
|
|
65
|
+
return self.callWithInterceptorChain({
|
|
66
|
+
target,
|
|
67
|
+
methodName: propertyKey,
|
|
68
|
+
moduleName,
|
|
69
|
+
args,
|
|
70
|
+
metadataByToken,
|
|
71
|
+
interceptors,
|
|
72
|
+
proceed: () => value.apply(target, args),
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
wrappers.set(propertyKey, wrapped);
|
|
76
|
+
return wrapped;
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
callWithInterceptorChain({ target, methodName, moduleName, args, metadataByToken, interceptors, proceed, }) {
|
|
81
|
+
const invoke = (index, next) => {
|
|
82
|
+
if (index >= interceptors.length)
|
|
83
|
+
return next();
|
|
84
|
+
const current = interceptors[index];
|
|
85
|
+
if (!current)
|
|
86
|
+
return next();
|
|
87
|
+
const metadata = metadataByToken.get(current.token.stateSymbol);
|
|
88
|
+
if (metadata === undefined) {
|
|
89
|
+
return invoke(index + 1, next);
|
|
90
|
+
}
|
|
91
|
+
const interceptorName = current.constructor.name;
|
|
92
|
+
const callInterceptor = () => current.intercept({
|
|
93
|
+
target,
|
|
94
|
+
methodName,
|
|
95
|
+
args,
|
|
96
|
+
moduleName,
|
|
97
|
+
metadata: metadata.method,
|
|
98
|
+
decoratorState: metadata.state,
|
|
99
|
+
proceed: () => invoke(index + 1, next),
|
|
100
|
+
});
|
|
101
|
+
if (!this.devtoolsTracer)
|
|
102
|
+
return callInterceptor();
|
|
103
|
+
return this.devtoolsTracer.recordSpan({
|
|
104
|
+
kind: "interceptor",
|
|
105
|
+
moduleName,
|
|
106
|
+
providerKey: interceptorName,
|
|
107
|
+
methodName: String(methodName),
|
|
108
|
+
args,
|
|
109
|
+
callback: callInterceptor,
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
return invoke(0, proceed);
|
|
113
|
+
}
|
|
114
|
+
get devtoolsTracer() {
|
|
115
|
+
return this.devtoolsProcessorRef.current?.tracer;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as Awilix from "awilix";
|
|
2
|
+
import type { RegisteredModuleScope } from "../contexts/container-context-base.js";
|
|
3
|
+
import type { InternalModuleLike as M } from "../modules/runtime-module.types.js";
|
|
4
|
+
type KeyedFeatureKind = "queryPreHandlers" | "commandPreHandlers" | "interceptors" | "initializers";
|
|
5
|
+
type RegisterKeyedFeatureParams = {
|
|
6
|
+
featureKind: KeyedFeatureKind;
|
|
7
|
+
module: M;
|
|
8
|
+
scope: Awilix.AwilixContainer;
|
|
9
|
+
importedModulesWithScope: RegisteredModuleScope[];
|
|
10
|
+
};
|
|
11
|
+
export declare class KeyedFeatureRegistrar {
|
|
12
|
+
private readonly providerOptions;
|
|
13
|
+
constructor(providerOptions: Partial<Awilix.BuildResolverOptions<any>>);
|
|
14
|
+
private readonly featureConfig;
|
|
15
|
+
register<T>({ featureKind, module, scope, importedModulesWithScope, }: RegisterKeyedFeatureParams): Map<string, () => T>;
|
|
16
|
+
private getProviderOptions;
|
|
17
|
+
private ensureInitializerTokenUniqueness;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as Awilix from "awilix";
|
|
2
|
+
import * as ERRORS from "../errors.js";
|
|
3
|
+
import { ProviderResolver } from "../providers/provider-resolver.js";
|
|
4
|
+
import { getOrCreateRequestScope, resolveFromRequestScope, } from "../request-scope-context.js";
|
|
5
|
+
export class KeyedFeatureRegistrar {
|
|
6
|
+
providerOptions;
|
|
7
|
+
constructor(providerOptions) {
|
|
8
|
+
this.providerOptions = providerOptions;
|
|
9
|
+
}
|
|
10
|
+
featureConfig = {
|
|
11
|
+
queryPreHandlers: {
|
|
12
|
+
exportKey: "queryPreHandlerExports",
|
|
13
|
+
featureName: "pre-handler",
|
|
14
|
+
},
|
|
15
|
+
commandPreHandlers: {
|
|
16
|
+
exportKey: "commandPreHandlerExports",
|
|
17
|
+
featureName: "pre-handler",
|
|
18
|
+
},
|
|
19
|
+
interceptors: {
|
|
20
|
+
exportKey: "interceptorExports",
|
|
21
|
+
featureName: "interceptor",
|
|
22
|
+
},
|
|
23
|
+
initializers: {
|
|
24
|
+
exportKey: "initializerExports",
|
|
25
|
+
featureName: "initializer",
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
register({ featureKind, module, scope, importedModulesWithScope, }) {
|
|
29
|
+
const ownerByKey = new Map();
|
|
30
|
+
const resolverMap = new Map();
|
|
31
|
+
const ownerByInitializerToken = new Map();
|
|
32
|
+
const config = this.featureConfig[featureKind];
|
|
33
|
+
const providerResolver = new ProviderResolver(undefined, this.getProviderOptions(featureKind), {}, getOrCreateRequestScope);
|
|
34
|
+
for (const { module: importedModule, moduleScope: importedModuleScope, } of importedModulesWithScope) {
|
|
35
|
+
for (const key of importedModule[config.exportKey] ?? []) {
|
|
36
|
+
const feature = importedModule[featureKind]?.[key];
|
|
37
|
+
if (!feature) {
|
|
38
|
+
throw new ERRORS.InvalidProviderDefinitionError(importedModule.name, key);
|
|
39
|
+
}
|
|
40
|
+
if (ownerByKey.has(key)) {
|
|
41
|
+
throw new ERRORS.FeatureNameConflictError(module.name, config.featureName, key, ownerByKey.get(key) ?? importedModule.name);
|
|
42
|
+
}
|
|
43
|
+
const symbol = Symbol(`${featureKind}_export_${importedModule.name}_${key}`);
|
|
44
|
+
scope.register({
|
|
45
|
+
[symbol]: providerResolver.resolveClassProvider({
|
|
46
|
+
provider: feature,
|
|
47
|
+
resolutionScope: importedModuleScope.scope,
|
|
48
|
+
module: importedModule,
|
|
49
|
+
wrapForExport: true,
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
if (featureKind === "initializers") {
|
|
53
|
+
this.ensureInitializerTokenUniqueness({
|
|
54
|
+
scope,
|
|
55
|
+
symbol,
|
|
56
|
+
ownerByInitializerToken,
|
|
57
|
+
moduleName: module.name,
|
|
58
|
+
featureName: feature.name || key,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
resolverMap.set(key, featureKind === "initializers"
|
|
62
|
+
? () => scope.resolve(symbol)
|
|
63
|
+
: () => resolveFromRequestScope(scope, symbol));
|
|
64
|
+
ownerByKey.set(key, importedModule.name);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const [key, feature] of Object.entries(module[featureKind] ?? {})) {
|
|
68
|
+
if (ownerByKey.has(key)) {
|
|
69
|
+
throw new ERRORS.FeatureNameConflictError(module.name, config.featureName, key, ownerByKey.get(key) ?? module.name);
|
|
70
|
+
}
|
|
71
|
+
const symbol = Symbol(`${featureKind}_${module.name}_${key}`);
|
|
72
|
+
scope.register({
|
|
73
|
+
[symbol]: providerResolver.resolveClassProvider({
|
|
74
|
+
provider: feature,
|
|
75
|
+
resolutionScope: scope,
|
|
76
|
+
module,
|
|
77
|
+
}),
|
|
78
|
+
});
|
|
79
|
+
if (featureKind === "initializers") {
|
|
80
|
+
this.ensureInitializerTokenUniqueness({
|
|
81
|
+
scope,
|
|
82
|
+
symbol,
|
|
83
|
+
ownerByInitializerToken,
|
|
84
|
+
moduleName: module.name,
|
|
85
|
+
featureName: feature.name || key,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
resolverMap.set(key, featureKind === "initializers"
|
|
89
|
+
? () => scope.resolve(symbol)
|
|
90
|
+
: () => resolveFromRequestScope(scope, symbol));
|
|
91
|
+
ownerByKey.set(key, module.name);
|
|
92
|
+
}
|
|
93
|
+
return resolverMap;
|
|
94
|
+
}
|
|
95
|
+
getProviderOptions(featureKind) {
|
|
96
|
+
if (featureKind === "initializers") {
|
|
97
|
+
return {
|
|
98
|
+
lifetime: Awilix.Lifetime.SINGLETON,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return this.providerOptions;
|
|
102
|
+
}
|
|
103
|
+
ensureInitializerTokenUniqueness({ scope, symbol, ownerByInitializerToken, moduleName, featureName, }) {
|
|
104
|
+
const initializer = scope.resolve(symbol);
|
|
105
|
+
const token = initializer.token.stateSymbol;
|
|
106
|
+
const existingModuleName = ownerByInitializerToken.get(token);
|
|
107
|
+
if (existingModuleName) {
|
|
108
|
+
throw new ERRORS.DuplicateInitializerTokenError(moduleName, featureName, existingModuleName);
|
|
109
|
+
}
|
|
110
|
+
ownerByInitializerToken.set(token, moduleName);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as Awilix from "awilix";
|
|
2
|
+
import type { InternalModuleLike as M } from "../modules/runtime-module.types.js";
|
|
3
|
+
import type { InitializerTask } from "./initializer-processor.js";
|
|
4
|
+
export interface ModuleInitOptions {
|
|
5
|
+
excludeInitializers?: true | readonly string[];
|
|
6
|
+
excludePostInit?: true | readonly string[];
|
|
7
|
+
}
|
|
8
|
+
export interface LifecycleMethods {
|
|
9
|
+
init(options?: ModuleInitOptions): Promise<void>;
|
|
10
|
+
dispose(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
export declare class LifecycleProcessor {
|
|
13
|
+
private readonly providerOptions;
|
|
14
|
+
private readonly eagerProviderRefs;
|
|
15
|
+
private readonly initializerTasks;
|
|
16
|
+
private readonly createdScopes;
|
|
17
|
+
private initPromise?;
|
|
18
|
+
private disposePromise?;
|
|
19
|
+
constructor(providerOptions: Partial<Awilix.BuildResolverOptions<any>>);
|
|
20
|
+
collectEagerProviders(m: M, scope: Awilix.AwilixContainer): void;
|
|
21
|
+
trackScope(scope: Awilix.AwilixContainer): void;
|
|
22
|
+
addInitializerTask(task: InitializerTask | null): void;
|
|
23
|
+
init(options?: ModuleInitOptions): Promise<void>;
|
|
24
|
+
dispose(): Promise<void>;
|
|
25
|
+
private executeInit;
|
|
26
|
+
private executeDispose;
|
|
27
|
+
private shouldSkipProviderPostInit;
|
|
28
|
+
private getEagerProviderKeys;
|
|
29
|
+
private ensureEagerProviderUsesSingletonLifetime;
|
|
30
|
+
private resolveEagerProvider;
|
|
31
|
+
private callProviderInitAsync;
|
|
32
|
+
private callProviderPostInitAsync;
|
|
33
|
+
private sortEagerProviderRefs;
|
|
34
|
+
private getProviderInitAfter;
|
|
35
|
+
private sortEagerProviderIds;
|
|
36
|
+
private static buildEagerProviderNodeKeyMap;
|
|
37
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import * as Awilix from "awilix";
|
|
2
|
+
import * as ERRORS from "../errors.js";
|
|
3
|
+
import * as GUARGS from "../type-guards.js";
|
|
4
|
+
export class LifecycleProcessor {
|
|
5
|
+
providerOptions;
|
|
6
|
+
eagerProviderRefs = [];
|
|
7
|
+
initializerTasks = [];
|
|
8
|
+
createdScopes = [];
|
|
9
|
+
initPromise;
|
|
10
|
+
disposePromise;
|
|
11
|
+
constructor(providerOptions) {
|
|
12
|
+
this.providerOptions = providerOptions;
|
|
13
|
+
}
|
|
14
|
+
collectEagerProviders(m, scope) {
|
|
15
|
+
for (const key of this.getEagerProviderKeys(m)) {
|
|
16
|
+
this.eagerProviderRefs.push({
|
|
17
|
+
module: m,
|
|
18
|
+
scope,
|
|
19
|
+
key,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
trackScope(scope) {
|
|
24
|
+
this.createdScopes.push(scope);
|
|
25
|
+
}
|
|
26
|
+
addInitializerTask(task) {
|
|
27
|
+
if (!task)
|
|
28
|
+
return;
|
|
29
|
+
this.initializerTasks.push(task);
|
|
30
|
+
}
|
|
31
|
+
init(options) {
|
|
32
|
+
this.initPromise ??= this.executeInit(options);
|
|
33
|
+
return this.initPromise;
|
|
34
|
+
}
|
|
35
|
+
dispose() {
|
|
36
|
+
this.disposePromise ??= this.executeDispose();
|
|
37
|
+
return this.disposePromise;
|
|
38
|
+
}
|
|
39
|
+
async executeInit(options) {
|
|
40
|
+
const initializedProviders = [];
|
|
41
|
+
for (const { module, scope, key } of this.sortEagerProviderRefs()) {
|
|
42
|
+
this.ensureEagerProviderUsesSingletonLifetime(module, scope, key);
|
|
43
|
+
const instance = await this.resolveEagerProvider({
|
|
44
|
+
module,
|
|
45
|
+
scope,
|
|
46
|
+
key,
|
|
47
|
+
});
|
|
48
|
+
initializedProviders.push({ module, scope, key, instance });
|
|
49
|
+
await this.callProviderInitAsync(instance);
|
|
50
|
+
}
|
|
51
|
+
for (const initialize of this.initializerTasks) {
|
|
52
|
+
await initialize(options);
|
|
53
|
+
}
|
|
54
|
+
for (const { key, instance } of initializedProviders) {
|
|
55
|
+
if (this.shouldSkipProviderPostInit(key, options))
|
|
56
|
+
continue;
|
|
57
|
+
await this.callProviderPostInitAsync(instance);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async executeDispose() {
|
|
61
|
+
const uniqueScopes = Array.from(new Set(this.createdScopes)).reverse();
|
|
62
|
+
for (const scope of uniqueScopes) {
|
|
63
|
+
await scope.dispose();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
shouldSkipProviderPostInit(key, options) {
|
|
67
|
+
if (options?.excludePostInit === true)
|
|
68
|
+
return true;
|
|
69
|
+
if (!Array.isArray(options?.excludePostInit))
|
|
70
|
+
return false;
|
|
71
|
+
return new Set(options.excludePostInit).has(key);
|
|
72
|
+
}
|
|
73
|
+
getEagerProviderKeys(m) {
|
|
74
|
+
return Object.entries(m.providers || {})
|
|
75
|
+
.filter(([, provider]) => GUARGS.isEagerProvider(provider))
|
|
76
|
+
.map(([key]) => key);
|
|
77
|
+
}
|
|
78
|
+
ensureEagerProviderUsesSingletonLifetime(m, scope, key) {
|
|
79
|
+
if (scope.registrations[key]?.lifetime !== Awilix.Lifetime.SINGLETON) {
|
|
80
|
+
throw new ERRORS.EagerProviderRequiresSingletonLifetimeError(m.name, key);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async resolveEagerProvider({ module, scope, key, }) {
|
|
84
|
+
const provider = module.providers?.[key];
|
|
85
|
+
if (!provider ||
|
|
86
|
+
!GUARGS.isFactoryProvider(provider) ||
|
|
87
|
+
!GUARGS.isAsyncFactoryProvider(provider)) {
|
|
88
|
+
return scope.resolve(key);
|
|
89
|
+
}
|
|
90
|
+
const factoryDeps = (provider.inject || []).map((dependencyKey) => scope.registrations[dependencyKey].resolve(scope));
|
|
91
|
+
const resolvedValue = await provider.useFactory(...factoryDeps);
|
|
92
|
+
const { eager, initAfter, inject, useFactory, ...providerResolverOptions } = provider;
|
|
93
|
+
const resolverOptions = {
|
|
94
|
+
...this.providerOptions,
|
|
95
|
+
...module.providerOptions,
|
|
96
|
+
...providerResolverOptions,
|
|
97
|
+
};
|
|
98
|
+
scope.register({
|
|
99
|
+
[key]: Awilix.asFunction(() => resolvedValue, resolverOptions),
|
|
100
|
+
});
|
|
101
|
+
return scope.resolve(key);
|
|
102
|
+
}
|
|
103
|
+
async callProviderInitAsync(instance) {
|
|
104
|
+
if (!GUARGS.hasProviderInit(instance))
|
|
105
|
+
return;
|
|
106
|
+
await instance.init();
|
|
107
|
+
}
|
|
108
|
+
async callProviderPostInitAsync(instance) {
|
|
109
|
+
if (!GUARGS.hasProviderPostInit(instance))
|
|
110
|
+
return;
|
|
111
|
+
await instance.postInit();
|
|
112
|
+
}
|
|
113
|
+
sortEagerProviderRefs() {
|
|
114
|
+
const nodes = this.eagerProviderRefs.map((ref) => ({
|
|
115
|
+
...ref,
|
|
116
|
+
id: `${ref.module.name}:${ref.key}`,
|
|
117
|
+
initAfter: this.getProviderInitAfter(ref.module, ref.key),
|
|
118
|
+
}));
|
|
119
|
+
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
120
|
+
const nodeByKey = LifecycleProcessor.buildEagerProviderNodeKeyMap(nodes);
|
|
121
|
+
const graph = new Map(nodes.map((node) => [node.id, []]));
|
|
122
|
+
const inDegree = new Map(nodes.map((node) => [node.id, 0]));
|
|
123
|
+
for (const node of nodes) {
|
|
124
|
+
for (const dependencyKey of node.initAfter) {
|
|
125
|
+
const dependencyNode = nodeByKey.get(dependencyKey);
|
|
126
|
+
if (!dependencyNode) {
|
|
127
|
+
throw new ERRORS.EagerProviderInitDependencyNotFoundError(node.module.name, node.key, dependencyKey);
|
|
128
|
+
}
|
|
129
|
+
const dependencies = graph.get(dependencyNode.id);
|
|
130
|
+
if (!dependencies)
|
|
131
|
+
continue;
|
|
132
|
+
dependencies.push(node.id);
|
|
133
|
+
inDegree.set(node.id, (inDegree.get(node.id) || 0) + 1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const sortedIds = this.sortEagerProviderIds(graph, inDegree);
|
|
137
|
+
if (sortedIds.length !== nodes.length) {
|
|
138
|
+
throw new ERRORS.CircularProviderInitDependencyError(nodes.map((node) => node.key));
|
|
139
|
+
}
|
|
140
|
+
return sortedIds.map((id) => {
|
|
141
|
+
const { module, scope, key } = nodeById.get(id);
|
|
142
|
+
return { module, scope, key };
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
getProviderInitAfter(module, key) {
|
|
146
|
+
const provider = module.providers?.[key];
|
|
147
|
+
if (!GUARGS.hasInitAfter(provider))
|
|
148
|
+
return [];
|
|
149
|
+
return [...provider.initAfter];
|
|
150
|
+
}
|
|
151
|
+
sortEagerProviderIds(graph, inDegree) {
|
|
152
|
+
const queue = Array.from(inDegree.entries())
|
|
153
|
+
.filter(([, degree]) => degree === 0)
|
|
154
|
+
.map(([id]) => id);
|
|
155
|
+
const sortedIds = [];
|
|
156
|
+
while (queue.length > 0) {
|
|
157
|
+
const current = queue.shift();
|
|
158
|
+
sortedIds.push(current);
|
|
159
|
+
for (const dependent of graph.get(current) || []) {
|
|
160
|
+
const nextDegree = (inDegree.get(dependent) || 0) - 1;
|
|
161
|
+
inDegree.set(dependent, nextDegree);
|
|
162
|
+
if (nextDegree === 0)
|
|
163
|
+
queue.push(dependent);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return sortedIds;
|
|
167
|
+
}
|
|
168
|
+
static buildEagerProviderNodeKeyMap(nodes) {
|
|
169
|
+
const nodeByKey = new Map();
|
|
170
|
+
for (const node of nodes) {
|
|
171
|
+
nodeByKey.set(node.key, node);
|
|
172
|
+
}
|
|
173
|
+
return nodeByKey;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AnyModuleOverride } from "../modules/module-overrides.js";
|
|
2
|
+
import type { InternalModuleLike } from "../modules/runtime-module.types.js";
|
|
3
|
+
export type OverrideOptions = {
|
|
4
|
+
moduleOverrides?: readonly AnyModuleOverride[];
|
|
5
|
+
};
|
|
6
|
+
export declare const FeatureKind: {
|
|
7
|
+
readonly Providers: "providers";
|
|
8
|
+
readonly QueryPreHandlers: "queryPreHandlers";
|
|
9
|
+
readonly CommandPreHandlers: "commandPreHandlers";
|
|
10
|
+
readonly Interceptors: "interceptors";
|
|
11
|
+
readonly Initializers: "initializers";
|
|
12
|
+
};
|
|
13
|
+
export declare class OverridesProcessor<M extends InternalModuleLike> {
|
|
14
|
+
private readonly options;
|
|
15
|
+
private readonly effectiveModuleMap;
|
|
16
|
+
private readonly appliedModuleOverrides;
|
|
17
|
+
constructor(options: OverrideOptions);
|
|
18
|
+
applyModuleOverrides(module: M): M;
|
|
19
|
+
getModuleWithOverrides(module: M): M;
|
|
20
|
+
ensureAllModuleOverridesApplied(): void;
|
|
21
|
+
private ensureModuleOverrideKeysExist;
|
|
22
|
+
private remapFeatureOverrides;
|
|
23
|
+
private ensureFeatureOverrideKeysExist;
|
|
24
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as ERRORS from "../errors.js";
|
|
2
|
+
export const FeatureKind = {
|
|
3
|
+
Providers: "providers",
|
|
4
|
+
QueryPreHandlers: "queryPreHandlers",
|
|
5
|
+
CommandPreHandlers: "commandPreHandlers",
|
|
6
|
+
Interceptors: "interceptors",
|
|
7
|
+
Initializers: "initializers",
|
|
8
|
+
};
|
|
9
|
+
const FEATURE_KINDS = Object.values(FeatureKind);
|
|
10
|
+
export class OverridesProcessor {
|
|
11
|
+
options;
|
|
12
|
+
effectiveModuleMap = new WeakMap();
|
|
13
|
+
appliedModuleOverrides = new WeakSet();
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.options = options;
|
|
16
|
+
}
|
|
17
|
+
applyModuleOverrides(module) {
|
|
18
|
+
const moduleOverride = this.options.moduleOverrides?.find((override) => override.module === module);
|
|
19
|
+
if (!moduleOverride) {
|
|
20
|
+
this.effectiveModuleMap.set(module, module);
|
|
21
|
+
return module;
|
|
22
|
+
}
|
|
23
|
+
this.ensureModuleOverrideKeysExist(module, moduleOverride);
|
|
24
|
+
this.appliedModuleOverrides.add(module);
|
|
25
|
+
const remappedFeatures = FEATURE_KINDS.reduce((acc, featureKind) => {
|
|
26
|
+
Object.assign(acc, this.remapFeatureOverrides(module, moduleOverride, featureKind));
|
|
27
|
+
return acc;
|
|
28
|
+
}, {});
|
|
29
|
+
const effectiveModule = {
|
|
30
|
+
...module,
|
|
31
|
+
...remappedFeatures,
|
|
32
|
+
};
|
|
33
|
+
this.effectiveModuleMap.set(module, effectiveModule);
|
|
34
|
+
return effectiveModule;
|
|
35
|
+
}
|
|
36
|
+
getModuleWithOverrides(module) {
|
|
37
|
+
return this.effectiveModuleMap.get(module) ?? module;
|
|
38
|
+
}
|
|
39
|
+
ensureAllModuleOverridesApplied() {
|
|
40
|
+
for (const override of this.options.moduleOverrides ?? []) {
|
|
41
|
+
if (!this.appliedModuleOverrides.has(override.module)) {
|
|
42
|
+
throw new ERRORS.ModuleOverrideTargetNotFoundError(override.module.name);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
ensureModuleOverrideKeysExist(module, moduleOverride) {
|
|
47
|
+
for (const featureKind of FEATURE_KINDS) {
|
|
48
|
+
this.ensureFeatureOverrideKeysExist(module, featureKind, moduleOverride.overrides[featureKind]);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
remapFeatureOverrides(module, moduleOverride, featureKind) {
|
|
52
|
+
return {
|
|
53
|
+
[featureKind]: {
|
|
54
|
+
...module[featureKind],
|
|
55
|
+
...moduleOverride.overrides[featureKind],
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
ensureFeatureOverrideKeysExist(module, featureKind, overrides) {
|
|
60
|
+
if (!overrides)
|
|
61
|
+
return;
|
|
62
|
+
const featureKeys = new Set(Object.keys(module[featureKind] || {}));
|
|
63
|
+
for (const key of Object.keys(overrides)) {
|
|
64
|
+
if (!featureKeys.has(key)) {
|
|
65
|
+
throw new ERRORS.ModuleFeatureOverrideNotFoundError(module.name, featureKind, key);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|