@opennextjs/cloudflare 1.9.2 → 1.10.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.
@@ -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: join(options.outputDir, "cloudflare-templates/shims/empty.js"),
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).toString());
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 options = getNormalizedOptions(config);
15
+ const buildOpts = getNormalizedOptions(config);
16
16
  const wranglerConfig = readWranglerConfig(args);
17
- const envVars = await getEnvFromPlatformProxy({
18
- configPath: args.wranglerConfigPath,
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
- runWrangler(options, [
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
- * Return the string env vars from the worker.
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
- * Return the string env vars from the worker.
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
- const envVars = {};
10
- const proxy = await getPlatformProxy(options);
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(options, config, populateCacheOptions) {
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(options);
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(options, [
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(options, config, populateCacheOptions) {
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(options);
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(options.outputDir, "cloudflare", `cache-chunk-${i}.json`);
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(options, [
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(options, config, populateCacheOptions) {
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(options, [
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 options = getNormalizedOptions(config);
13
+ const buildOpts = getNormalizedOptions(config);
13
14
  const wranglerConfig = readWranglerConfig(args);
14
- await populateCache(options, config, wranglerConfig, {
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(options, ["dev", ...args.wranglerArgs], { logging: "all" });
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 options Build options
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(options: BuildOptions, config: OpenNextConfig, workerEnvVars: WorkerEnvVar): Promise<Record<string, string> | undefined>;
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 options Build options
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(options, config, workerEnvVars) {
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(options.appBuildOutputPath, ".next"));
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 options = getNormalizedOptions(config);
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(options, config, envVars);
22
- await populateCache(options, config, wranglerConfig, {
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(options, [
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
- let pathname;
15
- let url;
16
- try {
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
- if (/\/_next\/image($|\/)/.test(pathname)) {
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
- const mapping = process.env[DEPLOYMENT_MAPPING_ENV_NAME]
40
+ deploymentMapping ??= process.env[DEPLOYMENT_MAPPING_ENV_NAME]
40
41
  ? JSON.parse(process.env[DEPLOYMENT_MAPPING_ENV_NAME])
41
42
  : {};
42
- if (!(requestDeploymentId in mapping)) {
43
+ if (!(requestDeploymentId in deploymentMapping)) {
43
44
  // Unknown deployment id, serve the current version
44
45
  return undefined;
45
46
  }
46
- const version = mapping[requestDeploymentId];
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.9.2",
4
+ "version": "1.10.0",
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.1",
46
+ "@opennextjs/aws": "3.8.2",
47
47
  "cloudflare": "^4.4.1",
48
48
  "enquirer": "^2.4.1",
49
49
  "glob": "^11.0.0",