UNPKG

@andrew_l/service-actor

Version:

Forget about passing data like trace IDs between functions.

1 lines 6.95 kB
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n hasInjectionContext,\n inject,\n provide,\n withContext,\n} from '@andrew_l/context';\nimport {\n type AnyFunction,\n type OverwriteWith,\n isFunction,\n} from '@andrew_l/toolkit';\n\ntype ServiceActorInternal = {\n traceId: unknown;\n actorId: unknown;\n actorType: unknown;\n [SYM_API]: Record<PropertyKey, Function>;\n [SYM_MARKER]: boolean;\n [SYM_RAW]: any;\n [SYM_DATA]: Record<PropertyKey, unknown>;\n};\n\nexport type AnyServiceActor = {\n traceId: any;\n actorId: any;\n actorType: any;\n [x: PropertyKey]: any;\n};\n\nexport type ServiceActorData<T extends Record<PropertyKey, any> = {}> =\n OverwriteWith<\n {\n traceId: string;\n actorId: string | null;\n actorType: 'unknown' | string;\n },\n T\n >;\n\nexport type ServiceActor<T extends Record<PropertyKey, any> = {}> =\n ServiceActorData<T> & {\n assign(params: Partial<ServiceActorData<T>>): ServiceActor<T>;\n };\n\nvar SYM_MARKER = Symbol();\nvar SYM_RAW = Symbol();\nvar SYM_DATA = Symbol();\nvar SYM_API = Symbol();\n\n/**\n * Create service actor hooks\n *\n * @example\n * const { use: useServiceActor, with: withServiceActor } = serviceActor(() => ({\n * actorType: 'http-request',\n * ipAddress: '',\n * }));\n *\n * app.use((ctx, next) => {\n * return withServiceActor(\n * {\n * traceId: ctx.headers['x-request-id'],\n * ipAddress: ctx.headers['x-forwarded-for'],\n * },\n * next,\n * );\n * });\n *\n * app.get('/', () => {\n * const actor = useServiceActor();\n *\n * // { traceId: 'req_35123', actorId: null, actorType: 'http-request', ipAddress: '::' }\n * console.log(actor);\n * });\n *\n * @group Main\n */\nexport function serviceActor<T extends Record<PropertyKey, any> = {}>(\n factory?: () => T,\n): {\n /**\n * Wrap a function to execute it with service actor providers\n */\n with: <Fn extends AnyFunction>(\n fn: Fn,\n params?: Partial<ServiceActor<T>>,\n ) => Fn;\n\n /**\n * Returns the service actor instance from the current context.\n */\n inject: () => ServiceActor<T> | undefined;\n\n /**\n * Returns the service actor instance from the context, or creates and binds a new instance if none exists.\n */\n use: () => ServiceActor<T>;\n} {\n var injectKey = Symbol();\n var idSeq = 0;\n\n var createActor = (): AnyServiceActor => {\n var actor = createServiceActor();\n\n actor.traceId = `trace-${++idSeq}`;\n\n if (factory) {\n actor.assign(factory());\n }\n\n return actor;\n };\n\n var withHook = <Fn extends AnyFunction>(\n fn: Fn,\n params?: Partial<ServiceActor<T>>,\n ): Fn => {\n return withContext(function (this: any, ...args: any[]) {\n var parentActor = inject<ServiceActor>(injectKey);\n var actor = createActor();\n\n if (parentActor) {\n actor.assign(parentActor);\n }\n\n if (params) {\n actor.assign(params);\n }\n\n provide(injectKey, actor);\n\n return fn.apply(this, args);\n }) as Fn;\n };\n\n var injectHook = (): AnyServiceActor | undefined => {\n return hasInjectionContext() ? inject<ServiceActor>(injectKey) : undefined;\n };\n\n var useHok = (): AnyServiceActor => {\n var _actor: AnyServiceActor | undefined;\n\n if (!hasInjectionContext()) {\n _actor = createActor();\n provide(injectKey, _actor, true);\n\n return _actor;\n }\n\n // check for existed\n _actor = inject<AnyServiceActor>(injectKey);\n\n if (_actor) return _actor;\n\n _actor = createActor();\n provide(injectKey, _actor);\n\n return _actor;\n };\n\n return { with: withHook, use: useHok as any, inject: injectHook as any };\n}\n\nfunction createServiceActor(): ServiceActor {\n var actor: ServiceActorInternal = {\n traceId: '',\n actorId: null,\n actorType: 'unknown',\n [SYM_API]: {\n assign,\n },\n [SYM_DATA]: {},\n [SYM_RAW]: null,\n [SYM_MARKER]: true,\n };\n\n var reservedKeys = new Set(Object.keys(actor));\n\n actor[SYM_RAW] = actor;\n\n var newProxy = new Proxy(actor[SYM_DATA], {\n defineProperty(target, key, value) {\n if (key in actor) {\n (actor as any)[key] = value;\n } else {\n target[key] = value;\n }\n\n return true;\n },\n deleteProperty(target, key) {\n if (key in actor) {\n delete (actor as any)[key];\n } else {\n delete target[key];\n }\n\n return true;\n },\n has(target, key) {\n return key in actor || key in target;\n },\n ownKeys(target) {\n return [...reservedKeys, ...Object.keys(target)];\n },\n set: function (target, key, value) {\n if (key in actor[SYM_API]) return false;\n\n if (key in actor) {\n Reflect.set(actor, key, value);\n } else if (isFunction(value)) {\n Reflect.set(actor[SYM_API], key, value);\n } else {\n Reflect.set(target, key, value);\n }\n\n return true;\n },\n get: (target, key) => {\n var value: any;\n\n if (key in actor) {\n value = Reflect.get(actor, key, value);\n } else if (key in actor[SYM_API]) {\n value = Reflect.get(actor[SYM_API], key, value).bind(newProxy);\n } else {\n value = Reflect.get(target, key);\n }\n\n return value;\n },\n getOwnPropertyDescriptor(k) {\n return {\n enumerable: true,\n configurable: true,\n };\n },\n });\n\n return newProxy as ServiceActor;\n}\n\nfunction assign(this: ServiceActor, params: Record<any, any>) {\n Object.assign(this, params);\n return this;\n}\n"],"mappings":";;AA4CA,IAAI,aAAa,OAAO;AACxB,IAAI,UAAU,OAAO;AACrB,IAAI,WAAW,OAAO;AACtB,IAAI,UAAU,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BrB,OAAgB;EAqBd;EACA,SAAI,OAAQ,SAAA;EAEZ,IAAI,QAAA,OAAA;EACF,SAAI,YAAQ;EAEZ,QAAM,WAAU,MAAS;EAEzB,OAAI;;CAKN,OAAA;EAEA,MAAI;EAIF,KAAA;UACM;;;SAOA,qBACW;KAGf,QAAQ;WAED;EACT,SAAC;EACH,WAAA;GAEA,UAAI,EAAA,OAAA;GACF,WAAO,CAAA;GACT,UAAA;GAEA,aAAI;;KAGF,eAAK,IAAA,IAAoB,OAAG,KAAA,KAAA,CAAA;OAC1B,WAAS;KACT,WAAQ,IAAA,MAAW,MAAQ,WAAI;iBAExB,QAAA,KAAA,OAAA;GACT,IAAA,OAAA,OAAA,MAAA,OAAA;QAGA,OAAS,OAAwB;GAEjC,OAAI;EAEJ;EACA,eAAQ,QAAW,KAAM;GAEzB,IAAA,OAAO,OAAA,OAAA,MAAA;QACT,OAAA,OAAA;GAEA,OAAO;EAAE;EAAgB,IAAA,QAAK,KAAA;GAAe,OAAQ,OAAA,SAAA,OAAA;EAAkB;EACzE,QAAA,QAAA;GAEA,OAAS,CAAA,GAAA,cAAA,GAAmC,OAAA,KAAA,MAAA,CAAA;EAC1C;EACE,KAAA,SAAS,QAAA,KAAA,OAAA;GACT,IAAA,OAAS,MAAA,UAAA,OAAA;GACT,IAAA,OAAW,OAAA,QAAA,IAAA,OAAA,KAAA,KAAA;QACV,IAAA,WAED,KAAA,GAAA,QAAA,IAAA,MAAA,UAAA,KAAA,KAAA;QACC,QAAY,IAAA,QAAA,KAAA,KAAA;GACZ,OAAA;;EAEH,MAAA,QAAA,QAAA;GAEA,IAAI;GAEJ,IAAM,OAAA,OAAW,QAAA,QAAA,IAAA,OAAA,KAAA,KAAA;QAEb,IAAA,OAAW,MAAI,UAAY,QAAW,QAAA,IAAA,MAAA,UAAA,KAAA,KAAA,CAAA,CAAA,KAAA,QAAA;QACxC,QAAA,QAAe,IAAQ,QAAK,GAAO;GACjC,OAAI;;2BAMG,GAAA;GACT,OAAA;IACA,YAAA;IACE,cAAW;;;EAOb;QACI;;SAEJ,OAAA,QAAA;QACA,OAAQ,MAAQ,MAAA;QACd;;SAGI"}