ts-fp-di
Version:
Tiny TypeScript functional dependency injection, based on AsyncLocalStorage. Supports Node.js, Deno
131 lines (130 loc) • 4.84 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.diseSet = exports.diMapOnce = exports.dic = exports.diScope = exports.diContext = exports.diExists = exports.diOnceSet = exports.diOnce = exports.diInit = exports.diHas = exports.diSet = exports.diDep = exports.div = exports.dis = exports.di = exports.clearGlobalState = exports.als = void 0;
exports.diMap = diMap;
exports.dise = dise;
const node_async_hooks_1 = require("node:async_hooks");
exports.als = new node_async_hooks_1.AsyncLocalStorage();
const globalState = new Map();
const clearGlobalState = () => globalState.clear();
exports.clearGlobalState = clearGlobalState;
const di = (fn) => {
const overrideFn = function (...args) {
const store = storeOrError();
const userDep = store.deps.get(overrideFn);
return (userDep ?? fn).apply(this, args);
};
return overrideFn;
};
exports.di = di;
const dis = (fn, defaultState, isGlobal = false) => {
const stateFn = function (payload) {
let store;
const stateMap = isGlobal ? globalState : ((store = storeOrError()), store.state);
const oldState = stateMap.get(stateFn);
if (payload == null) {
return oldState ?? defaultState;
}
const newState = fn(oldState ?? defaultState, payload);
stateMap.set(stateFn, newState);
return newState;
};
return stateFn;
};
exports.dis = dis;
const div = () => {
const dio = (0, exports.dis)((_, x) => x, void 0);
return dio;
};
exports.div = div;
const diDep = (dep) => {
const store = storeOrError();
const userDep = store.deps.get(dep);
if (typeof dep === 'string' && userDep == null) {
throw new Error(`Dependency with key ${dep} not registered!`);
}
return userDep ?? dep;
};
exports.diDep = diDep;
const diSet = (dep, value) => {
const store = storeOrError();
store.deps.set(dep, value);
};
exports.diSet = diSet;
const diHas = (dep) => {
const store = storeOrError();
return store.deps.has(dep);
};
exports.diHas = diHas;
const diInit = (cb, ctx) => {
return (0, exports.diExists)()
? ctx
? exports.als.run({
...exports.als.getStore(),
deps: new Map(Array.from(exports.als.getStore().deps.entries()).concat(Array.from(ctx.deps.entries()))),
once: new Map(Array.from(exports.als.getStore().once.entries()).concat(Array.from(ctx.once.entries()))),
state: new Map(Array.from(exports.als.getStore().state.entries()).concat(Array.from(ctx.state.entries()))),
derived: new Map(Array.from(exports.als.getStore().derived.entries()).concat(Array.from(ctx.derived.entries()))),
}, cb)
: cb()
: exports.als.run(ctx ?? (0, exports.diContext)(), cb);
};
exports.diInit = diInit;
const diOnce = (fn) => {
const onceFn = function (...args) {
const store = storeOrError();
return store.once.get(onceFn) ?? store.once.set(onceFn, fn.apply(this, args)).get(onceFn);
};
return onceFn;
};
exports.diOnce = diOnce;
const diOnceSet = (fn, value) => {
const store = storeOrError();
store.once.set(fn, value);
};
exports.diOnceSet = diOnceSet;
const diExists = () => (exports.als.getStore() == null) === false;
exports.diExists = diExists;
const diContext = () => ({ deps: new Map(), once: new Map(), state: new Map(), derived: new Map() });
exports.diContext = diContext;
const diScope = (scope, init) => {
const ctx = (0, exports.diContext)();
if (init) {
(0, exports.diInit)(init, ctx);
}
return Object.fromEntries(Object.entries(scope).map(([key, fn]) => [
key,
function (...args) {
return (0, exports.diInit)(() => fn.apply(this, args), ctx);
},
]));
};
exports.diScope = diScope;
const dic = () => {
const dio = (0, exports.diOnce)((x) => x);
return dio;
};
exports.dic = dic;
function diMap(pred, ...fns) {
const diMapFn = () => {
const r = pred(...fns.map(f => f()));
storeOrError().derived.set(diMapFn, r);
return r;
};
return Object.assign(diMapFn, { raw: pred });
}
const diMapOnce = (raw, ...fns) => Object.assign((0, exports.diOnce)(diMap(raw, ...fns)), { raw });
exports.diMapOnce = diMapOnce;
function dise(effect, dicOutput, ...dicInputs) {
const raw = (0, exports.di)(effect);
return Object.assign(() => raw(...dicInputs.map(dic => dic())).then(r => dicOutput(r)), { raw });
}
const diseSet = (fun, replacement) => (0, exports.diSet)(fun.raw, replacement);
exports.diseSet = diseSet;
const storeOrError = () => {
const store = exports.als.getStore();
if (store == null) {
throw new Error('DI container not registered! Consider, that you call "diInit" before');
}
return store;
};