@opennextjs/aws 3.5.5 → 3.5.7
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/dist/build/createAssets.d.ts +11 -1
- package/dist/build/createAssets.js +18 -3
- package/dist/build/createServerBundle.js +6 -2
- package/dist/build/patch/patches/index.d.ts +4 -0
- package/dist/build/patch/patches/index.js +4 -0
- package/dist/build/patch/patches/patchBackgroundRevalidation.d.ts +11 -0
- package/dist/build/patch/patches/patchBackgroundRevalidation.js +27 -0
- package/dist/build/patch/patches/patchEnvVar.d.ts +10 -0
- package/dist/build/patch/patches/patchEnvVar.js +52 -0
- package/dist/build/patch/{patchFetchCacheISR.d.ts → patches/patchFetchCacheISR.d.ts} +1 -1
- package/dist/build/patch/{patchFetchCacheISR.js → patches/patchFetchCacheISR.js} +2 -2
- package/dist/build/patch/{patchFetchCacheWaitUntil.d.ts → patches/patchFetchCacheWaitUntil.d.ts} +1 -1
- package/dist/build/patch/{patchFetchCacheWaitUntil.js → patches/patchFetchCacheWaitUntil.js} +2 -2
- package/dist/build/patch/patches/patchNextServer.d.ts +5 -0
- package/dist/build/patch/patches/patchNextServer.js +91 -0
- package/dist/build/validateConfig.js +1 -0
- package/dist/core/createMainHandler.js +2 -0
- package/dist/core/requestHandler.js +7 -1
- package/dist/core/routing/matcher.js +4 -10
- package/dist/core/routing/middleware.d.ts +8 -1
- package/dist/core/routing/middleware.js +25 -40
- package/dist/core/routing/util.d.ts +5 -2
- package/dist/core/routing/util.js +15 -17
- package/dist/core/routingHandler.js +5 -1
- package/dist/core/util.js +41 -1
- package/dist/http/util.d.ts +7 -0
- package/dist/http/util.js +23 -0
- package/dist/overrides/converters/edge.js +2 -14
- package/dist/overrides/converters/node.js +2 -2
- package/dist/overrides/converters/utils.d.ts +7 -0
- package/dist/overrides/converters/utils.js +10 -0
- package/dist/overrides/wrappers/aws-lambda-compressed.d.ts +7 -0
- package/dist/overrides/wrappers/aws-lambda-compressed.js +96 -0
- package/dist/overrides/wrappers/aws-lambda-streaming.js +1 -0
- package/dist/overrides/wrappers/aws-lambda.d.ts +2 -0
- package/dist/overrides/wrappers/aws-lambda.js +1 -1
- package/dist/types/aws-lambda.d.ts +4 -1
- package/dist/types/global.d.ts +6 -0
- package/dist/types/open-next.d.ts +12 -1
- package/package.json +1 -1
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import type { TagCacheMetaFile } from "../types/cache.js";
|
|
2
2
|
import * as buildHelper from "./helper.js";
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Copy the static assets to the output folder
|
|
5
|
+
*
|
|
6
|
+
* WARNING: `useBasePath` should be set to `false` when the output file is used.
|
|
7
|
+
*
|
|
8
|
+
* @param options OpenNext build options
|
|
9
|
+
* @param useBasePath whether to copy files into the to Next.js configured basePath
|
|
10
|
+
*/
|
|
11
|
+
export declare function createStaticAssets(options: buildHelper.BuildOptions, { useBasePath }?: {
|
|
12
|
+
useBasePath?: boolean | undefined;
|
|
13
|
+
}): void;
|
|
4
14
|
/**
|
|
5
15
|
* Create the cache assets.
|
|
6
16
|
*
|
|
@@ -1,16 +1,31 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { loadConfig } from "../adapters/config/util.js";
|
|
3
4
|
import logger from "../logger.js";
|
|
4
5
|
import { isBinaryContentType } from "../utils/binary.js";
|
|
5
6
|
import * as buildHelper from "./helper.js";
|
|
6
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Copy the static assets to the output folder
|
|
9
|
+
*
|
|
10
|
+
* WARNING: `useBasePath` should be set to `false` when the output file is used.
|
|
11
|
+
*
|
|
12
|
+
* @param options OpenNext build options
|
|
13
|
+
* @param useBasePath whether to copy files into the to Next.js configured basePath
|
|
14
|
+
*/
|
|
15
|
+
export function createStaticAssets(options, { useBasePath = false } = {}) {
|
|
7
16
|
logger.info("Bundling static assets...");
|
|
8
17
|
const { appBuildOutputPath, appPublicPath, outputDir, appPath } = options;
|
|
18
|
+
const NextConfig = loadConfig(path.join(appBuildOutputPath, ".next"));
|
|
19
|
+
const basePath = useBasePath ? (NextConfig.basePath ?? "") : "";
|
|
9
20
|
// Create output folder
|
|
10
|
-
const outputPath = path.join(outputDir, "assets");
|
|
21
|
+
const outputPath = path.join(outputDir, "assets", basePath);
|
|
11
22
|
fs.mkdirSync(outputPath, { recursive: true });
|
|
12
23
|
/**
|
|
13
|
-
* Next.js outputs assets into multiple files.
|
|
24
|
+
* Next.js outputs assets into multiple files.
|
|
25
|
+
*
|
|
26
|
+
* Copy into the same directory:
|
|
27
|
+
* - `.open-next/assets` when `useBasePath` is `false`
|
|
28
|
+
* - `.open-next/assets/basePath` when `useBasePath` is `true`
|
|
14
29
|
*
|
|
15
30
|
* Copy over:
|
|
16
31
|
* - .next/BUILD_ID => BUILD_ID
|
|
@@ -14,8 +14,8 @@ import { copyMiddlewareResources, generateEdgeBundle, } from "./edge/createEdgeB
|
|
|
14
14
|
import * as buildHelper from "./helper.js";
|
|
15
15
|
import { installDependencies } from "./installDeps.js";
|
|
16
16
|
import { applyCodePatches } from "./patch/codePatcher.js";
|
|
17
|
-
import { patchFetchCacheForISR, patchUnstableCacheForISR, } from "./patch/
|
|
18
|
-
import {
|
|
17
|
+
import { patchEnvVars, patchFetchCacheForISR, patchFetchCacheSetMissingWaitUntil, patchNextServer, patchUnstableCacheForISR, } from "./patch/patches/index.js";
|
|
18
|
+
import { patchBackgroundRevalidation } from "./patch/patches/patchBackgroundRevalidation.js";
|
|
19
19
|
export async function createServerBundle(options, codeCustomization) {
|
|
20
20
|
const { config } = options;
|
|
21
21
|
const foundRoutes = new Set();
|
|
@@ -120,6 +120,9 @@ async function generateBundle(name, options, fnOptions, codeCustomization) {
|
|
|
120
120
|
patchFetchCacheSetMissingWaitUntil,
|
|
121
121
|
patchFetchCacheForISR,
|
|
122
122
|
patchUnstableCacheForISR,
|
|
123
|
+
patchNextServer,
|
|
124
|
+
patchEnvVars,
|
|
125
|
+
patchBackgroundRevalidation,
|
|
123
126
|
...additionalCodePatches,
|
|
124
127
|
]);
|
|
125
128
|
// Build Lambda code
|
|
@@ -145,6 +148,7 @@ async function generateBundle(name, options, fnOptions, codeCustomization) {
|
|
|
145
148
|
...(disableNextPrebundledReact ? ["applyNextjsPrebundledReact"] : []),
|
|
146
149
|
...(disableRouting ? ["withRouting"] : []),
|
|
147
150
|
...(isAfter142 ? ["patchAsyncStorage"] : []),
|
|
151
|
+
...(isAfter141 ? ["appendPrefetch"] : []),
|
|
148
152
|
],
|
|
149
153
|
}),
|
|
150
154
|
openNextReplacementPlugin({
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { envVarRuleCreator, patchEnvVars } from "./patchEnvVar.js";
|
|
2
|
+
export { patchNextServer } from "./patchNextServer.js";
|
|
3
|
+
export { patchFetchCacheForISR, patchUnstableCacheForISR, } from "./patchFetchCacheISR.js";
|
|
4
|
+
export { patchFetchCacheSetMissingWaitUntil } from "./patchFetchCacheWaitUntil.js";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { envVarRuleCreator, patchEnvVars } from "./patchEnvVar.js";
|
|
2
|
+
export { patchNextServer } from "./patchNextServer.js";
|
|
3
|
+
export { patchFetchCacheForISR, patchUnstableCacheForISR, } from "./patchFetchCacheISR.js";
|
|
4
|
+
export { patchFetchCacheSetMissingWaitUntil } from "./patchFetchCacheWaitUntil.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const rule = "\nrule:\n kind: binary_expression\n all:\n - has:\n kind: unary_expression\n regex: \"!cachedResponse.isStale\"\n - has:\n kind: member_expression\n regex: \"context.isPrefetch\"\nfix:\n 'true'";
|
|
2
|
+
export declare const patchBackgroundRevalidation: {
|
|
3
|
+
name: string;
|
|
4
|
+
patches: {
|
|
5
|
+
versions: ">=14.1.0";
|
|
6
|
+
field: {
|
|
7
|
+
pathFilter: RegExp;
|
|
8
|
+
patchCode: import("../codePatcher.js").PatchCodeFn;
|
|
9
|
+
};
|
|
10
|
+
}[];
|
|
11
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { getCrossPlatformPathRegex } from "../../../utils/regex.js";
|
|
2
|
+
import { createPatchCode } from "../astCodePatcher.js";
|
|
3
|
+
export const rule = `
|
|
4
|
+
rule:
|
|
5
|
+
kind: binary_expression
|
|
6
|
+
all:
|
|
7
|
+
- has:
|
|
8
|
+
kind: unary_expression
|
|
9
|
+
regex: "!cachedResponse.isStale"
|
|
10
|
+
- has:
|
|
11
|
+
kind: member_expression
|
|
12
|
+
regex: "context.isPrefetch"
|
|
13
|
+
fix:
|
|
14
|
+
'true'`;
|
|
15
|
+
export const patchBackgroundRevalidation = {
|
|
16
|
+
name: "patchBackgroundRevalidation",
|
|
17
|
+
patches: [
|
|
18
|
+
{
|
|
19
|
+
// TODO: test for earlier versions of Next
|
|
20
|
+
versions: ">=14.1.0",
|
|
21
|
+
field: {
|
|
22
|
+
pathFilter: getCrossPlatformPathRegex("server/response-cache/index.js"),
|
|
23
|
+
patchCode: createPatchCode(rule),
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CodePatcher } from "../codePatcher";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a rule to replace `process.env.${envVar}` by `value` in the condition of if statements
|
|
4
|
+
* This is used to avoid loading unnecessary deps at runtime
|
|
5
|
+
* @param envVar The env var that we want to replace
|
|
6
|
+
* @param value The value that we want to replace it with
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export declare const envVarRuleCreator: (envVar: string, value: string) => string;
|
|
10
|
+
export declare const patchEnvVars: CodePatcher;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createPatchCode } from "../astCodePatcher.js";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a rule to replace `process.env.${envVar}` by `value` in the condition of if statements
|
|
4
|
+
* This is used to avoid loading unnecessary deps at runtime
|
|
5
|
+
* @param envVar The env var that we want to replace
|
|
6
|
+
* @param value The value that we want to replace it with
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export const envVarRuleCreator = (envVar, value) => `
|
|
10
|
+
rule:
|
|
11
|
+
kind: member_expression
|
|
12
|
+
pattern: process.env.${envVar}
|
|
13
|
+
inside:
|
|
14
|
+
kind: parenthesized_expression
|
|
15
|
+
stopBy: end
|
|
16
|
+
inside:
|
|
17
|
+
kind: if_statement
|
|
18
|
+
fix:
|
|
19
|
+
'${value}'
|
|
20
|
+
`;
|
|
21
|
+
export const patchEnvVars = {
|
|
22
|
+
name: "patch-env-vars",
|
|
23
|
+
patches: [
|
|
24
|
+
// This patch will set the `NEXT_RUNTIME` env var to "node" to avoid loading unnecessary edge deps at runtime
|
|
25
|
+
{
|
|
26
|
+
versions: ">=15.0.0",
|
|
27
|
+
field: {
|
|
28
|
+
pathFilter: /module\.compiled\.js$/,
|
|
29
|
+
contentFilter: /process\.env\.NEXT_RUNTIME/,
|
|
30
|
+
patchCode: createPatchCode(envVarRuleCreator("NEXT_RUNTIME", '"node"')),
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
// This patch will set `NODE_ENV` to production to avoid loading unnecessary dev deps at runtime
|
|
34
|
+
{
|
|
35
|
+
versions: ">=15.0.0",
|
|
36
|
+
field: {
|
|
37
|
+
pathFilter: /(module\.compiled|react\/index|react\/jsx-runtime|react-dom\/index)\.js$/,
|
|
38
|
+
contentFilter: /process\.env\.NODE_ENV/,
|
|
39
|
+
patchCode: createPatchCode(envVarRuleCreator("NODE_ENV", '"production"')),
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
// This patch will set `TURBOPACK` env to false to avoid loading turbopack related deps at runtime
|
|
43
|
+
{
|
|
44
|
+
versions: ">=15.0.0",
|
|
45
|
+
field: {
|
|
46
|
+
pathFilter: /module\.compiled\.js$/,
|
|
47
|
+
contentFilter: /process\.env\.TURBOPACK/,
|
|
48
|
+
patchCode: createPatchCode(envVarRuleCreator("TURBOPACK", "false")),
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CodePatcher } from "
|
|
1
|
+
import type { CodePatcher } from "../codePatcher.js";
|
|
2
2
|
export declare const fetchRule = "\nrule:\n kind: member_expression\n pattern: $WORK_STORE.isOnDemandRevalidate\n inside:\n kind: ternary_expression\n all:\n - has: {kind: 'null'}\n - has: \n kind: await_expression\n has:\n kind: call_expression\n all:\n - has:\n kind: member_expression\n has:\n kind: property_identifier\n field: property\n regex: get\n - has:\n kind: arguments\n has:\n kind: object\n has:\n kind: pair\n all:\n - has:\n kind: property_identifier\n field: key\n regex: softTags\n inside:\n kind: variable_declarator\n\nfix:\n ($WORK_STORE.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation)\n";
|
|
3
3
|
export declare const unstable_cacheRule = "\nrule:\n kind: member_expression\n pattern: $STORE_OR_CACHE.isOnDemandRevalidate\n inside:\n kind: if_statement\n stopBy: end\n has:\n kind: statement_block\n has:\n kind: variable_declarator\n has: \n kind: await_expression\n has:\n kind: call_expression\n all:\n - has:\n kind: member_expression\n has:\n kind: property_identifier\n field: property\n regex: get\n - has:\n kind: arguments\n has:\n kind: object\n has:\n kind: pair\n all:\n - has:\n kind: property_identifier\n field: key\n regex: softTags\n stopBy: end\nfix:\n ($STORE_OR_CACHE.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation)\n";
|
|
4
4
|
export declare const patchFetchCacheForISR: CodePatcher;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Lang } from "@ast-grep/napi";
|
|
2
|
-
import { getCrossPlatformPathRegex } from "
|
|
3
|
-
import { createPatchCode } from "
|
|
2
|
+
import { getCrossPlatformPathRegex } from "../../../utils/regex.js";
|
|
3
|
+
import { createPatchCode } from "../astCodePatcher.js";
|
|
4
4
|
export const fetchRule = `
|
|
5
5
|
rule:
|
|
6
6
|
kind: member_expression
|
package/dist/build/patch/{patchFetchCacheWaitUntil.d.ts → patches/patchFetchCacheWaitUntil.d.ts}
RENAMED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { CodePatcher } from "
|
|
1
|
+
import type { CodePatcher } from "../codePatcher.js";
|
|
2
2
|
export declare const rule = "\nrule:\n kind: call_expression\n pattern: $PROMISE\n all:\n - has: { pattern: $_.arrayBuffer().then, stopBy: end }\n - has: { pattern: \"Buffer.from\", stopBy: end }\n - any:\n - inside:\n kind: sequence_expression\n inside:\n kind: return_statement\n - inside:\n kind: expression_statement\n precedes:\n kind: return_statement\n - has: { pattern: $_.FETCH, stopBy: end }\n\nfix: |\n globalThis.__openNextAls?.getStore()?.pendingPromiseRunner.add($PROMISE)\n";
|
|
3
3
|
export declare const patchFetchCacheSetMissingWaitUntil: CodePatcher;
|
package/dist/build/patch/{patchFetchCacheWaitUntil.js → patches/patchFetchCacheWaitUntil.js}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getCrossPlatformPathRegex } from "
|
|
2
|
-
import { createPatchCode } from "
|
|
1
|
+
import { getCrossPlatformPathRegex } from "../../../utils/regex.js";
|
|
2
|
+
import { createPatchCode } from "../astCodePatcher.js";
|
|
3
3
|
export const rule = `
|
|
4
4
|
rule:
|
|
5
5
|
kind: call_expression
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CodePatcher } from "../codePatcher.js";
|
|
2
|
+
export declare const minimalRule = "\nrule:\n kind: member_expression\n pattern: process.env.NEXT_MINIMAL\n any:\n - inside:\n kind: parenthesized_expression\n stopBy: end\n inside:\n kind: if_statement\n any:\n - inside:\n kind: statement_block\n inside:\n kind: method_definition\n any:\n - has: {kind: property_identifier, field: name, regex: runEdgeFunction}\n - has: {kind: property_identifier, field: name, regex: runMiddleware}\n - has: {kind: property_identifier, field: name, regex: imageOptimizer}\n - has:\n kind: statement_block\n has:\n kind: expression_statement\n pattern: res.statusCode = 400;\nfix:\n 'true'\n";
|
|
3
|
+
export declare const disablePreloadingRule = "\nrule:\n kind: statement_block\n inside:\n kind: if_statement\n any: \n - has: \n kind: member_expression\n pattern: this.nextConfig.experimental.preloadEntriesOnStart\n stopBy: end\n - has: \n kind: binary_expression\n pattern: appDocumentPreloading === true\n stopBy: end\nfix:\n '{}'\n";
|
|
4
|
+
export declare const removeMiddlewareManifestRule = "\nrule:\n kind: statement_block\n inside:\n kind: method_definition\n has:\n kind: property_identifier\n regex: getMiddlewareManifest\nfix:\n '{return null;}'\n";
|
|
5
|
+
export declare const patchNextServer: CodePatcher;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { createPatchCode } from "../astCodePatcher.js";
|
|
2
|
+
// This rule will replace the `NEXT_MINIMAL` env variable with true in multiple places to avoid executing unwanted path (i.e. next middleware, edge functions and image optimization)
|
|
3
|
+
export const minimalRule = `
|
|
4
|
+
rule:
|
|
5
|
+
kind: member_expression
|
|
6
|
+
pattern: process.env.NEXT_MINIMAL
|
|
7
|
+
any:
|
|
8
|
+
- inside:
|
|
9
|
+
kind: parenthesized_expression
|
|
10
|
+
stopBy: end
|
|
11
|
+
inside:
|
|
12
|
+
kind: if_statement
|
|
13
|
+
any:
|
|
14
|
+
- inside:
|
|
15
|
+
kind: statement_block
|
|
16
|
+
inside:
|
|
17
|
+
kind: method_definition
|
|
18
|
+
any:
|
|
19
|
+
- has: {kind: property_identifier, field: name, regex: runEdgeFunction}
|
|
20
|
+
- has: {kind: property_identifier, field: name, regex: runMiddleware}
|
|
21
|
+
- has: {kind: property_identifier, field: name, regex: imageOptimizer}
|
|
22
|
+
- has:
|
|
23
|
+
kind: statement_block
|
|
24
|
+
has:
|
|
25
|
+
kind: expression_statement
|
|
26
|
+
pattern: res.statusCode = 400;
|
|
27
|
+
fix:
|
|
28
|
+
'true'
|
|
29
|
+
`;
|
|
30
|
+
// This rule will disable the background preloading of route done by NextServer by default during the creation of NextServer
|
|
31
|
+
export const disablePreloadingRule = `
|
|
32
|
+
rule:
|
|
33
|
+
kind: statement_block
|
|
34
|
+
inside:
|
|
35
|
+
kind: if_statement
|
|
36
|
+
any:
|
|
37
|
+
- has:
|
|
38
|
+
kind: member_expression
|
|
39
|
+
pattern: this.nextConfig.experimental.preloadEntriesOnStart
|
|
40
|
+
stopBy: end
|
|
41
|
+
- has:
|
|
42
|
+
kind: binary_expression
|
|
43
|
+
pattern: appDocumentPreloading === true
|
|
44
|
+
stopBy: end
|
|
45
|
+
fix:
|
|
46
|
+
'{}'
|
|
47
|
+
`;
|
|
48
|
+
// This rule is mostly for splitted edge functions so that we don't try to match them on the other non edge functions
|
|
49
|
+
export const removeMiddlewareManifestRule = `
|
|
50
|
+
rule:
|
|
51
|
+
kind: statement_block
|
|
52
|
+
inside:
|
|
53
|
+
kind: method_definition
|
|
54
|
+
has:
|
|
55
|
+
kind: property_identifier
|
|
56
|
+
regex: getMiddlewareManifest
|
|
57
|
+
fix:
|
|
58
|
+
'{return null;}'
|
|
59
|
+
`;
|
|
60
|
+
export const patchNextServer = {
|
|
61
|
+
name: "patch-next-server",
|
|
62
|
+
patches: [
|
|
63
|
+
// Skip executing next middleware, edge functions and image optimization inside NextServer
|
|
64
|
+
{
|
|
65
|
+
versions: ">=15.0.0",
|
|
66
|
+
field: {
|
|
67
|
+
pathFilter: /next-server\.(js)$/,
|
|
68
|
+
contentFilter: /process\.env\.NEXT_MINIMAL/,
|
|
69
|
+
patchCode: createPatchCode(minimalRule),
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
// Disable Next background preloading done at creation of `NetxServer`
|
|
73
|
+
{
|
|
74
|
+
versions: ">=15.0.0",
|
|
75
|
+
field: {
|
|
76
|
+
pathFilter: /next-server\.(js)$/,
|
|
77
|
+
contentFilter: /this\.nextConfig\.experimental\.preloadEntriesOnStart/,
|
|
78
|
+
patchCode: createPatchCode(disablePreloadingRule),
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
// Don't match edge functions in `NextServer`
|
|
82
|
+
{
|
|
83
|
+
versions: ">=15.0.0",
|
|
84
|
+
field: {
|
|
85
|
+
pathFilter: /next-server\.(js)$/,
|
|
86
|
+
contentFilter: /getMiddlewareManifest/,
|
|
87
|
+
patchCode: createPatchCode(removeMiddlewareManifestRule),
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
};
|
|
@@ -10,6 +10,8 @@ export async function createMainHandler() {
|
|
|
10
10
|
: config.default;
|
|
11
11
|
globalThis.serverId = generateUniqueId();
|
|
12
12
|
globalThis.openNextConfig = config;
|
|
13
|
+
// If route preloading behavior is set to start, it will wait for every single route to be preloaded before even creating the main handler.
|
|
14
|
+
await globalThis.__next_route_preloader("start");
|
|
13
15
|
// Default queue
|
|
14
16
|
globalThis.queue = await resolveQueue(thisFunction.override?.queue);
|
|
15
17
|
globalThis.incrementalCache = await resolveIncrementalCache(thisFunction.override?.incrementalCache);
|
|
@@ -19,6 +19,7 @@ export async function openNextHandler(internalEvent, options) {
|
|
|
19
19
|
isISRRevalidation: initialHeaders["x-isr"] === "1",
|
|
20
20
|
waitUntil: options?.waitUntil,
|
|
21
21
|
}, async () => {
|
|
22
|
+
await globalThis.__next_route_preloader("waitUntil");
|
|
22
23
|
if (initialHeaders["x-forwarded-host"]) {
|
|
23
24
|
initialHeaders.host = initialHeaders["x-forwarded-host"];
|
|
24
25
|
}
|
|
@@ -114,7 +115,12 @@ export async function openNextHandler(internalEvent, options) {
|
|
|
114
115
|
// 1. We could just let the revalidation go as normal, but due to race conditions the revalidation will be unreliable
|
|
115
116
|
// 2. We could alter the lastModified time of our cache to make next believe that the cache is fresh, but this could cause issues with stale data since the cdn will cache the stale data as if it was fresh
|
|
116
117
|
// 3. OUR CHOICE: We could pass a purpose prefetch header to the serverless function to make next believe that the request is a prefetch request and not trigger revalidation (This could potentially break in the future if next changes the behavior of prefetch requests)
|
|
117
|
-
headers: {
|
|
118
|
+
headers: {
|
|
119
|
+
...headers,
|
|
120
|
+
//#override appendPrefetch
|
|
121
|
+
purpose: "prefetch",
|
|
122
|
+
//#endOverride
|
|
123
|
+
},
|
|
118
124
|
body: preprocessedEvent.body,
|
|
119
125
|
remoteAddress: preprocessedEvent.remoteAddress,
|
|
120
126
|
};
|
|
@@ -114,19 +114,13 @@ export function handleRewrites(event, rewrites) {
|
|
|
114
114
|
const { pathname, protocol, hostname, queryString } = getUrlParts(rewrite.destination, isExternalRewrite);
|
|
115
115
|
// We need to use a localized path if the rewrite is not locale specific
|
|
116
116
|
const pathToUse = rewrite.locale === false ? rawPath : localizedRawPath;
|
|
117
|
-
// We need to encode the "+" character with its UTF-8 equivalent "%20" to avoid 2 issues:
|
|
118
|
-
// 1. The compile function from path-to-regexp will throw an error if it finds a "+" character.
|
|
119
|
-
// https://github.com/pillarjs/path-to-regexp?tab=readme-ov-file#unexpected--or-
|
|
120
|
-
// 2. The convertToQueryString function will replace the "+" character with %2B instead of %20.
|
|
121
|
-
// %2B does not get interpreted as a space in the URL thus breaking the query string.
|
|
122
|
-
const encodePlusQueryString = queryString.replaceAll("+", "%20");
|
|
123
117
|
debug("urlParts", { pathname, protocol, hostname, queryString });
|
|
124
|
-
const toDestinationPath = compile(escapeRegex(pathname));
|
|
118
|
+
const toDestinationPath = compile(escapeRegex(pathname, { isPath: true }));
|
|
125
119
|
const toDestinationHost = compile(escapeRegex(hostname));
|
|
126
|
-
const toDestinationQuery = compile(escapeRegex(
|
|
120
|
+
const toDestinationQuery = compile(escapeRegex(queryString));
|
|
127
121
|
const params = {
|
|
128
122
|
// params for the source
|
|
129
|
-
...getParamsFromSource(match(escapeRegex(rewrite.source)))(pathToUse),
|
|
123
|
+
...getParamsFromSource(match(escapeRegex(rewrite.source, { isPath: true })))(pathToUse),
|
|
130
124
|
// params for the has
|
|
131
125
|
...rewrite.has?.reduce((acc, cur) => {
|
|
132
126
|
return Object.assign(acc, computeHas(cur));
|
|
@@ -137,7 +131,7 @@ export function handleRewrites(event, rewrites) {
|
|
|
137
131
|
}, {}),
|
|
138
132
|
};
|
|
139
133
|
const isUsingParams = Object.keys(params).length > 0;
|
|
140
|
-
let rewrittenQuery =
|
|
134
|
+
let rewrittenQuery = queryString;
|
|
141
135
|
let rewrittenHost = hostname;
|
|
142
136
|
let rewrittenPath = pathname;
|
|
143
137
|
if (isUsingParams) {
|
|
@@ -7,5 +7,12 @@ type Middleware = (request: Request) => Response | Promise<Response>;
|
|
|
7
7
|
type MiddlewareLoader = () => Promise<{
|
|
8
8
|
default: Middleware;
|
|
9
9
|
}>;
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* @param internalEvent the internal event
|
|
13
|
+
* @param initialSearch the initial query string as it was received in the handler
|
|
14
|
+
* @param middlewareLoader Only used for unit test
|
|
15
|
+
* @returns `Promise<MiddlewareEvent | InternalResult>`
|
|
16
|
+
*/
|
|
17
|
+
export declare function handleMiddleware(internalEvent: InternalEvent, initialSearch: string, middlewareLoader?: MiddlewareLoader): Promise<MiddlewareEvent | InternalResult>;
|
|
11
18
|
export {};
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import { FunctionsConfigManifest, MiddlewareManifest, NextConfig, PrerenderManifest, } from "../../adapters/config/index.js";
|
|
2
2
|
import { emptyReadableStream } from "../../utils/stream.js";
|
|
3
3
|
import { localizePath } from "./i18n/index.js";
|
|
4
|
-
|
|
5
|
-
// every release of next could break this
|
|
6
|
-
// const { run } = require("next/dist/server/web/sandbox");
|
|
7
|
-
// const { getCloneableBody } = require("next/dist/server/body-streams");
|
|
8
|
-
// const {
|
|
9
|
-
// signalFromNodeResponse,
|
|
10
|
-
// } = require("next/dist/server/web/spec-extension/adapters/next-request");
|
|
11
|
-
import { convertBodyToReadableStream, convertToQueryString, getMiddlewareMatch, isExternal, } from "./util.js";
|
|
4
|
+
import { convertBodyToReadableStream, getMiddlewareMatch, isExternal, } from "./util.js";
|
|
12
5
|
const middlewareManifest = MiddlewareManifest;
|
|
13
6
|
const functionsConfigManifest = FunctionsConfigManifest;
|
|
14
7
|
const middleMatch = getMiddlewareMatch(middlewareManifest, functionsConfigManifest);
|
|
@@ -16,12 +9,14 @@ function defaultMiddlewareLoader() {
|
|
|
16
9
|
// @ts-expect-error - This is bundled
|
|
17
10
|
return import("./middleware.mjs");
|
|
18
11
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @param internalEvent the internal event
|
|
15
|
+
* @param initialSearch the initial query string as it was received in the handler
|
|
16
|
+
* @param middlewareLoader Only used for unit test
|
|
17
|
+
* @returns `Promise<MiddlewareEvent | InternalResult>`
|
|
18
|
+
*/
|
|
19
|
+
export async function handleMiddleware(internalEvent, initialSearch, middlewareLoader = defaultMiddlewareLoader) {
|
|
25
20
|
const headers = internalEvent.headers;
|
|
26
21
|
// We bypass the middleware if the request is internal
|
|
27
22
|
// We should only do that if the request has the correct `x-prerender-revalidate` header
|
|
@@ -36,7 +31,7 @@ export async function handleMiddleware(internalEvent, middlewareLoader = default
|
|
|
36
31
|
if (!hasMatch)
|
|
37
32
|
return internalEvent;
|
|
38
33
|
const initialUrl = new URL(normalizedPath, internalEvent.url);
|
|
39
|
-
initialUrl.search =
|
|
34
|
+
initialUrl.search = initialSearch;
|
|
40
35
|
const url = initialUrl.href;
|
|
41
36
|
const middleware = await middlewareLoader();
|
|
42
37
|
const result = await middleware.default({
|
|
@@ -76,13 +71,15 @@ export async function handleMiddleware(internalEvent, middlewareLoader = default
|
|
|
76
71
|
const responseHeaders = result.headers;
|
|
77
72
|
const reqHeaders = {};
|
|
78
73
|
const resHeaders = {};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
74
|
+
// These are internal headers used by Next.js, we don't want to expose them to the client
|
|
75
|
+
const filteredHeaders = [
|
|
76
|
+
"x-middleware-override-headers",
|
|
77
|
+
"x-middleware-set-cookie",
|
|
78
|
+
"x-middleware-next",
|
|
79
|
+
"x-middleware-rewrite",
|
|
80
|
+
// We need to drop `content-encoding` because it will be decoded
|
|
81
|
+
"content-encoding",
|
|
82
|
+
];
|
|
86
83
|
const xMiddlewareKey = "x-middleware-request-";
|
|
87
84
|
responseHeaders.forEach((value, key) => {
|
|
88
85
|
if (key.startsWith(xMiddlewareKey)) {
|
|
@@ -90,6 +87,8 @@ export async function handleMiddleware(internalEvent, middlewareLoader = default
|
|
|
90
87
|
reqHeaders[k] = value;
|
|
91
88
|
}
|
|
92
89
|
else {
|
|
90
|
+
if (filteredHeaders.includes(key.toLowerCase()))
|
|
91
|
+
return;
|
|
93
92
|
if (key.toLowerCase() === "set-cookie") {
|
|
94
93
|
resHeaders[key] = resHeaders[key]
|
|
95
94
|
? [...resHeaders[key], value]
|
|
@@ -100,20 +99,6 @@ export async function handleMiddleware(internalEvent, middlewareLoader = default
|
|
|
100
99
|
}
|
|
101
100
|
}
|
|
102
101
|
});
|
|
103
|
-
// If the middleware returned a Redirect, we set the `Location` header with
|
|
104
|
-
// the redirected url and end the response.
|
|
105
|
-
if (statusCode >= 300 && statusCode < 400) {
|
|
106
|
-
resHeaders.location =
|
|
107
|
-
responseHeaders.get("location") ?? resHeaders.location;
|
|
108
|
-
// res.setHeader("Location", location);
|
|
109
|
-
return {
|
|
110
|
-
body: emptyReadableStream(),
|
|
111
|
-
type: internalEvent.type,
|
|
112
|
-
statusCode: statusCode,
|
|
113
|
-
headers: resHeaders,
|
|
114
|
-
isBase64Encoded: false,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
102
|
// If the middleware returned a Rewrite, set the `url` to the pathname of the rewrite
|
|
118
103
|
// NOTE: the header was added to `req` from above
|
|
119
104
|
const rewriteUrl = responseHeaders.get("x-middleware-rewrite");
|
|
@@ -139,11 +124,11 @@ export async function handleMiddleware(internalEvent, middlewareLoader = default
|
|
|
139
124
|
});
|
|
140
125
|
}
|
|
141
126
|
}
|
|
142
|
-
// If the middleware
|
|
143
|
-
//
|
|
144
|
-
if (
|
|
127
|
+
// If the middleware wants to directly return a response (i.e. not using `NextResponse.next()` or `NextResponse.rewrite()`)
|
|
128
|
+
// we return the response directly
|
|
129
|
+
if (!rewriteUrl && !responseHeaders.get("x-middleware-next")) {
|
|
145
130
|
// transfer response body to res
|
|
146
|
-
const body = result.body;
|
|
131
|
+
const body = result.body ?? emptyReadableStream();
|
|
147
132
|
return {
|
|
148
133
|
type: internalEvent.type,
|
|
149
134
|
statusCode: statusCode,
|
|
@@ -8,7 +8,7 @@ import type { InternalEvent, InternalResult, RoutingResult, StreamCreator } from
|
|
|
8
8
|
* @__PURE__
|
|
9
9
|
*/
|
|
10
10
|
export declare function isExternal(url?: string, host?: string): boolean;
|
|
11
|
-
export declare function convertFromQueryString(query: string): Record<string, string>;
|
|
11
|
+
export declare function convertFromQueryString(query: string): Record<string, string | string[]>;
|
|
12
12
|
/**
|
|
13
13
|
*
|
|
14
14
|
* @__PURE__
|
|
@@ -38,6 +38,7 @@ export declare function convertRes(res: OpenNextNodeResponse): InternalResult;
|
|
|
38
38
|
* Make sure that multi-value query parameters are transformed to
|
|
39
39
|
* ?key=value1&key=value2&... so that Next converts those parameters
|
|
40
40
|
* to an array when reading the query parameters
|
|
41
|
+
* query should be properly encoded before using this function
|
|
41
42
|
* @__PURE__
|
|
42
43
|
*/
|
|
43
44
|
export declare function convertToQueryString(query: Record<string, string | string[]>): string;
|
|
@@ -56,7 +57,9 @@ export declare function getMiddlewareMatch(middlewareManifest: MiddlewareManifes
|
|
|
56
57
|
*
|
|
57
58
|
* @__PURE__
|
|
58
59
|
*/
|
|
59
|
-
export declare function escapeRegex(str: string
|
|
60
|
+
export declare function escapeRegex(str: string, { isPath }?: {
|
|
61
|
+
isPath?: boolean;
|
|
62
|
+
}): string;
|
|
60
63
|
/**
|
|
61
64
|
*
|
|
62
65
|
* @__PURE__
|
|
@@ -2,7 +2,7 @@ import crypto from "node:crypto";
|
|
|
2
2
|
import { Readable } from "node:stream";
|
|
3
3
|
import { BuildId, HtmlPages, NextConfig } from "../../adapters/config/index.js";
|
|
4
4
|
import { OpenNextNodeResponse } from "../../http/openNextResponse.js";
|
|
5
|
-
import { parseHeaders } from "../../http/util.js";
|
|
5
|
+
import { getQueryFromIterator, parseHeaders } from "../../http/util.js";
|
|
6
6
|
import { debug, error } from "../../adapters/logger.js";
|
|
7
7
|
import { isBinaryContentType } from "../../utils/binary.js";
|
|
8
8
|
import { localizePath } from "./i18n/index.js";
|
|
@@ -24,11 +24,10 @@ export function convertFromQueryString(query) {
|
|
|
24
24
|
if (query === "")
|
|
25
25
|
return {};
|
|
26
26
|
const queryParts = query.split("&");
|
|
27
|
-
return queryParts.
|
|
28
|
-
const [key, value] =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}, {});
|
|
27
|
+
return getQueryFromIterator(queryParts.map((p) => {
|
|
28
|
+
const [key, value] = p.split("=");
|
|
29
|
+
return [key, value];
|
|
30
|
+
}));
|
|
32
31
|
}
|
|
33
32
|
/**
|
|
34
33
|
*
|
|
@@ -99,23 +98,20 @@ export function convertRes(res) {
|
|
|
99
98
|
* Make sure that multi-value query parameters are transformed to
|
|
100
99
|
* ?key=value1&key=value2&... so that Next converts those parameters
|
|
101
100
|
* to an array when reading the query parameters
|
|
101
|
+
* query should be properly encoded before using this function
|
|
102
102
|
* @__PURE__
|
|
103
103
|
*/
|
|
104
104
|
export function convertToQueryString(query) {
|
|
105
|
-
|
|
106
|
-
// So we must decode the value before appending it to the URLSearchParams.
|
|
107
|
-
// https://stackoverflow.com/a/45516812
|
|
108
|
-
const urlQuery = new URLSearchParams();
|
|
105
|
+
const queryStrings = [];
|
|
109
106
|
Object.entries(query).forEach(([key, value]) => {
|
|
110
107
|
if (Array.isArray(value)) {
|
|
111
|
-
value.forEach((entry) =>
|
|
108
|
+
value.forEach((entry) => queryStrings.push(`${key}=${entry}`));
|
|
112
109
|
}
|
|
113
110
|
else {
|
|
114
|
-
|
|
111
|
+
queryStrings.push(`${key}=${value}`);
|
|
115
112
|
}
|
|
116
113
|
});
|
|
117
|
-
|
|
118
|
-
return queryString ? `?${queryString}` : "";
|
|
114
|
+
return queryStrings.length > 0 ? `?${queryStrings.join("&")}` : "";
|
|
119
115
|
}
|
|
120
116
|
/**
|
|
121
117
|
* Given a raw query string, returns a record with key value-array pairs
|
|
@@ -148,11 +144,12 @@ export function getMiddlewareMatch(middlewareManifest, functionsManifest) {
|
|
|
148
144
|
*
|
|
149
145
|
* @__PURE__
|
|
150
146
|
*/
|
|
151
|
-
export function escapeRegex(str) {
|
|
152
|
-
|
|
147
|
+
export function escapeRegex(str, { isPath } = {}) {
|
|
148
|
+
const result = str
|
|
153
149
|
.replaceAll("(.)", "_µ1_")
|
|
154
150
|
.replaceAll("(..)", "_µ2_")
|
|
155
151
|
.replaceAll("(...)", "_µ3_");
|
|
152
|
+
return isPath ? result : result.replaceAll("+", "_µ4_");
|
|
156
153
|
}
|
|
157
154
|
/**
|
|
158
155
|
*
|
|
@@ -162,7 +159,8 @@ export function unescapeRegex(str) {
|
|
|
162
159
|
return str
|
|
163
160
|
.replaceAll("_µ1_", "(.)")
|
|
164
161
|
.replaceAll("_µ2_", "(..)")
|
|
165
|
-
.replaceAll("_µ3_", "(...)")
|
|
162
|
+
.replaceAll("_µ3_", "(...)")
|
|
163
|
+
.replaceAll("_µ4_", "+");
|
|
166
164
|
}
|
|
167
165
|
/**
|
|
168
166
|
* @__PURE__
|
|
@@ -58,7 +58,11 @@ export default async function routingHandler(event) {
|
|
|
58
58
|
debug("redirect", redirect);
|
|
59
59
|
return redirect;
|
|
60
60
|
}
|
|
61
|
-
const eventOrResult = await handleMiddleware(internalEvent
|
|
61
|
+
const eventOrResult = await handleMiddleware(internalEvent,
|
|
62
|
+
// We need to pass the initial search without any decoding
|
|
63
|
+
// TODO: we'd need to refactor InternalEvent to include the initial querystring directly
|
|
64
|
+
// Should be done in another PR because it is a breaking change
|
|
65
|
+
new URL(event.url).search);
|
|
62
66
|
const isResult = "statusCode" in eventOrResult;
|
|
63
67
|
if (isResult) {
|
|
64
68
|
return eventOrResult;
|
package/dist/core/util.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AppPathsManifestKeys, NextConfig, RoutesManifest, } from "../adapters/config/index.js";
|
|
2
2
|
// @ts-ignore
|
|
3
3
|
import NextServer from "next/dist/server/next-server.js";
|
|
4
|
-
import { debug } from "../adapters/logger.js";
|
|
4
|
+
import { debug, error } from "../adapters/logger.js";
|
|
5
5
|
import { applyOverride as applyNextjsRequireHooksOverride, overrideHooks as overrideNextjsRequireHooks, } from "./require-hooks.js";
|
|
6
6
|
// WORKAROUND: Set `__NEXT_PRIVATE_PREBUNDLED_REACT` to use prebundled Reac
|
|
7
7
|
// See https://opennext.js.org/aws/v2/advanced/workaround#workaround-set-__next_private_prebundled_react-to-use-prebundled-react
|
|
@@ -48,6 +48,46 @@ const nextServer = new NextServer.default({
|
|
|
48
48
|
dev: false,
|
|
49
49
|
dir: __dirname,
|
|
50
50
|
});
|
|
51
|
+
let routesLoaded = false;
|
|
52
|
+
globalThis.__next_route_preloader = async (stage) => {
|
|
53
|
+
if (routesLoaded) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const thisFunction = globalThis.fnName
|
|
57
|
+
? globalThis.openNextConfig.functions[globalThis.fnName]
|
|
58
|
+
: globalThis.openNextConfig.default;
|
|
59
|
+
const routePreloadingBehavior = thisFunction?.routePreloadingBehavior ?? "none";
|
|
60
|
+
if (routePreloadingBehavior === "none") {
|
|
61
|
+
routesLoaded = true;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (!("unstable_preloadEntries" in nextServer)) {
|
|
65
|
+
debug("The current version of Next.js does not support route preloading. Skipping route preloading.");
|
|
66
|
+
routesLoaded = true;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (stage === "waitUntil" && routePreloadingBehavior === "withWaitUntil") {
|
|
70
|
+
// We need to access the waitUntil
|
|
71
|
+
const waitUntil = globalThis.__openNextAls.getStore()?.waitUntil;
|
|
72
|
+
if (!waitUntil) {
|
|
73
|
+
error("You've tried to use the 'withWaitUntil' route preloading behavior, but the 'waitUntil' function is not available.");
|
|
74
|
+
routesLoaded = true;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
debug("Preloading entries with waitUntil");
|
|
78
|
+
waitUntil?.(nextServer.unstable_preloadEntries());
|
|
79
|
+
routesLoaded = true;
|
|
80
|
+
}
|
|
81
|
+
else if ((stage === "start" && routePreloadingBehavior === "onStart") ||
|
|
82
|
+
(stage === "warmerEvent" && routePreloadingBehavior === "onWarmerEvent") ||
|
|
83
|
+
stage === "onDemand") {
|
|
84
|
+
const startTimestamp = Date.now();
|
|
85
|
+
debug("Preloading entries");
|
|
86
|
+
await nextServer.unstable_preloadEntries();
|
|
87
|
+
debug("Preloading entries took", Date.now() - startTimestamp, "ms");
|
|
88
|
+
routesLoaded = true;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
51
91
|
// `getRequestHandlerWithMetadata` is not available in older versions of Next.js
|
|
52
92
|
// It is required to for next 15.2 to pass metadata for page router data route
|
|
53
93
|
export const requestHandler = (metadata) => "getRequestHandlerWithMetadata" in nextServer
|
package/dist/http/util.d.ts
CHANGED
|
@@ -2,3 +2,10 @@ import type http from "node:http";
|
|
|
2
2
|
export declare const parseHeaders: (headers?: http.OutgoingHttpHeader[] | http.OutgoingHttpHeaders) => Record<string, string>;
|
|
3
3
|
export declare const convertHeader: (header: http.OutgoingHttpHeader) => string;
|
|
4
4
|
export declare function parseCookies(cookies: string | string[] | null | undefined): string[];
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* Get the query object from an iterable of [key, value] pairs
|
|
8
|
+
* @param it - The iterable of [key, value] pairs
|
|
9
|
+
* @returns The query object
|
|
10
|
+
*/
|
|
11
|
+
export declare function getQueryFromIterator(it: Iterable<[string, string]>): Record<string, string | string[]>;
|
package/dist/http/util.js
CHANGED
|
@@ -28,3 +28,26 @@ export function parseCookies(cookies) {
|
|
|
28
28
|
? cookies.split(/(?<!Expires=\w+),/i).map((c) => c.trim())
|
|
29
29
|
: cookies;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* Get the query object from an iterable of [key, value] pairs
|
|
34
|
+
* @param it - The iterable of [key, value] pairs
|
|
35
|
+
* @returns The query object
|
|
36
|
+
*/
|
|
37
|
+
export function getQueryFromIterator(it) {
|
|
38
|
+
const query = {};
|
|
39
|
+
for (const [key, value] of it) {
|
|
40
|
+
if (key in query) {
|
|
41
|
+
if (Array.isArray(query[key])) {
|
|
42
|
+
query[key].push(value);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
query[key] = [query[key], value];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
query[key] = value;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return query;
|
|
53
|
+
}
|
|
@@ -1,23 +1,11 @@
|
|
|
1
1
|
import { Buffer } from "node:buffer";
|
|
2
2
|
import { parseCookies } from "../../http/util";
|
|
3
|
+
import { getQueryFromSearchParams } from "./utils.js";
|
|
3
4
|
const converter = {
|
|
4
5
|
convertFrom: async (event) => {
|
|
5
6
|
const url = new URL(event.url);
|
|
6
7
|
const searchParams = url.searchParams;
|
|
7
|
-
const query =
|
|
8
|
-
for (const [key, value] of searchParams.entries()) {
|
|
9
|
-
if (key in query) {
|
|
10
|
-
if (Array.isArray(query[key])) {
|
|
11
|
-
query[key].push(value);
|
|
12
|
-
}
|
|
13
|
-
else {
|
|
14
|
-
query[key] = [query[key], value];
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
query[key] = value;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
8
|
+
const query = getQueryFromSearchParams(searchParams);
|
|
21
9
|
// Transform body into Buffer
|
|
22
10
|
const body = await event.arrayBuffer();
|
|
23
11
|
const headers = {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseCookies } from "../../http/util";
|
|
2
|
-
import { extractHostFromHeaders } from "./utils";
|
|
2
|
+
import { extractHostFromHeaders, getQueryFromSearchParams } from "./utils.js";
|
|
3
3
|
const converter = {
|
|
4
4
|
convertFrom: async (req) => {
|
|
5
5
|
const body = await new Promise((resolve) => {
|
|
@@ -18,7 +18,7 @@ const converter = {
|
|
|
18
18
|
])
|
|
19
19
|
.filter(([key]) => key));
|
|
20
20
|
const url = new URL(req.url, `http://${extractHostFromHeaders(headers)}`);
|
|
21
|
-
const query =
|
|
21
|
+
const query = getQueryFromSearchParams(url.searchParams);
|
|
22
22
|
return {
|
|
23
23
|
type: "core",
|
|
24
24
|
method: req.method ?? "GET",
|
|
@@ -6,3 +6,10 @@ export declare function removeUndefinedFromQuery(query: Record<string, string |
|
|
|
6
6
|
* @returns The host
|
|
7
7
|
*/
|
|
8
8
|
export declare function extractHostFromHeaders(headers: Record<string, string>): string;
|
|
9
|
+
/**
|
|
10
|
+
* Get the query object from an URLSearchParams
|
|
11
|
+
*
|
|
12
|
+
* @param searchParams
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
export declare function getQueryFromSearchParams(searchParams: URLSearchParams): Record<string, string | string[]>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getQueryFromIterator } from "../../http/util.js";
|
|
1
2
|
export function removeUndefinedFromQuery(query) {
|
|
2
3
|
const newQuery = {};
|
|
3
4
|
for (const [key, value] of Object.entries(query)) {
|
|
@@ -16,3 +17,12 @@ export function removeUndefinedFromQuery(query) {
|
|
|
16
17
|
export function extractHostFromHeaders(headers) {
|
|
17
18
|
return headers["x-forwarded-host"] ?? headers.host ?? "on";
|
|
18
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Get the query object from an URLSearchParams
|
|
22
|
+
*
|
|
23
|
+
* @param searchParams
|
|
24
|
+
* @returns
|
|
25
|
+
*/
|
|
26
|
+
export function getQueryFromSearchParams(searchParams) {
|
|
27
|
+
return getQueryFromIterator(searchParams.entries());
|
|
28
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Readable, Writable } from "node:stream";
|
|
2
|
+
import zlib from "node:zlib";
|
|
3
|
+
import { error } from "../../adapters/logger";
|
|
4
|
+
import { formatWarmerResponse } from "./aws-lambda";
|
|
5
|
+
const handler = async (handler, converter) => async (event) => {
|
|
6
|
+
// Handle warmer event
|
|
7
|
+
if ("type" in event) {
|
|
8
|
+
return formatWarmerResponse(event);
|
|
9
|
+
}
|
|
10
|
+
const internalEvent = await converter.convertFrom(event);
|
|
11
|
+
// This is a workaround
|
|
12
|
+
// https://github.com/opennextjs/opennextjs-aws/blob/e9b37fd44eb856eb8ae73168bf455ff85dd8b285/packages/open-next/src/overrides/wrappers/aws-lambda.ts#L49-L53
|
|
13
|
+
const fakeStream = {
|
|
14
|
+
writeHeaders: () => {
|
|
15
|
+
return new Writable({
|
|
16
|
+
write: (_chunk, _encoding, callback) => {
|
|
17
|
+
callback();
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
const handlerResponse = await handler(internalEvent, {
|
|
23
|
+
streamCreator: fakeStream,
|
|
24
|
+
});
|
|
25
|
+
// Check if response is already compressed
|
|
26
|
+
// The handlers response headers are lowercase
|
|
27
|
+
const alreadyEncoded = handlerResponse.headers["content-encoding"] ?? "";
|
|
28
|
+
// Return early here if the response is already compressed
|
|
29
|
+
if (alreadyEncoded) {
|
|
30
|
+
return converter.convertTo(handlerResponse, event);
|
|
31
|
+
}
|
|
32
|
+
// We compress the body if the client accepts it
|
|
33
|
+
const acceptEncoding = internalEvent.headers["accept-encoding"] ??
|
|
34
|
+
internalEvent.headers["Accept-Encoding"] ??
|
|
35
|
+
"";
|
|
36
|
+
let contentEncoding = null;
|
|
37
|
+
if (acceptEncoding?.includes("br")) {
|
|
38
|
+
contentEncoding = "br";
|
|
39
|
+
}
|
|
40
|
+
else if (acceptEncoding?.includes("gzip")) {
|
|
41
|
+
contentEncoding = "gzip";
|
|
42
|
+
}
|
|
43
|
+
else if (acceptEncoding?.includes("deflate")) {
|
|
44
|
+
contentEncoding = "deflate";
|
|
45
|
+
}
|
|
46
|
+
const response = {
|
|
47
|
+
...handlerResponse,
|
|
48
|
+
body: compressBody(handlerResponse.body, contentEncoding),
|
|
49
|
+
headers: {
|
|
50
|
+
...handlerResponse.headers,
|
|
51
|
+
...(contentEncoding ? { "content-encoding": contentEncoding } : {}),
|
|
52
|
+
},
|
|
53
|
+
isBase64Encoded: !!contentEncoding || handlerResponse.isBase64Encoded,
|
|
54
|
+
};
|
|
55
|
+
return converter.convertTo(response, event);
|
|
56
|
+
};
|
|
57
|
+
export default {
|
|
58
|
+
wrapper: handler,
|
|
59
|
+
name: "aws-lambda-compressed",
|
|
60
|
+
supportStreaming: false,
|
|
61
|
+
};
|
|
62
|
+
function compressBody(body, encoding) {
|
|
63
|
+
// If no encoding is specified, return original body
|
|
64
|
+
if (!encoding)
|
|
65
|
+
return body;
|
|
66
|
+
try {
|
|
67
|
+
const readable = Readable.fromWeb(body);
|
|
68
|
+
let transform;
|
|
69
|
+
switch (encoding) {
|
|
70
|
+
case "br":
|
|
71
|
+
transform = zlib.createBrotliCompress({
|
|
72
|
+
params: {
|
|
73
|
+
// This is a compromise between speed and compression ratio.
|
|
74
|
+
// The default one will most likely timeout an AWS Lambda with default configuration on large bodies (>6mb).
|
|
75
|
+
// Therefore we set it to 6, which is a good compromise.
|
|
76
|
+
[zlib.constants.BROTLI_PARAM_QUALITY]: Number(process.env.BROTLI_QUALITY) ?? 6,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
break;
|
|
80
|
+
case "gzip":
|
|
81
|
+
transform = zlib.createGzip();
|
|
82
|
+
break;
|
|
83
|
+
case "deflate":
|
|
84
|
+
transform = zlib.createDeflate();
|
|
85
|
+
break;
|
|
86
|
+
default:
|
|
87
|
+
return body;
|
|
88
|
+
}
|
|
89
|
+
return Readable.toWeb(readable.pipe(transform));
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
error("Error compressing body:", e);
|
|
93
|
+
// Fall back to no compression on error
|
|
94
|
+
return body;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -14,6 +14,7 @@ const handler = async (handler, converter) => awslambda.streamifyResponse(async
|
|
|
14
14
|
if ("type" in event) {
|
|
15
15
|
const result = await formatWarmerResponse(event);
|
|
16
16
|
responseStream.end(Buffer.from(JSON.stringify(result)), "utf-8");
|
|
17
|
+
await globalThis.__next_route_preloader("warmerEvent");
|
|
17
18
|
return;
|
|
18
19
|
}
|
|
19
20
|
const internalEvent = await converter.convertFrom(event);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { WrapperHandler } from "../../types/overrides";
|
|
2
|
+
import type { WarmerEvent, WarmerResponse } from "../../adapters/warmer-function";
|
|
3
|
+
export declare function formatWarmerResponse(event: WarmerEvent): Promise<WarmerResponse>;
|
|
2
4
|
declare const _default: {
|
|
3
5
|
wrapper: WrapperHandler;
|
|
4
6
|
name: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Writable } from "node:stream";
|
|
2
|
-
import type { APIGatewayProxyEventV2, Context } from "aws-lambda";
|
|
2
|
+
import type { APIGatewayProxyEvent, APIGatewayProxyEventV2, APIGatewayProxyResult, APIGatewayProxyResultV2, CloudFrontRequestEvent, CloudFrontRequestResult, Context } from "aws-lambda";
|
|
3
|
+
import type { WarmerEvent, WarmerResponse } from "../adapters/warmer-function";
|
|
3
4
|
export interface ResponseStream extends Writable {
|
|
4
5
|
getBufferedData(): Buffer;
|
|
5
6
|
setContentType(contentType: string): void;
|
|
@@ -17,4 +18,6 @@ declare global {
|
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
}
|
|
21
|
+
export type AwsLambdaEvent = APIGatewayProxyEventV2 | CloudFrontRequestEvent | APIGatewayProxyEvent | WarmerEvent;
|
|
22
|
+
export type AwsLambdaReturn = APIGatewayProxyResultV2 | APIGatewayProxyResult | CloudFrontRequestResult | WarmerResponse;
|
|
20
23
|
export {};
|
package/dist/types/global.d.ts
CHANGED
|
@@ -179,5 +179,11 @@ declare global {
|
|
|
179
179
|
* Defined in `createMainHandler`
|
|
180
180
|
*/
|
|
181
181
|
var cdnInvalidationHandler: CDNInvalidationHandler;
|
|
182
|
+
/**
|
|
183
|
+
* A function to preload the routes.
|
|
184
|
+
* This needs to be defined on globalThis because it can be used by custom overrides.
|
|
185
|
+
* Only available in main functions.
|
|
186
|
+
*/
|
|
187
|
+
var __next_route_preloader: (stage: "waitUntil" | "start" | "warmerEvent" | "onDemand") => Promise<void>;
|
|
182
188
|
}
|
|
183
189
|
export {};
|
|
@@ -67,13 +67,23 @@ export interface Origin {
|
|
|
67
67
|
port?: number;
|
|
68
68
|
customHeaders?: Record<string, string>;
|
|
69
69
|
}
|
|
70
|
-
export type IncludedWrapper = "aws-lambda" | "aws-lambda-streaming" | "node" | "cloudflare" | "cloudflare-edge" | "cloudflare-node" | "express-dev" | "dummy";
|
|
70
|
+
export type IncludedWrapper = "aws-lambda" | "aws-lambda-streaming" | "aws-lambda-compressed" | "node" | "cloudflare" | "cloudflare-edge" | "cloudflare-node" | "express-dev" | "dummy";
|
|
71
71
|
export type IncludedConverter = "aws-apigw-v2" | "aws-apigw-v1" | "aws-cloudfront" | "edge" | "node" | "sqs-revalidate" | "dummy";
|
|
72
72
|
export type RouteType = "route" | "page" | "app";
|
|
73
73
|
export interface ResolvedRoute {
|
|
74
74
|
route: string;
|
|
75
75
|
type: RouteType;
|
|
76
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* The route preloading behavior. Only supported in Next 15+.
|
|
79
|
+
* Default behavior of Next is disabled. You should do your own testing to choose which one suits you best
|
|
80
|
+
* - "none" - No preloading of the route at all. This is the default
|
|
81
|
+
* - "withWaitUntil" - Preload the route using the waitUntil provided by the wrapper - If not supported, it will fallback to "none". At the moment only cloudflare wrappers provide a `waitUntil`
|
|
82
|
+
* - "onWarmerEvent" - Preload the route on the warmer event - Needs to be implemented by the wrapper. Only supported in `aws-lambda-streaming` wrapper for now
|
|
83
|
+
* - "onStart" - Preload the route before even invoking the wrapper - This is a blocking operation. The handler will only be created after all the routes have been loaded, it may increase the cold start time by a lot in some cases. Useful for long running server or in serverless with some careful testing
|
|
84
|
+
* @default "none"
|
|
85
|
+
*/
|
|
86
|
+
export type RoutePreloadingBehavior = "none" | "withWaitUntil" | "onWarmerEvent" | "onStart";
|
|
77
87
|
export interface RoutingResult {
|
|
78
88
|
internalEvent: InternalEvent;
|
|
79
89
|
isExternalRewrite: boolean;
|
|
@@ -212,6 +222,7 @@ export interface FunctionOptions extends DefaultFunctionOptions {
|
|
|
212
222
|
* @deprecated This is not supported in 14.2+
|
|
213
223
|
*/
|
|
214
224
|
experimentalBundledNextServer?: boolean;
|
|
225
|
+
routePreloadingBehavior?: RoutePreloadingBehavior;
|
|
215
226
|
}
|
|
216
227
|
export type RouteTemplate = `app/${string}/route` | `app/${string}/page` | `app/page` | `app/route` | `pages/${string}`;
|
|
217
228
|
export interface SplittedFunctionOptions extends FunctionOptions {
|