UNPKG

context

Version:

Lightweight context propagation for JavaScript and TypeScript. Create a scoped storage object, run code inside it, and read the active value anywhere down the call stack - without depending on React.

63 lines (61 loc) 1.66 kB
import { assign, defaultTo, dynamicValue, invariant } from "vest-utils"; //#region src/context.ts const USEX_DEFAULT_ERROR_MESSAGE = "Not inside of a running context."; const EMPTY_CONTEXT = Symbol(); /** * Base context interface. */ function createContext(defaultContextValue) { let contextValue = EMPTY_CONTEXT; return { run, use, useX }; function use() { return isInsideContext() ? contextValue : defaultContextValue; } function useX(errorMessage) { invariant(isInsideContext(), defaultTo(errorMessage, USEX_DEFAULT_ERROR_MESSAGE)); return contextValue; } function run(value, cb) { const parentContext = isInsideContext() ? use() : EMPTY_CONTEXT; contextValue = value; const res = cb(); contextValue = parentContext; return res; } function isInsideContext() { return contextValue !== EMPTY_CONTEXT; } } /** * Cascading context - another implementation of context, that assumes the context value is an object. * When nesting context runs, the the values of the current layer merges with the layers above it. */ function createCascade(init) { const ctx = createContext(); return { bind, run, use: ctx.use, useX: ctx.useX }; function run(value, fn) { const parentContext = ctx.use(); const initResult = dynamicValue(init, value, parentContext) ?? value; const out = assign({}, parentContext ? parentContext : {}, initResult); return ctx.run(Object.freeze(out), fn); } function bind(value, fn) { return function(...runTimeArgs) { return run(value, function() { return fn(...runTimeArgs); }); }; } } //#endregion export { createCascade, createContext }; //# sourceMappingURL=context.mjs.map