{"version":3,"sources":["../src/server/request.ts"],"sourcesContent":["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"],"mappings":"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","names":[]}