@stacksjs/router
Version:
The Stacks framework router.
79 lines (78 loc) • 2.56 kB
JavaScript
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;
}
});