moost
Version:
958 lines (931 loc) • 33.5 kB
JavaScript
import { Mate, getConstructor, isConstructor } from "@prostojs/mate";
import { ContextInjector, EventLogger, createAsyncEventContext, getContextInjector, replaceContextInjector, useAsyncEventContext, useEventId, useEventLogger, useRouteParams } from "@wooksjs/event-core";
import { Infact, createProvideRegistry, createReplaceRegistry } from "@prostojs/infact";
import { ProstoLogger, coloredConsole, createConsoleTransort } from "@prostojs/logger";
import { Hookable } from "hookable";
import { clearGlobalWooks, getGlobalWooks } from "wooks";
//#region packages/moost/src/logger.ts
let defaultLogger;
function setDefaultLogger(logger) {
defaultLogger = logger;
}
function getDefaultLogger(topic) {
if (!defaultLogger) defaultLogger = new ProstoLogger({
level: 4,
transports: [createConsoleTransort({ format: coloredConsole })]
});
return topic && defaultLogger instanceof ProstoLogger ? defaultLogger.createTopic(topic) : defaultLogger;
}
//#endregion
//#region packages/moost/src/pipes/run-pipes.ts
async function runPipes(pipes, initialValue, metas, level) {
let v = initialValue;
for (const pipe of pipes) v = await pipe.handler(v, metas, level);
return v;
}
//#endregion
//#region packages/moost/src/metadata/moost-metadata.ts
const METADATA_WORKSPACE = "moost";
const moostMate = new Mate(METADATA_WORKSPACE, {
readType: true,
readReturnType: true,
collectPropKeys: true,
inherit(classMeta, targetMeta, level) {
if (level === "CLASS") return !!classMeta?.inherit;
if (level === "PROP") return !!targetMeta?.inherit || !!(classMeta?.inherit && !targetMeta);
return !!targetMeta?.inherit;
}
});
function getMoostMate() {
return moostMate;
}
//#endregion
//#region packages/moost/src/metadata/infact.ts
const sharedMoostInfact = getNewMoostInfact();
const INFACT_BANNER = `${"\x1B[2m\x1B[35m"}infact`;
let loggingOptions = {
newInstance: "SINGLETON",
warn: true,
error: true
};
function setInfactLoggingOptions(options) {
loggingOptions = {
...loggingOptions,
...options
};
}
function getMoostInfact() {
return sharedMoostInfact;
}
const scopeVarsMap = new Map();
function defineInfactScope(name, scopeVars) {
scopeVarsMap.set(name, scopeVars);
getMoostInfact().registerScope(name);
}
function getInfactScopeVars(name) {
return scopeVarsMap.get(name);
}
function getNewMoostInfact() {
return new Infact({
describeClass(classConstructor) {
const meta = getMoostMate().read(classConstructor);
return {
injectable: !!meta?.injectable,
global: false,
constructorParams: meta?.params || [],
provide: meta?.provide,
properties: meta?.properties || [],
scopeId: meta?.injectable === "FOR_EVENT" ? useEventId().getId() : undefined
};
},
resolveParam({ paramMeta, customData, classConstructor, index, scopeId }) {
if (paramMeta && customData?.pipes) return runPipes(customData.pipes, undefined, {
paramMeta,
type: classConstructor,
key: "constructor",
scopeId,
classMeta: getMoostMate().read(classConstructor),
index,
targetMeta: paramMeta
}, "PARAM");
},
describeProp(classConstructor, key) {
const meta = getMoostMate().read(classConstructor, key);
return meta;
},
resolveProp({ instance, key, initialValue, propMeta, scopeId, classMeta, customData, classConstructor }) {
if (propMeta && customData?.pipes) return runPipes(customData.pipes, initialValue, {
instance,
type: classConstructor,
key,
scopeId,
propMeta,
targetMeta: propMeta,
classMeta
}, "PROP");
},
storeProvideRegByInstance: true,
on: (event, targetClass, message, args) => {
switch (event) {
case "new-instance": {
const scope = getMoostMate().read(targetClass)?.injectable || "SINGLETON";
if (loggingOptions.newInstance === false || !(loggingOptions.newInstance === scope || loggingOptions.newInstance === "SINGLETON" && scope === true)) return;
break;
}
case "warn": {
if (!loggingOptions.warn) return;
break;
}
case "error": {
if (!loggingOptions.error) return;
break;
}
}
let logger;
try {
logger = event === "error" ? getDefaultLogger(INFACT_BANNER) : useEventLogger(INFACT_BANNER);
} catch (error) {
logger = getDefaultLogger(INFACT_BANNER);
}
const instance = `${"\x1B[4m"}${targetClass.name}${"\x1B[24m"}`;
switch (event) {
case "new-instance": {
const params = args?.map((a) => {
switch (typeof a) {
case "number":
case "boolean": return `${"\x1B[33m"}${a}${"\x1B[2m\x1B[34m"}`;
case "string": return `${"\x1B[92m"}"${a.slice(0, 1)}..."${"\x1B[2m\x1B[34m"}`;
case "object": {
if (Array.isArray(a)) return `[${a.length}]`;
if (getConstructor(a)) return getConstructor(a).name;
return "{}";
}
default: return "*";
}
}).map((a) => `${"\x1B[2m\x1B[1m"}${a}${"\x1B[22m\x1B[2m"}`).join(", ") || "";
logger.info(`new ${instance}${"\x1B[2m\x1B[34m"}(${params})`);
break;
}
case "warn": {
const hier = `${"\x1B[2m\x1B[34m"}⋱ ${args?.map(String).join(" → ") || ""}`;
logger.warn(`${instance} - ${message} ${hier}`);
break;
}
case "error": {
const hier = `${"\x1B[2m\x1B[34m"}⋱ ${args?.map(String).join(" → ") || ""}`;
logger.error(`Failed to instantiate ${instance}. ${message} ${hier}`);
break;
}
default: break;
}
}
});
}
//#endregion
//#region packages/moost/src/composables/controller.composable.ts
function setControllerContext(controller, method, route) {
const { store } = useAsyncEventContext();
const { set } = store("controller");
set("instance", controller);
set("method", method);
set("route", route);
}
function useControllerContext() {
const { store } = useAsyncEventContext();
const { get } = store("controller");
const getController = () => get("instance");
const getMethod = () => get("method");
const getRoute = () => get("route");
const getControllerMeta = () => getMoostMate().read(getController());
const getMethodMeta = (name) => getMoostMate().read(getController(), name || getMethod());
function instantiate(c) {
return getMoostInfact().getForInstance(getController(), c);
}
return {
instantiate,
getRoute,
getController,
getMethod,
getControllerMeta,
getMethodMeta,
getPropertiesList: () => getControllerMeta()?.properties || [],
getScope: () => getControllerMeta()?.injectable || "SINGLETON",
getParamsMeta: () => getMethodMeta()?.params || [],
getPropMeta: (name) => getMethodMeta(name)
};
}
//#endregion
//#region packages/moost/src/adapter-utils.ts
const infact = getMoostInfact();
function registerEventScope(scopeId) {
infact.registerScope(scopeId);
return () => {
infact.unregisterScope(scopeId);
};
}
function defineMoostEventHandler(options) {
const ci = getContextInjector();
return async () => {
const scopeId = useEventId().getId();
const logger = useEventLogger(options.loggerTitle);
const unscope = registerEventScope(scopeId);
let response;
const hookOptions = {
scopeId,
logger,
unscope,
method: options.controllerMethod,
getResponse: () => response,
reply: (r) => response = r
};
if (options.hooks?.init) await options.hooks.init(hookOptions);
const instance = await options.getControllerInstance();
if (instance) {
setControllerContext(instance, options.controllerMethod || "", options.targetPath);
ci.hook(options.handlerType, "Controller:registered");
}
const interceptorHandler = await options.getIterceptorHandler();
if (interceptorHandler?.count) try {
response = await ci.with("Interceptors:init", () => interceptorHandler.init());
if (response !== undefined) return await endWithResponse();
} catch (error) {
options.logErrors && logger.error(error);
response = error;
return endWithResponse(true);
}
let args = [];
if (options.resolveArgs) try {
args = await ci.with("Arguments:resolve", () => options.resolveArgs());
} catch (error) {
options.logErrors && logger.error(error);
response = error;
return endWithResponse(true);
}
if (interceptorHandler?.countBefore) {
response = await ci.with("Interceptors:before", () => interceptorHandler.fireBefore(response));
if (response !== undefined) return endWithResponse();
}
const callControllerMethod = () => {
if (options.callControllerMethod) return options.callControllerMethod(args);
else if (instance && options.controllerMethod && typeof instance[options.controllerMethod] === "function") return instance[options.controllerMethod](...args);
};
try {
response = await ci.with(`Handler:${options.targetPath}`, {
"moost.handler": options.controllerMethod || "",
"moost.controller": getConstructor(instance).name
}, () => callControllerMethod());
} catch (error) {
options.logErrors && logger.error(error);
response = error;
return endWithResponse(true);
}
async function endWithResponse(raise = false) {
if (interceptorHandler?.countAfter || interceptorHandler?.countOnError) try {
response = await ci.with("Interceptors:after", () => interceptorHandler.fireAfter(response));
} catch (error) {
options.logErrors && logger.error(error);
if (!options.manualUnscope) unscope();
throw error;
}
if (!options.manualUnscope) unscope();
if (options.hooks?.end) await options.hooks.end(hookOptions);
if (raise) throw response;
return response;
}
return endWithResponse();
};
}
//#endregion
//#region packages/moost/src/binding/utils.ts
function getInstanceOwnMethods(instance) {
const proto = Object.getPrototypeOf(instance);
return [
...getParentProps(getConstructor(instance)),
...Object.getOwnPropertyNames(proto),
...Object.getOwnPropertyNames(instance)
].filter((m) => typeof instance[m] === "function");
}
function getInstanceOwnProps(instance) {
const proto = Object.getPrototypeOf(instance);
return [
...getParentProps(getConstructor(instance)),
...Object.getOwnPropertyNames(proto),
...Object.getOwnPropertyNames(instance)
].filter((m) => typeof instance[m] !== "function");
}
const fnProto = Object.getPrototypeOf(Function);
function getParentProps(constructor) {
const parent = Object.getPrototypeOf(constructor);
if (typeof parent === "function" && parent !== fnProto && parent !== constructor && parent.prototype) return [...getParentProps(parent), ...Object.getOwnPropertyNames(parent.prototype)];
return [];
}
//#endregion
//#region packages/moost/src/class-function/class-function.ts
async function getCallableFn(targetInstance, fn, pipes, logger) {
const mate$1 = getMoostMate();
const meta = mate$1.read(fn);
if (meta?.injectable) {
const infact$1 = getMoostInfact();
const instance = await infact$1.getForInstance(targetInstance, fn, { customData: { pipes: [...pipes || [], ...meta.pipes || []].sort((a, b) => a.priority - b.priority) } });
return (...args) => instance.handler(...args);
}
if (typeof fn === "function") return fn;
const e = new Error(`getCallableFn failed for "${getConstructor(targetInstance).name}" because the passed arg is not a Function nor TClassFunction`);
logger.error(e);
throw e;
}
//#endregion
//#region packages/moost/src/interceptor-handler.ts
function _define_property$1(obj, key, value) {
if (key in obj) Object.defineProperty(obj, key, {
value,
enumerable: true,
configurable: true,
writable: true
});
else obj[key] = value;
return obj;
}
var InterceptorHandler = class {
get count() {
return this.handlers.length;
}
get countBefore() {
return this.before.length;
}
get countAfter() {
return this.after.length;
}
get countOnError() {
return this.onError.length;
}
replyFn(reply) {
this.response = reply;
this.responseOverwritten = true;
}
async init() {
const ci = getContextInjector();
for (const { handler, name } of this.handlers) {
const response = await ci.with(`Interceptor:${name}`, { "moost.interceptor.stage": "init" }, () => handler((fn) => {
this.before.push({
name,
fn
});
}, (fn) => {
this.after.unshift({
name,
fn
});
}, (fn) => {
this.onError.unshift({
name,
fn
});
}));
if (response !== undefined) return response;
}
}
async fireBefore(response) {
const ci = getContextInjector();
this.response = response;
for (const { name, fn } of this.before) {
await ci.with(`Interceptor:${name}`, { "moost.interceptor.stage": "before" }, () => fn(this.replyFn.bind(this)));
if (this.responseOverwritten) break;
}
return this.response;
}
async fireAfter(response) {
const ci = getContextInjector();
this.response = response;
if (response instanceof Error) for (const { name, fn } of this.onError) await ci.with(`Interceptor:${name}`, { "moost.interceptor.stage": "after" }, () => fn(response, this.replyFn.bind(this)));
else for (const { name, fn } of this.after) await ci.with(`Interceptor:${name}`, { "moost.interceptor.stage": "onError" }, () => fn(response, this.replyFn.bind(this)));
return this.response;
}
constructor(handlers) {
_define_property$1(this, "handlers", void 0);
_define_property$1(this, "before", void 0);
_define_property$1(this, "after", void 0);
_define_property$1(this, "onError", void 0);
_define_property$1(this, "response", void 0);
_define_property$1(this, "responseOverwritten", void 0);
this.handlers = handlers;
this.before = [];
this.after = [];
this.onError = [];
this.responseOverwritten = false;
}
};
//#endregion
//#region packages/moost/src/utils.ts
const mate = getMoostMate();
function getIterceptorHandlerFactory(interceptors, getTargetInstance, pipes, logger) {
return () => {
const interceptorHandlers = [];
for (const { handler, name } of interceptors) {
const interceptorMeta = mate.read(handler);
if (interceptorMeta?.injectable) interceptorHandlers.push({
handler: async (...args) => {
const targetInstance = await getTargetInstance();
return (await getCallableFn(targetInstance, handler, pipes, logger))(...args);
},
name
});
else interceptorHandlers.push({
handler,
name
});
}
return Promise.resolve(new InterceptorHandler(interceptorHandlers));
};
}
//#endregion
//#region packages/moost/src/binding/bind-controller.ts
async function bindControllerMethods(options) {
const opts = options || {};
const { getInstance } = opts;
const { classConstructor } = opts;
const { adapters } = opts;
opts.globalPrefix = opts.globalPrefix || "";
opts.provide = opts.provide || {};
const fakeInstance = Object.create(classConstructor.prototype);
const methods = getInstanceOwnMethods(fakeInstance);
const mate$1 = getMoostMate();
const meta = mate$1.read(classConstructor) || {};
const ownPrefix = typeof opts.replaceOwnPrefix === "string" ? opts.replaceOwnPrefix : meta.controller?.prefix || "";
const prefix = `${opts.globalPrefix}/${ownPrefix}`;
const controllerOverview = {
meta,
computedPrefix: prefix,
type: classConstructor,
handlers: []
};
for (const method of methods) {
const methodMeta = getMoostMate().read(fakeInstance, method) || {};
if (!methodMeta.handlers?.length) continue;
const pipes = [...opts.pipes || [], ...methodMeta.pipes || []].sort((a, b) => a.priority - b.priority);
const interceptors = [
...opts.interceptors || [],
...meta.interceptors || [],
...methodMeta.interceptors || []
].sort((a, b) => a.priority - b.priority);
const getIterceptorHandler = getIterceptorHandlerFactory(interceptors, getInstance, pipes, options.logger);
const argsPipes = [];
for (const p of methodMeta.params || []) argsPipes.push({
meta: p,
pipes: [...pipes, ...p.pipes || []].sort((a, b) => a.priority - b.priority)
});
const resolveArgs = async () => {
const args = [];
for (const [i, { pipes: pipes$1, meta: paramMeta }] of argsPipes.entries()) args[i] = await runPipes(pipes$1, undefined, {
classMeta: meta,
methodMeta,
paramMeta,
type: classConstructor,
key: method,
index: i,
targetMeta: paramMeta
}, "PARAM");
return args;
};
const wm = new WeakMap();
controllerOverview.handlers.push(...methodMeta.handlers.map((h) => {
const data = {
meta: methodMeta,
path: h.path,
type: h.type,
method,
handler: h,
registeredAs: []
};
wm.set(h, data);
return data;
}));
for (const adapter of adapters) await adapter.bindHandler({
prefix,
fakeInstance,
getInstance,
method,
handlers: methodMeta.handlers,
getIterceptorHandler,
resolveArgs,
logHandler: (eventName) => {
options.moostInstance.logMappedHandler(eventName, classConstructor, method);
},
register(h, path, args) {
const data = wm.get(h);
if (data) data.registeredAs.push({
path,
args
});
}
});
}
return controllerOverview;
}
//#endregion
//#region packages/moost/src/decorators/circular.decorator.ts
function Circular(resolver) {
return getMoostMate().decorate("circular", resolver);
}
//#endregion
//#region packages/moost/src/decorators/common.decorator.ts
function Label(value) {
return getMoostMate().decorate("label", value);
}
function Description(value) {
return getMoostMate().decorate("description", value);
}
function Value(value) {
return getMoostMate().decorate("value", value);
}
function Id(value) {
return getMoostMate().decorate("id", value);
}
function Optional() {
return getMoostMate().decorate("optional", true);
}
function Required() {
const mate$1 = getMoostMate();
return mate$1.apply(mate$1.decorate("required", true), mate$1.decorateClass((meta, level, key, index) => {
if (typeof index !== "number" && meta && ["string", "symbol"].includes(typeof key)) {
meta.requiredProps = meta.requiredProps || [];
meta.requiredProps.push(key);
}
return meta;
}));
}
//#endregion
//#region packages/moost/src/decorators/injectable.decorator.ts
function Injectable(scope = true) {
return getMoostMate().decorate("injectable", scope);
}
const insureInjectable = getMoostMate().decorate((meta) => {
if (!meta.injectable) meta.injectable = true;
return meta;
});
//#endregion
//#region packages/moost/src/decorators/controller.decorator.ts
function Controller(prefix) {
const mate$1 = getMoostMate();
return mate$1.apply(insureInjectable, mate$1.decorate("controller", { prefix: prefix || "" }));
}
function ImportController(prefix, controller, provide) {
return getMoostMate().decorate("importController", {
prefix: typeof prefix === "string" ? prefix : undefined,
typeResolver: typeof prefix === "string" ? controller : prefix,
provide: typeof prefix === "string" ? provide || undefined : controller || undefined
}, true);
}
//#endregion
//#region packages/moost/src/decorators/inherit.decorator.ts
const Inherit = () => getMoostMate().decorate("inherit", true);
//#endregion
//#region packages/moost/src/decorators/intercept.decorator.ts
var TInterceptorPriority = /*#__PURE__*/ function(TInterceptorPriority$1) {
TInterceptorPriority$1[TInterceptorPriority$1["BEFORE_ALL"] = 0] = "BEFORE_ALL";
TInterceptorPriority$1[TInterceptorPriority$1["BEFORE_GUARD"] = 1] = "BEFORE_GUARD";
TInterceptorPriority$1[TInterceptorPriority$1["GUARD"] = 2] = "GUARD";
TInterceptorPriority$1[TInterceptorPriority$1["AFTER_GUARD"] = 3] = "AFTER_GUARD";
TInterceptorPriority$1[TInterceptorPriority$1["INTERCEPTOR"] = 4] = "INTERCEPTOR";
TInterceptorPriority$1[TInterceptorPriority$1["CATCH_ERROR"] = 5] = "CATCH_ERROR";
TInterceptorPriority$1[TInterceptorPriority$1["AFTER_ALL"] = 6] = "AFTER_ALL";
return TInterceptorPriority$1;
}({});
function Intercept(handler, priority, name) {
return getMoostMate().decorate("interceptors", {
handler,
priority: priority || handler.priority || 4,
name: name || handler._name || handler.name
}, true);
}
//#endregion
//#region packages/moost/src/decorators/resolve.decorator.ts
function Resolve(resolver, label) {
return (target, key, index) => {
const i = typeof index === "number" ? index : undefined;
getMoostMate().decorate("resolver", (metas, level) => {
let newLabel = label;
if (!newLabel && level === "PROP" && typeof metas.key === "string") newLabel = metas.key;
fillLabel(target, key || "", i, newLabel);
return resolver(metas, level);
})(target, key, i);
};
}
function Param(name) {
return getMoostMate().apply(getMoostMate().decorate("paramSource", "ROUTE"), getMoostMate().decorate("paramName", name), Resolve(() => useRouteParams().get(name), name));
}
function Params() {
return Resolve(() => useRouteParams().params, "params");
}
function Const(value, label) {
return Resolve(() => value, label);
}
function ConstFactory(factory, label) {
return Resolve(async () => factory(), label);
}
function fillLabel(target, key, index, name) {
if (name) {
const meta = getMoostMate().read(target, key);
if (typeof index === "number") {
if (!meta?.params?.[index]?.label) Label(name)(target, key, index);
} else if (!meta?.label) Label(name)(target, key);
}
}
//#endregion
//#region packages/moost/src/decorators/logger.decorator.ts
function InjectEventLogger(topic) {
return Resolve(() => useEventLogger(topic));
}
function InjectMoostLogger(topic) {
return Resolve(async (metas) => {
const { instantiate, getController } = useControllerContext();
const controller = getController();
const moostApp = controller instanceof Moost ? controller : await instantiate(Moost);
const meta = metas.classMeta;
return moostApp.getLogger(meta?.loggerTopic || topic || meta?.id);
});
}
function LoggerTopic(topic) {
return getMoostMate().decorate("loggerTopic", topic);
}
//#endregion
//#region packages/moost/src/pipes/types.ts
var TPipePriority = /*#__PURE__*/ function(TPipePriority$1) {
TPipePriority$1[TPipePriority$1["BEFORE_RESOLVE"] = 0] = "BEFORE_RESOLVE";
TPipePriority$1[TPipePriority$1["RESOLVE"] = 1] = "RESOLVE";
TPipePriority$1[TPipePriority$1["AFTER_RESOLVE"] = 2] = "AFTER_RESOLVE";
TPipePriority$1[TPipePriority$1["BEFORE_TRANSFORM"] = 3] = "BEFORE_TRANSFORM";
TPipePriority$1[TPipePriority$1["TRANSFORM"] = 4] = "TRANSFORM";
TPipePriority$1[TPipePriority$1["AFTER_TRANSFORM"] = 5] = "AFTER_TRANSFORM";
TPipePriority$1[TPipePriority$1["BEFORE_VALIDATE"] = 6] = "BEFORE_VALIDATE";
TPipePriority$1[TPipePriority$1["VALIDATE"] = 7] = "VALIDATE";
TPipePriority$1[TPipePriority$1["AFTER_VALIDATE"] = 8] = "AFTER_VALIDATE";
return TPipePriority$1;
}({});
//#endregion
//#region packages/moost/src/decorators/pipe.decorator.ts
function Pipe(handler, priority) {
if (typeof priority !== "number") priority = typeof handler.priority === "number" ? handler.priority : TPipePriority.TRANSFORM;
return getMoostMate().decorate("pipes", {
handler,
priority
}, true);
}
//#endregion
//#region packages/moost/src/decorators/provide.decorator.ts
function Provide(type, fn) {
return getMoostMate().decorate((meta) => {
meta.provide = meta.provide || {};
Object.assign(meta.provide, createProvideRegistry([type, fn]));
return meta;
});
}
function Replace(type, newType) {
return getMoostMate().decorate((meta) => {
meta.replace = meta.replace || {};
Object.assign(meta.replace, createReplaceRegistry([type, newType]));
return meta;
});
}
function Inject(type) {
return getMoostMate().decorate("inject", type);
}
function InjectFromScope(name) {
return getMoostMate().decorate("fromScope", name);
}
function InjectScopeVars(name) {
return Resolve(({ scopeId }) => {
if (scopeId) return name ? getInfactScopeVars(scopeId)?.[name] : getInfactScopeVars(scopeId);
return undefined;
});
}
//#endregion
//#region packages/moost/src/define.ts
function defineInterceptorFn(fn, priority = TInterceptorPriority.INTERCEPTOR) {
fn.priority = priority;
return fn;
}
function definePipeFn(fn, priority = TPipePriority.TRANSFORM) {
fn.priority = priority;
return fn;
}
//#endregion
//#region packages/moost/src/pipes/resolve.pipe.ts
const resolvePipe = definePipeFn((_value, metas, level) => {
const resolver = metas.targetMeta?.resolver;
if (resolver) return resolver(metas, level);
return undefined;
}, TPipePriority.RESOLVE);
//#endregion
//#region packages/moost/src/pipes/shared-pipes.ts
const sharedPipes = [{
handler: resolvePipe,
priority: TPipePriority.RESOLVE
}];
//#endregion
//#region packages/moost/src/moost.ts
function _define_property(obj, key, value) {
if (key in obj) Object.defineProperty(obj, key, {
value,
enumerable: true,
configurable: true,
writable: true
});
else obj[key] = value;
return obj;
}
var Moost = class Moost extends Hookable {
_fireEventStart(source) {
this.callHook("event-start", source);
}
_fireEventEnd(source) {
this.callHook("event-end", source);
}
/**
* ### getLogger
* Provides application logger
* ```js
* // get logger with topic = "App"
* const logger = app.getLogger('App')
* logger.log('...')
* ```
* @param topic
* @returns
*/ getLogger(topic) {
if (topic && this.logger instanceof ProstoLogger) return this.logger.createTopic(topic);
return this.logger;
}
adapter(a) {
this.adapters.push(a);
return a;
}
getControllersOverview() {
return this.controllersOverview;
}
/**
* ### init
* Ititializes adapter. Must be called after adapters are attached.
*/ async init() {
this.setProvideRegistry(createProvideRegistry([Moost, () => this], [ProstoLogger, () => this.logger], ["MOOST_LOGGER", () => this.logger]));
for (const a of this.adapters) {
const constructor = getConstructor(a);
if (constructor) this.setProvideRegistry(createProvideRegistry([constructor, () => a]));
if (typeof a.getProvideRegistry === "function") this.setProvideRegistry(a.getProvideRegistry());
}
this.unregisteredControllers.unshift(this);
await this.bindControllers();
for (const a of this.adapters) await (a.onInit && a.onInit(this));
}
async bindControllers() {
const meta = getMoostMate();
const thisMeta = meta.read(this);
const provide = {
...thisMeta?.provide,
...this.provide
};
const replace = {
...thisMeta?.replace,
...this.replace
};
for (const _controller of this.unregisteredControllers) {
let newPrefix;
let controller = _controller;
if (Array.isArray(_controller) && typeof _controller[0] === "string") {
newPrefix = _controller[0];
controller = _controller[1];
}
await this.bindController(controller, provide, replace, this.options?.globalPrefix || "", newPrefix);
}
this.unregisteredControllers = [];
}
async bindController(controller, provide, replace, globalPrefix, replaceOwnPrefix) {
const mate$1 = getMoostMate();
const classMeta = mate$1.read(controller);
const infact$1 = getMoostInfact();
const isControllerConsructor = isConstructor(controller);
const pipes = [...this.pipes, ...classMeta?.pipes || []].sort((a, b) => a.priority - b.priority);
let instance;
const infactOpts = {
provide,
replace,
customData: { pipes }
};
if (isControllerConsructor && (classMeta?.injectable === "SINGLETON" || classMeta?.injectable === true)) await createAsyncEventContext({
event: { type: "init" },
options: {}
})(async () => {
setControllerContext(this, "bindController", "");
instance = await infact$1.get(controller, infactOpts);
});
else if (!isControllerConsructor) {
instance = controller;
infact$1.setInstanceRegistries(instance, provide, replace, { pipes });
}
const getInstance = instance ? () => Promise.resolve(instance) : async () => await infact$1.get(controller, { ...infactOpts });
const classConstructor = isConstructor(controller) ? controller : getConstructor(controller);
this.controllersOverview.push(await bindControllerMethods({
getInstance,
classConstructor,
adapters: this.adapters,
globalPrefix,
replaceOwnPrefix,
interceptors: Array.from(this.interceptors),
pipes,
provide: classMeta?.provide,
replace: classMeta?.replace,
logger: this.logger,
moostInstance: this
}));
if (classMeta?.importController) {
const prefix = typeof replaceOwnPrefix === "string" ? replaceOwnPrefix : classMeta.controller?.prefix;
const mergedProvide = {
...provide,
...classMeta.provide
};
const mergedReplace = {
...this.replace,
...classMeta.replace
};
for (const ic of classMeta.importController) if (ic.typeResolver) {
const isConstr = isConstructor(ic.typeResolver);
const isFunc = typeof ic.typeResolver === "function";
await this.bindController(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
isConstr ? ic.typeResolver : isFunc ? await ic.typeResolver() : ic.typeResolver,
ic.provide ? {
...mergedProvide,
...ic.provide
} : mergedProvide,
mergedReplace,
`${globalPrefix}/${prefix || ""}`,
ic.prefix
);
}
}
}
applyGlobalPipes(...items) {
for (const item of items) if (typeof item === "function") this.pipes.push({
handler: item,
priority: typeof item.priority === "number" ? item.priority : TPipePriority.TRANSFORM
});
else this.pipes.push({
handler: item.handler,
priority: item.priority
});
this.globalInterceptorHandler = undefined;
return this;
}
/**
* Provides InterceptorHandler with global interceptors and pipes.
* Used to process interceptors when event handler was not found.
*
* @returns IterceptorHandler
*/ getGlobalInterceptorHandler() {
if (!this.globalInterceptorHandler) {
const mate$1 = getMoostMate();
const thisMeta = mate$1.read(this);
const pipes = [...this.pipes || [], ...thisMeta?.pipes || []].sort((a, b) => a.priority - b.priority);
const interceptors = [...this.interceptors, ...thisMeta?.interceptors || []].sort((a, b) => a.priority - b.priority);
this.globalInterceptorHandler = getIterceptorHandlerFactory(interceptors, () => Promise.resolve(this), pipes, this.logger);
}
return this.globalInterceptorHandler();
}
applyGlobalInterceptors(...items) {
for (const item of items) if (typeof item === "function") this.interceptors.push({
handler: item,
priority: typeof item.priority === "number" ? item.priority : TInterceptorPriority.INTERCEPTOR,
name: item._name || item.name || "<anonymous>"
});
else this.interceptors.push({
handler: item.handler,
priority: item.priority,
name: item.name || item.handler._name || item.handler.name || "<anonymous>"
});
this.globalInterceptorHandler = undefined;
return this;
}
/**
* Register new entries to provide as dependency injections
* @param provide - Provide Registry (use createProvideRegistry from '\@prostojs/infact')
* @returns
*/ setProvideRegistry(provide) {
this.provide = {
...this.provide,
...provide
};
return this;
}
/**
* Register replace classes to provide as dependency injections
* @param replace - Replace Registry (use createReplaceRegistry from '\@prostojs/infact')
* @returns
*/ setReplaceRegistry(replace) {
this.replace = {
...this.replace,
...replace
};
return this;
}
/**
* Register controllers (similar to @ImportController decorator)
* @param controllers - list of target controllers (instances)
* @returns
*/ registerControllers(...controllers) {
this.unregisteredControllers.push(...controllers);
return this;
}
logMappedHandler(eventName, classConstructor, method, stroke, prefix) {
const c = stroke ? "\x1B[9m" : "";
const coff = stroke ? "\x1B[29m" : "";
this.logger.info(`${prefix || ""}${c}${eventName} ${"\x1B[0m\x1B[2m\x1B[32m" + c}→ ${classConstructor.name}.${"\x1B[36m" + c}${method}${"\x1B[32m"}()${coff}`);
}
constructor(options) {
super(), _define_property(this, "options", void 0), _define_property(this, "logger", void 0), _define_property(this, "pipes", void 0), _define_property(this, "interceptors", void 0), _define_property(this, "adapters", void 0), _define_property(this, "controllersOverview", void 0), _define_property(this, "provide", void 0), _define_property(this, "replace", void 0), _define_property(this, "unregisteredControllers", void 0), _define_property(this, "globalInterceptorHandler", void 0), this.options = options, this.pipes = Array.from(sharedPipes), this.interceptors = [], this.adapters = [], this.controllersOverview = [], this.provide = createProvideRegistry([Infact, getMoostInfact], [Mate, getMoostMate]), this.replace = {}, this.unregisteredControllers = [];
this.logger = options?.logger || getDefaultLogger(`${"\x1B[2m\x1B[35m"}moost`);
setDefaultLogger(this.logger);
const mate$1 = getMoostMate();
Object.assign(mate$1, { logger: this.getLogger("mate") });
}
};
//#endregion
export { Circular, Const, ConstFactory, ContextInjector, Controller, Description, EventLogger, Id, ImportController, Inherit, Inject, InjectEventLogger, InjectFromScope, InjectMoostLogger, InjectScopeVars, Injectable, Intercept, InterceptorHandler, Label, LoggerTopic, Moost, Optional, Param, Params, Pipe, ProstoLogger, Provide, Replace, Required, Resolve, TInterceptorPriority, TPipePriority, Value, clearGlobalWooks, createProvideRegistry, createReplaceRegistry, defineInfactScope, defineInterceptorFn, defineMoostEventHandler, definePipeFn, getConstructor, getContextInjector, getGlobalWooks, getInfactScopeVars, getInstanceOwnMethods, getInstanceOwnProps, getMoostInfact, getMoostMate, getNewMoostInfact, isConstructor, registerEventScope, replaceContextInjector, resolvePipe, setControllerContext, setInfactLoggingOptions, useAsyncEventContext, useControllerContext, useEventLogger };