{"version":3,"file":"request.cjs","names":[],"sources":["../../../src/adapters/node/request.ts"],"sourcesContent":["import type {\n\tIncomingHttpHeaders,\n\tIncomingMessage,\n\tServerResponse,\n} from \"node:http\";\nimport * as set_cookie_parser from \"set-cookie-parser\";\n\ntype NodeRequestWithBody = IncomingMessage & {\n\tbody?: unknown;\n};\n\nconst getFirstHeaderValue = (\n\theader: IncomingHttpHeaders[string],\n): string | undefined => {\n\tif (Array.isArray(header)) {\n\t\treturn header[0];\n\t}\n\treturn header;\n};\n\nconst hasFormUrlEncodedContentType = (\n\theaders: IncomingHttpHeaders,\n): boolean => {\n\tconst contentType = getFirstHeaderValue(headers[\"content-type\"]);\n\tif (!contentType) {\n\t\treturn false;\n\t}\n\treturn contentType\n\t\t.toLowerCase()\n\t\t.startsWith(\"application/x-www-form-urlencoded\");\n};\n\nconst isPlainObject = (value: unknown): value is Record => {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn false;\n\t}\n\tconst prototype = Object.getPrototypeOf(value);\n\treturn prototype === Object.prototype || prototype === null;\n};\n\nconst appendFormValue = (\n\tparams: URLSearchParams,\n\tkey: string,\n\tvalue: unknown,\n) => {\n\tif (value === undefined) {\n\t\treturn;\n\t}\n\tif (Array.isArray(value)) {\n\t\tfor (const item of value) {\n\t\t\tappendFormValue(params, key, item);\n\t\t}\n\t\treturn;\n\t}\n\tif (value === null) {\n\t\tparams.append(key, \"\");\n\t\treturn;\n\t}\n\tif (isPlainObject(value)) {\n\t\tparams.append(key, JSON.stringify(value));\n\t\treturn;\n\t}\n\tparams.append(key, `${value}`);\n};\n\nconst toFormUrlEncodedBody = (\n\tbody: Readonly>,\n): string => {\n\tconst params = new URLSearchParams();\n\tfor (const [key, value] of Object.entries(body)) {\n\t\tappendFormValue(params, key, value);\n\t}\n\treturn params.toString();\n};\n\nconst canReadRawBody = (request: IncomingMessage): boolean => {\n\treturn (\n\t\t!request.destroyed && request.readableEnded !== true && request.readable\n\t);\n};\n\nconst serializeParsedBody = (\n\tparsedBody: unknown,\n\tisFormUrlEncoded: boolean,\n): string => {\n\tif (typeof parsedBody === \"string\") {\n\t\treturn parsedBody;\n\t}\n\tif (parsedBody instanceof URLSearchParams) {\n\t\treturn parsedBody.toString();\n\t}\n\tif (isFormUrlEncoded && isPlainObject(parsedBody)) {\n\t\treturn toFormUrlEncodedBody(parsedBody);\n\t}\n\treturn JSON.stringify(parsedBody);\n};\n\nfunction get_raw_body(req: IncomingMessage, body_size_limit?: number) {\n\tconst h = req.headers;\n\n\tif (!h[\"content-type\"]) return null;\n\n\tconst content_length = Number(h[\"content-length\"]);\n\n\t// check if no request body\n\tif (\n\t\t(req.httpVersionMajor === 1 &&\n\t\t\tisNaN(content_length) &&\n\t\t\th[\"transfer-encoding\"] == null) ||\n\t\tcontent_length === 0\n\t) {\n\t\treturn null;\n\t}\n\n\tlet length = content_length;\n\n\tif (body_size_limit) {\n\t\tif (!length) {\n\t\t\tlength = body_size_limit;\n\t\t} else if (length > body_size_limit) {\n\t\t\tthrow Error(\n\t\t\t\t`Received content-length of ${length}, but only accept up to ${body_size_limit} bytes.`,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (req.destroyed) {\n\t\tconst readable = new ReadableStream();\n\t\treadable.cancel();\n\t\treturn readable;\n\t}\n\n\tlet size = 0;\n\tlet cancelled = false;\n\n\treturn new ReadableStream({\n\t\tstart(controller) {\n\t\t\treq.on(\"error\", (error) => {\n\t\t\t\tcancelled = true;\n\t\t\t\tcontroller.error(error);\n\t\t\t});\n\n\t\t\treq.on(\"end\", () => {\n\t\t\t\tif (cancelled) return;\n\t\t\t\tcontroller.close();\n\t\t\t});\n\n\t\t\treq.on(\"data\", (chunk) => {\n\t\t\t\tif (cancelled) return;\n\n\t\t\t\tsize += chunk.length;\n\n\t\t\t\tif (size > length) {\n\t\t\t\t\tcancelled = true;\n\n\t\t\t\t\tcontroller.error(\n\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t`request body size exceeded ${\n\t\t\t\t\t\t\t\tcontent_length ? \"'content-length'\" : \"BODY_SIZE_LIMIT\"\n\t\t\t\t\t\t\t} of ${length}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tcontroller.enqueue(chunk);\n\n\t\t\t\tif (controller.desiredSize === null || controller.desiredSize <= 0) {\n\t\t\t\t\treq.pause();\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tpull() {\n\t\t\treq.resume();\n\t\t},\n\n\t\tcancel(reason) {\n\t\t\tcancelled = true;\n\t\t\treq.destroy(reason);\n\t\t},\n\t});\n}\n\nfunction constructRelativeUrl(\n\treq: IncomingMessage & { baseUrl?: string; originalUrl?: string },\n) {\n\tconst baseUrl = req.baseUrl;\n\tconst originalUrl = req.originalUrl;\n\n\tif (!baseUrl || !originalUrl) {\n\t\t// In express.js sub-routers `req.url` is relative to the mount\n\t\t// path (e.g., '/auth/xxx'), and `req.baseUrl` will hold the mount\n\t\t// path (e.g., '/api'). Build the full path as baseUrl + url when\n\t\t// available to preserve the full route. For application level routes\n\t\t// baseUrl will be an empty string\n\t\treturn baseUrl ? baseUrl + req.url : req.url;\n\t}\n\n\tif (baseUrl + req.url === originalUrl) {\n\t\treturn baseUrl + req.url;\n\t}\n\n\t// For certain subroutes or when mounting wildcard middlewares in express\n\t// it is possible `baseUrl + req.url` will result in a url constructed\n\t// which has a trailing forward slash the original url did not have.\n\t// Checking the `req.originalUrl` path ending can prevent this issue.\n\n\tconst originalPathEnding = originalUrl.split(\"?\")[0]!.at(-1);\n\treturn originalPathEnding === \"/\" ? baseUrl + req.url : baseUrl;\n}\n\nexport function getRequest({\n\trequest,\n\tbase,\n\tbodySizeLimit,\n}: {\n\tbase: string;\n\tbodySizeLimit?: number;\n\trequest: IncomingMessage;\n}) {\n\t// Check if body has already been parsed by Express middleware\n\tconst maybeConsumedReq = request as NodeRequestWithBody;\n\tconst isFormUrlEncoded = hasFormUrlEncodedContentType(request.headers);\n\tlet body = undefined;\n\n\tconst method = request.method;\n\t// Request with GET/HEAD method cannot have body.\n\tif (method !== \"GET\" && method !== \"HEAD\") {\n\t\t// Raw-first strategy: prefer consuming the original request stream whenever it is still readable.\n\t\tif (canReadRawBody(request)) {\n\t\t\tbody = get_raw_body(request, bodySizeLimit);\n\t\t} else if (maybeConsumedReq.body !== undefined) {\n\t\t\tconst parsedBody = maybeConsumedReq.body;\n\n\t\t\tconst bodyContent = serializeParsedBody(parsedBody, isFormUrlEncoded);\n\t\t\tbody = new ReadableStream({\n\t\t\t\tstart(controller) {\n\t\t\t\t\tcontroller.enqueue(new TextEncoder().encode(bodyContent));\n\t\t\t\t\tcontroller.close();\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t}\n\n\treturn new Request(base + constructRelativeUrl(request), {\n\t\t// @ts-expect-error\n\t\tduplex: \"half\",\n\t\tmethod: request.method,\n\t\tbody,\n\t\theaders: request.headers as Record,\n\t});\n}\n\nexport async function setResponse(res: ServerResponse, response: Response) {\n\tfor (const [key, value] of response.headers as any) {\n\t\ttry {\n\t\t\tres.setHeader(\n\t\t\t\tkey,\n\t\t\t\tkey === \"set-cookie\"\n\t\t\t\t\t? set_cookie_parser.splitCookiesString(\n\t\t\t\t\t\t\tresponse.headers.get(key) as string,\n\t\t\t\t\t\t)\n\t\t\t\t\t: value,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tres.getHeaderNames().forEach((name) => res.removeHeader(name));\n\t\t\tres.writeHead(500).end(String(error));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tres.statusCode = response.status;\n\tres.writeHead(response.status);\n\n\tif (!response.body) {\n\t\tres.end();\n\t\treturn;\n\t}\n\n\tif (response.body.locked) {\n\t\tres.end(\n\t\t\t\"Fatal error: Response body is locked. \" +\n\t\t\t\t\"This can happen when the response was already read (for example through 'response.json()' or 'response.text()').\",\n\t\t);\n\t\treturn;\n\t}\n\n\tconst reader = response.body.getReader();\n\n\tif (res.destroyed) {\n\t\treader.cancel();\n\t\treturn;\n\t}\n\n\tconst cancel = (error?: Error) => {\n\t\tres.off(\"close\", cancel);\n\t\tres.off(\"error\", cancel);\n\n\t\t// If the reader has already been interrupted with an error earlier,\n\t\t// then it will appear here, it is useless, but it needs to be catch.\n\t\treader.cancel(error).catch(() => {});\n\t\tif (error) res.destroy(error);\n\t};\n\n\tres.on(\"close\", cancel);\n\tres.on(\"error\", cancel);\n\n\tnext();\n\tasync function next() {\n\t\ttry {\n\t\t\tfor (;;) {\n\t\t\t\tconst { done, value } = await reader.read();\n\n\t\t\t\tif (done) break;\n\n\t\t\t\tconst writeResult = res.write(value);\n\t\t\t\tif (!writeResult) {\n\t\t\t\t\t// In AWS Lambda/serverless environments, drain events may not work properly\n\t\t\t\t\t// Check if we're in a Lambda-like environment and handle differently\n\t\t\t\t\tif (\n\t\t\t\t\t\tprocess.env.AWS_LAMBDA_FUNCTION_NAME ||\n\t\t\t\t\t\tprocess.env.LAMBDA_TASK_ROOT\n\t\t\t\t\t) {\n\t\t\t\t\t\t// In Lambda, continue without waiting for drain\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Standard Node.js behavior\n\t\t\t\t\t\tres.once(\"drain\", next);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tres.end();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tcancel(error instanceof Error ? error : new Error(String(error)));\n\t\t}\n\t}\n}\n"],"mappings":"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"}