@geostrategists/react-router-aws 2.0.0 → 2.1.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 CHANGED
@@ -1,52 +1,115 @@
1
- # @geostrategists/react-router-aws
2
-
3
- ## AWS adapters for React Router v7 (successor to Remix)
4
-
5
1
  [![npm version](https://badge.fury.io/js/@geostrategists%2Freact-router-aws.svg)](https://badge.fury.io/js/@geostrategists%2Freact-router-aws)
6
2
  [![install size](https://packagephobia.com/badge?p=@geostrategists/react-router-aws)](https://packagephobia.com/result?p=@geostrategists/react-router-aws)
7
3
 
8
- Forked from [remix-aws](https://github.com/wingleung/remix-aws) to support React Router v7, which Remix was merged into.
4
+ # AWS Lambda adapters for React Router v7
5
+
6
+ This project provides adapters for running React Router Framework applications on AWS Lambda
7
+ behind a number of different HTTP gateways.
8
+
9
+ ## 🚀 Supported gateways
10
+
11
+ - Lambda function URL (streaming) _✨(recommended)_
12
+ - Lambda function URL (buffered)
13
+ - API Gateway v2
14
+ - API Gateway v1
15
+ - Application Load Balancer
9
16
 
10
- ## 🚀 support
17
+ ### Acknowledgements
11
18
 
12
- - API gateway v1
13
- - API gateway v2
14
- - Application load balancer
19
+ This project was forked from [remix-aws](https://github.com/wingleung/remix-aws) to support React Router v7, which Remix was merged into.
15
20
 
16
21
  ## Getting started
17
22
 
23
+ ### Installation
24
+
25
+ ```shell
26
+ npm add @geostrategists/react-router-aws
27
+ ```
28
+
18
29
  ```shell
19
- npm install --save @geostrategists/react-router-aws
30
+ yarn add @geostrategists/react-router-aws
20
31
  ```
21
32
 
33
+ ### Gateway-specific handlers
34
+
35
+ Next, choose the handler that matches your AWS integration:
36
+
37
+ - `createALBRequestHandler` for Application Load Balancer
38
+ - `createAPIGatewayV1RequestHandler` for API Gateway v1
39
+ - `createAPIGatewayV2RequestHandler` for API Gateway v2
40
+ - `createFunctionURLRequestHandler` for Lambda Function URLs (Buffered)
41
+ - `createFunctionURLStreamingRequestHandler` for Lambda Function URLs (Streaming)
42
+
43
+ Example for API Gateway v2:
44
+
22
45
  ```javascript
23
- // server.js
46
+ // lambda-handler.ts
24
47
  import * as build from "virtual:react-router/server-build";
25
- import { AWSProxy, createRequestHandler } from "@geostrategists/react-router-aws";
48
+ import { createAPIGatewayV2RequestHandler } from "@geostrategists/react-router-aws";
26
49
 
27
- export const handler = createRequestHandler({
50
+ export const handler = createAPIGatewayV2RequestHandler({
28
51
  build,
29
52
  mode: process.env.NODE_ENV,
30
- awsProxy: AWSProxy.APIGatewayV2,
31
53
  });
32
54
  ```
33
55
 
34
- ### `awsProxy`
56
+ > [!NOTE]
57
+ >
58
+ > **DEPRECATION NOTICE**: The previous `createRequestHandler` method still exists, but is kept only for
59
+ > backwards-compatibility reasons and will be removed in the next major release.
60
+ > It does not allow tree-shaking and will include all gateway adapters in your bundle.
61
+ > For optimal bundle size, always use the method specific to your gateway:
35
62
 
36
- By default, the `awsProxy` is set to `AWSProxy.APIGatewayV2`.
63
+ ### Streaming support for Lambda Function URLs
37
64
 
38
- #### Options
65
+ React Router and React allow you to stream responses from the server to the client, reducing the TTFB (time to first byte)
66
+ and improving the user experience. See [Streaming with Suspense](https://reactrouter.com/how-to/suspense) for details.
39
67
 
40
- - `AWSProxy.APIGatewayV1`
41
- - `AWSProxy.APIGatewayV2`
42
- - `AWSProxy.ALB`
43
- - `AWSProxy.FunctionURL`
68
+ For this to work, the response from the Lambda must also be streamed. This is currently only possible with
69
+ Lambda Function URLs, which is why we recommend this setup.
70
+
71
+ For streaming responses from React Router on AWS Lambda Function URLs, use `createFunctionURLStreamingRequestHandler`:
72
+
73
+ ```typescript
74
+ // lambda-handler.ts
75
+ import * as build from "virtual:react-router/server-build";
76
+ import { createFunctionURLStreamingRequestHandler } from "@geostrategists/react-router-aws";
77
+
78
+ export const handler = createFunctionURLStreamingRequestHandler({
79
+ build,
80
+ mode: process.env.NODE_ENV,
81
+ });
82
+ ```
83
+
84
+ The Function URL must be configured to use streaming responses.
85
+
86
+ For example, in CDK:
87
+
88
+ ```typescript
89
+ // frontend-stack.ts
90
+ declare const fn: lambda.Function;
91
+
92
+ fn.addFunctionUrl({
93
+ authType: lambda.FunctionUrlAuthType.NONE,
94
+ invokeMode: lambda.InvokeMode.RESPONSE_STREAM,
95
+ });
96
+ ```
97
+
98
+ > [!TIP]
99
+ > It is strongly recommended to include the `@geostrategists/react-router-aws` dependency in your Lambda handler bundle.
100
+ > Otherwise (if it is externalized and perhaps put in a Lambda layer), AWS Lambda may not detect the handler as a
101
+ > streaming handler.
102
+ >
103
+ > If you encounter responses that show a `{ statusCode, headers, body }` JSON object instead of just the body,
104
+ > this might be the reason.
105
+
106
+ More on response streaming: https://docs.aws.amazon.com/lambda/latest/dg/configuration-response-streaming.html
44
107
 
45
108
  ## Deployment recommendation
46
109
 
47
110
  Since Vite already bundles the project into a single entry point, there is no need to further
48
111
  bundle the lambda code.
49
- For example, when using AWS CDK, we recommend using lambda.Function directly instead of lambda.NodeJsFunction.
112
+ For example, when using AWS CDK, we recommend using [lambda.Function](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.Function.html) directly instead of lambda.NodeJsFunction.
50
113
 
51
114
  Dependencies can be provided using a layer, for example.
52
115
 
@@ -61,16 +124,4 @@ There are two primary methods to achieve this:
61
124
  - Use the .mjs extension:
62
125
  Alternatively, you can change the file extension to `.mjs`. For example, you can configure the React Router `serverBuildFile` setting to output `index.mjs`.
63
126
 
64
- more info: [AWS docs on ES module support in AWS lambdas](https://docs.aws.amazon.com/lambda/latest/dg/lambda-nodejs.html#designate-es-module)
65
-
66
- ## Notes
67
-
68
- ### split from @remix/architect
69
-
70
- As mentioned in [#3173](https://github.com/remix-run/remix/pull/3173) the goal would be to provide an AWS adapter for
71
- the community by the community.
72
- In doing so the focus will be on AWS integrations and less on Architect. I do think it's added value to provide examples
73
- for Architect, AWS SAM, AWS CDK, Serverless,...
74
-
75
- **info:** [ALB types](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/aws-lambda/trigger/alb.d.ts#L29-L48)
76
- vs [API gateway v1 types](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/aws-lambda/trigger/api-gateway-proxy.d.ts#L116-L145)
127
+ See [AWS docs on ES module support in AWS lambdas](https://docs.aws.amazon.com/lambda/latest/dg/lambda-nodejs.html#designate-es-module) for more information.
package/dist/index.d.mts CHANGED
@@ -1,12 +1,7 @@
1
- import { APIGatewayProxyEventV2, APIGatewayProxyEvent, ALBEvent, APIGatewayProxyHandlerV2, APIGatewayProxyHandler, ALBHandler } from 'aws-lambda';
2
1
  import { UNSAFE_MiddlewareEnabled, unstable_InitialContext, AppLoadContext, ServerBuild } from 'react-router';
2
+ import { ALBEvent, ALBHandler, APIGatewayProxyEvent, APIGatewayProxyHandler, APIGatewayProxyEventV2, APIGatewayProxyHandlerV2, LambdaFunctionURLEvent, LambdaFunctionURLHandler, Handler, APIGatewayProxyResult, APIGatewayProxyStructuredResultV2, ALBResult } from 'aws-lambda';
3
+ import { StreamifyHandler } from 'aws-lambda/handler';
3
4
 
4
- declare enum AWSProxy {
5
- APIGatewayV1 = "APIGatewayV1",
6
- APIGatewayV2 = "APIGatewayV2",
7
- ALB = "ALB",
8
- FunctionURL = "FunctionURL"
9
- }
10
5
  type MaybePromise<T> = T | Promise<T>;
11
6
  /**
12
7
  * A function that returns the value to use as `context` in route `loader` and
@@ -15,17 +10,93 @@ type MaybePromise<T> = T | Promise<T>;
15
10
  * You can think of this as an escape hatch that allows you to pass
16
11
  * environment/platform-specific values through to your loader/action.
17
12
  */
18
- type GetLoadContextFunction = (event: APIGatewayProxyEventV2 | APIGatewayProxyEvent | ALBEvent) => UNSAFE_MiddlewareEnabled extends true ? MaybePromise<unstable_InitialContext> : MaybePromise<AppLoadContext>;
19
- type RequestHandler = APIGatewayProxyHandlerV2 | APIGatewayProxyHandler | ALBHandler;
20
- /**
21
- * Returns a request handler for AWS that serves the response using
22
- * React Router.
23
- */
24
- declare function createRequestHandler({ build, getLoadContext, mode, awsProxy, }: {
13
+ type GetLoadContextFunction<E> = (event: E) => UNSAFE_MiddlewareEnabled extends true ? MaybePromise<unstable_InitialContext> : MaybePromise<AppLoadContext>;
14
+ type CreateRequestHandlerArgs<T> = {
25
15
  build: ServerBuild;
26
- getLoadContext?: GetLoadContextFunction;
16
+ getLoadContext?: GetLoadContextFunction<T>;
27
17
  mode?: string;
28
- awsProxy?: AWSProxy;
29
- }): RequestHandler;
18
+ };
19
+ /**
20
+ * Returns a request handler for AWS API Gateway V1
21
+ *
22
+ */
23
+ /**
24
+ * Returns a request handler for AWS API Gateway V1 events.
25
+ *
26
+ * @param options - The handler options, including the React Router server build,
27
+ * optional getLoadContext function, and mode string.
28
+ * @returns An AWS API Gateway V1 handler compatible with APIGatewayProxyHandler.
29
+ */
30
+ declare function createAPIGatewayV1RequestHandler(options: CreateRequestHandlerArgs<APIGatewayProxyEvent>): APIGatewayProxyHandler;
31
+ /**
32
+ * Returns a request handler for AWS API Gateway V2 events.
33
+ *
34
+ * @param options - The handler options, including the React Router server build,
35
+ * optional getLoadContext function, and mode string.
36
+ * @returns An AWS API Gateway V2 handler compatible with APIGatewayProxyHandlerV2.
37
+ */
38
+ declare function createAPIGatewayV2RequestHandler(options: CreateRequestHandlerArgs<APIGatewayProxyEventV2>): APIGatewayProxyHandlerV2;
39
+ /**
40
+ * Returns a request handler for AWS Application Load Balancer events.
41
+ *
42
+ * @param options - The handler options, including the React Router server build,
43
+ * optional getLoadContext function, and mode string.
44
+ * @returns An AWS ALB handler compatible with ALBHandler.
45
+ */
46
+ declare function createALBRequestHandler(options: CreateRequestHandlerArgs<ALBEvent>): ALBHandler;
47
+ /**
48
+ * Returns a request handler for AWS Lambda Function URL events (invoke mode BUFFERED).
49
+ *
50
+ * @param options - The handler options, including the React Router server build,
51
+ * optional getLoadContext function, and mode string.
52
+ * @returns An AWS Lambda Function URL handler compatible with Lambda Function URLs with InvokeMode BUFFERED.
53
+ */
54
+ declare function createFunctionURLRequestHandler(options: CreateRequestHandlerArgs<LambdaFunctionURLEvent>): LambdaFunctionURLHandler;
55
+ /**
56
+ * Returns a request handler for AWS Lambda Function URL events (invoke mode RESPONSE_STREAM).
57
+ *
58
+ * @param options - The handler options, including the React Router server build,
59
+ * optional getLoadContext function, and mode string.
60
+ * @returns A streaming AWS Lambda Function URL handler compatible with Lambda Function URLs with InvokeMode RESPONSE_STREAM.
61
+ */
62
+ declare function createFunctionURLStreamingRequestHandler(options: CreateRequestHandlerArgs<LambdaFunctionURLEvent>): StreamifyHandler<LambdaFunctionURLEvent, void>;
63
+
64
+ interface ReactRouterAdapter<E, Ret, Res = void, H = Handler<E, Ret>> {
65
+ wrapHandler: (handler: (event: E, res: Res) => Promise<Ret>) => H;
66
+ createReactRouterRequest: (event: E) => Request;
67
+ sendReactRouterResponse: (nodeResponse: Response, response: Res) => Promise<Ret>;
68
+ }
69
+
70
+ type ApiGatewayV1Adapter = ReactRouterAdapter<APIGatewayProxyEvent, APIGatewayProxyResult>;
71
+
72
+ type ApiGatewayV2Adapter = ReactRouterAdapter<APIGatewayProxyEventV2, APIGatewayProxyStructuredResultV2>;
73
+
74
+ type ApplicationLoadBalancerAdapter = ReactRouterAdapter<ALBEvent, ALBResult>;
75
+
76
+ type FunctionUrlStreamingAdapter = ReactRouterAdapter<LambdaFunctionURLEvent, void, awslambda.HttpResponseStream, StreamifyHandler<LambdaFunctionURLEvent, void>>;
77
+
78
+ declare enum AWSProxy {
79
+ APIGatewayV1 = "APIGatewayV1",
80
+ APIGatewayV2 = "APIGatewayV2",
81
+ ALB = "ALB",
82
+ FunctionURL = "FunctionURL",
83
+ FunctionURLStreaming = "FunctionURLStreaming"
84
+ }
85
+ type InferAdapter<T extends AWSProxy> = T extends AWSProxy.APIGatewayV1 ? ApiGatewayV1Adapter : T extends AWSProxy.APIGatewayV2 | AWSProxy.FunctionURL ? ApiGatewayV2Adapter : T extends AWSProxy.ALB ? ApplicationLoadBalancerAdapter : T extends AWSProxy.FunctionURLStreaming ? FunctionUrlStreamingAdapter : never;
86
+ type InferEventType<T extends AWSProxy> = InferAdapter<T> extends ReactRouterAdapter<infer E, any, any, any> ? E : never;
87
+ type InferHandlerType<T extends AWSProxy> = InferAdapter<T> extends ReactRouterAdapter<any, any, any, infer H> ? H : never;
88
+ /**
89
+ * Returns a request handler for AWS that serves the response using React Router.
90
+ *
91
+ * @deprecated Use one of the gateway-specific create*RequestHandler methods instead for better tree-shaking.
92
+ * - `createAPIGatewayV1RequestHandler`
93
+ * - `createAPIGatewayV2RequestHandler`
94
+ * - `createALBRequestHandler`
95
+ * - `createFunctionURLRequestHandler`
96
+ * - `createFunctionURLStreamingRequestHandler`
97
+ */
98
+ declare function createRequestHandler<T extends AWSProxy>(options: CreateRequestHandlerArgs<InferEventType<T>> & {
99
+ awsProxy?: T;
100
+ }): InferHandlerType<T>;
30
101
 
31
- export { AWSProxy, type GetLoadContextFunction, type RequestHandler, createRequestHandler };
102
+ export { AWSProxy, type GetLoadContextFunction, createALBRequestHandler, createAPIGatewayV1RequestHandler, createAPIGatewayV2RequestHandler, createFunctionURLRequestHandler, createFunctionURLStreamingRequestHandler, createRequestHandler };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,7 @@
1
- import { APIGatewayProxyEventV2, APIGatewayProxyEvent, ALBEvent, APIGatewayProxyHandlerV2, APIGatewayProxyHandler, ALBHandler } from 'aws-lambda';
2
1
  import { UNSAFE_MiddlewareEnabled, unstable_InitialContext, AppLoadContext, ServerBuild } from 'react-router';
2
+ import { ALBEvent, ALBHandler, APIGatewayProxyEvent, APIGatewayProxyHandler, APIGatewayProxyEventV2, APIGatewayProxyHandlerV2, LambdaFunctionURLEvent, LambdaFunctionURLHandler, Handler, APIGatewayProxyResult, APIGatewayProxyStructuredResultV2, ALBResult } from 'aws-lambda';
3
+ import { StreamifyHandler } from 'aws-lambda/handler';
3
4
 
4
- declare enum AWSProxy {
5
- APIGatewayV1 = "APIGatewayV1",
6
- APIGatewayV2 = "APIGatewayV2",
7
- ALB = "ALB",
8
- FunctionURL = "FunctionURL"
9
- }
10
5
  type MaybePromise<T> = T | Promise<T>;
11
6
  /**
12
7
  * A function that returns the value to use as `context` in route `loader` and
@@ -15,17 +10,93 @@ type MaybePromise<T> = T | Promise<T>;
15
10
  * You can think of this as an escape hatch that allows you to pass
16
11
  * environment/platform-specific values through to your loader/action.
17
12
  */
18
- type GetLoadContextFunction = (event: APIGatewayProxyEventV2 | APIGatewayProxyEvent | ALBEvent) => UNSAFE_MiddlewareEnabled extends true ? MaybePromise<unstable_InitialContext> : MaybePromise<AppLoadContext>;
19
- type RequestHandler = APIGatewayProxyHandlerV2 | APIGatewayProxyHandler | ALBHandler;
20
- /**
21
- * Returns a request handler for AWS that serves the response using
22
- * React Router.
23
- */
24
- declare function createRequestHandler({ build, getLoadContext, mode, awsProxy, }: {
13
+ type GetLoadContextFunction<E> = (event: E) => UNSAFE_MiddlewareEnabled extends true ? MaybePromise<unstable_InitialContext> : MaybePromise<AppLoadContext>;
14
+ type CreateRequestHandlerArgs<T> = {
25
15
  build: ServerBuild;
26
- getLoadContext?: GetLoadContextFunction;
16
+ getLoadContext?: GetLoadContextFunction<T>;
27
17
  mode?: string;
28
- awsProxy?: AWSProxy;
29
- }): RequestHandler;
18
+ };
19
+ /**
20
+ * Returns a request handler for AWS API Gateway V1
21
+ *
22
+ */
23
+ /**
24
+ * Returns a request handler for AWS API Gateway V1 events.
25
+ *
26
+ * @param options - The handler options, including the React Router server build,
27
+ * optional getLoadContext function, and mode string.
28
+ * @returns An AWS API Gateway V1 handler compatible with APIGatewayProxyHandler.
29
+ */
30
+ declare function createAPIGatewayV1RequestHandler(options: CreateRequestHandlerArgs<APIGatewayProxyEvent>): APIGatewayProxyHandler;
31
+ /**
32
+ * Returns a request handler for AWS API Gateway V2 events.
33
+ *
34
+ * @param options - The handler options, including the React Router server build,
35
+ * optional getLoadContext function, and mode string.
36
+ * @returns An AWS API Gateway V2 handler compatible with APIGatewayProxyHandlerV2.
37
+ */
38
+ declare function createAPIGatewayV2RequestHandler(options: CreateRequestHandlerArgs<APIGatewayProxyEventV2>): APIGatewayProxyHandlerV2;
39
+ /**
40
+ * Returns a request handler for AWS Application Load Balancer events.
41
+ *
42
+ * @param options - The handler options, including the React Router server build,
43
+ * optional getLoadContext function, and mode string.
44
+ * @returns An AWS ALB handler compatible with ALBHandler.
45
+ */
46
+ declare function createALBRequestHandler(options: CreateRequestHandlerArgs<ALBEvent>): ALBHandler;
47
+ /**
48
+ * Returns a request handler for AWS Lambda Function URL events (invoke mode BUFFERED).
49
+ *
50
+ * @param options - The handler options, including the React Router server build,
51
+ * optional getLoadContext function, and mode string.
52
+ * @returns An AWS Lambda Function URL handler compatible with Lambda Function URLs with InvokeMode BUFFERED.
53
+ */
54
+ declare function createFunctionURLRequestHandler(options: CreateRequestHandlerArgs<LambdaFunctionURLEvent>): LambdaFunctionURLHandler;
55
+ /**
56
+ * Returns a request handler for AWS Lambda Function URL events (invoke mode RESPONSE_STREAM).
57
+ *
58
+ * @param options - The handler options, including the React Router server build,
59
+ * optional getLoadContext function, and mode string.
60
+ * @returns A streaming AWS Lambda Function URL handler compatible with Lambda Function URLs with InvokeMode RESPONSE_STREAM.
61
+ */
62
+ declare function createFunctionURLStreamingRequestHandler(options: CreateRequestHandlerArgs<LambdaFunctionURLEvent>): StreamifyHandler<LambdaFunctionURLEvent, void>;
63
+
64
+ interface ReactRouterAdapter<E, Ret, Res = void, H = Handler<E, Ret>> {
65
+ wrapHandler: (handler: (event: E, res: Res) => Promise<Ret>) => H;
66
+ createReactRouterRequest: (event: E) => Request;
67
+ sendReactRouterResponse: (nodeResponse: Response, response: Res) => Promise<Ret>;
68
+ }
69
+
70
+ type ApiGatewayV1Adapter = ReactRouterAdapter<APIGatewayProxyEvent, APIGatewayProxyResult>;
71
+
72
+ type ApiGatewayV2Adapter = ReactRouterAdapter<APIGatewayProxyEventV2, APIGatewayProxyStructuredResultV2>;
73
+
74
+ type ApplicationLoadBalancerAdapter = ReactRouterAdapter<ALBEvent, ALBResult>;
75
+
76
+ type FunctionUrlStreamingAdapter = ReactRouterAdapter<LambdaFunctionURLEvent, void, awslambda.HttpResponseStream, StreamifyHandler<LambdaFunctionURLEvent, void>>;
77
+
78
+ declare enum AWSProxy {
79
+ APIGatewayV1 = "APIGatewayV1",
80
+ APIGatewayV2 = "APIGatewayV2",
81
+ ALB = "ALB",
82
+ FunctionURL = "FunctionURL",
83
+ FunctionURLStreaming = "FunctionURLStreaming"
84
+ }
85
+ type InferAdapter<T extends AWSProxy> = T extends AWSProxy.APIGatewayV1 ? ApiGatewayV1Adapter : T extends AWSProxy.APIGatewayV2 | AWSProxy.FunctionURL ? ApiGatewayV2Adapter : T extends AWSProxy.ALB ? ApplicationLoadBalancerAdapter : T extends AWSProxy.FunctionURLStreaming ? FunctionUrlStreamingAdapter : never;
86
+ type InferEventType<T extends AWSProxy> = InferAdapter<T> extends ReactRouterAdapter<infer E, any, any, any> ? E : never;
87
+ type InferHandlerType<T extends AWSProxy> = InferAdapter<T> extends ReactRouterAdapter<any, any, any, infer H> ? H : never;
88
+ /**
89
+ * Returns a request handler for AWS that serves the response using React Router.
90
+ *
91
+ * @deprecated Use one of the gateway-specific create*RequestHandler methods instead for better tree-shaking.
92
+ * - `createAPIGatewayV1RequestHandler`
93
+ * - `createAPIGatewayV2RequestHandler`
94
+ * - `createALBRequestHandler`
95
+ * - `createFunctionURLRequestHandler`
96
+ * - `createFunctionURLStreamingRequestHandler`
97
+ */
98
+ declare function createRequestHandler<T extends AWSProxy>(options: CreateRequestHandlerArgs<InferEventType<T>> & {
99
+ awsProxy?: T;
100
+ }): InferHandlerType<T>;
30
101
 
31
- export { AWSProxy, type GetLoadContextFunction, type RequestHandler, createRequestHandler };
102
+ export { AWSProxy, type GetLoadContextFunction, createALBRequestHandler, createAPIGatewayV1RequestHandler, createAPIGatewayV2RequestHandler, createFunctionURLRequestHandler, createFunctionURLStreamingRequestHandler, createRequestHandler };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @geostrategists/react-router-aws v2.0.0
2
+ * @geostrategists/react-router-aws v2.1.0
3
3
  *
4
4
  * Copyright (c) Geostrategists Consulting GmbH
5
5
  *
@@ -9,9 +9,11 @@
9
9
  * @license MIT
10
10
  */
11
11
  "use strict";
12
+ var __create = Object.create;
12
13
  var __defProp = Object.defineProperty;
13
14
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
14
15
  var __getOwnPropNames = Object.getOwnPropertyNames;
16
+ var __getProtoOf = Object.getPrototypeOf;
15
17
  var __hasOwnProp = Object.prototype.hasOwnProperty;
16
18
  var __export = (target, all) => {
17
19
  for (var name in all)
@@ -25,12 +27,25 @@ var __copyProps = (to, from, except, desc) => {
25
27
  }
26
28
  return to;
27
29
  };
30
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
31
+ // If the importer is in node compatibility mode or this is not an ESM
32
+ // file that has been converted to a CommonJS file using a Babel-
33
+ // compatible transform (i.e. "__esModule" has not been set), then set
34
+ // "default" to the CommonJS "module.exports" for node compatibility.
35
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
36
+ mod
37
+ ));
28
38
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
39
 
30
40
  // src/index.ts
31
41
  var index_exports = {};
32
42
  __export(index_exports, {
33
43
  AWSProxy: () => AWSProxy,
44
+ createALBRequestHandler: () => createALBRequestHandler,
45
+ createAPIGatewayV1RequestHandler: () => createAPIGatewayV1RequestHandler,
46
+ createAPIGatewayV2RequestHandler: () => createAPIGatewayV2RequestHandler,
47
+ createFunctionURLRequestHandler: () => createFunctionURLRequestHandler,
48
+ createFunctionURLStreamingRequestHandler: () => createFunctionURLStreamingRequestHandler,
34
49
  createRequestHandler: () => createRequestHandler
35
50
  });
36
51
  module.exports = __toCommonJS(index_exports);
@@ -38,9 +53,8 @@ module.exports = __toCommonJS(index_exports);
38
53
  // src/server.ts
39
54
  var import_react_router = require("react-router");
40
55
 
41
- // src/adapters/api-gateway-v1.ts
56
+ // src/adapters/api-gateway-v2.ts
42
57
  var import_node = require("@react-router/node");
43
- var import_url = require("url");
44
58
 
45
59
  // src/binaryTypes.ts
46
60
  var binaryTypes = [
@@ -108,32 +122,38 @@ function isBinaryType(contentType) {
108
122
  return binaryTypes.includes(test);
109
123
  }
110
124
 
111
- // src/adapters/api-gateway-v1.ts
112
- function createReactRouterRequestAPIGatewayV1(event) {
113
- const host = event.headers["x-forwarded-host"] || event.headers.Host;
125
+ // src/adapters/api-gateway-v2.ts
126
+ function createReactRouterRequestAPIGateywayV2(event) {
127
+ const host = event.headers["x-forwarded-host"] || event.headers.host;
128
+ const search = event.rawQueryString.length ? `?${event.rawQueryString}` : "";
114
129
  const scheme = event.headers["x-forwarded-proto"] || "http";
115
- const rawQueryString = new import_url.URLSearchParams(event.queryStringParameters).toString();
116
- const search = rawQueryString.length > 0 ? `?${rawQueryString}` : "";
117
- const url = new URL(event.path + search, `${scheme}://${host}`);
130
+ const url = new URL(event.rawPath + search, `${scheme}://${host}`);
118
131
  const isFormData = event.headers["content-type"]?.includes("multipart/form-data");
119
132
  const controller = new AbortController();
120
133
  return new Request(url.href, {
121
- method: event.requestContext.httpMethod,
122
- headers: createReactRouterHeadersAPIGatewayV1(event.headers),
134
+ method: event.requestContext.http.method,
135
+ headers: createReactRouterHeadersAPIGatewayV2(event.headers, event.cookies),
123
136
  signal: controller.signal,
124
- body: event.body && event.isBase64Encoded ? isFormData ? Buffer.from(event.body, "base64") : Buffer.from(event.body, "base64").toString() : event.body || void 0
137
+ body: event.body && event.isBase64Encoded ? isFormData ? Buffer.from(event.body, "base64") : Buffer.from(event.body, "base64").toString() : event.body
125
138
  });
126
139
  }
127
- function createReactRouterHeadersAPIGatewayV1(requestHeaders) {
140
+ function createReactRouterHeadersAPIGatewayV2(requestHeaders, requestCookies) {
128
141
  const headers = new Headers();
129
142
  for (const [header, value] of Object.entries(requestHeaders)) {
130
143
  if (value) {
131
144
  headers.append(header, value);
132
145
  }
133
146
  }
147
+ if (requestCookies) {
148
+ headers.append("Cookie", requestCookies.join("; "));
149
+ }
134
150
  return headers;
135
151
  }
136
- async function sendReactRouterResponseAPIGatewayV1(nodeResponse) {
152
+ async function sendReactRouterResponseAPIGatewayV2(nodeResponse) {
153
+ const cookies = nodeResponse.headers.getSetCookie();
154
+ if (cookies.length) {
155
+ nodeResponse.headers.delete("Set-Cookie");
156
+ }
137
157
  const contentType = nodeResponse.headers.get("Content-Type");
138
158
  const isBase64Encoded = isBinaryType(contentType);
139
159
  let body;
@@ -147,48 +167,45 @@ async function sendReactRouterResponseAPIGatewayV1(nodeResponse) {
147
167
  return {
148
168
  statusCode: nodeResponse.status,
149
169
  headers: Object.fromEntries(nodeResponse.headers.entries()),
150
- body: body || "",
170
+ cookies,
171
+ body,
151
172
  isBase64Encoded
152
173
  };
153
174
  }
154
- var apiGatewayV1Adapter = {
155
- createReactRouterRequest: createReactRouterRequestAPIGatewayV1,
156
- sendReactRouterResponse: sendReactRouterResponseAPIGatewayV1
175
+ var apiGatewayV2Adapter = {
176
+ wrapHandler: (handler) => (e) => handler(e),
177
+ createReactRouterRequest: createReactRouterRequestAPIGateywayV2,
178
+ sendReactRouterResponse: sendReactRouterResponseAPIGatewayV2
157
179
  };
158
180
 
159
- // src/adapters/api-gateway-v2.ts
181
+ // src/adapters/api-gateway-v1.ts
160
182
  var import_node2 = require("@react-router/node");
161
- function createReactRouterRequestAPIGateywayV2(event) {
162
- const host = event.headers["x-forwarded-host"] || event.headers.host;
163
- const search = event.rawQueryString.length ? `?${event.rawQueryString}` : "";
183
+ var import_url = require("url");
184
+ function createReactRouterRequestAPIGatewayV1(event) {
185
+ const host = event.headers["x-forwarded-host"] || event.headers.Host;
164
186
  const scheme = event.headers["x-forwarded-proto"] || "http";
165
- const url = new URL(event.rawPath + search, `${scheme}://${host}`);
187
+ const rawQueryString = new import_url.URLSearchParams(event.queryStringParameters).toString();
188
+ const search = rawQueryString.length > 0 ? `?${rawQueryString}` : "";
189
+ const url = new URL(event.path + search, `${scheme}://${host}`);
166
190
  const isFormData = event.headers["content-type"]?.includes("multipart/form-data");
167
191
  const controller = new AbortController();
168
192
  return new Request(url.href, {
169
- method: event.requestContext.http.method,
170
- headers: createReactRouterHeadersAPIGatewayV2(event.headers, event.cookies),
193
+ method: event.requestContext.httpMethod,
194
+ headers: createReactRouterHeadersAPIGatewayV1(event.headers),
171
195
  signal: controller.signal,
172
- body: event.body && event.isBase64Encoded ? isFormData ? Buffer.from(event.body, "base64") : Buffer.from(event.body, "base64").toString() : event.body
196
+ body: event.body && event.isBase64Encoded ? isFormData ? Buffer.from(event.body, "base64") : Buffer.from(event.body, "base64").toString() : event.body || void 0
173
197
  });
174
198
  }
175
- function createReactRouterHeadersAPIGatewayV2(requestHeaders, requestCookies) {
199
+ function createReactRouterHeadersAPIGatewayV1(requestHeaders) {
176
200
  const headers = new Headers();
177
201
  for (const [header, value] of Object.entries(requestHeaders)) {
178
202
  if (value) {
179
203
  headers.append(header, value);
180
204
  }
181
205
  }
182
- if (requestCookies) {
183
- headers.append("Cookie", requestCookies.join("; "));
184
- }
185
206
  return headers;
186
207
  }
187
- async function sendReactRouterResponseAPIGatewayV2(nodeResponse) {
188
- const cookies = nodeResponse.headers.getSetCookie();
189
- if (cookies.length) {
190
- nodeResponse.headers.delete("Set-Cookie");
191
- }
208
+ async function sendReactRouterResponseAPIGatewayV1(nodeResponse) {
192
209
  const contentType = nodeResponse.headers.get("Content-Type");
193
210
  const isBase64Encoded = isBinaryType(contentType);
194
211
  let body;
@@ -202,14 +219,14 @@ async function sendReactRouterResponseAPIGatewayV2(nodeResponse) {
202
219
  return {
203
220
  statusCode: nodeResponse.status,
204
221
  headers: Object.fromEntries(nodeResponse.headers.entries()),
205
- cookies,
206
- body,
222
+ body: body || "",
207
223
  isBase64Encoded
208
224
  };
209
225
  }
210
- var apiGatewayV2Adapter = {
211
- createReactRouterRequest: createReactRouterRequestAPIGateywayV2,
212
- sendReactRouterResponse: sendReactRouterResponseAPIGatewayV2
226
+ var apiGatewayV1Adapter = {
227
+ wrapHandler: (handler) => (e) => handler(e),
228
+ createReactRouterRequest: createReactRouterRequestAPIGatewayV1,
229
+ sendReactRouterResponse: sendReactRouterResponseAPIGatewayV1
213
230
  };
214
231
 
215
232
  // src/adapters/application-load-balancer.ts
@@ -259,55 +276,109 @@ async function sendReactRouterResponseALB(nodeResponse) {
259
276
  };
260
277
  }
261
278
  var applicationLoadBalancerAdapter = {
279
+ wrapHandler: (handler) => (e) => handler(e),
262
280
  createReactRouterRequest: createReactRouterRequestALB,
263
281
  sendReactRouterResponse: sendReactRouterResponseALB
264
282
  };
265
283
 
266
- // src/adapters/index.ts
267
- var createReactRouterAdapter = (awsProxy) => {
268
- switch (awsProxy) {
269
- case "APIGatewayV1" /* APIGatewayV1 */:
270
- return apiGatewayV1Adapter;
271
- case "APIGatewayV2" /* APIGatewayV2 */:
272
- case "FunctionURL" /* FunctionURL */:
273
- return apiGatewayV2Adapter;
274
- case "ALB" /* ALB */:
275
- return applicationLoadBalancerAdapter;
284
+ // src/adapters/function-url-streaming.ts
285
+ var import_node4 = require("@react-router/node");
286
+ var import_promises = __toESM(require("stream/promises"));
287
+ var sendReactRouterResponseFunctionUrlStreaming = async (response, responseStream) => {
288
+ const httpResponseStream = awslambda.HttpResponseStream.from(responseStream, {
289
+ statusCode: response.status,
290
+ headers: Object.fromEntries(response.headers.entries())
291
+ });
292
+ if (response.body) {
293
+ await (0, import_node4.writeReadableStreamToWritable)(response.body, httpResponseStream);
294
+ } else {
295
+ await import_promises.default.finished(httpResponseStream);
276
296
  }
277
297
  };
298
+ var functionUrlStreamingAdapter = {
299
+ wrapHandler: awslambda.streamifyResponse,
300
+ createReactRouterRequest: apiGatewayV2Adapter.createReactRouterRequest,
301
+ sendReactRouterResponse: sendReactRouterResponseFunctionUrlStreaming
302
+ };
278
303
 
279
304
  // src/server.ts
280
- var AWSProxy = /* @__PURE__ */ ((AWSProxy2) => {
281
- AWSProxy2["APIGatewayV1"] = "APIGatewayV1";
282
- AWSProxy2["APIGatewayV2"] = "APIGatewayV2";
283
- AWSProxy2["ALB"] = "ALB";
284
- AWSProxy2["FunctionURL"] = "FunctionURL";
285
- return AWSProxy2;
286
- })(AWSProxy || {});
287
- function createRequestHandler({
288
- build,
289
- getLoadContext,
290
- mode = process.env.NODE_ENV,
291
- awsProxy = "APIGatewayV2" /* APIGatewayV2 */
292
- }) {
305
+ function createAPIGatewayV1RequestHandler(options) {
306
+ return createRequestHandlerForAdapter(apiGatewayV1Adapter, options);
307
+ }
308
+ function createAPIGatewayV2RequestHandler(options) {
309
+ return createRequestHandlerForAdapter(apiGatewayV2Adapter, options);
310
+ }
311
+ function createALBRequestHandler(options) {
312
+ return createRequestHandlerForAdapter(applicationLoadBalancerAdapter, options);
313
+ }
314
+ function createFunctionURLRequestHandler(options) {
315
+ return createRequestHandlerForAdapter(apiGatewayV2Adapter, options);
316
+ }
317
+ function createFunctionURLStreamingRequestHandler(options) {
318
+ return createRequestHandlerForAdapter(functionUrlStreamingAdapter, options);
319
+ }
320
+ function createRequestHandlerForAdapter(awsAdapter, { build, getLoadContext, mode = process.env.NODE_ENV }) {
293
321
  const handleRequest = (0, import_react_router.createRequestHandler)(build, mode);
294
- return async (event) => {
295
- const awsAdapter = createReactRouterAdapter(awsProxy);
322
+ return awsAdapter.wrapHandler(async (event, res) => {
296
323
  let request;
297
324
  try {
298
325
  request = awsAdapter.createReactRouterRequest(event);
299
326
  } catch (e) {
300
- return awsAdapter.sendReactRouterResponse(
301
- new Response(`Bad Request: ${e instanceof Error ? e.message : e}`, { status: 400 })
327
+ return await awsAdapter.sendReactRouterResponse(
328
+ new Response(`Bad Request: ${e instanceof Error ? e.message : e}`, { status: 400 }),
329
+ res
302
330
  );
303
331
  }
304
332
  const loadContext = await getLoadContext?.(event);
305
333
  const response = await handleRequest(request, loadContext);
306
- return awsAdapter.sendReactRouterResponse(response);
307
- };
334
+ return await awsAdapter.sendReactRouterResponse(response, res);
335
+ });
336
+ }
337
+
338
+ // src/legacy.ts
339
+ var AWSProxy = /* @__PURE__ */ ((AWSProxy2) => {
340
+ AWSProxy2["APIGatewayV1"] = "APIGatewayV1";
341
+ AWSProxy2["APIGatewayV2"] = "APIGatewayV2";
342
+ AWSProxy2["ALB"] = "ALB";
343
+ AWSProxy2["FunctionURL"] = "FunctionURL";
344
+ AWSProxy2["FunctionURLStreaming"] = "FunctionURLStreaming";
345
+ return AWSProxy2;
346
+ })(AWSProxy || {});
347
+ function createRequestHandler(options) {
348
+ const { awsProxy = "APIGatewayV2" /* APIGatewayV2 */, ...opts } = options;
349
+ switch (awsProxy) {
350
+ case "APIGatewayV1" /* APIGatewayV1 */:
351
+ return createAPIGatewayV1RequestHandler(
352
+ opts
353
+ );
354
+ case "APIGatewayV2" /* APIGatewayV2 */:
355
+ return createAPIGatewayV2RequestHandler(
356
+ opts
357
+ );
358
+ case "ALB" /* ALB */:
359
+ return createALBRequestHandler(opts);
360
+ case "FunctionURL" /* FunctionURL */:
361
+ return createFunctionURLRequestHandler(
362
+ opts
363
+ );
364
+ case "FunctionURLStreaming" /* FunctionURLStreaming */:
365
+ return createFunctionURLStreamingRequestHandler(
366
+ opts
367
+ );
368
+ default:
369
+ return assertNever(awsProxy, `Unsupported buffered AWS Proxy type: ${awsProxy}`);
370
+ }
371
+ }
372
+ function assertNever(x, message) {
373
+ throw new Error(message);
308
374
  }
309
375
  // Annotate the CommonJS export names for ESM import in node:
310
376
  0 && (module.exports = {
311
377
  AWSProxy,
378
+ createALBRequestHandler,
379
+ createAPIGatewayV1RequestHandler,
380
+ createAPIGatewayV2RequestHandler,
381
+ createFunctionURLRequestHandler,
382
+ createFunctionURLStreamingRequestHandler,
312
383
  createRequestHandler
313
384
  });
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @geostrategists/react-router-aws v2.0.0
2
+ * @geostrategists/react-router-aws v2.1.0
3
3
  *
4
4
  * Copyright (c) Geostrategists Consulting GmbH
5
5
  *
@@ -10,11 +10,12 @@
10
10
  */
11
11
 
12
12
  // src/server.ts
13
- import { createRequestHandler as createReactRouterRequestHandler } from "react-router";
13
+ import {
14
+ createRequestHandler as createReactRouterRequestHandler
15
+ } from "react-router";
14
16
 
15
- // src/adapters/api-gateway-v1.ts
17
+ // src/adapters/api-gateway-v2.ts
16
18
  import { readableStreamToString } from "@react-router/node";
17
- import { URLSearchParams } from "url";
18
19
 
19
20
  // src/binaryTypes.ts
20
21
  var binaryTypes = [
@@ -82,32 +83,38 @@ function isBinaryType(contentType) {
82
83
  return binaryTypes.includes(test);
83
84
  }
84
85
 
85
- // src/adapters/api-gateway-v1.ts
86
- function createReactRouterRequestAPIGatewayV1(event) {
87
- const host = event.headers["x-forwarded-host"] || event.headers.Host;
86
+ // src/adapters/api-gateway-v2.ts
87
+ function createReactRouterRequestAPIGateywayV2(event) {
88
+ const host = event.headers["x-forwarded-host"] || event.headers.host;
89
+ const search = event.rawQueryString.length ? `?${event.rawQueryString}` : "";
88
90
  const scheme = event.headers["x-forwarded-proto"] || "http";
89
- const rawQueryString = new URLSearchParams(event.queryStringParameters).toString();
90
- const search = rawQueryString.length > 0 ? `?${rawQueryString}` : "";
91
- const url = new URL(event.path + search, `${scheme}://${host}`);
91
+ const url = new URL(event.rawPath + search, `${scheme}://${host}`);
92
92
  const isFormData = event.headers["content-type"]?.includes("multipart/form-data");
93
93
  const controller = new AbortController();
94
94
  return new Request(url.href, {
95
- method: event.requestContext.httpMethod,
96
- headers: createReactRouterHeadersAPIGatewayV1(event.headers),
95
+ method: event.requestContext.http.method,
96
+ headers: createReactRouterHeadersAPIGatewayV2(event.headers, event.cookies),
97
97
  signal: controller.signal,
98
- body: event.body && event.isBase64Encoded ? isFormData ? Buffer.from(event.body, "base64") : Buffer.from(event.body, "base64").toString() : event.body || void 0
98
+ body: event.body && event.isBase64Encoded ? isFormData ? Buffer.from(event.body, "base64") : Buffer.from(event.body, "base64").toString() : event.body
99
99
  });
100
100
  }
101
- function createReactRouterHeadersAPIGatewayV1(requestHeaders) {
101
+ function createReactRouterHeadersAPIGatewayV2(requestHeaders, requestCookies) {
102
102
  const headers = new Headers();
103
103
  for (const [header, value] of Object.entries(requestHeaders)) {
104
104
  if (value) {
105
105
  headers.append(header, value);
106
106
  }
107
107
  }
108
+ if (requestCookies) {
109
+ headers.append("Cookie", requestCookies.join("; "));
110
+ }
108
111
  return headers;
109
112
  }
110
- async function sendReactRouterResponseAPIGatewayV1(nodeResponse) {
113
+ async function sendReactRouterResponseAPIGatewayV2(nodeResponse) {
114
+ const cookies = nodeResponse.headers.getSetCookie();
115
+ if (cookies.length) {
116
+ nodeResponse.headers.delete("Set-Cookie");
117
+ }
111
118
  const contentType = nodeResponse.headers.get("Content-Type");
112
119
  const isBase64Encoded = isBinaryType(contentType);
113
120
  let body;
@@ -121,48 +128,45 @@ async function sendReactRouterResponseAPIGatewayV1(nodeResponse) {
121
128
  return {
122
129
  statusCode: nodeResponse.status,
123
130
  headers: Object.fromEntries(nodeResponse.headers.entries()),
124
- body: body || "",
131
+ cookies,
132
+ body,
125
133
  isBase64Encoded
126
134
  };
127
135
  }
128
- var apiGatewayV1Adapter = {
129
- createReactRouterRequest: createReactRouterRequestAPIGatewayV1,
130
- sendReactRouterResponse: sendReactRouterResponseAPIGatewayV1
136
+ var apiGatewayV2Adapter = {
137
+ wrapHandler: (handler) => (e) => handler(e),
138
+ createReactRouterRequest: createReactRouterRequestAPIGateywayV2,
139
+ sendReactRouterResponse: sendReactRouterResponseAPIGatewayV2
131
140
  };
132
141
 
133
- // src/adapters/api-gateway-v2.ts
142
+ // src/adapters/api-gateway-v1.ts
134
143
  import { readableStreamToString as readableStreamToString2 } from "@react-router/node";
135
- function createReactRouterRequestAPIGateywayV2(event) {
136
- const host = event.headers["x-forwarded-host"] || event.headers.host;
137
- const search = event.rawQueryString.length ? `?${event.rawQueryString}` : "";
144
+ import { URLSearchParams } from "url";
145
+ function createReactRouterRequestAPIGatewayV1(event) {
146
+ const host = event.headers["x-forwarded-host"] || event.headers.Host;
138
147
  const scheme = event.headers["x-forwarded-proto"] || "http";
139
- const url = new URL(event.rawPath + search, `${scheme}://${host}`);
148
+ const rawQueryString = new URLSearchParams(event.queryStringParameters).toString();
149
+ const search = rawQueryString.length > 0 ? `?${rawQueryString}` : "";
150
+ const url = new URL(event.path + search, `${scheme}://${host}`);
140
151
  const isFormData = event.headers["content-type"]?.includes("multipart/form-data");
141
152
  const controller = new AbortController();
142
153
  return new Request(url.href, {
143
- method: event.requestContext.http.method,
144
- headers: createReactRouterHeadersAPIGatewayV2(event.headers, event.cookies),
154
+ method: event.requestContext.httpMethod,
155
+ headers: createReactRouterHeadersAPIGatewayV1(event.headers),
145
156
  signal: controller.signal,
146
- body: event.body && event.isBase64Encoded ? isFormData ? Buffer.from(event.body, "base64") : Buffer.from(event.body, "base64").toString() : event.body
157
+ body: event.body && event.isBase64Encoded ? isFormData ? Buffer.from(event.body, "base64") : Buffer.from(event.body, "base64").toString() : event.body || void 0
147
158
  });
148
159
  }
149
- function createReactRouterHeadersAPIGatewayV2(requestHeaders, requestCookies) {
160
+ function createReactRouterHeadersAPIGatewayV1(requestHeaders) {
150
161
  const headers = new Headers();
151
162
  for (const [header, value] of Object.entries(requestHeaders)) {
152
163
  if (value) {
153
164
  headers.append(header, value);
154
165
  }
155
166
  }
156
- if (requestCookies) {
157
- headers.append("Cookie", requestCookies.join("; "));
158
- }
159
167
  return headers;
160
168
  }
161
- async function sendReactRouterResponseAPIGatewayV2(nodeResponse) {
162
- const cookies = nodeResponse.headers.getSetCookie();
163
- if (cookies.length) {
164
- nodeResponse.headers.delete("Set-Cookie");
165
- }
169
+ async function sendReactRouterResponseAPIGatewayV1(nodeResponse) {
166
170
  const contentType = nodeResponse.headers.get("Content-Type");
167
171
  const isBase64Encoded = isBinaryType(contentType);
168
172
  let body;
@@ -176,14 +180,14 @@ async function sendReactRouterResponseAPIGatewayV2(nodeResponse) {
176
180
  return {
177
181
  statusCode: nodeResponse.status,
178
182
  headers: Object.fromEntries(nodeResponse.headers.entries()),
179
- cookies,
180
- body,
183
+ body: body || "",
181
184
  isBase64Encoded
182
185
  };
183
186
  }
184
- var apiGatewayV2Adapter = {
185
- createReactRouterRequest: createReactRouterRequestAPIGateywayV2,
186
- sendReactRouterResponse: sendReactRouterResponseAPIGatewayV2
187
+ var apiGatewayV1Adapter = {
188
+ wrapHandler: (handler) => (e) => handler(e),
189
+ createReactRouterRequest: createReactRouterRequestAPIGatewayV1,
190
+ sendReactRouterResponse: sendReactRouterResponseAPIGatewayV1
187
191
  };
188
192
 
189
193
  // src/adapters/application-load-balancer.ts
@@ -233,54 +237,108 @@ async function sendReactRouterResponseALB(nodeResponse) {
233
237
  };
234
238
  }
235
239
  var applicationLoadBalancerAdapter = {
240
+ wrapHandler: (handler) => (e) => handler(e),
236
241
  createReactRouterRequest: createReactRouterRequestALB,
237
242
  sendReactRouterResponse: sendReactRouterResponseALB
238
243
  };
239
244
 
240
- // src/adapters/index.ts
241
- var createReactRouterAdapter = (awsProxy) => {
242
- switch (awsProxy) {
243
- case "APIGatewayV1" /* APIGatewayV1 */:
244
- return apiGatewayV1Adapter;
245
- case "APIGatewayV2" /* APIGatewayV2 */:
246
- case "FunctionURL" /* FunctionURL */:
247
- return apiGatewayV2Adapter;
248
- case "ALB" /* ALB */:
249
- return applicationLoadBalancerAdapter;
245
+ // src/adapters/function-url-streaming.ts
246
+ import { writeReadableStreamToWritable } from "@react-router/node";
247
+ import stream from "node:stream/promises";
248
+ var sendReactRouterResponseFunctionUrlStreaming = async (response, responseStream) => {
249
+ const httpResponseStream = awslambda.HttpResponseStream.from(responseStream, {
250
+ statusCode: response.status,
251
+ headers: Object.fromEntries(response.headers.entries())
252
+ });
253
+ if (response.body) {
254
+ await writeReadableStreamToWritable(response.body, httpResponseStream);
255
+ } else {
256
+ await stream.finished(httpResponseStream);
250
257
  }
251
258
  };
259
+ var functionUrlStreamingAdapter = {
260
+ wrapHandler: awslambda.streamifyResponse,
261
+ createReactRouterRequest: apiGatewayV2Adapter.createReactRouterRequest,
262
+ sendReactRouterResponse: sendReactRouterResponseFunctionUrlStreaming
263
+ };
252
264
 
253
265
  // src/server.ts
254
- var AWSProxy = /* @__PURE__ */ ((AWSProxy2) => {
255
- AWSProxy2["APIGatewayV1"] = "APIGatewayV1";
256
- AWSProxy2["APIGatewayV2"] = "APIGatewayV2";
257
- AWSProxy2["ALB"] = "ALB";
258
- AWSProxy2["FunctionURL"] = "FunctionURL";
259
- return AWSProxy2;
260
- })(AWSProxy || {});
261
- function createRequestHandler({
262
- build,
263
- getLoadContext,
264
- mode = process.env.NODE_ENV,
265
- awsProxy = "APIGatewayV2" /* APIGatewayV2 */
266
- }) {
266
+ function createAPIGatewayV1RequestHandler(options) {
267
+ return createRequestHandlerForAdapter(apiGatewayV1Adapter, options);
268
+ }
269
+ function createAPIGatewayV2RequestHandler(options) {
270
+ return createRequestHandlerForAdapter(apiGatewayV2Adapter, options);
271
+ }
272
+ function createALBRequestHandler(options) {
273
+ return createRequestHandlerForAdapter(applicationLoadBalancerAdapter, options);
274
+ }
275
+ function createFunctionURLRequestHandler(options) {
276
+ return createRequestHandlerForAdapter(apiGatewayV2Adapter, options);
277
+ }
278
+ function createFunctionURLStreamingRequestHandler(options) {
279
+ return createRequestHandlerForAdapter(functionUrlStreamingAdapter, options);
280
+ }
281
+ function createRequestHandlerForAdapter(awsAdapter, { build, getLoadContext, mode = process.env.NODE_ENV }) {
267
282
  const handleRequest = createReactRouterRequestHandler(build, mode);
268
- return async (event) => {
269
- const awsAdapter = createReactRouterAdapter(awsProxy);
283
+ return awsAdapter.wrapHandler(async (event, res) => {
270
284
  let request;
271
285
  try {
272
286
  request = awsAdapter.createReactRouterRequest(event);
273
287
  } catch (e) {
274
- return awsAdapter.sendReactRouterResponse(
275
- new Response(`Bad Request: ${e instanceof Error ? e.message : e}`, { status: 400 })
288
+ return await awsAdapter.sendReactRouterResponse(
289
+ new Response(`Bad Request: ${e instanceof Error ? e.message : e}`, { status: 400 }),
290
+ res
276
291
  );
277
292
  }
278
293
  const loadContext = await getLoadContext?.(event);
279
294
  const response = await handleRequest(request, loadContext);
280
- return awsAdapter.sendReactRouterResponse(response);
281
- };
295
+ return await awsAdapter.sendReactRouterResponse(response, res);
296
+ });
297
+ }
298
+
299
+ // src/legacy.ts
300
+ var AWSProxy = /* @__PURE__ */ ((AWSProxy2) => {
301
+ AWSProxy2["APIGatewayV1"] = "APIGatewayV1";
302
+ AWSProxy2["APIGatewayV2"] = "APIGatewayV2";
303
+ AWSProxy2["ALB"] = "ALB";
304
+ AWSProxy2["FunctionURL"] = "FunctionURL";
305
+ AWSProxy2["FunctionURLStreaming"] = "FunctionURLStreaming";
306
+ return AWSProxy2;
307
+ })(AWSProxy || {});
308
+ function createRequestHandler(options) {
309
+ const { awsProxy = "APIGatewayV2" /* APIGatewayV2 */, ...opts } = options;
310
+ switch (awsProxy) {
311
+ case "APIGatewayV1" /* APIGatewayV1 */:
312
+ return createAPIGatewayV1RequestHandler(
313
+ opts
314
+ );
315
+ case "APIGatewayV2" /* APIGatewayV2 */:
316
+ return createAPIGatewayV2RequestHandler(
317
+ opts
318
+ );
319
+ case "ALB" /* ALB */:
320
+ return createALBRequestHandler(opts);
321
+ case "FunctionURL" /* FunctionURL */:
322
+ return createFunctionURLRequestHandler(
323
+ opts
324
+ );
325
+ case "FunctionURLStreaming" /* FunctionURLStreaming */:
326
+ return createFunctionURLStreamingRequestHandler(
327
+ opts
328
+ );
329
+ default:
330
+ return assertNever(awsProxy, `Unsupported buffered AWS Proxy type: ${awsProxy}`);
331
+ }
332
+ }
333
+ function assertNever(x, message) {
334
+ throw new Error(message);
282
335
  }
283
336
  export {
284
337
  AWSProxy,
338
+ createALBRequestHandler,
339
+ createAPIGatewayV1RequestHandler,
340
+ createAPIGatewayV2RequestHandler,
341
+ createFunctionURLRequestHandler,
342
+ createFunctionURLStreamingRequestHandler,
285
343
  createRequestHandler
286
344
  };
package/eslint.config.mjs CHANGED
@@ -5,7 +5,7 @@ import tseslint from "typescript-eslint";
5
5
 
6
6
  export default tseslint.config(
7
7
  {
8
- ignores: ["dist"],
8
+ ignores: ["dist", "lib"],
9
9
  },
10
10
  eslint.configs.recommended,
11
11
  tseslint.configs.recommended,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geostrategists/react-router-aws",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "AWS adapter for React Router v7",
5
5
  "license": "MIT",
6
6
  "bugs": {
@@ -48,7 +48,7 @@
48
48
  "@react-router/dev": "^7.3.0",
49
49
  "@react-router/node": "^7.3.0",
50
50
  "@semantic-release/exec": "^7.0.3",
51
- "@types/aws-lambda": "^8.10.125",
51
+ "@types/aws-lambda": "^8.10.152",
52
52
  "@types/node": "^20",
53
53
  "eslint": "^9.22.0",
54
54
  "husky": "^9.1.7",
@@ -58,10 +58,10 @@
58
58
  "react-router": "^7.3.0",
59
59
  "semantic-release": "^24.2.3",
60
60
  "tsup": "^8.4.0",
61
- "typescript": "^5.8.2",
62
- "typescript-eslint": "^8.26.1"
61
+ "typescript": "^5.9.2",
62
+ "typescript-eslint": "^8.41.0"
63
63
  },
64
- "packageManager": "yarn@4.7.0+sha256.293632d8a095d8ea4786eb2c5798c83c37544abed17ed31186a3ec4549a07c06",
64
+ "packageManager": "yarn@4.9.4+sha512.7b1cb0b62abba6a537b3a2ce00811a843bea02bcf53138581a6ae5b1bf563f734872bd47de49ce32a9ca9dcaff995aa789577ffb16811da7c603dcf69e73750b",
65
65
  "release": {
66
66
  "plugins": [
67
67
  "@semantic-release/commit-analyzer",