109 lines
4.4 KiB
JavaScript
109 lines
4.4 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.ExpoError = void 0;
|
|
exports.createRequestHandler = createRequestHandler;
|
|
exports.convertHeaders = convertHeaders;
|
|
exports.convertRequest = convertRequest;
|
|
exports.respond = respond;
|
|
const node_stream_1 = require("node:stream");
|
|
const promises_1 = require("node:stream/promises");
|
|
const abstract_1 = require("./abstract");
|
|
const runtime_1 = require("../runtime");
|
|
const node_1 = require("./environment/node");
|
|
const createReadableStreamFromReadable_1 = require("../utils/createReadableStreamFromReadable");
|
|
var abstract_2 = require("./abstract");
|
|
Object.defineProperty(exports, "ExpoError", { enumerable: true, get: function () { return abstract_2.ExpoError; } });
|
|
const scopeSymbol = Symbol.for('expoServerScope');
|
|
const SYMBOL_FOR_REQ_CONTEXT = Symbol.for('@vercel/request-context');
|
|
/** @see https://github.com/vercel/vercel/blob/b189b39/packages/functions/src/get-context.ts */
|
|
function getContext() {
|
|
const fromSymbol = globalThis;
|
|
return fromSymbol[SYMBOL_FOR_REQ_CONTEXT]?.get?.() ?? {};
|
|
}
|
|
// Vercel already has an async-scoped context in VercelContext, so we can attach
|
|
// our scope context to this object
|
|
const STORE = {
|
|
getStore: () => getContext()[scopeSymbol],
|
|
run(scope, runner, ...args) {
|
|
getContext()[scopeSymbol] = scope;
|
|
return runner(...args);
|
|
},
|
|
};
|
|
/**
|
|
* Returns a request handler for Vercel's Node.js runtime that serves the
|
|
* response using Remix.
|
|
*/
|
|
function createRequestHandler(params) {
|
|
const makeRequestAPISetup = (request) => {
|
|
const host = request.headers.get('host');
|
|
const proto = request.headers.get('x-forwarded-proto') || 'https';
|
|
return {
|
|
origin: host ? `${proto}://${host}` : 'null',
|
|
// See: https://github.com/vercel/vercel/blob/b189b39/packages/functions/src/get-env.ts#L25C3-L25C13
|
|
environment: process.env.VERCEL_ENV ?? process.env.NODE_ENV,
|
|
waitUntil: getContext().waitUntil,
|
|
};
|
|
};
|
|
const run = (0, runtime_1.createRequestScope)(STORE, makeRequestAPISetup);
|
|
const onRequest = (0, abstract_1.createRequestHandler)((0, node_1.createNodeEnv)(params));
|
|
return async (req, res) => {
|
|
return respond(res, await run(onRequest, convertRequest(req, res)));
|
|
};
|
|
}
|
|
function convertHeaders(requestHeaders) {
|
|
const headers = new Headers();
|
|
for (const [key, values] of Object.entries(requestHeaders)) {
|
|
if (values) {
|
|
if (Array.isArray(values)) {
|
|
for (const value of values) {
|
|
headers.append(key, value);
|
|
}
|
|
}
|
|
else {
|
|
headers.set(key, values);
|
|
}
|
|
}
|
|
}
|
|
return headers;
|
|
}
|
|
function convertRawHeaders(requestHeaders) {
|
|
const headers = new Headers();
|
|
for (let index = 0; index < requestHeaders.length; index += 2) {
|
|
headers.append(requestHeaders[index], requestHeaders[index + 1]);
|
|
}
|
|
return headers;
|
|
}
|
|
function convertRequest(req, res) {
|
|
const host = req.headers['x-forwarded-host'] || req.headers['host'];
|
|
// doesn't seem to be available on their req object!
|
|
const protocol = req.headers['x-forwarded-proto'] || 'https';
|
|
const url = new URL(`${protocol}://${host}${req.url}`);
|
|
// Abort action/loaders once we can no longer write a response
|
|
const controller = new AbortController();
|
|
res.on('close', () => controller.abort());
|
|
const init = {
|
|
method: req.method,
|
|
headers: convertRawHeaders(req.rawHeaders),
|
|
// Cast until reason/throwIfAborted added
|
|
// https://github.com/mysticatea/abort-controller/issues/36
|
|
signal: controller.signal,
|
|
};
|
|
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
// NOTE(@krystofwoldrich) Readable.toWeb breaks the stream in Vercel Functions, unknown why.
|
|
// No error is thrown, but reading the stream like `await req.json()` never resolves.
|
|
init.body = (0, createReadableStreamFromReadable_1.createReadableStreamFromReadable)(req);
|
|
init.duplex = 'half';
|
|
}
|
|
return new Request(url.href, init);
|
|
}
|
|
async function respond(res, expoRes) {
|
|
res.statusMessage = expoRes.statusText;
|
|
res.writeHead(expoRes.status, expoRes.statusText, [...expoRes.headers.entries()].flat());
|
|
if (expoRes.body) {
|
|
await (0, promises_1.pipeline)(node_stream_1.Readable.fromWeb(expoRes.body), res);
|
|
}
|
|
else {
|
|
res.end();
|
|
}
|
|
}
|
|
//# sourceMappingURL=vercel.js.map
|