UNPKG

@stacksjs/router

Version:
79 lines (78 loc) 2.56 kB
import process from "node:process"; import { AsyncLocalStorage } from "node:async_hooks"; import { log } from "@stacksjs/logging"; const REQUEST_STORAGE_KEY = Symbol.for("stacks.router.requestStorage"), requestStorage = globalThis[REQUEST_STORAGE_KEY] ??= new AsyncLocalStorage, TRACE_STORAGE_KEY = Symbol.for("stacks.router.traceStorage"), traceStorage = globalThis[TRACE_STORAGE_KEY] ??= new AsyncLocalStorage; export function getTraceId() { const explicit = traceStorage.getStore(); if (explicit) return explicit; return requestStorage.getStore()?._requestId; } export function withTraceId(id, fn) { return traceStorage.run(id, fn); } const REQUEST_QUERY_CACHE_KEY = Symbol.for("stacks.requestQueryCache"); function getRequestCache() { const req = requestStorage.getStore(); if (!req) return; let cache = req[REQUEST_QUERY_CACHE_KEY]; if (!cache) { cache = { map: new Map }; req[REQUEST_QUERY_CACHE_KEY] = cache; } return cache; } export async function cacheRequestQuery(key, fetcher) { const cache = getRequestCache(); if (!cache) return fetcher(); const existing = cache.map.get(key); if (existing) return existing; const promise = Promise.resolve().then(() => fetcher()); cache.map.set(key, promise); promise.catch(() => cache.map.delete(key)); return promise; } export function setCurrentRequest(req) { log.debug(`[request] ${req.method} ${new URL(req.url).pathname}`); requestStorage.enterWith(req); } export function clearCurrentRequest() { requestStorage.disable(); } export function runWithRequest(req, fn) { return requestStorage.run(req, fn); } export function getCurrentRequest() { return requestStorage.getStore(); } export const request = new Proxy({}, { get(_target, prop) { const currentRequest = getCurrentRequest(); if (!currentRequest) { if (process.env.NODE_ENV !== "production") console.warn(`[RequestContext] Accessing request.${String(prop)} outside of request context`); if (prop === "bearerToken") return () => null; if (prop === "user" || prop === "userToken") return async () => { return; }; if (prop === "tokenCan" || prop === "tokenCant") return async () => !1; if (prop === "headers") return new Headers; if (prop === "url") return ""; if (prop === "method") return "GET"; return; } const value = currentRequest[prop]; if (typeof value === "function") return value.bind(currentRequest); return value; } });