@opennextjs/cloudflare 1.9.2 → 1.10.1
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/api/cloudflare-context.js +4 -0
- package/dist/cli/build/patches/plugins/require-hook.js +2 -1
- package/dist/cli/build/utils/extract-project-env-vars.js +1 -1
- package/dist/cli/commands/deploy.js +6 -9
- package/dist/cli/commands/helpers.d.ts +26 -2
- package/dist/cli/commands/helpers.js +48 -4
- package/dist/cli/commands/populate-cache.d.ts +2 -1
- package/dist/cli/commands/populate-cache.js +61 -62
- package/dist/cli/commands/preview.js +6 -4
- package/dist/cli/commands/skew-protection.d.ts +2 -2
- package/dist/cli/commands/skew-protection.js +3 -3
- package/dist/cli/commands/upload.js +6 -6
- package/dist/cli/templates/images.js +8 -9
- package/dist/cli/templates/skew-protection.js +4 -3
- package/package.json +2 -2
|
@@ -143,6 +143,10 @@ async function getCloudflareContextFromWrangler(options) {
|
|
|
143
143
|
const environment = options?.environment ?? process.env.NEXT_DEV_WRANGLER_ENV;
|
|
144
144
|
const { env, cf, ctx } = await getPlatformProxy({
|
|
145
145
|
...options,
|
|
146
|
+
// The `env` passed to the fetch handler does not contain variables from `.env*` files.
|
|
147
|
+
// because we invoke wrangler with `CLOUDFLARE_LOAD_DEV_VARS_FROM_DOT_ENV`=`"false"`.
|
|
148
|
+
// Initializing `envFiles` with an empty list is the equivalent for this API call.
|
|
149
|
+
envFiles: [],
|
|
146
150
|
environment,
|
|
147
151
|
});
|
|
148
152
|
return {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js";
|
|
3
3
|
export function shimRequireHook(options) {
|
|
4
|
+
const emptyShimPath = join(options.outputDir, "cloudflare-templates/shims/empty.js");
|
|
4
5
|
return {
|
|
5
6
|
name: "replaceRelative",
|
|
6
7
|
setup(build) {
|
|
7
8
|
// Note: we (empty) shim require-hook modules as they generate problematic code that uses requires
|
|
8
9
|
build.onResolve({ filter: getCrossPlatformPathRegex(String.raw `^\./require-hook$`, { escape: false }) }, () => ({
|
|
9
|
-
path:
|
|
10
|
+
path: emptyShimPath,
|
|
10
11
|
}));
|
|
11
12
|
},
|
|
12
13
|
};
|
|
@@ -3,7 +3,7 @@ import * as path from "node:path";
|
|
|
3
3
|
import { parse } from "@dotenvx/dotenvx";
|
|
4
4
|
function readEnvFile(filePath) {
|
|
5
5
|
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
6
|
-
return parse(fs.readFileSync(filePath)
|
|
6
|
+
return parse(fs.readFileSync(filePath, "utf-8"));
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
@@ -12,21 +12,18 @@ import { getNormalizedOptions, printHeaders, readWranglerConfig, retrieveCompile
|
|
|
12
12
|
export async function deployCommand(args) {
|
|
13
13
|
printHeaders("deploy");
|
|
14
14
|
const { config } = await retrieveCompiledConfig();
|
|
15
|
-
const
|
|
15
|
+
const buildOpts = getNormalizedOptions(config);
|
|
16
16
|
const wranglerConfig = readWranglerConfig(args);
|
|
17
|
-
const envVars = await getEnvFromPlatformProxy(
|
|
18
|
-
|
|
19
|
-
environment: args.env,
|
|
20
|
-
});
|
|
21
|
-
const deploymentMapping = await getDeploymentMapping(options, config, envVars);
|
|
22
|
-
await populateCache(options, config, wranglerConfig, {
|
|
17
|
+
const envVars = await getEnvFromPlatformProxy(config, buildOpts);
|
|
18
|
+
await populateCache(buildOpts, config, wranglerConfig, {
|
|
23
19
|
target: "remote",
|
|
24
20
|
environment: args.env,
|
|
25
21
|
wranglerConfigPath: args.wranglerConfigPath,
|
|
26
22
|
cacheChunkSize: args.cacheChunkSize,
|
|
27
23
|
shouldUsePreviewId: false,
|
|
28
|
-
});
|
|
29
|
-
|
|
24
|
+
}, envVars);
|
|
25
|
+
const deploymentMapping = await getDeploymentMapping(buildOpts, config, envVars);
|
|
26
|
+
runWrangler(buildOpts, [
|
|
30
27
|
"deploy",
|
|
31
28
|
...args.wranglerArgs,
|
|
32
29
|
...(deploymentMapping
|
|
@@ -1,12 +1,36 @@
|
|
|
1
|
+
import { type BuildOptions } from "@opennextjs/aws/build/helper.js";
|
|
1
2
|
import { type GetPlatformProxyOptions } from "wrangler";
|
|
2
3
|
export type WorkerEnvVar = Record<keyof CloudflareEnv, string | undefined>;
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
+
* Returns the env vars to use by the CLI.
|
|
6
|
+
*
|
|
7
|
+
* The environments variables are returned from a combination of `process.env`, wrangler config, and `.env*` files.
|
|
8
|
+
*
|
|
9
|
+
* Recommended usage on CI:
|
|
10
|
+
*
|
|
11
|
+
* - Add you secrets to `process.env` (i.e. `CF_ACCOUNT_ID`)
|
|
12
|
+
* - Add public values to the wrangler config `wrangler.jsonc` (i.e. `R2_CACHE_PREFIX_ENV_NAME`)
|
|
13
|
+
*
|
|
14
|
+
* Note: `.dev.vars*` and `.env*` should not be checked in.
|
|
15
|
+
*
|
|
16
|
+
* Recommended usage for local dev:
|
|
17
|
+
*
|
|
18
|
+
* - Add you secrets to either a `.dev.vars*` or `.env*` file (i.e. `CF_ACCOUNT_ID`)
|
|
19
|
+
* - Add public values to the wrangler config `wrangler.jsonc` (i.e. `R2_CACHE_PREFIX_ENV_NAME`)
|
|
20
|
+
*
|
|
21
|
+
* Note: `.env*` files are also used by `next dev` while `.dev.var*` files are only loaded by `wrangler`.
|
|
22
|
+
*
|
|
23
|
+
* Loading details:
|
|
24
|
+
*
|
|
25
|
+
* 1. The variables are first initialized from `process.env`
|
|
26
|
+
* 2. They are then augmented/replaced with variables from the wrangler config (`wrangler.jsonc` and `.dev.vars*`)
|
|
27
|
+
* 3. They are then augmented with variables from `.env*` files (existing values are not replaced).
|
|
5
28
|
*
|
|
6
29
|
* @param options Options to pass to `getPlatformProxy`, i.e. to set the environment
|
|
30
|
+
* @param buildOpts Open Next build options
|
|
7
31
|
* @returns the env vars
|
|
8
32
|
*/
|
|
9
|
-
export declare function getEnvFromPlatformProxy(options: GetPlatformProxyOptions): Promise<WorkerEnvVar>;
|
|
33
|
+
export declare function getEnvFromPlatformProxy(options: GetPlatformProxyOptions, buildOpts: BuildOptions): Promise<WorkerEnvVar>;
|
|
10
34
|
/**
|
|
11
35
|
* Escape shell metacharacters.
|
|
12
36
|
*
|
|
@@ -1,19 +1,63 @@
|
|
|
1
1
|
import { getPlatformProxy } from "wrangler";
|
|
2
|
+
import { extractProjectEnvVars } from "../build/utils/extract-project-env-vars.js";
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
+
* Returns the env vars to use by the CLI.
|
|
5
|
+
*
|
|
6
|
+
* The environments variables are returned from a combination of `process.env`, wrangler config, and `.env*` files.
|
|
7
|
+
*
|
|
8
|
+
* Recommended usage on CI:
|
|
9
|
+
*
|
|
10
|
+
* - Add you secrets to `process.env` (i.e. `CF_ACCOUNT_ID`)
|
|
11
|
+
* - Add public values to the wrangler config `wrangler.jsonc` (i.e. `R2_CACHE_PREFIX_ENV_NAME`)
|
|
12
|
+
*
|
|
13
|
+
* Note: `.dev.vars*` and `.env*` should not be checked in.
|
|
14
|
+
*
|
|
15
|
+
* Recommended usage for local dev:
|
|
16
|
+
*
|
|
17
|
+
* - Add you secrets to either a `.dev.vars*` or `.env*` file (i.e. `CF_ACCOUNT_ID`)
|
|
18
|
+
* - Add public values to the wrangler config `wrangler.jsonc` (i.e. `R2_CACHE_PREFIX_ENV_NAME`)
|
|
19
|
+
*
|
|
20
|
+
* Note: `.env*` files are also used by `next dev` while `.dev.var*` files are only loaded by `wrangler`.
|
|
21
|
+
*
|
|
22
|
+
* Loading details:
|
|
23
|
+
*
|
|
24
|
+
* 1. The variables are first initialized from `process.env`
|
|
25
|
+
* 2. They are then augmented/replaced with variables from the wrangler config (`wrangler.jsonc` and `.dev.vars*`)
|
|
26
|
+
* 3. They are then augmented with variables from `.env*` files (existing values are not replaced).
|
|
4
27
|
*
|
|
5
28
|
* @param options Options to pass to `getPlatformProxy`, i.e. to set the environment
|
|
29
|
+
* @param buildOpts Open Next build options
|
|
6
30
|
* @returns the env vars
|
|
7
31
|
*/
|
|
8
|
-
export async function getEnvFromPlatformProxy(options) {
|
|
9
|
-
|
|
10
|
-
const
|
|
32
|
+
export async function getEnvFromPlatformProxy(options, buildOpts) {
|
|
33
|
+
// 1. Start from `process.env`
|
|
34
|
+
const envVars = process.env;
|
|
35
|
+
// 2. Apply vars from workers `env`
|
|
36
|
+
const proxy = await getPlatformProxy({
|
|
37
|
+
...options,
|
|
38
|
+
// Next.js uses a different mechanism to load `.env*` files from wrangler.
|
|
39
|
+
// We prevent wrangler for loading the files and handle that in `getEnvFromPlatformProxy`.
|
|
40
|
+
envFiles: [],
|
|
41
|
+
});
|
|
11
42
|
Object.entries(proxy.env).forEach(([key, value]) => {
|
|
12
43
|
if (typeof value === "string") {
|
|
44
|
+
// filter out bindings by only considering string values
|
|
13
45
|
envVars[key] = value;
|
|
14
46
|
}
|
|
15
47
|
});
|
|
16
48
|
await proxy.dispose();
|
|
49
|
+
// 3. Apply new vars from `.env*` files
|
|
50
|
+
let mode = "production";
|
|
51
|
+
if (envVars.NEXTJS_ENV === "development") {
|
|
52
|
+
mode = "development";
|
|
53
|
+
}
|
|
54
|
+
else if (envVars.NEXTJS_ENV === "test") {
|
|
55
|
+
mode = "test";
|
|
56
|
+
}
|
|
57
|
+
const dotEnvVars = extractProjectEnvVars(mode, buildOpts);
|
|
58
|
+
for (const varName in dotEnvVars) {
|
|
59
|
+
envVars[varName] ??= dotEnvVars[varName];
|
|
60
|
+
}
|
|
17
61
|
return envVars;
|
|
18
62
|
}
|
|
19
63
|
/**
|
|
@@ -3,6 +3,8 @@ import type { OpenNextConfig } from "@opennextjs/aws/types/open-next.js";
|
|
|
3
3
|
import type { Unstable_Config as WranglerConfig } from "wrangler";
|
|
4
4
|
import type yargs from "yargs";
|
|
5
5
|
import type { WranglerTarget } from "../utils/run-wrangler.js";
|
|
6
|
+
import { type WorkerEnvVar } from "./helpers.js";
|
|
7
|
+
export declare function populateCache(buildOpts: BuildOptions, config: OpenNextConfig, wranglerConfig: WranglerConfig, populateCacheOptions: PopulateCacheOptions, envVars: WorkerEnvVar): Promise<void>;
|
|
6
8
|
export type CacheAsset = {
|
|
7
9
|
isFetch: boolean;
|
|
8
10
|
fullPath: string;
|
|
@@ -34,7 +36,6 @@ type PopulateCacheOptions = {
|
|
|
34
36
|
*/
|
|
35
37
|
shouldUsePreviewId: boolean;
|
|
36
38
|
};
|
|
37
|
-
export declare function populateCache(options: BuildOptions, config: OpenNextConfig, wranglerConfig: WranglerConfig, populateCacheOptions: PopulateCacheOptions): Promise<void>;
|
|
38
39
|
/**
|
|
39
40
|
* Add the `populateCache` command to yargs configuration, with nested commands for `local` and `remote`.
|
|
40
41
|
*
|
|
@@ -12,6 +12,58 @@ import { normalizePath } from "../build/utils/normalize-path.js";
|
|
|
12
12
|
import { runWrangler } from "../utils/run-wrangler.js";
|
|
13
13
|
import { getEnvFromPlatformProxy, quoteShellMeta } from "./helpers.js";
|
|
14
14
|
import { getNormalizedOptions, printHeaders, readWranglerConfig, retrieveCompiledConfig, withWranglerOptions, withWranglerPassthroughArgs, } from "./utils.js";
|
|
15
|
+
/**
|
|
16
|
+
* Implementation of the `opennextjs-cloudflare populateCache` command.
|
|
17
|
+
*
|
|
18
|
+
* @param args
|
|
19
|
+
*/
|
|
20
|
+
async function populateCacheCommand(target, args) {
|
|
21
|
+
printHeaders(`populate cache - ${target}`);
|
|
22
|
+
const { config } = await retrieveCompiledConfig();
|
|
23
|
+
const buildOpts = getNormalizedOptions(config);
|
|
24
|
+
const wranglerConfig = readWranglerConfig(args);
|
|
25
|
+
const envVars = await getEnvFromPlatformProxy(config, buildOpts);
|
|
26
|
+
await populateCache(buildOpts, config, wranglerConfig, {
|
|
27
|
+
target,
|
|
28
|
+
environment: args.env,
|
|
29
|
+
wranglerConfigPath: args.wranglerConfigPath,
|
|
30
|
+
cacheChunkSize: args.cacheChunkSize,
|
|
31
|
+
shouldUsePreviewId: false,
|
|
32
|
+
}, envVars);
|
|
33
|
+
}
|
|
34
|
+
export async function populateCache(buildOpts, config, wranglerConfig, populateCacheOptions, envVars) {
|
|
35
|
+
const { incrementalCache, tagCache } = config.default.override ?? {};
|
|
36
|
+
if (!existsSync(buildOpts.outputDir)) {
|
|
37
|
+
logger.error("Unable to populate cache: Open Next build not found");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
if (!config.dangerous?.disableIncrementalCache && incrementalCache) {
|
|
41
|
+
const name = await resolveCacheName(incrementalCache);
|
|
42
|
+
switch (name) {
|
|
43
|
+
case R2_CACHE_NAME:
|
|
44
|
+
await populateR2IncrementalCache(buildOpts, wranglerConfig, populateCacheOptions, envVars);
|
|
45
|
+
break;
|
|
46
|
+
case KV_CACHE_NAME:
|
|
47
|
+
await populateKVIncrementalCache(buildOpts, wranglerConfig, populateCacheOptions, envVars);
|
|
48
|
+
break;
|
|
49
|
+
case STATIC_ASSETS_CACHE_NAME:
|
|
50
|
+
populateStaticAssetsIncrementalCache(buildOpts);
|
|
51
|
+
break;
|
|
52
|
+
default:
|
|
53
|
+
logger.info("Incremental cache does not need populating");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (!config.dangerous?.disableTagCache && !config.dangerous?.disableIncrementalCache && tagCache) {
|
|
57
|
+
const name = await resolveCacheName(tagCache);
|
|
58
|
+
switch (name) {
|
|
59
|
+
case D1_TAG_NAME:
|
|
60
|
+
populateD1TagCache(buildOpts, wranglerConfig, populateCacheOptions);
|
|
61
|
+
break;
|
|
62
|
+
default:
|
|
63
|
+
logger.info("Tag cache does not need populating");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
15
67
|
async function resolveCacheName(value) {
|
|
16
68
|
return typeof value === "function" ? (await value()).name : value;
|
|
17
69
|
}
|
|
@@ -51,7 +103,7 @@ export function getCacheAssets(opts) {
|
|
|
51
103
|
}
|
|
52
104
|
return assets;
|
|
53
105
|
}
|
|
54
|
-
async function populateR2IncrementalCache(
|
|
106
|
+
async function populateR2IncrementalCache(buildOpts, config, populateCacheOptions, envVars) {
|
|
55
107
|
logger.info("\nPopulating R2 incremental cache...");
|
|
56
108
|
const binding = config.r2_buckets.find(({ binding }) => binding === R2_CACHE_BINDING_NAME);
|
|
57
109
|
if (!binding) {
|
|
@@ -61,16 +113,15 @@ async function populateR2IncrementalCache(options, config, populateCacheOptions)
|
|
|
61
113
|
if (!bucket) {
|
|
62
114
|
throw new Error(`R2 binding ${JSON.stringify(R2_CACHE_BINDING_NAME)} should have a 'bucket_name'`);
|
|
63
115
|
}
|
|
64
|
-
const envVars = await getEnvFromPlatformProxy(populateCacheOptions);
|
|
65
116
|
const prefix = envVars[R2_CACHE_PREFIX_ENV_NAME];
|
|
66
|
-
const assets = getCacheAssets(
|
|
117
|
+
const assets = getCacheAssets(buildOpts);
|
|
67
118
|
for (const { fullPath, key, buildId, isFetch } of tqdm(assets)) {
|
|
68
119
|
const cacheKey = computeCacheKey(key, {
|
|
69
120
|
prefix,
|
|
70
121
|
buildId,
|
|
71
122
|
cacheType: isFetch ? "fetch" : "cache",
|
|
72
123
|
});
|
|
73
|
-
runWrangler(
|
|
124
|
+
runWrangler(buildOpts, [
|
|
74
125
|
"r2 object put",
|
|
75
126
|
quoteShellMeta(normalizePath(path.join(bucket, cacheKey))),
|
|
76
127
|
`--file ${quoteShellMeta(fullPath)}`,
|
|
@@ -85,20 +136,19 @@ async function populateR2IncrementalCache(options, config, populateCacheOptions)
|
|
|
85
136
|
}
|
|
86
137
|
logger.info(`Successfully populated cache with ${assets.length} assets`);
|
|
87
138
|
}
|
|
88
|
-
async function populateKVIncrementalCache(
|
|
139
|
+
async function populateKVIncrementalCache(buildOpts, config, populateCacheOptions, envVars) {
|
|
89
140
|
logger.info("\nPopulating KV incremental cache...");
|
|
90
141
|
const binding = config.kv_namespaces.find(({ binding }) => binding === KV_CACHE_BINDING_NAME);
|
|
91
142
|
if (!binding) {
|
|
92
143
|
throw new Error(`No KV binding ${JSON.stringify(KV_CACHE_BINDING_NAME)} found!`);
|
|
93
144
|
}
|
|
94
|
-
const envVars = await getEnvFromPlatformProxy(populateCacheOptions);
|
|
95
145
|
const prefix = envVars[KV_CACHE_PREFIX_ENV_NAME];
|
|
96
|
-
const assets = getCacheAssets(
|
|
146
|
+
const assets = getCacheAssets(buildOpts);
|
|
97
147
|
const chunkSize = Math.max(1, populateCacheOptions.cacheChunkSize ?? 25);
|
|
98
148
|
const totalChunks = Math.ceil(assets.length / chunkSize);
|
|
99
149
|
logger.info(`Inserting ${assets.length} assets to KV in chunks of ${chunkSize}`);
|
|
100
150
|
for (const i of tqdm(Array.from({ length: totalChunks }, (_, i) => i))) {
|
|
101
|
-
const chunkPath = path.join(
|
|
151
|
+
const chunkPath = path.join(buildOpts.outputDir, "cloudflare", `cache-chunk-${i}.json`);
|
|
102
152
|
const kvMapping = assets
|
|
103
153
|
.slice(i * chunkSize, (i + 1) * chunkSize)
|
|
104
154
|
.map(({ fullPath, key, buildId, isFetch }) => ({
|
|
@@ -110,7 +160,7 @@ async function populateKVIncrementalCache(options, config, populateCacheOptions)
|
|
|
110
160
|
value: readFileSync(fullPath, "utf8"),
|
|
111
161
|
}));
|
|
112
162
|
writeFileSync(chunkPath, JSON.stringify(kvMapping));
|
|
113
|
-
runWrangler(
|
|
163
|
+
runWrangler(buildOpts, [
|
|
114
164
|
"kv bulk put",
|
|
115
165
|
quoteShellMeta(chunkPath),
|
|
116
166
|
`--binding ${KV_CACHE_BINDING_NAME}`,
|
|
@@ -125,13 +175,13 @@ async function populateKVIncrementalCache(options, config, populateCacheOptions)
|
|
|
125
175
|
}
|
|
126
176
|
logger.info(`Successfully populated cache with ${assets.length} assets`);
|
|
127
177
|
}
|
|
128
|
-
function populateD1TagCache(
|
|
178
|
+
function populateD1TagCache(buildOpts, config, populateCacheOptions) {
|
|
129
179
|
logger.info("\nCreating D1 table if necessary...");
|
|
130
180
|
const binding = config.d1_databases.find(({ binding }) => binding === D1_TAG_BINDING_NAME);
|
|
131
181
|
if (!binding) {
|
|
132
182
|
throw new Error(`No D1 binding ${JSON.stringify(D1_TAG_BINDING_NAME)} found!`);
|
|
133
183
|
}
|
|
134
|
-
runWrangler(
|
|
184
|
+
runWrangler(buildOpts, [
|
|
135
185
|
"d1 execute",
|
|
136
186
|
D1_TAG_BINDING_NAME,
|
|
137
187
|
`--command "CREATE TABLE IF NOT EXISTS revalidations (tag TEXT NOT NULL, revalidatedAt INTEGER NOT NULL, UNIQUE(tag) ON CONFLICT REPLACE);"`,
|
|
@@ -149,57 +199,6 @@ function populateStaticAssetsIncrementalCache(options) {
|
|
|
149
199
|
cpSync(path.join(options.outputDir, "cache"), path.join(options.outputDir, "assets", STATIC_ASSETS_CACHE_DIR), { recursive: true });
|
|
150
200
|
logger.info(`Successfully populated static assets cache`);
|
|
151
201
|
}
|
|
152
|
-
export async function populateCache(options, config, wranglerConfig, populateCacheOptions) {
|
|
153
|
-
const { incrementalCache, tagCache } = config.default.override ?? {};
|
|
154
|
-
if (!existsSync(options.outputDir)) {
|
|
155
|
-
logger.error("Unable to populate cache: Open Next build not found");
|
|
156
|
-
process.exit(1);
|
|
157
|
-
}
|
|
158
|
-
if (!config.dangerous?.disableIncrementalCache && incrementalCache) {
|
|
159
|
-
const name = await resolveCacheName(incrementalCache);
|
|
160
|
-
switch (name) {
|
|
161
|
-
case R2_CACHE_NAME:
|
|
162
|
-
await populateR2IncrementalCache(options, wranglerConfig, populateCacheOptions);
|
|
163
|
-
break;
|
|
164
|
-
case KV_CACHE_NAME:
|
|
165
|
-
await populateKVIncrementalCache(options, wranglerConfig, populateCacheOptions);
|
|
166
|
-
break;
|
|
167
|
-
case STATIC_ASSETS_CACHE_NAME:
|
|
168
|
-
populateStaticAssetsIncrementalCache(options);
|
|
169
|
-
break;
|
|
170
|
-
default:
|
|
171
|
-
logger.info("Incremental cache does not need populating");
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
if (!config.dangerous?.disableTagCache && !config.dangerous?.disableIncrementalCache && tagCache) {
|
|
175
|
-
const name = await resolveCacheName(tagCache);
|
|
176
|
-
switch (name) {
|
|
177
|
-
case D1_TAG_NAME:
|
|
178
|
-
populateD1TagCache(options, wranglerConfig, populateCacheOptions);
|
|
179
|
-
break;
|
|
180
|
-
default:
|
|
181
|
-
logger.info("Tag cache does not need populating");
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Implementation of the `opennextjs-cloudflare populateCache` command.
|
|
187
|
-
*
|
|
188
|
-
* @param args
|
|
189
|
-
*/
|
|
190
|
-
async function populateCacheCommand(target, args) {
|
|
191
|
-
printHeaders(`populate cache - ${target}`);
|
|
192
|
-
const { config } = await retrieveCompiledConfig();
|
|
193
|
-
const options = getNormalizedOptions(config);
|
|
194
|
-
const wranglerConfig = readWranglerConfig(args);
|
|
195
|
-
await populateCache(options, config, wranglerConfig, {
|
|
196
|
-
target,
|
|
197
|
-
environment: args.env,
|
|
198
|
-
wranglerConfigPath: args.wranglerConfigPath,
|
|
199
|
-
cacheChunkSize: args.cacheChunkSize,
|
|
200
|
-
shouldUsePreviewId: false,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
202
|
/**
|
|
204
203
|
* Add the `populateCache` command to yargs configuration, with nested commands for `local` and `remote`.
|
|
205
204
|
*
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { runWrangler } from "../utils/run-wrangler.js";
|
|
2
|
+
import { getEnvFromPlatformProxy } from "./helpers.js";
|
|
2
3
|
import { populateCache, withPopulateCacheOptions } from "./populate-cache.js";
|
|
3
4
|
import { getNormalizedOptions, printHeaders, readWranglerConfig, retrieveCompiledConfig, withWranglerPassthroughArgs, } from "./utils.js";
|
|
4
5
|
/**
|
|
@@ -9,16 +10,17 @@ import { getNormalizedOptions, printHeaders, readWranglerConfig, retrieveCompile
|
|
|
9
10
|
export async function previewCommand(args) {
|
|
10
11
|
printHeaders("preview");
|
|
11
12
|
const { config } = await retrieveCompiledConfig();
|
|
12
|
-
const
|
|
13
|
+
const buildOpts = getNormalizedOptions(config);
|
|
13
14
|
const wranglerConfig = readWranglerConfig(args);
|
|
14
|
-
await
|
|
15
|
+
const envVars = await getEnvFromPlatformProxy(config, buildOpts);
|
|
16
|
+
await populateCache(buildOpts, config, wranglerConfig, {
|
|
15
17
|
target: args.remote ? "remote" : "local",
|
|
16
18
|
environment: args.env,
|
|
17
19
|
wranglerConfigPath: args.wranglerConfigPath,
|
|
18
20
|
cacheChunkSize: args.cacheChunkSize,
|
|
19
21
|
shouldUsePreviewId: args.remote,
|
|
20
|
-
});
|
|
21
|
-
runWrangler(
|
|
22
|
+
}, envVars);
|
|
23
|
+
runWrangler(buildOpts, ["dev", ...args.wranglerArgs], { logging: "all" });
|
|
22
24
|
}
|
|
23
25
|
/**
|
|
24
26
|
* Add the `preview` command to yargs configuration.
|
|
@@ -26,12 +26,12 @@ import type { WorkerEnvVar } from "./helpers.js";
|
|
|
26
26
|
/**
|
|
27
27
|
* Compute the deployment mapping for a deployment.
|
|
28
28
|
*
|
|
29
|
-
* @param
|
|
29
|
+
* @param buildOpts Build options
|
|
30
30
|
* @param config OpenNext config
|
|
31
31
|
* @param workerEnvVars Worker Environment variables (taken from the wrangler config files)
|
|
32
32
|
* @returns Deployment mapping or undefined
|
|
33
33
|
*/
|
|
34
|
-
export declare function getDeploymentMapping(
|
|
34
|
+
export declare function getDeploymentMapping(buildOpts: BuildOptions, config: OpenNextConfig, workerEnvVars: WorkerEnvVar): Promise<Record<string, string> | undefined>;
|
|
35
35
|
/**
|
|
36
36
|
* Update an existing deployment mapping:
|
|
37
37
|
* - Replace the "current" version with the latest deployed version
|
|
@@ -34,12 +34,12 @@ const MS_PER_DAY = 24 * 3600 * 1000;
|
|
|
34
34
|
/**
|
|
35
35
|
* Compute the deployment mapping for a deployment.
|
|
36
36
|
*
|
|
37
|
-
* @param
|
|
37
|
+
* @param buildOpts Build options
|
|
38
38
|
* @param config OpenNext config
|
|
39
39
|
* @param workerEnvVars Worker Environment variables (taken from the wrangler config files)
|
|
40
40
|
* @returns Deployment mapping or undefined
|
|
41
41
|
*/
|
|
42
|
-
export async function getDeploymentMapping(
|
|
42
|
+
export async function getDeploymentMapping(buildOpts, config, workerEnvVars) {
|
|
43
43
|
if (config.cloudflare?.skewProtection?.enabled !== true) {
|
|
44
44
|
return undefined;
|
|
45
45
|
}
|
|
@@ -47,7 +47,7 @@ export async function getDeploymentMapping(options, config, workerEnvVars) {
|
|
|
47
47
|
// system environment variables to take precedence over the variables defined
|
|
48
48
|
// in the wrangler config files
|
|
49
49
|
const envVars = { ...workerEnvVars, ...process.env };
|
|
50
|
-
const nextConfig = loadConfig(path.join(
|
|
50
|
+
const nextConfig = loadConfig(path.join(buildOpts.appBuildOutputPath, ".next"));
|
|
51
51
|
const deploymentId = nextConfig.deploymentId;
|
|
52
52
|
if (!deploymentId) {
|
|
53
53
|
logger.error("Deployment ID should be set in the Next config when skew protection is enabled");
|
|
@@ -12,21 +12,21 @@ import { getNormalizedOptions, printHeaders, readWranglerConfig, retrieveCompile
|
|
|
12
12
|
export async function uploadCommand(args) {
|
|
13
13
|
printHeaders("upload");
|
|
14
14
|
const { config } = await retrieveCompiledConfig();
|
|
15
|
-
const
|
|
15
|
+
const buildOpts = getNormalizedOptions(config);
|
|
16
16
|
const wranglerConfig = readWranglerConfig(args);
|
|
17
17
|
const envVars = await getEnvFromPlatformProxy({
|
|
18
18
|
configPath: args.wranglerConfigPath,
|
|
19
19
|
environment: args.env,
|
|
20
|
-
});
|
|
21
|
-
const deploymentMapping = await getDeploymentMapping(
|
|
22
|
-
await populateCache(
|
|
20
|
+
}, buildOpts);
|
|
21
|
+
const deploymentMapping = await getDeploymentMapping(buildOpts, config, envVars);
|
|
22
|
+
await populateCache(buildOpts, config, wranglerConfig, {
|
|
23
23
|
target: "remote",
|
|
24
24
|
environment: args.env,
|
|
25
25
|
wranglerConfigPath: args.wranglerConfigPath,
|
|
26
26
|
cacheChunkSize: args.cacheChunkSize,
|
|
27
27
|
shouldUsePreviewId: false,
|
|
28
|
-
});
|
|
29
|
-
runWrangler(
|
|
28
|
+
}, envVars);
|
|
29
|
+
runWrangler(buildOpts, [
|
|
30
30
|
"versions upload",
|
|
31
31
|
...args.wranglerArgs,
|
|
32
32
|
...(deploymentMapping
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
let NEXT_IMAGE_REGEXP;
|
|
1
2
|
/**
|
|
2
3
|
* Fetches an images.
|
|
3
4
|
*
|
|
@@ -11,17 +12,15 @@ export async function fetchImage(fetcher, imageUrl, ctx) {
|
|
|
11
12
|
}
|
|
12
13
|
// Local
|
|
13
14
|
if (imageUrl.startsWith("/")) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// We only need pathname and search
|
|
18
|
-
url = new URL(imageUrl, "http://n");
|
|
19
|
-
pathname = decodeURIComponent(url.pathname);
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
15
|
+
// @ts-expect-error TS2339 Missing types for URL.parse
|
|
16
|
+
const url = URL.parse(imageUrl, "http://n");
|
|
17
|
+
if (url == null) {
|
|
22
18
|
return getUrlErrorResponse();
|
|
23
19
|
}
|
|
24
|
-
|
|
20
|
+
// This method will never throw because URL parser will handle invalid input.
|
|
21
|
+
const pathname = decodeURIComponent(url.pathname);
|
|
22
|
+
NEXT_IMAGE_REGEXP ??= /\/_next\/image($|\/)/;
|
|
23
|
+
if (NEXT_IMAGE_REGEXP.test(pathname)) {
|
|
25
24
|
return getUrlErrorResponse();
|
|
26
25
|
}
|
|
27
26
|
// If localPatterns are not defined all local images are allowed.
|
|
@@ -3,6 +3,7 @@ import process from "node:process";
|
|
|
3
3
|
export const DEPLOYMENT_MAPPING_ENV_NAME = "CF_DEPLOYMENT_MAPPING";
|
|
4
4
|
/** Version used for the latest worker */
|
|
5
5
|
export const CURRENT_VERSION_ID = "current";
|
|
6
|
+
let deploymentMapping;
|
|
6
7
|
/**
|
|
7
8
|
* Routes the request to the requested deployment.
|
|
8
9
|
*
|
|
@@ -36,14 +37,14 @@ export function maybeGetSkewProtectionResponse(request) {
|
|
|
36
37
|
// The request does not specify a deployment id or it is the current deployment id
|
|
37
38
|
return undefined;
|
|
38
39
|
}
|
|
39
|
-
|
|
40
|
+
deploymentMapping ??= process.env[DEPLOYMENT_MAPPING_ENV_NAME]
|
|
40
41
|
? JSON.parse(process.env[DEPLOYMENT_MAPPING_ENV_NAME])
|
|
41
42
|
: {};
|
|
42
|
-
if (!(requestDeploymentId in
|
|
43
|
+
if (!(requestDeploymentId in deploymentMapping)) {
|
|
43
44
|
// Unknown deployment id, serve the current version
|
|
44
45
|
return undefined;
|
|
45
46
|
}
|
|
46
|
-
const version =
|
|
47
|
+
const version = deploymentMapping[requestDeploymentId];
|
|
47
48
|
if (!version || version === CURRENT_VERSION_ID) {
|
|
48
49
|
return undefined;
|
|
49
50
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opennextjs/cloudflare",
|
|
3
3
|
"description": "Cloudflare builder for next apps",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.10.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"opennextjs-cloudflare": "dist/cli/index.js"
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"homepage": "https://github.com/opennextjs/opennextjs-cloudflare",
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@dotenvx/dotenvx": "1.31.0",
|
|
46
|
-
"@opennextjs/aws": "3.8.
|
|
46
|
+
"@opennextjs/aws": "3.8.4",
|
|
47
47
|
"cloudflare": "^4.4.1",
|
|
48
48
|
"enquirer": "^2.4.1",
|
|
49
49
|
"glob": "^11.0.0",
|