UNPKG

@edgeone/astro

Version:

Astro adapter for EdgeOne Pages

2 lines 21.6 kB
export declare 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.d.ts.map