@andrew_l/service-actor
Version:
Forget about passing data like trace IDs between functions.
125 lines (122 loc) • 3.14 kB
JavaScript
import { withContext, inject, provide, hasInjectionContext } from '@andrew_l/context';
import { isFunction } from '@andrew_l/toolkit';
const SYM_MARKER = Symbol();
const SYM_RAW = Symbol();
const SYM_DATA = Symbol();
const SYM_API = Symbol();
function serviceActor(factory) {
const injectKey = Symbol();
let idSeq = 0;
const createActor = () => {
const actor = createServiceActor();
actor.traceId = `trace-${++idSeq}`;
if (factory) {
actor.assign(factory());
}
return actor;
};
const withHook = (fn, params) => {
return withContext(function(...args) {
const parentActor = inject(injectKey);
const actor = createActor();
if (parentActor) {
actor.assign(parentActor);
}
if (params) {
actor.assign(params);
}
provide(injectKey, actor);
return fn.apply(this, args);
});
};
const injectHook = () => {
return hasInjectionContext() ? inject(injectKey) : void 0;
};
const useHok = () => {
if (!hasInjectionContext()) {
const actor2 = createActor();
provide(injectKey, actor2, true);
return actor2;
}
const existed = inject(injectKey);
if (existed) return existed;
const actor = createActor();
provide(injectKey, actor);
return actor;
};
return { with: withHook, use: useHok, inject: injectHook };
}
function createServiceActor() {
const actor = {
traceId: "",
actorId: null,
actorType: "unknown",
[SYM_API]: {
assign
},
[SYM_DATA]: {},
[SYM_RAW]: null,
[SYM_MARKER]: true
};
const reservedKeys = new Set(Object.keys(actor));
actor[SYM_RAW] = actor;
const newProxy = new Proxy(actor[SYM_DATA], {
defineProperty(target, key, value) {
if (key in actor) {
actor[key] = value;
} else {
target[key] = value;
}
return true;
},
deleteProperty(target, key) {
if (key in actor) {
delete actor[key];
} else {
delete target[key];
}
return true;
},
has(target, key) {
return key in actor || key in target;
},
ownKeys(target) {
return [...reservedKeys, ...Object.keys(target)];
},
set: function(target, key, value) {
if (key in actor[SYM_API]) return false;
if (key in actor) {
Reflect.set(actor, key, value);
} else if (isFunction(value)) {
Reflect.set(actor[SYM_API], key, value);
} else {
Reflect.set(target, key, value);
}
return true;
},
get: (target, key) => {
let value;
if (key in actor) {
value = Reflect.get(actor, key, value);
} else if (key in actor[SYM_API]) {
value = Reflect.get(actor[SYM_API], key, value).bind(newProxy);
} else {
value = Reflect.get(target, key);
}
return value;
},
getOwnPropertyDescriptor(k) {
return {
enumerable: true,
configurable: true
};
}
});
return newProxy;
}
function assign(params) {
Object.assign(this, params);
return this;
}
export { serviceActor };
//# sourceMappingURL=index.mjs.map