@geostrategists/react-router-aws 2.2.0 → 2.3.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -0
- package/dist/index.cjs +352 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +661 -0
- package/dist/index.d.mts +583 -24
- package/dist/index.mjs +302 -305
- package/dist/index.mjs.map +1 -0
- package/package.json +43 -84
- package/biome.json +0 -47
- package/dist/index.d.ts +0 -102
- package/dist/index.js +0 -378
- package/test/alb.test.ts +0 -95
- package/test/apigw-v1.test.ts +0 -101
- package/test/apigw-v2.test.ts +0 -107
- package/test/function-url-streaming.test.ts +0 -107
- package/test/function-url.test.ts +0 -107
- package/test/lambda-stream/HttpResponseStream.ts +0 -34
- package/test/lambda-stream/ResponseStream.ts +0 -44
- package/test/lambda-stream/index.ts +0 -56
- package/test/setup.ts +0 -7
- package/test/utils.ts +0 -69
- package/vitest.config.ts +0 -12
package/README.md
CHANGED
|
@@ -60,6 +60,73 @@ export const handler = createAPIGatewayV2RequestHandler({
|
|
|
60
60
|
> It does not allow tree-shaking and will include all gateway adapters in your bundle.
|
|
61
61
|
> For optimal bundle size, always use the method specific to your gateway:
|
|
62
62
|
|
|
63
|
+
### Request host & CSRF (`getHost`)
|
|
64
|
+
|
|
65
|
+
React Router derives the host used for its built-in cross-origin (CSRF) check on
|
|
66
|
+
action requests from the constructed request URL (`new URL(request.url).host`),
|
|
67
|
+
comparing it against the incoming `Origin` header. It is therefore important that
|
|
68
|
+
the adapter builds that host from a source you trust.
|
|
69
|
+
|
|
70
|
+
By default the adapters build the host from the `x-forwarded-host` header
|
|
71
|
+
(falling back to the `host`/`Host` header). The resolved value is always
|
|
72
|
+
sanitized (invalid characters stripped, port validated) before use.
|
|
73
|
+
|
|
74
|
+
If the default forwarded host is not the host the browser actually sees, use the
|
|
75
|
+
`getHost` option to derive it yourself. The most common case is a **Lambda
|
|
76
|
+
Function URL behind CloudFront**: `event.requestContext.domainName` is always the
|
|
77
|
+
internal `*.lambda-url.<region>.on.aws` host (and cannot be overridden), and
|
|
78
|
+
CloudFront does not forward the viewer's host to the origin — the managed
|
|
79
|
+
`AllViewerExceptHostHeader` policy (recommended for Function URL / API Gateway
|
|
80
|
+
origins) sets `Host` to the origin domain. So you must forward the viewer host
|
|
81
|
+
yourself, typically with a CloudFront Function on the viewer request that copies
|
|
82
|
+
it into a header, then read that header via `getHost`:
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
// CloudFront Function (viewer request): copy the viewer host into a custom header.
|
|
86
|
+
// Always overwrite (or delete) it so a client can't spoof x-viewer-host.
|
|
87
|
+
function handler(event) {
|
|
88
|
+
var host = event.request.headers.host;
|
|
89
|
+
if (host) {
|
|
90
|
+
event.request.headers["x-viewer-host"] = { value: host.value };
|
|
91
|
+
} else {
|
|
92
|
+
delete event.request.headers["x-viewer-host"];
|
|
93
|
+
}
|
|
94
|
+
return event.request;
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
// lambda-handler.ts
|
|
100
|
+
import * as build from "virtual:react-router/server-build";
|
|
101
|
+
import { createFunctionURLRequestHandler } from "@geostrategists/react-router-aws";
|
|
102
|
+
|
|
103
|
+
export const handler = createFunctionURLRequestHandler({
|
|
104
|
+
build,
|
|
105
|
+
getHost: (event) => event.headers["x-viewer-host"],
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
(If you copy the viewer host into `x-forwarded-host` instead, the default already
|
|
110
|
+
picks it up and `getHost` isn't needed.)
|
|
111
|
+
|
|
112
|
+
`getHost` receives the (correctly typed) gateway event and may return a host
|
|
113
|
+
string, or `undefined`/`null` to fall back to the default `x-forwarded-host`
|
|
114
|
+
behavior. It is supported by all handlers (API Gateway v1, API Gateway v2, Lambda
|
|
115
|
+
Function URL buffered & streaming, and ALB). For example, to prefer the
|
|
116
|
+
API Gateway request-context domain name: `getHost: (event) => event.requestContext.domainName`.
|
|
117
|
+
|
|
118
|
+
> [!IMPORTANT]
|
|
119
|
+
> The default currently uses the `x-forwarded-host` header, which is
|
|
120
|
+
> **client-controlled** and can be spoofed. Because that host drives React
|
|
121
|
+
> Router's CSRF check, trusting it should be a deliberate choice. To make the
|
|
122
|
+
> safe option the default (and to align with the upstream
|
|
123
|
+
> `@react-router/architect` adapter), the default **will change to the
|
|
124
|
+
> AWS-provided, non-spoofable request-context domain name
|
|
125
|
+
> (`event.requestContext.domainName`) in the next major version**. After that,
|
|
126
|
+
> relying on `x-forwarded-host` (or any other forwarded header) becomes an
|
|
127
|
+
> explicit opt-in via `getHost`. Setups where the request-context host is not the
|
|
128
|
+
> browser-facing host (e.g. Function URLs behind CloudFront) must set `getHost`.
|
|
129
|
+
|
|
63
130
|
### Streaming support for Lambda Function URLs
|
|
64
131
|
|
|
65
132
|
React Router and React allow you to stream responses from the server to the client, reducing the TTFB (time to first byte)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @geostrategists/react-router-aws v2.3.0-rc.0
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) Geostrategists Consulting GmbH
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*
|
|
9
|
+
* @license MIT
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
12
|
+
let react_router = require("react-router");
|
|
13
|
+
let _react_router_node = require("@react-router/node");
|
|
14
|
+
let url = require("url");
|
|
15
|
+
//#region src/binaryTypes.ts
|
|
16
|
+
/**
|
|
17
|
+
* Common binary MIME types
|
|
18
|
+
* @see https://github.com/architect/functions/blob/45254fc1936a1794c185aac07e9889b241a2e5c6/src/http/helpers/binary-types.js
|
|
19
|
+
*/
|
|
20
|
+
const binaryTypes = [
|
|
21
|
+
"application/octet-stream",
|
|
22
|
+
"application/epub+zip",
|
|
23
|
+
"application/msword",
|
|
24
|
+
"application/pdf",
|
|
25
|
+
"application/rtf",
|
|
26
|
+
"application/vnd.amazon.ebook",
|
|
27
|
+
"application/vnd.ms-excel",
|
|
28
|
+
"application/vnd.ms-powerpoint",
|
|
29
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
30
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
31
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
32
|
+
"font/otf",
|
|
33
|
+
"font/woff",
|
|
34
|
+
"font/woff2",
|
|
35
|
+
"image/avif",
|
|
36
|
+
"image/bmp",
|
|
37
|
+
"image/gif",
|
|
38
|
+
"image/jpeg",
|
|
39
|
+
"image/png",
|
|
40
|
+
"image/tiff",
|
|
41
|
+
"image/vnd.microsoft.icon",
|
|
42
|
+
"image/webp",
|
|
43
|
+
"audio/3gpp",
|
|
44
|
+
"audio/aac",
|
|
45
|
+
"audio/basic",
|
|
46
|
+
"audio/mpeg",
|
|
47
|
+
"audio/ogg",
|
|
48
|
+
"audio/wav",
|
|
49
|
+
"audio/webm",
|
|
50
|
+
"audio/x-aiff",
|
|
51
|
+
"audio/x-midi",
|
|
52
|
+
"audio/x-wav",
|
|
53
|
+
"video/3gpp",
|
|
54
|
+
"video/mp2t",
|
|
55
|
+
"video/mpeg",
|
|
56
|
+
"video/ogg",
|
|
57
|
+
"video/quicktime",
|
|
58
|
+
"video/webm",
|
|
59
|
+
"video/x-msvideo",
|
|
60
|
+
"application/java-archive",
|
|
61
|
+
"application/vnd.apple.installer+xml",
|
|
62
|
+
"application/x-7z-compressed",
|
|
63
|
+
"application/x-apple-diskimage",
|
|
64
|
+
"application/x-bzip",
|
|
65
|
+
"application/x-bzip2",
|
|
66
|
+
"application/x-gzip",
|
|
67
|
+
"application/x-java-archive",
|
|
68
|
+
"application/x-rar-compressed",
|
|
69
|
+
"application/x-tar",
|
|
70
|
+
"application/x-zip",
|
|
71
|
+
"application/zip"
|
|
72
|
+
];
|
|
73
|
+
function isBinaryType(contentType) {
|
|
74
|
+
if (!contentType) return false;
|
|
75
|
+
const [test] = contentType.split(";");
|
|
76
|
+
return binaryTypes.includes(test);
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/adapters/host.ts
|
|
80
|
+
/**
|
|
81
|
+
* Resolves the host used to build the `Request` URL from a raw host value
|
|
82
|
+
* (e.g. an `x-forwarded-host`/`host` header or an API Gateway domain name).
|
|
83
|
+
*
|
|
84
|
+
* React Router derives the host for its built-in cross-origin (CSRF) check on
|
|
85
|
+
* action requests from `new URL(request.url).host`, so the host constructed
|
|
86
|
+
* here must be trustworthy and well-formed. Invalid characters are stripped and
|
|
87
|
+
* the port is validated to avoid constructing a malformed URL (or leaking a
|
|
88
|
+
* spoofed host) from a garbled header value.
|
|
89
|
+
*
|
|
90
|
+
* Mirrors the hardening done by the upstream `@react-router/architect` adapter.
|
|
91
|
+
*/
|
|
92
|
+
function resolveHost(rawHost) {
|
|
93
|
+
const [rawHostname = "", portStr] = (rawHost ?? "").split(":");
|
|
94
|
+
const hostname = rawHostname.split(/[\\/?#@]/)[0] || "localhost";
|
|
95
|
+
const hostPort = Number.parseInt(portStr ?? "", 10);
|
|
96
|
+
const port = Number.isSafeInteger(hostPort) ? hostPort : void 0;
|
|
97
|
+
return `${hostname}${port ? `:${port}` : ""}`;
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/adapters/api-gateway-v1.ts
|
|
101
|
+
function createReactRouterRequestAPIGatewayV1(event, getHost) {
|
|
102
|
+
const host = resolveHost(getHost?.(event) ?? (event.headers["x-forwarded-host"] || event.headers.Host));
|
|
103
|
+
const scheme = event.headers["x-forwarded-proto"] || "http";
|
|
104
|
+
const rawQueryString = new url.URLSearchParams(event.queryStringParameters).toString();
|
|
105
|
+
const search = rawQueryString.length > 0 ? `?${rawQueryString}` : "";
|
|
106
|
+
const url$2 = new URL(event.path + search, `${scheme}://${host}`);
|
|
107
|
+
const isFormData = event.headers["content-type"]?.includes("multipart/form-data");
|
|
108
|
+
const controller = new AbortController();
|
|
109
|
+
return new Request(url$2.href, {
|
|
110
|
+
method: event.requestContext.httpMethod,
|
|
111
|
+
headers: createReactRouterHeadersAPIGatewayV1(event.headers),
|
|
112
|
+
signal: controller.signal,
|
|
113
|
+
body: event.body && event.isBase64Encoded ? isFormData ? Buffer.from(event.body, "base64") : Buffer.from(event.body, "base64").toString() : event.body || void 0
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function createReactRouterHeadersAPIGatewayV1(requestHeaders) {
|
|
117
|
+
const headers = new Headers();
|
|
118
|
+
for (const [header, value] of Object.entries(requestHeaders)) if (value) headers.append(header, value);
|
|
119
|
+
return headers;
|
|
120
|
+
}
|
|
121
|
+
async function sendReactRouterResponseAPIGatewayV1(nodeResponse) {
|
|
122
|
+
const isBase64Encoded = isBinaryType(nodeResponse.headers.get("Content-Type"));
|
|
123
|
+
let body;
|
|
124
|
+
if (nodeResponse.body) if (isBase64Encoded) body = await (0, _react_router_node.readableStreamToString)(nodeResponse.body, "base64");
|
|
125
|
+
else body = await nodeResponse.text();
|
|
126
|
+
return {
|
|
127
|
+
statusCode: nodeResponse.status,
|
|
128
|
+
headers: Object.fromEntries(nodeResponse.headers.entries()),
|
|
129
|
+
body: body || "",
|
|
130
|
+
isBase64Encoded
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const apiGatewayV1Adapter = {
|
|
134
|
+
wrapHandler: (handler) => (e) => handler(e),
|
|
135
|
+
createReactRouterRequest: createReactRouterRequestAPIGatewayV1,
|
|
136
|
+
sendReactRouterResponse: sendReactRouterResponseAPIGatewayV1
|
|
137
|
+
};
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/adapters/api-gateway-v2.ts
|
|
140
|
+
function createReactRouterRequestAPIGateywayV2(event, getHost) {
|
|
141
|
+
const host = resolveHost(getHost?.(event) ?? (event.headers["x-forwarded-host"] || event.headers.host));
|
|
142
|
+
const search = event.rawQueryString.length ? `?${event.rawQueryString}` : "";
|
|
143
|
+
const scheme = event.headers["x-forwarded-proto"] || "http";
|
|
144
|
+
const url = new URL(event.rawPath + search, `${scheme}://${host}`);
|
|
145
|
+
const isFormData = event.headers["content-type"]?.includes("multipart/form-data");
|
|
146
|
+
const controller = new AbortController();
|
|
147
|
+
return new Request(url.href, {
|
|
148
|
+
method: event.requestContext.http.method,
|
|
149
|
+
headers: createReactRouterHeadersAPIGatewayV2(event.headers, event.cookies),
|
|
150
|
+
signal: controller.signal,
|
|
151
|
+
body: event.body && event.isBase64Encoded ? isFormData ? Buffer.from(event.body, "base64") : Buffer.from(event.body, "base64").toString() : event.body
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
function createReactRouterHeadersAPIGatewayV2(requestHeaders, requestCookies) {
|
|
155
|
+
const headers = new Headers();
|
|
156
|
+
for (const [header, value] of Object.entries(requestHeaders)) if (value) headers.append(header, value);
|
|
157
|
+
if (requestCookies) headers.append("Cookie", requestCookies.join("; "));
|
|
158
|
+
return headers;
|
|
159
|
+
}
|
|
160
|
+
function extractAPIGatewayV2ResponseMetadata(nodeResponse) {
|
|
161
|
+
const cookies = nodeResponse.headers.getSetCookie();
|
|
162
|
+
if (cookies.length) nodeResponse.headers.delete("Set-Cookie");
|
|
163
|
+
return {
|
|
164
|
+
statusCode: nodeResponse.status,
|
|
165
|
+
headers: Object.fromEntries(nodeResponse.headers.entries()),
|
|
166
|
+
cookies
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
async function sendReactRouterResponseAPIGatewayV2(nodeResponse) {
|
|
170
|
+
const result = extractAPIGatewayV2ResponseMetadata(nodeResponse);
|
|
171
|
+
result.isBase64Encoded = isBinaryType(nodeResponse.headers.get("Content-Type"));
|
|
172
|
+
if (nodeResponse.body) if (result.isBase64Encoded) result.body = await (0, _react_router_node.readableStreamToString)(nodeResponse.body, "base64");
|
|
173
|
+
else result.body = await nodeResponse.text();
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
const apiGatewayV2Adapter = {
|
|
177
|
+
wrapHandler: (handler) => (e) => handler(e),
|
|
178
|
+
createReactRouterRequest: createReactRouterRequestAPIGateywayV2,
|
|
179
|
+
sendReactRouterResponse: sendReactRouterResponseAPIGatewayV2
|
|
180
|
+
};
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/adapters/application-load-balancer.ts
|
|
183
|
+
function createReactRouterRequestALB(event, getHost) {
|
|
184
|
+
const headers = event?.headers || {};
|
|
185
|
+
const host = resolveHost(getHost?.(event) ?? (headers["x-forwarded-host"] || headers.Host));
|
|
186
|
+
const scheme = headers["x-forwarded-proto"] || "http";
|
|
187
|
+
const rawQueryString = new url.URLSearchParams(event.queryStringParameters).toString();
|
|
188
|
+
const search = rawQueryString.length > 0 ? `?${rawQueryString}` : "";
|
|
189
|
+
const url$1 = new URL(event.path + search, `${scheme}://${host}`);
|
|
190
|
+
const isFormData = headers["content-type"]?.includes("multipart/form-data");
|
|
191
|
+
const controller = new AbortController();
|
|
192
|
+
return new Request(url$1.href, {
|
|
193
|
+
method: event.httpMethod,
|
|
194
|
+
headers: createReactRouterHeadersALB(headers),
|
|
195
|
+
signal: controller.signal,
|
|
196
|
+
body: event.body && event.isBase64Encoded ? isFormData ? Buffer.from(event.body, "base64") : Buffer.from(event.body, "base64").toString() : event.body || void 0
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
function createReactRouterHeadersALB(requestHeaders) {
|
|
200
|
+
const headers = new Headers();
|
|
201
|
+
for (const [header, value] of Object.entries(requestHeaders)) if (value) headers.append(header, value);
|
|
202
|
+
return headers;
|
|
203
|
+
}
|
|
204
|
+
async function sendReactRouterResponseALB(nodeResponse) {
|
|
205
|
+
const isBase64Encoded = isBinaryType(nodeResponse.headers.get("Content-Type"));
|
|
206
|
+
let body;
|
|
207
|
+
if (nodeResponse.body) if (isBase64Encoded) body = await (0, _react_router_node.readableStreamToString)(nodeResponse.body, "base64");
|
|
208
|
+
else body = await nodeResponse.text();
|
|
209
|
+
return {
|
|
210
|
+
statusCode: nodeResponse.status,
|
|
211
|
+
headers: Object.fromEntries(nodeResponse.headers.entries()),
|
|
212
|
+
body: body || "",
|
|
213
|
+
isBase64Encoded
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const applicationLoadBalancerAdapter = {
|
|
217
|
+
wrapHandler: (handler) => (e) => handler(e),
|
|
218
|
+
createReactRouterRequest: createReactRouterRequestALB,
|
|
219
|
+
sendReactRouterResponse: sendReactRouterResponseALB
|
|
220
|
+
};
|
|
221
|
+
//#endregion
|
|
222
|
+
//#region src/adapters/function-url-streaming.ts
|
|
223
|
+
const emptyStream = () => new ReadableStream({ start(controller) {
|
|
224
|
+
controller.enqueue("");
|
|
225
|
+
controller.close();
|
|
226
|
+
} });
|
|
227
|
+
const sendReactRouterResponseFunctionUrlStreaming = async (response, responseStream) => {
|
|
228
|
+
const metadata = extractAPIGatewayV2ResponseMetadata(response);
|
|
229
|
+
let body = response.body;
|
|
230
|
+
if (!body) body = emptyStream();
|
|
231
|
+
const httpResponseStream = awslambda.HttpResponseStream.from(responseStream, metadata);
|
|
232
|
+
await (0, _react_router_node.writeReadableStreamToWritable)(body, httpResponseStream);
|
|
233
|
+
};
|
|
234
|
+
const functionUrlStreamingAdapter = {
|
|
235
|
+
wrapHandler: awslambda.streamifyResponse,
|
|
236
|
+
createReactRouterRequest: createReactRouterRequestAPIGateywayV2,
|
|
237
|
+
sendReactRouterResponse: sendReactRouterResponseFunctionUrlStreaming
|
|
238
|
+
};
|
|
239
|
+
//#endregion
|
|
240
|
+
//#region src/server.ts
|
|
241
|
+
/**
|
|
242
|
+
* Returns a request handler for AWS API Gateway V1
|
|
243
|
+
*
|
|
244
|
+
*/
|
|
245
|
+
/**
|
|
246
|
+
* Returns a request handler for AWS API Gateway V1 events.
|
|
247
|
+
*
|
|
248
|
+
* @param options - The handler options, including the React Router server build,
|
|
249
|
+
* optional getLoadContext function, and mode string.
|
|
250
|
+
* @returns An AWS API Gateway V1 handler compatible with APIGatewayProxyHandler.
|
|
251
|
+
*/
|
|
252
|
+
function createAPIGatewayV1RequestHandler(options) {
|
|
253
|
+
return createRequestHandlerForAdapter(apiGatewayV1Adapter, options);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Returns a request handler for AWS API Gateway V2 events.
|
|
257
|
+
*
|
|
258
|
+
* @param options - The handler options, including the React Router server build,
|
|
259
|
+
* optional getLoadContext function, and mode string.
|
|
260
|
+
* @returns An AWS API Gateway V2 handler compatible with APIGatewayProxyHandlerV2.
|
|
261
|
+
*/
|
|
262
|
+
function createAPIGatewayV2RequestHandler(options) {
|
|
263
|
+
return createRequestHandlerForAdapter(apiGatewayV2Adapter, options);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Returns a request handler for AWS Application Load Balancer events.
|
|
267
|
+
*
|
|
268
|
+
* @param options - The handler options, including the React Router server build,
|
|
269
|
+
* optional getLoadContext function, and mode string.
|
|
270
|
+
* @returns An AWS ALB handler compatible with ALBHandler.
|
|
271
|
+
*/
|
|
272
|
+
function createALBRequestHandler(options) {
|
|
273
|
+
return createRequestHandlerForAdapter(applicationLoadBalancerAdapter, options);
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Returns a request handler for AWS Lambda Function URL events (invoke mode BUFFERED).
|
|
277
|
+
*
|
|
278
|
+
* @param options - The handler options, including the React Router server build,
|
|
279
|
+
* optional getLoadContext function, and mode string.
|
|
280
|
+
* @returns An AWS Lambda Function URL handler compatible with Lambda Function URLs with InvokeMode BUFFERED.
|
|
281
|
+
*/
|
|
282
|
+
function createFunctionURLRequestHandler(options) {
|
|
283
|
+
return createRequestHandlerForAdapter(apiGatewayV2Adapter, options);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Returns a request handler for AWS Lambda Function URL events (invoke mode RESPONSE_STREAM).
|
|
287
|
+
*
|
|
288
|
+
* @param options - The handler options, including the React Router server build,
|
|
289
|
+
* optional getLoadContext function, and mode string.
|
|
290
|
+
* @returns A streaming AWS Lambda Function URL handler compatible with Lambda Function URLs with InvokeMode RESPONSE_STREAM.
|
|
291
|
+
*/
|
|
292
|
+
function createFunctionURLStreamingRequestHandler(options) {
|
|
293
|
+
return createRequestHandlerForAdapter(functionUrlStreamingAdapter, options);
|
|
294
|
+
}
|
|
295
|
+
function createRequestHandlerForAdapter(awsAdapter, { build, getLoadContext, mode = process.env.NODE_ENV, getHost }) {
|
|
296
|
+
const handleRequest = (0, react_router.createRequestHandler)(build, mode);
|
|
297
|
+
return awsAdapter.wrapHandler(async (event, res) => {
|
|
298
|
+
let request;
|
|
299
|
+
try {
|
|
300
|
+
request = awsAdapter.createReactRouterRequest(event, getHost);
|
|
301
|
+
} catch (e) {
|
|
302
|
+
return await awsAdapter.sendReactRouterResponse(new Response(`Bad Request: ${e instanceof Error ? e.message : e}`, { status: 400 }), res);
|
|
303
|
+
}
|
|
304
|
+
const loadContext = await getLoadContext?.(event);
|
|
305
|
+
const response = await handleRequest(request, loadContext);
|
|
306
|
+
return await awsAdapter.sendReactRouterResponse(response, res);
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
//#endregion
|
|
310
|
+
//#region src/legacy.ts
|
|
311
|
+
let AWSProxy = /* @__PURE__ */ function(AWSProxy) {
|
|
312
|
+
AWSProxy["APIGatewayV1"] = "APIGatewayV1";
|
|
313
|
+
AWSProxy["APIGatewayV2"] = "APIGatewayV2";
|
|
314
|
+
AWSProxy["ALB"] = "ALB";
|
|
315
|
+
AWSProxy["FunctionURL"] = "FunctionURL";
|
|
316
|
+
AWSProxy["FunctionURLStreaming"] = "FunctionURLStreaming";
|
|
317
|
+
return AWSProxy;
|
|
318
|
+
}({});
|
|
319
|
+
/**
|
|
320
|
+
* Returns a request handler for AWS that serves the response using React Router.
|
|
321
|
+
*
|
|
322
|
+
* @deprecated Use one of the gateway-specific create*RequestHandler methods instead for better tree-shaking.
|
|
323
|
+
* - `createAPIGatewayV1RequestHandler`
|
|
324
|
+
* - `createAPIGatewayV2RequestHandler`
|
|
325
|
+
* - `createALBRequestHandler`
|
|
326
|
+
* - `createFunctionURLRequestHandler`
|
|
327
|
+
* - `createFunctionURLStreamingRequestHandler`
|
|
328
|
+
*/
|
|
329
|
+
function createRequestHandler(options) {
|
|
330
|
+
const { awsProxy = "APIGatewayV2", ...opts } = options;
|
|
331
|
+
switch (awsProxy) {
|
|
332
|
+
case "APIGatewayV1": return createAPIGatewayV1RequestHandler(opts);
|
|
333
|
+
case "APIGatewayV2": return createAPIGatewayV2RequestHandler(opts);
|
|
334
|
+
case "ALB": return createALBRequestHandler(opts);
|
|
335
|
+
case "FunctionURL": return createFunctionURLRequestHandler(opts);
|
|
336
|
+
case "FunctionURLStreaming": return createFunctionURLStreamingRequestHandler(opts);
|
|
337
|
+
default: return assertNever(awsProxy, `Unsupported buffered AWS Proxy type: ${awsProxy}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function assertNever(_x, message) {
|
|
341
|
+
throw new Error(message);
|
|
342
|
+
}
|
|
343
|
+
//#endregion
|
|
344
|
+
exports.AWSProxy = AWSProxy;
|
|
345
|
+
exports.createALBRequestHandler = createALBRequestHandler;
|
|
346
|
+
exports.createAPIGatewayV1RequestHandler = createAPIGatewayV1RequestHandler;
|
|
347
|
+
exports.createAPIGatewayV2RequestHandler = createAPIGatewayV2RequestHandler;
|
|
348
|
+
exports.createFunctionURLRequestHandler = createFunctionURLRequestHandler;
|
|
349
|
+
exports.createFunctionURLStreamingRequestHandler = createFunctionURLStreamingRequestHandler;
|
|
350
|
+
exports.createRequestHandler = createRequestHandler;
|
|
351
|
+
|
|
352
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["URLSearchParams","url","URLSearchParams","url"],"sources":["../src/binaryTypes.ts","../src/adapters/host.ts","../src/adapters/api-gateway-v1.ts","../src/adapters/api-gateway-v2.ts","../src/adapters/application-load-balancer.ts","../src/adapters/function-url-streaming.ts","../src/server.ts","../src/legacy.ts"],"sourcesContent":["// TODO: check if we can replace this with https://www.npmjs.com/package/mime-types\n\n/**\n * Common binary MIME types\n * @see https://github.com/architect/functions/blob/45254fc1936a1794c185aac07e9889b241a2e5c6/src/http/helpers/binary-types.js\n */\nconst binaryTypes = [\n \"application/octet-stream\",\n // Docs\n \"application/epub+zip\",\n \"application/msword\",\n \"application/pdf\",\n \"application/rtf\",\n \"application/vnd.amazon.ebook\",\n \"application/vnd.ms-excel\",\n \"application/vnd.ms-powerpoint\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n // Fonts\n \"font/otf\",\n \"font/woff\",\n \"font/woff2\",\n // Images\n \"image/avif\",\n \"image/bmp\",\n \"image/gif\",\n \"image/jpeg\",\n \"image/png\",\n \"image/tiff\",\n \"image/vnd.microsoft.icon\",\n \"image/webp\",\n // Audio\n \"audio/3gpp\",\n \"audio/aac\",\n \"audio/basic\",\n \"audio/mpeg\",\n \"audio/ogg\",\n \"audio/wav\",\n \"audio/webm\",\n \"audio/x-aiff\",\n \"audio/x-midi\",\n \"audio/x-wav\",\n // Video\n \"video/3gpp\",\n \"video/mp2t\",\n \"video/mpeg\",\n \"video/ogg\",\n \"video/quicktime\",\n \"video/webm\",\n \"video/x-msvideo\",\n // Archives\n \"application/java-archive\",\n \"application/vnd.apple.installer+xml\",\n \"application/x-7z-compressed\",\n \"application/x-apple-diskimage\",\n \"application/x-bzip\",\n \"application/x-bzip2\",\n \"application/x-gzip\",\n \"application/x-java-archive\",\n \"application/x-rar-compressed\",\n \"application/x-tar\",\n \"application/x-zip\",\n \"application/zip\",\n];\n\nexport function isBinaryType(contentType: string | null | undefined): boolean {\n if (!contentType) {\n return false;\n }\n const [test] = contentType.split(\";\");\n return binaryTypes.includes(test);\n}\n","/**\n * Resolves the host used to build the `Request` URL from a raw host value\n * (e.g. an `x-forwarded-host`/`host` header or an API Gateway domain name).\n *\n * React Router derives the host for its built-in cross-origin (CSRF) check on\n * action requests from `new URL(request.url).host`, so the host constructed\n * here must be trustworthy and well-formed. Invalid characters are stripped and\n * the port is validated to avoid constructing a malformed URL (or leaking a\n * spoofed host) from a garbled header value.\n *\n * Mirrors the hardening done by the upstream `@react-router/architect` adapter.\n */\nexport function resolveHost(rawHost: string | undefined | null): string {\n const [rawHostname = \"\", portStr] = (rawHost ?? \"\").split(\":\");\n const hostname = rawHostname.split(/[\\\\/?#@]/)[0] || \"localhost\";\n const hostPort = Number.parseInt(portStr ?? \"\", 10);\n const port = Number.isSafeInteger(hostPort) ? hostPort : undefined;\n return `${hostname}${port ? `:${port}` : \"\"}`;\n}\n","import { readableStreamToString } from \"@react-router/node\";\nimport type { APIGatewayProxyEvent, APIGatewayProxyEventHeaders, APIGatewayProxyResult } from \"aws-lambda\";\nimport { URLSearchParams } from \"url\";\n\nimport { isBinaryType } from \"../binaryTypes\";\nimport { resolveHost } from \"./host\";\nimport type { GetHostFunction, ReactRouterAdapter } from \"./index\";\n\nfunction createReactRouterRequestAPIGatewayV1(\n event: APIGatewayProxyEvent,\n getHost?: GetHostFunction<APIGatewayProxyEvent>,\n): Request {\n const rawHost = getHost?.(event) ?? (event.headers[\"x-forwarded-host\"] || event.headers.Host);\n const host = resolveHost(rawHost);\n const scheme = event.headers[\"x-forwarded-proto\"] || \"http\";\n\n const rawQueryString = new URLSearchParams(event.queryStringParameters as Record<string, string>).toString();\n const search = rawQueryString.length > 0 ? `?${rawQueryString}` : \"\";\n const url = new URL(event.path + search, `${scheme}://${host}`);\n\n const isFormData = event.headers[\"content-type\"]?.includes(\"multipart/form-data\");\n // Note: No current way to abort these for AWS, but our router expects\n // requests to contain a signal, so it can detect aborted requests\n const controller = new AbortController();\n\n return new Request(url.href, {\n method: event.requestContext.httpMethod,\n headers: createReactRouterHeadersAPIGatewayV1(event.headers),\n signal: controller.signal,\n body:\n event.body && event.isBase64Encoded\n ? isFormData\n ? Buffer.from(event.body, \"base64\")\n : Buffer.from(event.body, \"base64\").toString()\n : event.body || undefined,\n });\n}\n\nfunction createReactRouterHeadersAPIGatewayV1(requestHeaders: APIGatewayProxyEventHeaders): Headers {\n const headers = new Headers();\n\n for (const [header, value] of Object.entries(requestHeaders)) {\n if (value) {\n headers.append(header, value);\n }\n }\n\n return headers;\n}\n\nasync function sendReactRouterResponseAPIGatewayV1(nodeResponse: Response): Promise<APIGatewayProxyResult> {\n const contentType = nodeResponse.headers.get(\"Content-Type\");\n const isBase64Encoded = isBinaryType(contentType);\n let body: string | undefined;\n\n if (nodeResponse.body) {\n if (isBase64Encoded) {\n body = await readableStreamToString(nodeResponse.body, \"base64\");\n } else {\n body = await nodeResponse.text();\n }\n }\n\n return {\n statusCode: nodeResponse.status,\n headers: Object.fromEntries(nodeResponse.headers.entries()),\n body: body || \"\",\n isBase64Encoded,\n };\n}\n\nexport type ApiGatewayV1Adapter = ReactRouterAdapter<APIGatewayProxyEvent, APIGatewayProxyResult>;\n\nexport const apiGatewayV1Adapter: ApiGatewayV1Adapter = {\n wrapHandler: (handler) => (e) => handler(e),\n createReactRouterRequest: createReactRouterRequestAPIGatewayV1,\n sendReactRouterResponse: sendReactRouterResponseAPIGatewayV1,\n};\n","import { readableStreamToString } from \"@react-router/node\";\nimport type {\n APIGatewayProxyEventHeaders,\n APIGatewayProxyEventV2,\n APIGatewayProxyStructuredResultV2,\n} from \"aws-lambda\";\n\nimport { isBinaryType } from \"../binaryTypes\";\nimport { resolveHost } from \"./host\";\nimport type { GetHostFunction, ReactRouterAdapter } from \"./index\";\n\nexport function createReactRouterRequestAPIGateywayV2(\n event: APIGatewayProxyEventV2,\n getHost?: GetHostFunction<APIGatewayProxyEventV2>,\n): Request {\n const rawHost = getHost?.(event) ?? (event.headers[\"x-forwarded-host\"] || event.headers.host);\n const host = resolveHost(rawHost);\n const search = event.rawQueryString.length ? `?${event.rawQueryString}` : \"\";\n const scheme = event.headers[\"x-forwarded-proto\"] || \"http\";\n\n const url = new URL(event.rawPath + search, `${scheme}://${host}`);\n const isFormData = event.headers[\"content-type\"]?.includes(\"multipart/form-data\");\n // Note: No current way to abort these for AWS, but our router expects\n // requests to contain a signal, so it can detect aborted requests\n const controller = new AbortController();\n\n return new Request(url.href, {\n method: event.requestContext.http.method,\n headers: createReactRouterHeadersAPIGatewayV2(event.headers, event.cookies),\n signal: controller.signal,\n body:\n event.body && event.isBase64Encoded\n ? isFormData\n ? Buffer.from(event.body, \"base64\")\n : Buffer.from(event.body, \"base64\").toString()\n : event.body,\n });\n}\n\nfunction createReactRouterHeadersAPIGatewayV2(\n requestHeaders: APIGatewayProxyEventHeaders,\n requestCookies?: string[],\n): Headers {\n const headers = new Headers();\n\n for (const [header, value] of Object.entries(requestHeaders)) {\n if (value) {\n headers.append(header, value);\n }\n }\n\n if (requestCookies) {\n headers.append(\"Cookie\", requestCookies.join(\"; \"));\n }\n\n return headers;\n}\n\nexport function extractAPIGatewayV2ResponseMetadata(nodeResponse: Response): {\n statusCode: number;\n headers: Record<string, string>;\n cookies: string[];\n} {\n // AWS API Gateway will send back set-cookies outside of response headers.\n const cookies = nodeResponse.headers.getSetCookie();\n if (cookies.length) {\n nodeResponse.headers.delete(\"Set-Cookie\");\n }\n\n return {\n statusCode: nodeResponse.status,\n headers: Object.fromEntries(nodeResponse.headers.entries()),\n cookies,\n };\n}\n\nasync function sendReactRouterResponseAPIGatewayV2(nodeResponse: Response): Promise<APIGatewayProxyStructuredResultV2> {\n const result: APIGatewayProxyStructuredResultV2 = extractAPIGatewayV2ResponseMetadata(nodeResponse);\n\n const contentType = nodeResponse.headers.get(\"Content-Type\");\n\n result.isBase64Encoded = isBinaryType(contentType);\n\n if (nodeResponse.body) {\n if (result.isBase64Encoded) {\n result.body = await readableStreamToString(nodeResponse.body, \"base64\");\n } else {\n result.body = await nodeResponse.text();\n }\n }\n\n return result;\n}\n\nexport type ApiGatewayV2Adapter = ReactRouterAdapter<APIGatewayProxyEventV2, APIGatewayProxyStructuredResultV2>;\n\nexport const apiGatewayV2Adapter: ApiGatewayV2Adapter = {\n wrapHandler: (handler) => (e) => handler(e),\n createReactRouterRequest: createReactRouterRequestAPIGateywayV2,\n sendReactRouterResponse: sendReactRouterResponseAPIGatewayV2,\n};\n","import { readableStreamToString } from \"@react-router/node\";\nimport type { ALBEvent, ALBEventHeaders, ALBResult } from \"aws-lambda\";\nimport { URLSearchParams } from \"url\";\n\nimport { isBinaryType } from \"../binaryTypes\";\nimport { resolveHost } from \"./host\";\nimport type { GetHostFunction, ReactRouterAdapter } from \"./index\";\n\nfunction createReactRouterRequestALB(event: ALBEvent, getHost?: GetHostFunction<ALBEvent>): Request {\n const headers = event?.headers || {};\n const host = resolveHost(getHost?.(event) ?? (headers[\"x-forwarded-host\"] || headers.Host));\n const scheme = headers[\"x-forwarded-proto\"] || \"http\";\n\n const rawQueryString = new URLSearchParams(event.queryStringParameters as Record<string, string>).toString();\n const search = rawQueryString.length > 0 ? `?${rawQueryString}` : \"\";\n const url = new URL(event.path + search, `${scheme}://${host}`);\n\n const isFormData = headers[\"content-type\"]?.includes(\"multipart/form-data\");\n\n // Note: No current way to abort these for AWS, but our router expects\n // requests to contain a signal, so it can detect aborted requests\n const controller = new AbortController();\n\n return new Request(url.href, {\n method: event.httpMethod,\n headers: createReactRouterHeadersALB(headers),\n signal: controller.signal,\n body:\n event.body && event.isBase64Encoded\n ? isFormData\n ? Buffer.from(event.body, \"base64\")\n : Buffer.from(event.body, \"base64\").toString()\n : event.body || undefined,\n });\n}\n\nfunction createReactRouterHeadersALB(requestHeaders: ALBEventHeaders): Headers {\n const headers = new Headers();\n\n for (const [header, value] of Object.entries(requestHeaders)) {\n if (value) {\n headers.append(header, value);\n }\n }\n\n return headers;\n}\n\nasync function sendReactRouterResponseALB(nodeResponse: Response): Promise<ALBResult> {\n const contentType = nodeResponse.headers.get(\"Content-Type\");\n const isBase64Encoded = isBinaryType(contentType);\n let body: string | undefined;\n\n if (nodeResponse.body) {\n if (isBase64Encoded) {\n body = await readableStreamToString(nodeResponse.body, \"base64\");\n } else {\n body = await nodeResponse.text();\n }\n }\n\n return {\n statusCode: nodeResponse.status,\n headers: Object.fromEntries(nodeResponse.headers.entries()),\n body: body || \"\",\n isBase64Encoded,\n };\n}\n\nexport type ApplicationLoadBalancerAdapter = ReactRouterAdapter<ALBEvent, ALBResult>;\n\nexport const applicationLoadBalancerAdapter: ApplicationLoadBalancerAdapter = {\n wrapHandler: (handler) => (e) => handler(e),\n createReactRouterRequest: createReactRouterRequestALB,\n sendReactRouterResponse: sendReactRouterResponseALB,\n};\n","import { writeReadableStreamToWritable } from \"@react-router/node\";\nimport type { LambdaFunctionURLEvent } from \"aws-lambda\";\nimport type { StreamifyHandler } from \"aws-lambda/handler\";\n\nimport { createReactRouterRequestAPIGateywayV2, extractAPIGatewayV2ResponseMetadata } from \"./api-gateway-v2\";\nimport type { ReactRouterAdapter } from \"./index\";\n\nconst emptyStream = () =>\n new ReadableStream({\n start(controller) {\n controller.enqueue(\"\");\n controller.close();\n },\n });\n\nconst sendReactRouterResponseFunctionUrlStreaming = async (\n response: Response,\n responseStream: awslambda.HttpResponseStream,\n) => {\n const metadata = extractAPIGatewayV2ResponseMetadata(response);\n\n let body = response.body;\n if (!body) {\n // Function URL needs a write to happen on the stream, otherwise it won't send headers.\n // See https://github.com/fastify/aws-lambda-fastify/issues/154#issuecomment-2614521719\n body = emptyStream();\n }\n\n const httpResponseStream = awslambda.HttpResponseStream.from(responseStream, metadata);\n\n await writeReadableStreamToWritable(body, httpResponseStream);\n};\n\nexport type FunctionUrlStreamingAdapter = ReactRouterAdapter<\n LambdaFunctionURLEvent,\n void,\n awslambda.HttpResponseStream,\n StreamifyHandler<LambdaFunctionURLEvent, void>\n>;\n\nexport const functionUrlStreamingAdapter: FunctionUrlStreamingAdapter = {\n wrapHandler: awslambda.streamifyResponse,\n createReactRouterRequest: createReactRouterRequestAPIGateywayV2,\n sendReactRouterResponse: sendReactRouterResponseFunctionUrlStreaming,\n};\n","import type {\n ALBEvent,\n ALBHandler,\n APIGatewayProxyEvent,\n APIGatewayProxyEventV2,\n APIGatewayProxyHandler,\n APIGatewayProxyHandlerV2,\n LambdaFunctionURLEvent,\n LambdaFunctionURLHandler,\n} from \"aws-lambda\";\nimport type { StreamifyHandler } from \"aws-lambda/handler\";\nimport {\n type AppLoadContext,\n createRequestHandler as createReactRouterRequestHandler,\n type RouterContextProvider,\n type ServerBuild,\n type UNSAFE_MiddlewareEnabled as MiddlewareEnabled,\n} from \"react-router\";\n\nimport type { GetHostFunction, ReactRouterAdapter } from \"./adapters\";\nimport { apiGatewayV1Adapter } from \"./adapters/api-gateway-v1\";\nimport { apiGatewayV2Adapter } from \"./adapters/api-gateway-v2\";\nimport { applicationLoadBalancerAdapter } from \"./adapters/application-load-balancer\";\nimport { functionUrlStreamingAdapter } from \"./adapters/function-url-streaming\";\n\ntype MaybePromise<T> = T | Promise<T>;\n\n/**\n * A function that returns the value to use as `context` in route `loader` and\n * `action` functions.\n *\n * You can think of this as an escape hatch that allows you to pass\n * environment/platform-specific values through to your loader/action.\n */\nexport type GetLoadContextFunction<E> = (\n event: E,\n) => MiddlewareEnabled extends true ? MaybePromise<RouterContextProvider> : MaybePromise<AppLoadContext>;\n\nexport type CreateRequestHandlerArgs<T> = {\n build: ServerBuild;\n getLoadContext?: GetLoadContextFunction<T>;\n mode?: string;\n /**\n * Override how the host for the request URL is derived from the Lambda event.\n *\n * React Router uses the request URL host (`new URL(request.url).host`) for its\n * built-in cross-origin (CSRF) check on action requests, comparing it against\n * the incoming `Origin` header. The returned value is sanitized (invalid\n * characters stripped, port validated) before use.\n *\n * Return `undefined`/`null` to fall back to the default: the\n * `x-forwarded-host` header (falling back to the `host`/`Host` header).\n *\n * Use this when the default forwarded host is not the host the browser sees.\n * For example, a Lambda Function URL behind CloudFront cannot use its\n * request-context domain name (always the internal `*.lambda-url` host), and\n * CloudFront does not forward the viewer host by default. Forward it yourself\n * (e.g. a CloudFront Function copying the viewer `Host` into a custom header)\n * and read that header here:\n *\n * ```ts\n * createFunctionURLRequestHandler({\n * build,\n * getHost: (event) => event.headers[\"x-viewer-host\"],\n * });\n * ```\n *\n * @remarks\n * The default currently uses the `x-forwarded-host` header, which is\n * client-controlled and can be spoofed. Because that host drives the CSRF\n * check, trusting it should be deliberate: the default will change to the\n * AWS-provided, non-spoofable request-context domain name\n * (`event.requestContext.domainName`) in the next major version (aligning with\n * the upstream `@react-router/architect` adapter), after which relying on a\n * forwarded header becomes an explicit opt-in via `getHost`. Setups where the\n * request-context host is not the browser-facing host (e.g. Function URLs\n * behind CloudFront) must set `getHost`.\n */\n getHost?: GetHostFunction<T>;\n};\n\n/**\n * Returns a request handler for AWS API Gateway V1\n *\n */\n/**\n * Returns a request handler for AWS API Gateway V1 events.\n *\n * @param options - The handler options, including the React Router server build,\n * optional getLoadContext function, and mode string.\n * @returns An AWS API Gateway V1 handler compatible with APIGatewayProxyHandler.\n */\nexport function createAPIGatewayV1RequestHandler(\n options: CreateRequestHandlerArgs<APIGatewayProxyEvent>,\n): APIGatewayProxyHandler {\n return createRequestHandlerForAdapter(apiGatewayV1Adapter, options);\n}\n\n/**\n * Returns a request handler for AWS API Gateway V2 events.\n *\n * @param options - The handler options, including the React Router server build,\n * optional getLoadContext function, and mode string.\n * @returns An AWS API Gateway V2 handler compatible with APIGatewayProxyHandlerV2.\n */\nexport function createAPIGatewayV2RequestHandler(\n options: CreateRequestHandlerArgs<APIGatewayProxyEventV2>,\n): APIGatewayProxyHandlerV2 {\n return createRequestHandlerForAdapter(apiGatewayV2Adapter, options);\n}\n\n/**\n * Returns a request handler for AWS Application Load Balancer events.\n *\n * @param options - The handler options, including the React Router server build,\n * optional getLoadContext function, and mode string.\n * @returns An AWS ALB handler compatible with ALBHandler.\n */\nexport function createALBRequestHandler(options: CreateRequestHandlerArgs<ALBEvent>): ALBHandler {\n return createRequestHandlerForAdapter(applicationLoadBalancerAdapter, options);\n}\n\n/**\n * Returns a request handler for AWS Lambda Function URL events (invoke mode BUFFERED).\n *\n * @param options - The handler options, including the React Router server build,\n * optional getLoadContext function, and mode string.\n * @returns An AWS Lambda Function URL handler compatible with Lambda Function URLs with InvokeMode BUFFERED.\n */\nexport function createFunctionURLRequestHandler(\n options: CreateRequestHandlerArgs<LambdaFunctionURLEvent>,\n): LambdaFunctionURLHandler {\n return createRequestHandlerForAdapter(apiGatewayV2Adapter, options);\n}\n\n/**\n * Returns a request handler for AWS Lambda Function URL events (invoke mode RESPONSE_STREAM).\n *\n * @param options - The handler options, including the React Router server build,\n * optional getLoadContext function, and mode string.\n * @returns A streaming AWS Lambda Function URL handler compatible with Lambda Function URLs with InvokeMode RESPONSE_STREAM.\n */\nexport function createFunctionURLStreamingRequestHandler(\n options: CreateRequestHandlerArgs<LambdaFunctionURLEvent>,\n): StreamifyHandler<LambdaFunctionURLEvent, void> {\n return createRequestHandlerForAdapter(functionUrlStreamingAdapter, options);\n}\n\nfunction createRequestHandlerForAdapter<E, Ret, Res, H>(\n awsAdapter: ReactRouterAdapter<E, Ret, Res, H>,\n { build, getLoadContext, mode = process.env.NODE_ENV, getHost }: CreateRequestHandlerArgs<E>,\n) {\n const handleRequest = createReactRouterRequestHandler(build, mode);\n\n return awsAdapter.wrapHandler(async (event, res) => {\n let request: Request;\n\n try {\n request = awsAdapter.createReactRouterRequest(event, getHost);\n } catch (e: unknown) {\n return await awsAdapter.sendReactRouterResponse(\n new Response(`Bad Request: ${e instanceof Error ? e.message : e}`, { status: 400 }),\n res,\n );\n }\n\n const loadContext = await getLoadContext?.(event);\n\n const response = await handleRequest(request, loadContext);\n\n return await awsAdapter.sendReactRouterResponse(response, res);\n });\n}\n","import type { ALBEvent, APIGatewayProxyEvent, APIGatewayProxyEventV2, LambdaFunctionURLEvent } from \"aws-lambda\";\n\nimport type { ReactRouterAdapter } from \"./adapters\";\n/* eslint-disable @typescript-eslint/no-explicit-any -- we need the any in extends parameters */\nimport type { ApiGatewayV1Adapter } from \"./adapters/api-gateway-v1\";\nimport type { ApiGatewayV2Adapter } from \"./adapters/api-gateway-v2\";\nimport type { ApplicationLoadBalancerAdapter } from \"./adapters/application-load-balancer\";\nimport type { FunctionUrlStreamingAdapter } from \"./adapters/function-url-streaming\";\nimport type { CreateRequestHandlerArgs } from \"./server\";\nimport {\n createALBRequestHandler,\n createAPIGatewayV1RequestHandler,\n createAPIGatewayV2RequestHandler,\n createFunctionURLRequestHandler,\n createFunctionURLStreamingRequestHandler,\n} from \"./server\";\n\nexport enum AWSProxy {\n APIGatewayV1 = \"APIGatewayV1\",\n APIGatewayV2 = \"APIGatewayV2\",\n ALB = \"ALB\",\n FunctionURL = \"FunctionURL\",\n FunctionURLStreaming = \"FunctionURLStreaming\",\n}\n\nexport type InferAdapter<T extends AWSProxy> = T extends AWSProxy.APIGatewayV1\n ? ApiGatewayV1Adapter\n : T extends AWSProxy.APIGatewayV2 | AWSProxy.FunctionURL\n ? ApiGatewayV2Adapter\n : T extends AWSProxy.ALB\n ? ApplicationLoadBalancerAdapter\n : T extends AWSProxy.FunctionURLStreaming\n ? FunctionUrlStreamingAdapter\n : never;\n\ntype InferEventType<T extends AWSProxy> =\n InferAdapter<T> extends ReactRouterAdapter<infer E, any, any, any> ? E : never;\ntype InferHandlerType<T extends AWSProxy> =\n InferAdapter<T> extends ReactRouterAdapter<any, any, any, infer H> ? H : never;\n\n/**\n * Returns a request handler for AWS that serves the response using React Router.\n *\n * @deprecated Use one of the gateway-specific create*RequestHandler methods instead for better tree-shaking.\n * - `createAPIGatewayV1RequestHandler`\n * - `createAPIGatewayV2RequestHandler`\n * - `createALBRequestHandler`\n * - `createFunctionURLRequestHandler`\n * - `createFunctionURLStreamingRequestHandler`\n */\nexport function createRequestHandler<T extends AWSProxy>(\n options: CreateRequestHandlerArgs<InferEventType<T>> & {\n awsProxy?: T;\n },\n): InferHandlerType<T> {\n const { awsProxy = AWSProxy.APIGatewayV2 as T, ...opts } = options;\n switch (awsProxy) {\n case AWSProxy.APIGatewayV1:\n return createAPIGatewayV1RequestHandler(\n opts as CreateRequestHandlerArgs<APIGatewayProxyEvent>,\n ) as InferHandlerType<T>;\n case AWSProxy.APIGatewayV2:\n return createAPIGatewayV2RequestHandler(\n opts as CreateRequestHandlerArgs<APIGatewayProxyEventV2>,\n ) as InferHandlerType<T>;\n case AWSProxy.ALB:\n return createALBRequestHandler(opts as CreateRequestHandlerArgs<ALBEvent>) as InferHandlerType<T>;\n case AWSProxy.FunctionURL:\n return createFunctionURLRequestHandler(\n opts as CreateRequestHandlerArgs<LambdaFunctionURLEvent>,\n ) as InferHandlerType<T>;\n case AWSProxy.FunctionURLStreaming:\n return createFunctionURLStreamingRequestHandler(\n opts as CreateRequestHandlerArgs<LambdaFunctionURLEvent>,\n ) as InferHandlerType<T>;\n default:\n return assertNever(awsProxy, `Unsupported buffered AWS Proxy type: ${awsProxy}`);\n }\n}\n\nfunction assertNever(_x: never, message: string): never {\n throw new Error(message);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAMA,MAAM,cAAc;CAClB;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAgB,aAAa,aAAiD;CAC5E,IAAI,CAAC,aACH,OAAO;CAET,MAAM,CAAC,QAAQ,YAAY,MAAM,GAAG;CACpC,OAAO,YAAY,SAAS,IAAI;AAClC;;;;;;;;;;;;;;;AC5DA,SAAgB,YAAY,SAA4C;CACtE,MAAM,CAAC,cAAc,IAAI,YAAY,WAAW,GAAA,CAAI,MAAM,GAAG;CAC7D,MAAM,WAAW,YAAY,MAAM,UAAU,CAAC,CAAC,MAAM;CACrD,MAAM,WAAW,OAAO,SAAS,WAAW,IAAI,EAAE;CAClD,MAAM,OAAO,OAAO,cAAc,QAAQ,IAAI,WAAW,KAAA;CACzD,OAAO,GAAG,WAAW,OAAO,IAAI,SAAS;AAC3C;;;ACVA,SAAS,qCACP,OACA,SACS;CAET,MAAM,OAAO,YADG,UAAU,KAAK,MAAM,MAAM,QAAQ,uBAAuB,MAAM,QAAQ,KACxD;CAChC,MAAM,SAAS,MAAM,QAAQ,wBAAwB;CAErD,MAAM,iBAAiB,IAAIA,IAAAA,gBAAgB,MAAM,qBAA+C,CAAC,CAAC,SAAS;CAC3G,MAAM,SAAS,eAAe,SAAS,IAAI,IAAI,mBAAmB;CAClE,MAAMC,QAAM,IAAI,IAAI,MAAM,OAAO,QAAQ,GAAG,OAAO,KAAK,MAAM;CAE9D,MAAM,aAAa,MAAM,QAAQ,eAAe,EAAE,SAAS,qBAAqB;CAGhF,MAAM,aAAa,IAAI,gBAAgB;CAEvC,OAAO,IAAI,QAAQA,MAAI,MAAM;EAC3B,QAAQ,MAAM,eAAe;EAC7B,SAAS,qCAAqC,MAAM,OAAO;EAC3D,QAAQ,WAAW;EACnB,MACE,MAAM,QAAQ,MAAM,kBAChB,aACE,OAAO,KAAK,MAAM,MAAM,QAAQ,IAChC,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,IAC7C,MAAM,QAAQ,KAAA;CACtB,CAAC;AACH;AAEA,SAAS,qCAAqC,gBAAsD;CAClG,MAAM,UAAU,IAAI,QAAQ;CAE5B,KAAK,MAAM,CAAC,QAAQ,UAAU,OAAO,QAAQ,cAAc,GACzD,IAAI,OACF,QAAQ,OAAO,QAAQ,KAAK;CAIhC,OAAO;AACT;AAEA,eAAe,oCAAoC,cAAwD;CAEzG,MAAM,kBAAkB,aADJ,aAAa,QAAQ,IAAI,cACE,CAAC;CAChD,IAAI;CAEJ,IAAI,aAAa,MACf,IAAI,iBACF,OAAO,OAAA,GAAA,mBAAA,uBAAA,CAA6B,aAAa,MAAM,QAAQ;MAE/D,OAAO,MAAM,aAAa,KAAK;CAInC,OAAO;EACL,YAAY,aAAa;EACzB,SAAS,OAAO,YAAY,aAAa,QAAQ,QAAQ,CAAC;EAC1D,MAAM,QAAQ;EACd;CACF;AACF;AAIA,MAAa,sBAA2C;CACtD,cAAc,aAAa,MAAM,QAAQ,CAAC;CAC1C,0BAA0B;CAC1B,yBAAyB;AAC3B;;;AClEA,SAAgB,sCACd,OACA,SACS;CAET,MAAM,OAAO,YADG,UAAU,KAAK,MAAM,MAAM,QAAQ,uBAAuB,MAAM,QAAQ,KACxD;CAChC,MAAM,SAAS,MAAM,eAAe,SAAS,IAAI,MAAM,mBAAmB;CAC1E,MAAM,SAAS,MAAM,QAAQ,wBAAwB;CAErD,MAAM,MAAM,IAAI,IAAI,MAAM,UAAU,QAAQ,GAAG,OAAO,KAAK,MAAM;CACjE,MAAM,aAAa,MAAM,QAAQ,eAAe,EAAE,SAAS,qBAAqB;CAGhF,MAAM,aAAa,IAAI,gBAAgB;CAEvC,OAAO,IAAI,QAAQ,IAAI,MAAM;EAC3B,QAAQ,MAAM,eAAe,KAAK;EAClC,SAAS,qCAAqC,MAAM,SAAS,MAAM,OAAO;EAC1E,QAAQ,WAAW;EACnB,MACE,MAAM,QAAQ,MAAM,kBAChB,aACE,OAAO,KAAK,MAAM,MAAM,QAAQ,IAChC,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,IAC7C,MAAM;CACd,CAAC;AACH;AAEA,SAAS,qCACP,gBACA,gBACS;CACT,MAAM,UAAU,IAAI,QAAQ;CAE5B,KAAK,MAAM,CAAC,QAAQ,UAAU,OAAO,QAAQ,cAAc,GACzD,IAAI,OACF,QAAQ,OAAO,QAAQ,KAAK;CAIhC,IAAI,gBACF,QAAQ,OAAO,UAAU,eAAe,KAAK,IAAI,CAAC;CAGpD,OAAO;AACT;AAEA,SAAgB,oCAAoC,cAIlD;CAEA,MAAM,UAAU,aAAa,QAAQ,aAAa;CAClD,IAAI,QAAQ,QACV,aAAa,QAAQ,OAAO,YAAY;CAG1C,OAAO;EACL,YAAY,aAAa;EACzB,SAAS,OAAO,YAAY,aAAa,QAAQ,QAAQ,CAAC;EAC1D;CACF;AACF;AAEA,eAAe,oCAAoC,cAAoE;CACrH,MAAM,SAA4C,oCAAoC,YAAY;CAIlG,OAAO,kBAAkB,aAFL,aAAa,QAAQ,IAAI,cAEG,CAAC;CAEjD,IAAI,aAAa,MACf,IAAI,OAAO,iBACT,OAAO,OAAO,OAAA,GAAA,mBAAA,uBAAA,CAA6B,aAAa,MAAM,QAAQ;MAEtE,OAAO,OAAO,MAAM,aAAa,KAAK;CAI1C,OAAO;AACT;AAIA,MAAa,sBAA2C;CACtD,cAAc,aAAa,MAAM,QAAQ,CAAC;CAC1C,0BAA0B;CAC1B,yBAAyB;AAC3B;;;AC5FA,SAAS,4BAA4B,OAAiB,SAA8C;CAClG,MAAM,UAAU,OAAO,WAAW,CAAC;CACnC,MAAM,OAAO,YAAY,UAAU,KAAK,MAAM,QAAQ,uBAAuB,QAAQ,KAAK;CAC1F,MAAM,SAAS,QAAQ,wBAAwB;CAE/C,MAAM,iBAAiB,IAAIC,IAAAA,gBAAgB,MAAM,qBAA+C,CAAC,CAAC,SAAS;CAC3G,MAAM,SAAS,eAAe,SAAS,IAAI,IAAI,mBAAmB;CAClE,MAAMC,QAAM,IAAI,IAAI,MAAM,OAAO,QAAQ,GAAG,OAAO,KAAK,MAAM;CAE9D,MAAM,aAAa,QAAQ,eAAe,EAAE,SAAS,qBAAqB;CAI1E,MAAM,aAAa,IAAI,gBAAgB;CAEvC,OAAO,IAAI,QAAQA,MAAI,MAAM;EAC3B,QAAQ,MAAM;EACd,SAAS,4BAA4B,OAAO;EAC5C,QAAQ,WAAW;EACnB,MACE,MAAM,QAAQ,MAAM,kBAChB,aACE,OAAO,KAAK,MAAM,MAAM,QAAQ,IAChC,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,IAC7C,MAAM,QAAQ,KAAA;CACtB,CAAC;AACH;AAEA,SAAS,4BAA4B,gBAA0C;CAC7E,MAAM,UAAU,IAAI,QAAQ;CAE5B,KAAK,MAAM,CAAC,QAAQ,UAAU,OAAO,QAAQ,cAAc,GACzD,IAAI,OACF,QAAQ,OAAO,QAAQ,KAAK;CAIhC,OAAO;AACT;AAEA,eAAe,2BAA2B,cAA4C;CAEpF,MAAM,kBAAkB,aADJ,aAAa,QAAQ,IAAI,cACE,CAAC;CAChD,IAAI;CAEJ,IAAI,aAAa,MACf,IAAI,iBACF,OAAO,OAAA,GAAA,mBAAA,uBAAA,CAA6B,aAAa,MAAM,QAAQ;MAE/D,OAAO,MAAM,aAAa,KAAK;CAInC,OAAO;EACL,YAAY,aAAa;EACzB,SAAS,OAAO,YAAY,aAAa,QAAQ,QAAQ,CAAC;EAC1D,MAAM,QAAQ;EACd;CACF;AACF;AAIA,MAAa,iCAAiE;CAC5E,cAAc,aAAa,MAAM,QAAQ,CAAC;CAC1C,0BAA0B;CAC1B,yBAAyB;AAC3B;;;ACpEA,MAAM,oBACJ,IAAI,eAAe,EACjB,MAAM,YAAY;CAChB,WAAW,QAAQ,EAAE;CACrB,WAAW,MAAM;AACnB,EACF,CAAC;AAEH,MAAM,8CAA8C,OAClD,UACA,mBACG;CACH,MAAM,WAAW,oCAAoC,QAAQ;CAE7D,IAAI,OAAO,SAAS;CACpB,IAAI,CAAC,MAGH,OAAO,YAAY;CAGrB,MAAM,qBAAqB,UAAU,mBAAmB,KAAK,gBAAgB,QAAQ;CAErF,OAAA,GAAA,mBAAA,8BAAA,CAAoC,MAAM,kBAAkB;AAC9D;AASA,MAAa,8BAA2D;CACtE,aAAa,UAAU;CACvB,0BAA0B;CAC1B,yBAAyB;AAC3B;;;;;;;;;;;;;;ACgDA,SAAgB,iCACd,SACwB;CACxB,OAAO,+BAA+B,qBAAqB,OAAO;AACpE;;;;;;;;AASA,SAAgB,iCACd,SAC0B;CAC1B,OAAO,+BAA+B,qBAAqB,OAAO;AACpE;;;;;;;;AASA,SAAgB,wBAAwB,SAAyD;CAC/F,OAAO,+BAA+B,gCAAgC,OAAO;AAC/E;;;;;;;;AASA,SAAgB,gCACd,SAC0B;CAC1B,OAAO,+BAA+B,qBAAqB,OAAO;AACpE;;;;;;;;AASA,SAAgB,yCACd,SACgD;CAChD,OAAO,+BAA+B,6BAA6B,OAAO;AAC5E;AAEA,SAAS,+BACP,YACA,EAAE,OAAO,gBAAgB,OAAO,QAAQ,IAAI,UAAU,WACtD;CACA,MAAM,iBAAA,GAAA,aAAA,qBAAA,CAAgD,OAAO,IAAI;CAEjE,OAAO,WAAW,YAAY,OAAO,OAAO,QAAQ;EAClD,IAAI;EAEJ,IAAI;GACF,UAAU,WAAW,yBAAyB,OAAO,OAAO;EAC9D,SAAS,GAAY;GACnB,OAAO,MAAM,WAAW,wBACtB,IAAI,SAAS,gBAAgB,aAAa,QAAQ,EAAE,UAAU,KAAK,EAAE,QAAQ,IAAI,CAAC,GAClF,GACF;EACF;EAEA,MAAM,cAAc,MAAM,iBAAiB,KAAK;EAEhD,MAAM,WAAW,MAAM,cAAc,SAAS,WAAW;EAEzD,OAAO,MAAM,WAAW,wBAAwB,UAAU,GAAG;CAC/D,CAAC;AACH;;;AC3JA,IAAY,WAAL,yBAAA,UAAA;CACL,SAAA,kBAAA;CACA,SAAA,kBAAA;CACA,SAAA,SAAA;CACA,SAAA,iBAAA;CACA,SAAA,0BAAA;;AACF,EAAA,CAAA,CAAA;;;;;;;;;;;AA2BA,SAAgB,qBACd,SAGqB;CACrB,MAAM,EAAE,WAAA,gBAAuC,GAAG,SAAS;CAC3D,QAAQ,UAAR;EACE,KAAA,gBACE,OAAO,iCACL,IACF;EACF,KAAA,gBACE,OAAO,iCACL,IACF;EACF,KAAA,OACE,OAAO,wBAAwB,IAA0C;EAC3E,KAAA,eACE,OAAO,gCACL,IACF;EACF,KAAA,wBACE,OAAO,yCACL,IACF;EACF,SACE,OAAO,YAAY,UAAU,wCAAwC,UAAU;CACnF;AACF;AAEA,SAAS,YAAY,IAAW,SAAwB;CACtD,MAAM,IAAI,MAAM,OAAO;AACzB"}
|