UNPKG

ts-fp-di

Version:

Tiny TypeScript functional dependency injection, based on AsyncLocalStorage. Supports Node.js, Deno

131 lines (130 loc) 4.84 kB
"use strict"; 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; };