trainsim/lib/context.ts

95 lines
2.5 KiB
TypeScript

type ContextStore = Record<string, any>;
const contextStack: ContextStore[] = [];
const defaultContext: ContextStore = {};
const debug = JSON.parse(localStorage.getItem("debug") || "false");
export function setDefaultContext(context: ContextStore) {
Object.assign(defaultContext, context);
}
export function withContext<T>(context: ContextStore, fn: () => T): T {
contextStack.push(context);
try {
return fn();
} finally {
contextStack.pop();
}
}
export const ctx = new Proxy(
{},
{
get(_, prop: string) {
for (let i = contextStack.length - 1; i >= 0; i--) {
if (prop in contextStack[i]) return contextStack[i][prop];
}
if (prop in defaultContext) return defaultContext[prop];
throw new Error(`Context variable '${prop}' is not defined.`);
},
},
) as Record<string, unknown>;
export function getContext() {
return ctx;
}
export function getContextItem<T>(prop: string): T {
return ctx[prop] as T;
}
export function getContextItemOrDefault<T>(prop: string, defaultValue: T): T {
try {
return ctx[prop] as T;
} catch {
return defaultValue;
}
}
export function setContextItem<T>(prop: string, value: T) {
Object.assign(contextStack[contextStack.length - 1] ?? defaultContext, {
[prop]: value,
});
}
if (debug) {
setInterval(() => {
let ctxEl = document.getElementById("context");
if (!ctxEl) {
ctxEl = document.createElement("div");
ctxEl.id = "context";
document.body.append(ctxEl);
}
ctxEl.innerHTML = "";
const div = document.createElement("div");
const pre = document.createElement("pre");
const h3 = document.createElement("h3");
h3.textContent = "Default";
div.append(h3);
pre.textContent = safeStringify(defaultContext);
div.append(pre);
ctxEl.append(div);
for (const [idx, ctx] of contextStack.entries()) {
const div = document.createElement("div");
const pre = document.createElement("pre");
const h3 = document.createElement("h3");
h3.textContent = "CTX " + idx;
div.append(h3);
pre.textContent = safeStringify(ctx);
div.append(pre);
ctxEl.append(div);
}
}, 1000);
}
function safeStringify(obj: any) {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return "[Circular]"; // Replace circular references
}
seen.add(value);
}
return value;
}, 2);
}