{"version":3,"sources":["../src/headers.ts","../src/server/request.ts","../src/server/request-bridge.ts"],"sourcesContent":["import { getCurrentRequest } from \"./server/request\";\n\nexport interface RequestCookie {\n  name: string;\n  value: string;\n}\n\nexport interface ReadonlyHeaders extends Iterable<[string, string]> {\n  get(name: string): string | null;\n  has(name: string): boolean;\n  forEach(callbackfn: (value: string, key: string, parent: Headers) => void, thisArg?: any): void;\n  entries(): IterableIterator<[string, string]>;\n  keys(): IterableIterator<string>;\n  values(): IterableIterator<string>;\n}\n\nexport interface ReadonlyRequestCookies extends Iterable<RequestCookie> {\n  get(name: string): RequestCookie | undefined;\n  getAll(name?: string): RequestCookie[];\n  has(name: string): boolean;\n  toString(): string;\n}\n\nexport function headers(): ReadonlyHeaders {\n  return new Headers(getCurrentRequest().headers) as ReadonlyHeaders;\n}\n\nexport function cookies(): ReadonlyRequestCookies {\n  const cookieHeader = getCurrentRequest().headers.get(\"cookie\") || \"\";\n  const parsed = parseCookieHeader(cookieHeader);\n\n  return {\n    get(name) {\n      return parsed.find((cookie) => cookie.name === name);\n    },\n    getAll(name) {\n      return name ? parsed.filter((cookie) => cookie.name === name) : [...parsed];\n    },\n    has(name) {\n      return parsed.some((cookie) => cookie.name === name);\n    },\n    toString() {\n      return cookieHeader;\n    },\n    [Symbol.iterator]() {\n      return parsed[Symbol.iterator]();\n    },\n  };\n}\n\nfunction parseCookieHeader(header: string): RequestCookie[] {\n  if (!header.trim()) return [];\n\n  return header\n    .split(\";\")\n    .map((part) => part.trim())\n    .filter(Boolean)\n    .map((part) => {\n      const separatorIndex = part.indexOf(\"=\");\n      if (separatorIndex === -1) {\n        return {\n          name: decodeCookiePart(part),\n          value: \"\",\n        };\n      }\n\n      return {\n        name: decodeCookiePart(part.slice(0, separatorIndex).trim()),\n        value: decodeCookiePart(part.slice(separatorIndex + 1).trim()),\n      };\n    })\n    .filter((cookie) => cookie.name.length > 0);\n}\n\nfunction decodeCookiePart(value: string) {\n  try {\n    return decodeURIComponent(value);\n  } catch {\n    return value;\n  }\n}\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;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;;;AD/FT,SAAS,UAA2B;AACzC,SAAO,IAAI,QAAQ,kBAAkB,EAAE,OAAO;AAChD;AAFgB;AAIT,SAAS,UAAkC;AAChD,QAAM,eAAe,kBAAkB,EAAE,QAAQ,IAAI,QAAQ,KAAK;AAClE,QAAM,SAAS,kBAAkB,YAAY;AAE7C,SAAO;AAAA,IACL,IAAI,MAAM;AACR,aAAO,OAAO,KAAK,CAAC,WAAW,OAAO,SAAS,IAAI;AAAA,IACrD;AAAA,IACA,OAAO,MAAM;AACX,aAAO,OAAO,OAAO,OAAO,CAAC,WAAW,OAAO,SAAS,IAAI,IAAI,CAAC,GAAG,MAAM;AAAA,IAC5E;AAAA,IACA,IAAI,MAAM;AACR,aAAO,OAAO,KAAK,CAAC,WAAW,OAAO,SAAS,IAAI;AAAA,IACrD;AAAA,IACA,WAAW;AACT,aAAO;AAAA,IACT;AAAA,IACA,CAAC,OAAO,QAAQ,IAAI;AAClB,aAAO,OAAO,OAAO,QAAQ,EAAE;AAAA,IACjC;AAAA,EACF;AACF;AArBgB;AAuBhB,SAAS,kBAAkB,QAAiC;AAC1D,MAAI,CAAC,OAAO,KAAK,EAAG,QAAO,CAAC;AAE5B,SAAO,OACJ,MAAM,GAAG,EACT,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,OAAO,OAAO,EACd,IAAI,CAAC,SAAS;AACb,UAAM,iBAAiB,KAAK,QAAQ,GAAG;AACvC,QAAI,mBAAmB,IAAI;AACzB,aAAO;AAAA,QACL,MAAM,iBAAiB,IAAI;AAAA,QAC3B,OAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM,iBAAiB,KAAK,MAAM,GAAG,cAAc,EAAE,KAAK,CAAC;AAAA,MAC3D,OAAO,iBAAiB,KAAK,MAAM,iBAAiB,CAAC,EAAE,KAAK,CAAC;AAAA,IAC/D;AAAA,EACF,CAAC,EACA,OAAO,CAAC,WAAW,OAAO,KAAK,SAAS,CAAC;AAC9C;AAtBS;AAwBT,SAAS,iBAAiB,OAAe;AACvC,MAAI;AACF,WAAO,mBAAmB,KAAK;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AANS;","names":[]}