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.

1 lines 3.48 kB
{"version":3,"file":"context.mjs","names":["contextValue: T | symbol"],"sources":["../src/context.ts"],"sourcesContent":["import type { CB, Maybe } from 'vest-utils';\nimport {\n assign,\n defaultTo,\n invariant,\n dynamicValue,\n Nullable,\n} from 'vest-utils';\n\nconst USEX_DEFAULT_ERROR_MESSAGE = 'Not inside of a running context.';\nconst EMPTY_CONTEXT = Symbol();\n\n/**\n * Base context interface.\n */\nexport function createContext<T>(defaultContextValue?: T): CtxApi<T> {\n let contextValue: T | symbol = EMPTY_CONTEXT;\n\n return {\n run,\n use,\n useX,\n };\n\n function use(): T {\n return (isInsideContext() ? contextValue : defaultContextValue) as T;\n }\n\n function useX(errorMessage?: string): T {\n invariant(\n isInsideContext(),\n defaultTo(errorMessage, USEX_DEFAULT_ERROR_MESSAGE),\n );\n return contextValue as T;\n }\n\n function run<R>(value: T, cb: () => R): R {\n const parentContext = isInsideContext() ? use() : EMPTY_CONTEXT;\n\n contextValue = value;\n\n const res = cb();\n\n contextValue = parentContext;\n return res;\n }\n\n function isInsideContext(): boolean {\n return contextValue !== EMPTY_CONTEXT;\n }\n}\n\n/**\n * Cascading context - another implementation of context, that assumes the context value is an object.\n * When nesting context runs, the the values of the current layer merges with the layers above it.\n */\nexport function createCascade<T extends Record<string, unknown>>(\n init?: (value: Partial<T>, parentContext: Maybe<T>) => Nullable<T>,\n): CtxCascadeApi<T> {\n const ctx = createContext<T>();\n\n return {\n bind,\n run,\n use: ctx.use,\n useX: ctx.useX,\n };\n\n function run<R>(value: Partial<T>, fn: () => R): R {\n const parentContext = ctx.use();\n\n const initResult = dynamicValue(init, value, parentContext) ?? value;\n\n const out = assign({}, parentContext ? parentContext : {}, initResult) as T;\n\n return ctx.run(Object.freeze(out), fn) as R;\n }\n\n function bind<Fn extends CB>(value: Partial<T>, fn: Fn) {\n return function (...runTimeArgs: Parameters<Fn>) {\n return run<ReturnType<Fn>>(value, function () {\n return fn(...runTimeArgs);\n });\n } as Fn;\n }\n}\n\ntype ContextConsumptionApi<T> = {\n use: () => T;\n useX: (errorMessage?: string) => T;\n};\n\nexport type CtxApi<T> = ContextConsumptionApi<T> & {\n run: <R>(value: T, cb: () => R) => R;\n};\n\nexport type CtxCascadeApi<T> = ContextConsumptionApi<T> & {\n run: <R>(value: Partial<T>, fn: () => R) => R;\n bind: <Fn extends CB>(value: Partial<T>, fn: Fn) => Fn;\n};\n"],"mappings":";;;AASA,MAAM,6BAA6B;AACnC,MAAM,gBAAgB,QAAQ;;;;AAK9B,SAAgB,cAAiB,qBAAoC;CACnE,IAAIA,eAA2B;AAE/B,QAAO;EACL;EACA;EACA;EACD;CAED,SAAS,MAAS;AAChB,SAAQ,iBAAiB,GAAG,eAAe;;CAG7C,SAAS,KAAK,cAA0B;AACtC,YACE,iBAAiB,EACjB,UAAU,cAAc,2BAA2B,CACpD;AACD,SAAO;;CAGT,SAAS,IAAO,OAAU,IAAgB;EACxC,MAAM,gBAAgB,iBAAiB,GAAG,KAAK,GAAG;AAElD,iBAAe;EAEf,MAAM,MAAM,IAAI;AAEhB,iBAAe;AACf,SAAO;;CAGT,SAAS,kBAA2B;AAClC,SAAO,iBAAiB;;;;;;;AAQ5B,SAAgB,cACd,MACkB;CAClB,MAAM,MAAM,eAAkB;AAE9B,QAAO;EACL;EACA;EACA,KAAK,IAAI;EACT,MAAM,IAAI;EACX;CAED,SAAS,IAAO,OAAmB,IAAgB;EACjD,MAAM,gBAAgB,IAAI,KAAK;EAE/B,MAAM,aAAa,aAAa,MAAM,OAAO,cAAc,IAAI;EAE/D,MAAM,MAAM,OAAO,EAAE,EAAE,gBAAgB,gBAAgB,EAAE,EAAE,WAAW;AAEtE,SAAO,IAAI,IAAI,OAAO,OAAO,IAAI,EAAE,GAAG;;CAGxC,SAAS,KAAoB,OAAmB,IAAQ;AACtD,SAAO,SAAU,GAAG,aAA6B;AAC/C,UAAO,IAAoB,OAAO,WAAY;AAC5C,WAAO,GAAG,GAAG,YAAY;KACzB"}