Locaft-backend/auth/node_modules/context/src/index.ts

174 lines
3.7 KiB
TypeScript

type TypeCTXRef = { [key: string]: any };
export interface ICTXFN {
(context: Context): any;
}
interface Init {
(ctxRef?: TypeCTXRef, parentContext?: Context | void): TypeCTXRef | null;
}
type ContextOptions = {
use: () => Context | void;
set: (value: any) => any;
addQueryableProperties: (ctxRef: TypeCTXRef) => TQueryableProperties;
init?: Init;
};
type TQueryableProperties = { [key: string]: true };
export type TCTX = {
use: () => Context | void;
run: (ctxRef: TypeCTXRef, fn: ICTXFN) => any;
bind: (
ctxRef: TypeCTXRef,
fn: (...args: any[]) => any,
...args: any[]
) => (...runTimeArgs: any[]) => any;
};
const getInnerName = (name: string): string => `__${name}`;
class Context {
private _parentContext: Context | null = null;
[key: string]: any;
static is(value: any): value is Context {
return value instanceof Context;
}
constructor(
{ use, set, addQueryableProperties, init }: ContextOptions,
ctxRef: TypeCTXRef
) {
const ctx = use();
const usedRef =
typeof init === 'function' ? init(ctxRef, ctx) ?? ctxRef : ctxRef;
const queryableProperties = addQueryableProperties(usedRef);
if (usedRef && typeof usedRef === 'object') {
for (const key in queryableProperties) {
if (Object.prototype.hasOwnProperty.call(usedRef, key)) {
this[getInnerName(key)] = usedRef[key];
}
this.addLookupProperty(key);
}
}
if (ctx) {
this.setParentContext(ctx);
}
set(this);
}
addLookupProperty(key: string) {
const innerName = getInnerName(key);
Object.defineProperty(this, key, {
get() {
return this.lookup(innerName);
},
set(value) {
throw new Error(
`Context: Unable to set "${key}" to \`${JSON.stringify(
value
)}\`. Context properties cannot be set directly. Use context.run() instead.`
);
},
});
}
// @ts-ignore - we actually do use lookup
private lookup(key: string) {
let ctx: Context = this;
do {
if (ctx.hasOwnProperty(key)) {
return ctx[key];
}
if (Context.is(ctx.parentContext)) {
ctx = ctx.parentContext;
} else {
return;
}
} while (ctx);
}
private setParentContext(parentContext: Context) {
if (Context.is(this)) {
this._parentContext = parentContext;
}
}
get parentContext(): Context | null {
return this._parentContext;
}
}
function createContext(init?: Init) {
const storage = {
ctx: undefined,
};
const queryableProperties: TQueryableProperties = {};
function addQueryableProperties(ctxRef: TypeCTXRef): TQueryableProperties {
if (!ctxRef || typeof ctxRef !== 'object') {
return {};
}
for (const key in ctxRef) {
if (Object.prototype.hasOwnProperty.call(ctxRef, key)) {
queryableProperties[key] = true;
}
}
return queryableProperties;
}
function use(): Context | void {
return storage.ctx;
}
function set(value: any) {
return (storage.ctx = value);
}
function clear() {
const ctx = use();
if (!Context.is(ctx)) {
return;
}
set(ctx.parentContext);
}
function run(ctxRef: TypeCTXRef, fn: ICTXFN) {
const ctx = new Context({ set, use, addQueryableProperties, init }, ctxRef);
const res = fn(ctx);
clear();
return res;
}
function bind(
ctxRef: TypeCTXRef,
fn: (...args: any[]) => any,
...args: any[]
) {
return function(...runTimeArgs: any[]) {
return run(ctxRef, function() {
return fn(...args, ...runTimeArgs);
});
};
}
return {
use,
run,
bind,
};
}
export default createContext;