UNPKG

@edgeone/astro

Version:

Astro adapter for EdgeOne Pages

2 lines 21.6 kB
export const BOOTSTRAP_TEMPLATE = "import { createServer } from 'http';\nimport { Readable } from 'stream';\nimport crypto from 'node:crypto';\n\n/**\n * \u9ed8\u8ba4\u914d\u7f6e\n */\nconst DEFAULT_CONFIG = {\n port: 9000,\n host: 'localhost'\n};\n\n// ==================== Fetch Proxy \u529f\u80fd ====================\n\nexport function setupFetchProxy(options = {}) {\n const {\n uuid = '{{PAGES_PROXY_UUID}}',\n proxyHost = '{{PAGES_PROXY_HOST}}',\n enableCache = true,\n cacheExpires = 1000 * 60 * 60, // 1\u5c0f\u65f6\n alwaysProxy = false\n } = options;\n\n // \u4fdd\u5b58\u539f\u59cbfetch\n const __originalFetch = globalThis.fetch;\n\n // \u83b7\u53d6URL\u4fe1\u606f\n function getUrl(request) {\n const req = new Request(request);\n const url = new URL(req.url);\n return url;\n }\n\n // \u83b7\u53d6Host\u7f13\u5b58\n function getHostCache(host) {\n if (!enableCache) return null;\n return new Map(globalThis._FETCHCACHES || []).get(host);\n }\n\n // \u8bbe\u7f6eHost\u7f13\u5b58\n function setHostCache(host) {\n if (!enableCache) return;\n \n const value = {\n needProxy: true,\n expires: Date.now() + cacheExpires,\n };\n \n if (globalThis._FETCHCACHES) {\n globalThis._FETCHCACHES.set(host, value);\n } else {\n const cache = new Map([[host, value]]);\n Object.defineProperty(globalThis, '_FETCHCACHES', {\n value: cache,\n writable: false,\n enumerable: false,\n configurable: false,\n });\n }\n }\n\n // MD5\u54c8\u5e0c\u51fd\u6570\n function md5(text) {\n const hash = crypto.createHash('md5');\n hash.update(text, 'utf8');\n return hash.digest('hex');\n }\n\n // \u751f\u6210\u7b7e\u540d\n function generateSign({ pathname, oeTimestamp }) {\n return md5(oeTimestamp + '-' + pathname + '-' + uuid);\n }\n\n // \u751f\u6210\u4ee3\u7406headers\n async function generateHeaders(request) {\n const { host, pathname } = getUrl(request);\n const timestamp = Date.now().toString();\n const sign = generateSign({ pathname, oeTimestamp: timestamp });\n return {\n host,\n timestamp,\n sign,\n };\n }\n\n /**\n * \u901a\u8fc7\u539f\u59cbfetch\u8bf7\u6c42(\u5e26\u8d85\u65f6)\n */\n async function fetchByOrigin(request, requestInit = {}) {\n try {\n const res = await __originalFetch(request, {\n eo: {\n timeoutSetting: {\n connectTimeout: 500,\n },\n },\n ...requestInit,\n });\n \n // \u68c0\u67e5\u54cd\u5e94\u72b6\u6001\n if (res.status > 300 || res.status < 200) {\n throw new Error('need proxy');\n }\n \n return res;\n } catch (error) {\n // \u5931\u8d25\u65f6\u5207\u6362\u5230\u4ee3\u7406\n const { host } = getUrl(request);\n setHostCache(host);\n return fetchByProxy(request, requestInit);\n }\n }\n\n /**\n * \u901a\u8fc7\u4ee3\u7406\u670d\u52a1\u5668\u8bf7\u6c42\n */\n async function fetchByProxy(request, requestInit = {}) {\n const options = {};\n if (requestInit) {\n Object.assign(options, requestInit);\n }\n \n options.headers = new Headers(options.headers || {});\n const { host, timestamp, sign } = await generateHeaders(request);\n \n // \u6dfb\u52a0\u4ee3\u7406headers\n options.headers.append('oe-host', host);\n options.headers.append('oe-timestamp', timestamp);\n options.headers.append('oe-sign', sign);\n \n // \u6784\u5efa\u4ee3\u7406\u8bf7\u6c42\n const originReq = new Request(request);\n const req = new Request(originReq.url.replace(host, proxyHost), {\n method: originReq.method,\n headers: originReq.headers,\n body: originReq.body,\n });\n \n return __originalFetch(req, options);\n }\n\n /**\n * \u81ea\u5b9a\u4e49fetch\u51fd\u6570\n */\n function _fetch(request, requestInit = {}) {\n const { host } = getUrl(request);\n \n // \u5982\u679c\u8bbe\u7f6e\u4e86\u603b\u662f\u4f7f\u7528\u4ee3\u7406\n if (alwaysProxy) {\n setHostCache(host);\n return fetchByProxy(request, requestInit);\n }\n \n // \u68c0\u67e5\u7f13\u5b58\n const cache = getHostCache(host);\n if (cache && cache.needProxy && cache.expires > Date.now()) {\n // \u4f7f\u7528\u4ee3\u7406\n return fetchByProxy(request, requestInit);\n }\n \n // \u5c1d\u8bd5\u539f\u59cb\u8bf7\u6c42,\u5931\u8d25\u5219\u4f7f\u7528\u4ee3\u7406\n return fetchByOrigin(request, requestInit);\n }\n\n // \u66ff\u6362\u5168\u5c40fetch\n if (typeof _fetch === 'function') {\n globalThis.fetch = _fetch;\n globalThis.__originalFetch = __originalFetch;\n console.log('[fetch-proxy] Fetch proxy initialized');\n } else {\n console.warn('[fetch-proxy] Failed to initialize, using original fetch');\n }\n\n // \u8fd4\u56de\u5de5\u5177\u51fd\u6570\n return {\n _fetch,\n __originalFetch,\n getHostCache,\n setHostCache,\n clearCache: () => {\n if (globalThis._FETCHCACHES) {\n globalThis._FETCHCACHES.clear();\n }\n }\n };\n}\n\n/**\n * \u7981\u7528Fetch Proxy\n */\nexport function disableFetchProxy() {\n if (globalThis.__originalFetch) {\n globalThis.fetch = globalThis.__originalFetch;\n console.log('[fetch-proxy] Fetch proxy disabled');\n }\n}\n\n// ==================== \u57fa\u7840\u5de5\u5177\u51fd\u6570 ====================\n\n/**\n * \u4ece\u8bf7\u6c42headers\u4e2d\u6784\u5efa\u5b8c\u6574URL\n * @param {Object} req - Node.js IncomingMessage\u5bf9\u8c61\n * @param {Object} options - \u9009\u9879 { defaultProtocol: 'http', defaultHost: 'localhost' }\n * @returns {Object} \u8fd4\u56de\u5305\u542b { host, path, fullPath } \u7684\u5bf9\u8c61\n */\nexport function buildURL(req, options = {}) {\n const {\n defaultProtocol = 'http',\n defaultHost = 'localhost:9000'\n } = options;\n\n // \u5c1d\u8bd5\u591a\u79cd\u65b9\u5f0f\u83b7\u53d6\u771f\u5b9ehost\n const realHost = \n req.headers['eo-pages-host'] ||\n defaultHost;\n\n // \u83b7\u53d6\u534f\u8bae\n const protocol = req.headers['x-forwarded-proto'] || defaultProtocol;\n\n // \u6784\u5efa\u5b8c\u6574URL\n const fullUrlRaw = `${protocol}://${realHost}${req.url || '/'}`;\n const urlObj = new URL(fullUrlRaw);\n \n // \u5904\u7406 pathname - \u79fb\u9664\u672b\u5c3e\u7684 /\n let pathname = urlObj.pathname;\n if (pathname !== '/' && pathname.endsWith('/')) {\n pathname = pathname.slice(0, -1);\n }\n\n // \u6839\u636e\u73af\u5883\u6784\u5efa fullPath\n let fullPath = '';\n if (req.headers.host === 'localhost:9000') {\n // localhost \u73af\u5883\u53ea\u4f7f\u7528 pathname\n fullPath = pathname;\n } else {\n // \u751f\u4ea7\u73af\u5883\u4f7f\u7528\u5b8c\u6574 URL\n const pageHost = req.headers['eo-pages-host'] || req.headers.host;\n const xForwardedProto = req.headers['x-forwarded-proto'] || 'https';\n fullPath = xForwardedProto + '://' + pageHost + req.url;\n // \u79fb\u9664\u672b\u5c3e\u7684 ?\n if (fullPath.endsWith('?')) {\n fullPath = fullPath.slice(0, -1);\n }\n }\n \n return {\n host: realHost,\n path: pathname,\n fullPath: fullPath\n };\n}\n\n/**\n * \u521b\u5efa\u8bf7\u6c42\u4f53ReadableStream (\u9002\u7528\u4e8e\u9700\u8981\u6d41\u5f0f\u5904\u7406\u7684\u6846\u67b6)\n * @param {Object} req - Node.js IncomingMessage\u5bf9\u8c61\n * @returns {ReadableStream} Web ReadableStream\n */\nexport function createRequestStream(req) {\n return new ReadableStream({\n start(controller) {\n req.on('data', chunk => {\n controller.enqueue(new Uint8Array(chunk));\n });\n \n req.on('end', () => {\n controller.close();\n });\n \n req.on('error', error => {\n controller.error(error);\n });\n },\n \n cancel() {\n req.destroy();\n }\n });\n}\n\n/**\n * \u8bfb\u53d6\u5b8c\u6574\u8bf7\u6c42\u4f53 (\u9002\u7528\u4e8e\u9700\u8981\u4e00\u6b21\u6027\u8bfb\u53d6body\u7684\u6846\u67b6)\n * @param {Object} req - Node.js IncomingMessage\u5bf9\u8c61\n * @returns {Promise<Buffer>} \u8bf7\u6c42\u4f53Buffer\n */\nexport async function readRequestBody(req) {\n return new Promise((resolve, reject) => {\n const chunks = [];\n req.on('data', chunk => chunks.push(chunk));\n req.on('end', () => resolve(Buffer.concat(chunks)));\n req.on('error', reject);\n });\n}\n\n/**\n * \u5904\u7406Response\u5bf9\u8c61\u5e76\u5199\u5165Node.js response\n * @param {Object} res - Node.js ServerResponse\u5bf9\u8c61\n * @param {Response} response - Web Response\u5bf9\u8c61\n * @param {Object} additionalHeaders - \u989d\u5916\u7684headers\n * @param {Object} options - \u5904\u7406\u9009\u9879\n * - useEdgeOneHeaders: \u662f\u5426\u4f7f\u7528EdgeOne\u7279\u5b9aheaders(\u9ed8\u8ba4false)\n * - requestId: \u8bf7\u6c42ID\n * @returns {Promise<void>}\n */\nexport async function handleResponse(res, response, additionalHeaders = {}, options = {}) {\n const startTime = Date.now();\n const { useEdgeOneHeaders = false, requestId = '' } = options;\n \n // \u5904\u7406null/undefined\u54cd\u5e94\n if (!response) {\n const notFoundHeaders = {\n 'Content-Type': 'application/json',\n ...additionalHeaders\n };\n \n // EdgeOne\u7279\u5b9aheaders\n if (useEdgeOneHeaders) {\n notFoundHeaders['Functions-Request-Id'] = requestId;\n notFoundHeaders['eo-pages-inner-scf-status'] = '404';\n notFoundHeaders['eo-pages-inner-status-intercept'] = 'true';\n }\n \n res.writeHead(404, notFoundHeaders);\n res.end(JSON.stringify({\n error: \"Not Found\",\n message: \"The requested path does not exist\"\n }));\n console.log(`Response: 404 Not Found - ${Date.now() - startTime}ms`);\n return;\n }\n\n try {\n // \u5904\u7406Response\u5bf9\u8c61\n if (response instanceof Response) {\n const headers = {};\n \n // \u590d\u5236Response\u7684headers\n for (const [key, value] of response.headers) {\n headers[key] = value;\n }\n \n // \u5408\u5e76\u989d\u5916\u7684headers\n Object.assign(headers, additionalHeaders);\n \n // EdgeOne\u7279\u5b9aheaders\u5904\u7406\n if (useEdgeOneHeaders) {\n headers['Functions-Request-Id'] = requestId;\n \n // \u5982\u679cResponse\u4e2d\u5df2\u7ecf\u8bbe\u7f6e\u4e86,\u4f7f\u7528\u5b83\u7684\u503c;\u5426\u5219\u4f7f\u7528responseStatus\n if (!headers['eo-pages-inner-scf-status']) {\n headers['eo-pages-inner-scf-status'] = String(response.status);\n }\n \n // \u5982\u679cResponse\u4e2d\u5df2\u7ecf\u8bbe\u7f6e\u4e86,\u4f7f\u7528\u5b83\u7684\u503c;\u5426\u5219\u9ed8\u8ba4\u4e3afalse\n if (!headers['eo-pages-inner-status-intercept']) {\n headers['eo-pages-inner-status-intercept'] = 'false';\n }\n }\n \n // \u79fb\u9664\u53ef\u80fd\u5bfc\u81f4\u95ee\u9898\u7684headers\n if (headers['eop-client-geo']) {\n delete headers['eop-client-geo'];\n }\n \n // \u5904\u7406set-cookie\u5934\u90e8(\u7279\u6b8a\u5904\u7406,\u53ef\u80fd\u6709\u591a\u4e2a\u503c)\n if (response.headers.has('set-cookie')) {\n const cookieArr = response.headers.getSetCookie ? response.headers.getSetCookie() : response.headers.get('set-cookie');\n headers['set-cookie'] = cookieArr;\n }\n\n // \u68c0\u67e5\u662f\u5426\u662f\u6d41\u5f0f\u54cd\u5e94\n const isStream = response.body && (\n response.headers.get('content-type')?.includes('text/event-stream') ||\n response.headers.get('transfer-encoding')?.includes('chunked') ||\n response.body instanceof ReadableStream ||\n typeof response.body.pipe === 'function' ||\n response.headers.get('x-content-type-stream') === 'true'\n );\n\n if (isStream) {\n // \u6d41\u5f0f\u54cd\u5e94\u5904\u7406\n const streamHeaders = { ...headers };\n\n if (response.headers.get('content-type')?.includes('text/event-stream')) {\n streamHeaders['Content-Type'] = 'text/event-stream';\n streamHeaders['Cache-Control'] = 'no-cache';\n streamHeaders['Connection'] = 'keep-alive';\n }\n\n res.writeHead(response.status, streamHeaders);\n\n // \u5904\u7406\u4e0d\u540c\u7c7b\u578b\u7684\u6d41\n if (typeof response.body.pipe === 'function') {\n // Node.js Stream\n response.body.pipe(res);\n } else {\n // Web ReadableStream\n const reader = response.body.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n \n if (value instanceof Uint8Array || Buffer.isBuffer(value)) {\n res.write(value);\n } else {\n const chunk = new TextDecoder().decode(value);\n res.write(chunk);\n }\n }\n } finally {\n reader.releaseLock();\n // SCF\u53ef\u80fd\u4f1a\u7acb\u5373\u51bb\u7ed3\u73af\u5883\u4e0a\u4e0b\u6587,\u5bfc\u81f4\u540e\u7eed\u65e5\u5fd7\u65e0\u6cd5\u8f93\u51fa\n // \u901a\u8fc7\u5ef6\u65f6\u6765\u786e\u4fdd\u65e5\u5fd7\u8f93\u51fa(\u4ec5\u5728EdgeOne\u6a21\u5f0f\u4e0b)\n if (useEdgeOneHeaders) {\n setTimeout(() => {\n res.end();\n }, 1);\n } else {\n res.end();\n }\n }\n }\n } else {\n // \u666e\u901a\u54cd\u5e94\n // \u5220\u9664\u53ef\u80fd\u4e0d\u51c6\u786e\u7684Content-Length,\u8ba9Node.js\u81ea\u52a8\u8ba1\u7b97\n delete headers['content-length'];\n delete headers['Content-Length'];\n \n res.writeHead(response.status, headers);\n \n // \u8bfb\u53d6body\n if (response.body) {\n const body = await response.arrayBuffer();\n res.end(Buffer.from(body));\n } else {\n res.end();\n }\n }\n } else {\n // \u975eResponse\u5bf9\u8c61,\u76f4\u63a5\u8fd4\u56deJSON\n const jsonHeaders = {\n 'Content-Type': 'application/json',\n ...additionalHeaders\n };\n \n if (useEdgeOneHeaders) {\n jsonHeaders['Functions-Request-Id'] = requestId;\n jsonHeaders['eo-pages-inner-scf-status'] = '200';\n jsonHeaders['eo-pages-inner-status-intercept'] = 'false';\n }\n \n res.writeHead(200, jsonHeaders);\n res.end(JSON.stringify(response));\n }\n } catch (error) {\n console.error('HandleResponse error:', error);\n \n // \u9519\u8bef\u5904\u7406\n if (!res.headersSent) {\n const errorHeaders = {\n 'Content-Type': 'application/json',\n ...additionalHeaders\n };\n \n if (useEdgeOneHeaders) {\n errorHeaders['Functions-Request-Id'] = requestId;\n errorHeaders['eo-pages-inner-scf-status'] = '502';\n errorHeaders['eo-pages-inner-status-intercept'] = 'true';\n }\n \n res.writeHead(useEdgeOneHeaders ? 502 : 500, errorHeaders);\n res.end(JSON.stringify({\n error: \"Internal Server Error\",\n message: error.message\n }));\n }\n } finally {\n const endTime = Date.now();\n console.log(`Response: ${response?.status || 'unknown'} - ${endTime - startTime}ms`);\n }\n}\n\n/**\n * \u521b\u5efa\u901a\u7528\u6846\u67b6\u670d\u52a1\u5668\n * @param {Function} handler - \u6846\u67b6\u7684\u8bf7\u6c42\u5904\u7406\u51fd\u6570 async (req, context) => Response\n * @param {Object} options - \u914d\u7f6e\u9009\u9879\n * - onBeforeRequest: \u8bf7\u6c42\u524d\u94a9\u5b50 (req) => void\n * - onAfterResponse: \u54cd\u5e94\u540e\u94a9\u5b50 (req, res, response) => void\n * - errorHandler: \u9519\u8bef\u5904\u7406\u51fd\u6570 (error, req, res) => void\n * - buildContext: \u6784\u5efacontext\u51fd\u6570 (req) => context\n * - useEdgeOneHeaders: \u662f\u5426\u4f7f\u7528EdgeOne\u7279\u5b9aheaders (\u9ed8\u8ba4false)\n * - logFullPath: \u662f\u5426\u8bb0\u5f55\u5b8c\u6574\u8bf7\u6c42\u8def\u5f84 (\u9ed8\u8ba4true)\n * - fetchProxy: Fetch Proxy\u914d\u7f6e\u5bf9\u8c61 (\u53ef\u9009,\u9ed8\u8ba4\u542f\u7528)\n * - enabled: \u662f\u5426\u542f\u7528(\u9ed8\u8ba4true,\u8bbe\u4e3afalse\u53ef\u7981\u7528)\n * - uuid: \u4ee3\u7406UUID (\u9ed8\u8ba4'{{PAGES_PROXY_UUID}}')\n * - proxyHost: \u4ee3\u7406\u670d\u52a1\u5668\u5730\u5740 (\u9ed8\u8ba4'{{PAGES_PROXY_HOST}}')\n * - enableCache: \u662f\u5426\u542f\u7528\u7f13\u5b58(\u9ed8\u8ba4true)\n * - cacheExpires: \u7f13\u5b58\u8fc7\u671f\u65f6\u95f4(\u6beb\u79d2,\u9ed8\u8ba41\u5c0f\u65f6)\n * - alwaysProxy: \u662f\u5426\u603b\u662f\u4f7f\u7528\u4ee3\u7406(\u9ed8\u8ba4false)\n * @returns {Server} HTTP Server\u5b9e\u4f8b\n */\nexport function createFrameworkServer(handler, options = {}) {\n const port = DEFAULT_CONFIG.port;\n const host = DEFAULT_CONFIG.host;\n const {\n onBeforeRequest = null,\n onAfterResponse = null,\n errorHandler = null,\n buildContext = () => ({}),\n useEdgeOneHeaders = true,\n logFullPath = true,\n fetchProxy = null\n } = options;\n\n // \u9ed8\u8ba4\u542f\u7528Fetch Proxy,\u9664\u975e\u660e\u786e\u8bbe\u7f6eenabled\u4e3afalse\n if (!fetchProxy || fetchProxy.enabled !== false) {\n setupFetchProxy({\n uuid: fetchProxy?.uuid || '{{PAGES_PROXY_UUID}}',\n proxyHost: fetchProxy?.proxyHost || '{{PAGES_PROXY_HOST}}',\n enableCache: fetchProxy?.enableCache !== undefined ? fetchProxy.enableCache : true,\n cacheExpires: fetchProxy?.cacheExpires || 1000 * 60 * 60,\n alwaysProxy: fetchProxy?.alwaysProxy || false\n });\n }\n\n const server = createServer(async (req, res) => {\n const requestStartTime = Date.now();\n \n try {\n // \u8bf7\u6c42\u524d\u94a9\u5b50\n if (onBeforeRequest) {\n await onBeforeRequest(req);\n }\n\n // \u6784\u5efaURL\n const url = buildURL(req, { defaultHost: `${host}:9000` });\n \n console.log(`Pages request path: ${url.fullPath}`);\n\n // \u6784\u5efacontext\n const context = buildContext(req);\n\n // \u8c03\u7528\u6846\u67b6handler\n const response = await handler(req, context);\n\n // \u8bbe\u7f6e\u989d\u5916headers\u548c\u9009\u9879\n const requestId = req.headers['x-scf-request-id'] || req.headers['functions-request-id'] || '';\n const additionalHeaders = {};\n additionalHeaders['functions-request-id'] = requestId;\n\n \n const handleResponseOptions = {\n useEdgeOneHeaders,\n requestId\n };\n\n // \u5904\u7406\u54cd\u5e94\n await handleResponse(res, response, additionalHeaders, handleResponseOptions);\n\n // \u54cd\u5e94\u540e\u94a9\u5b50\n if (onAfterResponse) {\n await onAfterResponse(req, res, response);\n }\n\n const requestEndTime = Date.now();\n console.log(`Request completed: ${requestEndTime - requestStartTime}ms\\n`);\n \n } catch (error) {\n console.error('Server error:', error);\n \n // EdgeOne\u65e5\u5fd7\u683c\u5f0f\n if (useEdgeOneHeaders) {\n console.log(`Pages response status: 502`);\n }\n \n // \u4f7f\u7528\u81ea\u5b9a\u4e49\u9519\u8bef\u5904\u7406\u5668\u6216\u9ed8\u8ba4\u5904\u7406\u5668\n if (errorHandler) {\n await errorHandler(error, req, res);\n } else {\n // \u9ed8\u8ba4\u9519\u8bef\u5904\u7406\n if (!res.headersSent) {\n const requestId = req.headers['x-scf-request-id'] || req.headers['functions-request-id'] || '';\n const errorHeaders = {\n 'Content-Type': 'application/json'\n };\n \n if (useEdgeOneHeaders) {\n errorHeaders['Functions-Request-Id'] = requestId;\n errorHeaders['eo-pages-inner-scf-status'] = '502';\n errorHeaders['eo-pages-inner-status-intercept'] = 'true';\n } else {\n errorHeaders['functions-request-id'] = requestId;\n }\n \n res.writeHead(useEdgeOneHeaders ? 502 : 500, errorHeaders);\n res.end(JSON.stringify({\n error: \"Internal Server Error\",\n code: \"FRAMEWORK_HANDLER_ERROR\",\n message: error.message,\n stack: process.env.NODE_ENV === 'development' ? error.stack : undefined\n }));\n }\n }\n }\n });\n\n // \u542f\u52a8\u670d\u52a1\u5668\n server.listen(port, host, () => {\n console.log(`Server is running on http://${host}:${port}`);\n });\n\n return server;\n}\n\n/**\n * \u8f85\u52a9\u51fd\u6570:\u5c06Node.js Stream\u8f6c\u6362\u4e3aWeb ReadableStream\n * @param {Stream} nodeStream - Node.js Stream\n * @returns {ReadableStream} Web ReadableStream\n */\nexport function nodeStreamToWebStream(nodeStream) {\n return Readable.toWeb(nodeStream);\n}\n\n/**\n * \u8f85\u52a9\u51fd\u6570:\u5c06Web ReadableStream\u8f6c\u6362\u4e3aNode.js Stream\n * @param {ReadableStream} webStream - Web ReadableStream\n * @returns {Stream} Node.js Stream\n */\nexport function webStreamToNodeStream(webStream) {\n return Readable.fromWeb(webStream);\n}\n\nexport default {\n createFrameworkServer,\n handleResponse,\n buildURL,\n createRequestStream,\n readRequestBody,\n nodeStreamToWebStream,\n webStreamToNodeStream,\n setupFetchProxy,\n disableFetchProxy\n};\n"; //# sourceMappingURL=bootstrap-template.js.map