{"version":3,"sources":["../src/request.ts","../src/server/request.ts","../src/server/request-bridge.ts"],"sourcesContent":["export { getCurrentRequest } from \"./server/request\";\n","import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { Readable } from \"node:stream\";\nimport type { FarmRequest } from \"../types\";\nimport { _setCurrentRequestResolver } from \"./request-bridge\";\n\nconst REQUEST_STORAGE_KEY = Symbol.for(\"@farm.js/core/request-storage\");\n\nfunction getRequestStore(): AsyncLocalStorage<Request> {\n  const runtime = globalThis as typeof globalThis & Record<PropertyKey, unknown>;\n  const existing = runtime[REQUEST_STORAGE_KEY];\n  if (existing instanceof AsyncLocalStorage) {\n    return existing as AsyncLocalStorage<Request>;\n  }\n\n  const storage = new AsyncLocalStorage<Request>();\n  runtime[REQUEST_STORAGE_KEY] = storage;\n  return storage;\n}\n\nconst requestStore = getRequestStore();\n\n_setCurrentRequestResolver(() => requestStore.getStore());\n\nexport interface FarmRequestURLOptions {\n  origin?: string | URL;\n  trustProxy?: boolean;\n}\n\nexport function resolveFarmRequestURL(req: FarmRequest, options: FarmRequestURLOptions = {}): URL {\n  if (options.origin) {\n    return new URL(req.url || \"/\", options.origin);\n  }\n\n  const forwardedHost = options.trustProxy\n    ? firstForwardedHeaderValue(req.headers[\"x-forwarded-host\"])\n    : undefined;\n  const fallbackHost = firstForwardedHeaderValue(req.headers.host) || \"localhost\";\n  const forwardedProto = options.trustProxy\n    ? firstForwardedHeaderValue(req.headers[\"x-forwarded-proto\"])\n    : undefined;\n  const normalizedProto = forwardedProto?.toLowerCase();\n  const proto =\n    normalizedProto === \"https\" || normalizedProto === \"http\"\n      ? normalizedProto\n      : isEncryptedFarmRequest(req)\n        ? \"https\"\n        : \"http\";\n  return new URL(req.url || \"/\", resolveRequestOrigin(proto, forwardedHost, fallbackHost));\n}\n\nexport function createWebRequestFromFarmRequest(\n  req: FarmRequest,\n  options: FarmRequestURLOptions = {},\n): Request {\n  const fullUrl = resolveFarmRequestURL(req, options).toString();\n\n  const headers = new Headers();\n  for (const [key, value] of Object.entries(req.headers)) {\n    if (value == null) {\n      continue;\n    }\n\n    if (Array.isArray(value)) {\n      for (const item of value) {\n        headers.append(key, item);\n      }\n      continue;\n    }\n\n    headers.set(key, value);\n  }\n\n  const method = (req.method || \"GET\").toUpperCase();\n  const init: RequestInit & { duplex?: \"half\" } = {\n    method: req.method,\n    headers,\n  };\n\n  if (method !== \"GET\" && method !== \"HEAD\") {\n    init.body = Readable.toWeb(req) as ReadableStream<Uint8Array>;\n    init.duplex = \"half\";\n  }\n\n  return new Request(fullUrl, init);\n}\n\nfunction isEncryptedFarmRequest(req: FarmRequest): boolean {\n  return Boolean((req.socket as { encrypted?: boolean } | undefined)?.encrypted);\n}\n\nfunction firstForwardedHeaderValue(value: string | string[] | undefined): string | undefined {\n  const first = Array.isArray(value) ? value[0] : value;\n  const token = first?.split(\",\", 1)[0]?.trim();\n  return token || undefined;\n}\n\nfunction resolveRequestOrigin(proto: \"http\" | \"https\", host: string | undefined, fallback: string) {\n  for (const candidate of [host, fallback, \"localhost\"]) {\n    if (!candidate) continue;\n    if (/[\\s/?#@\\\\]/u.test(candidate)) continue;\n    try {\n      const url = new URL(`${proto}://${candidate}`);\n      if (url.username || url.password || url.pathname !== \"/\" || url.search || url.hash) continue;\n      return url.origin;\n    } catch {\n      // Try the next host instead of turning an untrusted proxy header into a 500.\n    }\n  }\n  return `${proto}://localhost`;\n}\n\nexport async function _runWithCurrentRequest<T>(\n  request: Request,\n  fn: () => Promise<T> | T,\n): Promise<T> {\n  return requestStore.run(request, fn);\n}\n\nexport function getCurrentRequest(): Request {\n  const request = requestStore.getStore();\n  if (!request) {\n    throw new Error(\n      \"No current request is available. getCurrentRequest() can only be used during server rendering.\",\n    );\n  }\n\n  return request;\n}\n\n// Some runtimes (StackBlitz WebContainers among them) lose AsyncLocalStorage\n// context across async boundaries mid-render. Callers whose feature can\n// degrade gracefully should use this instead of getCurrentRequest() so a\n// missing store never turns into a 500.\nexport function getCurrentRequestOrNull(): Request | null {\n  return requestStore.getStore() ?? null;\n}\n","type CurrentRequestResolver = () => Request | undefined;\n\nconst CURRENT_REQUEST_RESOLVER_KEY = Symbol.for(\"farm.currentRequestResolver\");\n\ntype GlobalWithCurrentRequestResolver = typeof globalThis & {\n  [CURRENT_REQUEST_RESOLVER_KEY]?: CurrentRequestResolver;\n};\n\nfunction getGlobalState(): GlobalWithCurrentRequestResolver {\n  return globalThis as GlobalWithCurrentRequestResolver;\n}\n\nexport function _setCurrentRequestResolver(resolver: CurrentRequestResolver | undefined): void {\n  getGlobalState()[CURRENT_REQUEST_RESOLVER_KEY] = resolver;\n}\n\nexport function _resolveCurrentRequest(): Request | undefined {\n  return getGlobalState()[CURRENT_REQUEST_RESOLVER_KEY]?.();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,8BAAkC;AAClC,yBAAyB;;;ACCzB,IAAM,+BAA+B,uBAAO,IAAI,6BAA6B;AAM7E,SAAS,iBAAmD;AAC1D,SAAO;AACT;AAFS;AAIF,SAAS,2BAA2B,UAAoD;AAC7F,iBAAe,EAAE,4BAA4B,IAAI;AACnD;AAFgB;;;ADPhB,IAAM,sBAAsB,uBAAO,IAAI,+BAA+B;AAEtE,SAAS,kBAA8C;AACrD,QAAM,UAAU;AAChB,QAAM,WAAW,QAAQ,mBAAmB;AAC5C,MAAI,oBAAoB,2CAAmB;AACzC,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,IAAI,0CAA2B;AAC/C,UAAQ,mBAAmB,IAAI;AAC/B,SAAO;AACT;AAVS;AAYT,IAAM,eAAe,gBAAgB;AAErC,2BAA2B,MAAM,aAAa,SAAS,CAAC;AAiGjD,SAAS,oBAA6B;AAC3C,QAAM,UAAU,aAAa,SAAS;AACtC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AATgB;","names":[]}