UNPKG

@wooksjs/event-core

Version:
229 lines (221 loc) 5.74 kB
import { randomUUID } from "crypto"; import { AsyncLocalStorage } from "node:async_hooks"; import { ProstoLogger, coloredConsole, createConsoleTransort } from "@prostojs/logger"; //#region packages/event-core/src/context-injector.ts /** * ContextInjector * * Provides a way to inject context * Usefull when working with opentelemetry spans */ var ContextInjector = class { with(name, attributes, cb) { const fn = typeof attributes === "function" ? attributes : cb; return fn(); } hook(method, name, route) {} }; let ci = new ContextInjector(); function getContextInjector() { return ci; } function replaceContextInjector(newCi) { ci = newCi; } //#endregion //#region packages/event-core/src/hook.ts function attachHook(target, opts, name) { Object.defineProperty(target, name || "value", { get: opts.get, set: opts.set }); return target; } //#endregion //#region packages/event-core/src/context.ts /** * AsyncLocalStorage instance * * Use on your own risk only if you know what you're doing */ const asyncStorage = new AsyncLocalStorage(); /** * Create Wooks Context */ function createAsyncEventContext(data) { const newContext = { ...data }; const cc = asyncStorage.getStore(); if (cc && typeof cc === "object" && cc.event?.type) newContext.parentCtx = cc; const ci$1 = getContextInjector(); return (cb) => asyncStorage.run(newContext, () => ci$1.with("Event:start", { eventType: newContext.event.type }, cb)); } /** * Use Wooks Context */ function useAsyncEventContext(expectedTypes) { let cc = asyncStorage.getStore(); if (!cc) throw new Error("Event context does not exist at this point."); if (expectedTypes || typeof expectedTypes === "string") { const type = cc.event.type; const types = typeof expectedTypes === "string" ? [expectedTypes] : expectedTypes; if (!types.includes(type)) if (cc.parentCtx?.event.type && types.includes(cc.parentCtx.event.type)) cc = cc.parentCtx; else throw new Error(`Event context type mismatch: expected ${types.map((t) => `"${t}"`).join(", ")}, received "${type}"`); } return _getCtxHelpers(cc); } /** * * @param cc * @returns */ function _getCtxHelpers(cc) { /** * Hook to an event store property * * @param key store property key * @returns a hook { value: <prop value>, hook: (key2: keyof <prop value>) => { value: <nested prop value> }, ... } */ function store(key) { const obj = { value: null, hook, init, set: setNested, get: getNested, has: hasNested, del: delNested, entries, clear }; attachHook(obj, { set: (v) => { set(key, v); }, get: () => get(key) }); function init(key2, getter) { if (hasNested(key2)) return getNested(key2); return setNested(key2, getter()); } function hook(key2) { const obj$1 = { value: null, isDefined: null }; attachHook(obj$1, { set: (v) => setNested(key2, v), get: () => getNested(key2) }); attachHook(obj$1, { get: () => hasNested(key2) }, "isDefined"); return obj$1; } function setNested(key2, v) { if (obj.value === void 0) obj.value = {}; obj.value[key2] = v; return v; } function delNested(key2) { setNested(key2, void 0); } function getNested(key2) { return (obj.value || {})[key2]; } function hasNested(key2) { return (obj.value || {})[key2] !== void 0; } function entries() { return Object.entries(obj.value || {}); } function clear() { obj.value = {}; } return obj; } /** * Get event context object * * @returns whole context object */ function getCtx() { return cc; } /** * Get value of event store property * * @param key property name * @returns value of property by name */ function get(key) { return getCtx()[key]; } /** * Set value of event store property * * @param key property name * @param v property value */ function set(key, v) { getCtx()[key] = v; } const hasParentCtx = () => !!cc.parentCtx; return { getCtx, store, getStore: get, setStore: set, setParentCtx: (parentCtx) => { cc.parentCtx = parentCtx; }, hasParentCtx, getParentCtx: () => { if (!hasParentCtx()) throw new Error("Parent context is not available"); return _getCtxHelpers(cc.parentCtx); } }; } //#endregion //#region packages/event-core/src/composables/event-id.ts function useEventId() { const { store } = useAsyncEventContext(); const { init } = store("event"); const getId = () => init("id", () => randomUUID()); return { getId }; } //#endregion //#region packages/event-core/src/event-logger.ts var EventLogger = class extends ProstoLogger { constructor(eventId, opts) { const _opts = opts || { level: 4 }; if (!_opts.mapper) _opts.mapper = (msg) => ({ ...msg, eventId }); if (!_opts.transports) _opts.transports = [createConsoleTransort({ format: coloredConsole })]; super(_opts, opts?.topic || "event"); } }; //#endregion //#region packages/event-core/src/composables/event-logger.ts function useEventLogger(topic) { const { getId } = useEventId(); const { store, getCtx } = useAsyncEventContext(); const { init } = store("event"); const ctx = getCtx(); const get = () => init("logger", () => new EventLogger(getId(), ctx.options.eventLogger)); return topic ? get().createTopic(topic) : get(); } //#endregion //#region packages/event-core/src/composables/route-params.ts function useRouteParams() { const { store } = useAsyncEventContext(); const params = store("routeParams").value || {}; function get(name) { return params[name]; } return { params, get }; } //#endregion export { ContextInjector, EventLogger, asyncStorage, attachHook, createAsyncEventContext, getContextInjector, replaceContextInjector, useAsyncEventContext, useEventId, useEventLogger, useRouteParams };