Skip to content

Commit 8de2c04

Browse files
dario-piotrowiczvicb
andauthoredJan 27, 2025··
introduce new initOpenNextCloudflareForDev utility and make getCloudflareContext synchronous (#265)
Co-authored-by: Victor Berchet <victor@suumit.com>
1 parent 4983a36 commit 8de2c04

23 files changed

+361
-206
lines changed
 

‎.changeset/chilly-dryers-begin.md

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
"@opennextjs/cloudflare": minor
3+
---
4+
5+
introduce new `initOpenNextCloudflareForDev` utility and make `getCloudflareContext` synchronous
6+
7+
this change introduces a new `initOpenNextCloudflareForDev` function that must called in the [Next.js config file](https://nextjs.org/docs/app/api-reference/config/next-config-js) to integrate the Next.js dev server with the open-next Cloudflare adapter.
8+
9+
Also makes `getCloudflareContext` synchronous.
10+
11+
Additionally the `getCloudflareContext` can now work during local development (`next dev`) in the edge runtime (including middlewares).
12+
13+
Moving forward we'll recommend that all applications include the use of the `initOpenNextCloudflareForDev` utility in their config file (there is no downside in doing so and it only effect local development).
14+
15+
Example:
16+
17+
```js
18+
// next.config.mjs
19+
20+
import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
21+
22+
initOpenNextCloudflareForDev();
23+
24+
/** @type {import('next').NextConfig} */
25+
const nextConfig = {};
26+
27+
export default nextConfig;
28+
```

‎examples/api/app/api/hello/route.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ export async function GET() {
1111
return new Response("Hello World!");
1212
}
1313

14-
// Retrieve the bindings defined in wrangler.toml
15-
const { env } = await getCloudflareContext();
16-
return new Response(env.hello);
14+
// Retrieve the bindings defined in wrangler.json
15+
return new Response(getCloudflareContext().env.hello);
1716
}
1817

1918
export async function POST(request: Request) {

‎examples/api/e2e/playwright.config.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { defineConfig, devices } from "@playwright/test";
2+
import type nodeProcess from "node:process";
23

3-
declare var process: { env: Record<string, string> };
4+
declare const process: typeof nodeProcess;
45

56
/**
67
* See https://playwright.dev/docs/test-configuration.
@@ -49,5 +50,6 @@ export default defineConfig({
4950
command: "pnpm preview:worker",
5051
url: "http://localhost:8770",
5152
reuseExistingServer: !process.env.CI,
53+
timeout: 70_000,
5254
},
5355
});

‎examples/api/e2e/playwright.dev.config.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { defineConfig, devices } from "@playwright/test";
2+
import type nodeProcess from "node:process";
23

3-
declare var process: { env: Record<string, string> };
4+
declare const process: typeof nodeProcess;
45

56
/**
67
* See https://playwright.dev/docs/test-configuration.

‎examples/api/next.config.mjs

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
2+
3+
initOpenNextCloudflareForDev();
4+
15
/** @type {import('next').NextConfig} */
26
const nextConfig = {};
37

‎examples/create-next-app/e2e/playwright.config.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { defineConfig, devices } from "@playwright/test";
2+
import type nodeProcess from "node:process";
23

3-
declare const process: { env: Record<string, string> };
4+
declare const process: typeof nodeProcess;
45

56
/**
67
* See https://playwright.dev/docs/test-configuration.
@@ -49,5 +50,6 @@ export default defineConfig({
4950
command: "pnpm preview:worker",
5051
url: "http://localhost:8771",
5152
reuseExistingServer: !process.env.CI,
53+
timeout: 70_000,
5254
},
5355
});

‎examples/create-next-app/next.config.mjs

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
2+
3+
initOpenNextCloudflareForDev();
4+
15
/** @type {import('next').NextConfig} */
26
const nextConfig = {};
37

+23-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
import { headers } from "next/headers";
2+
13
export default function MiddlewarePage() {
2-
return <h1>Via middleware</h1>;
4+
const cloudflareContextHeader = headers().get("x-cloudflare-context");
5+
6+
return (
7+
<>
8+
<h1>Via middleware</h1>
9+
<p>
10+
The value of the <i>x-cloudflare-context</i> header is: <br />
11+
<span
12+
style={{
13+
display: "inline-block",
14+
margin: "1rem 2rem",
15+
color: "grey",
16+
fontSize: "1.2rem",
17+
}}
18+
data-testid="cloudflare-context-header"
19+
>
20+
{cloudflareContextHeader}
21+
</span>
22+
</p>
23+
</>
24+
);
325
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { test, expect } from "@playwright/test";
2+
3+
test("middlewares have access to the cloudflare context", async ({ page }) => {
4+
await page.goto("/middleware");
5+
const cloudflareContextHeaderElement = page.getByTestId("cloudflare-context-header");
6+
expect(await cloudflareContextHeaderElement.textContent()).toContain(
7+
"typeof `cloudflareContext.env` = object"
8+
);
9+
});

‎examples/middleware/e2e/playwright.config.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { defineConfig, devices } from "@playwright/test";
2+
import type nodeProcess from "node:process";
23

3-
declare const process: { env: Record<string, string> };
4+
declare const process: typeof nodeProcess;
45

56
/**
67
* See https://playwright.dev/docs/test-configuration.
@@ -49,5 +50,6 @@ export default defineConfig({
4950
command: "pnpm preview:worker",
5051
url: "http://localhost:8774",
5152
reuseExistingServer: !process.env.CI,
53+
timeout: 70_000,
5254
},
5355
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { defineConfig, devices } from "@playwright/test";
2+
import type nodeProcess from "node:process";
3+
4+
declare const process: typeof nodeProcess;
5+
6+
/**
7+
* See https://playwright.dev/docs/test-configuration.
8+
*/
9+
export default defineConfig({
10+
testDir: "./",
11+
/* Run tests in files in parallel */
12+
fullyParallel: true,
13+
/* Fail the build on CI if you accidentally left test.only in the source code. */
14+
forbidOnly: !!process.env.CI,
15+
/* Retry on CI only */
16+
retries: process.env.CI ? 2 : 0,
17+
/* Opt out of parallel tests on CI. */
18+
workers: process.env.CI ? 1 : undefined,
19+
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
20+
reporter: "html",
21+
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
22+
use: {
23+
/* Base URL to use in actions like `await page.goto('/')`. */
24+
baseURL: "http://localhost:3334",
25+
26+
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
27+
trace: "on-first-retry",
28+
},
29+
30+
/* Configure projects for major browsers */
31+
projects: [
32+
{
33+
name: "chromium",
34+
use: { ...devices["Desktop Chrome"] },
35+
},
36+
37+
{
38+
name: "firefox",
39+
use: { ...devices["Desktop Firefox"] },
40+
},
41+
42+
{
43+
name: "webkit",
44+
use: { ...devices["Desktop Safari"] },
45+
},
46+
],
47+
48+
/* Run your local dev server before starting the tests */
49+
webServer: {
50+
command: "pnpm dev --port 3334",
51+
url: "http://localhost:3334",
52+
reuseExistingServer: !process.env.CI,
53+
},
54+
});

‎examples/middleware/middleware.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { NextRequest, NextResponse, NextFetchEvent } from "next/server";
22
import { clerkMiddleware } from "@clerk/nextjs/server";
33

4+
import { getCloudflareContext } from "@opennextjs/cloudflare";
5+
46
export function middleware(request: NextRequest, event: NextFetchEvent) {
57
console.log("middleware");
68
if (request.nextUrl.pathname === "/about") {
@@ -16,7 +18,19 @@ export function middleware(request: NextRequest, event: NextFetchEvent) {
1618
})(request, event);
1719
}
1820

19-
return NextResponse.next();
21+
const requestHeaders = new Headers(request.headers);
22+
const cloudflareContext = getCloudflareContext();
23+
24+
requestHeaders.set(
25+
"x-cloudflare-context",
26+
`typeof \`cloudflareContext.env\` = ${typeof cloudflareContext.env}`
27+
);
28+
29+
return NextResponse.next({
30+
request: {
31+
headers: requestHeaders,
32+
},
33+
});
2034
}
2135

2236
export const config = {

‎examples/middleware/next.config.mjs

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
2+
3+
initOpenNextCloudflareForDev();
4+
15
/** @type {import('next').NextConfig} */
26
const nextConfig = {};
37

‎examples/middleware/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
"build:worker": "pnpm opennextjs-cloudflare",
1010
"dev:worker": "wrangler dev --port 8774 --inspector-port 9334",
1111
"preview:worker": "pnpm build:worker && pnpm dev:worker",
12-
"e2e": "playwright test -c e2e/playwright.config.ts"
12+
"e2e": "playwright test -c e2e/playwright.config.ts",
13+
"e2e:dev": "playwright test -c e2e/playwright.dev.config.ts"
1314
},
1415
"dependencies": {
1516
"@clerk/nextjs": "6.9.6",

‎examples/middleware/wrangler.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,9 @@
77
"assets": {
88
"directory": ".open-next/assets",
99
"binding": "ASSETS"
10-
}
10+
},
11+
"vars": {
12+
"MY_VAR": "my-var"
13+
},
14+
"kv_namespaces": [{ "binding": "MY_KV", "id": "<id>" }]
1115
}

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"prettier:fix": "prettier --write .",
1414
"lint:check": "pnpm -r lint:check",
1515
"lint:fix": "pnpm -r lint:fix",
16+
"fix": "pnpm prettier:fix && pnpm lint:fix",
1617
"ts:check": "pnpm -r ts:check",
1718
"test": "pnpm -r test",
1819
"code:checks": "pnpm prettier:check && pnpm lint:check && pnpm ts:check",

‎packages/cloudflare/README.md

+1-66
Original file line numberDiff line numberDiff line change
@@ -6,72 +6,7 @@ Deploy Next.js apps to Cloudflare!
66

77
## Get started
88

9-
You can use [`create-next-app`](https://nextjs.org/docs/pages/api-reference/cli/create-next-app) to start a new application or take an existing Next.js application and deploy it to Cloudflare using the following few steps:
10-
11-
## Configure your app
12-
13-
- add the following `devDependencies` to the `package.json`:
14-
15-
```bash
16-
npm add -D wrangler@latest @opennextjs/cloudflare
17-
# or
18-
pnpm add -D wrangler@latest @opennextjs/cloudflare
19-
# or
20-
yarn add -D wrangler@latest @opennextjs/cloudflare
21-
# or
22-
bun add -D wrangler@latest @opennextjs/cloudflare
23-
```
24-
25-
- add a `wrangler.json` at the root of your project
26-
27-
```json
28-
{
29-
"$schema": "node_modules/wrangler/config-schema.json",
30-
"main": ".open-next/worker.js",
31-
"name": "<your-app-name>",
32-
"compatibility_date": "2024-12-30",
33-
"compatibility_flags": ["nodejs_compat"],
34-
"assets": {
35-
"directory": ".open-next/assets",
36-
"binding": "ASSETS"
37-
}
38-
}
39-
```
40-
41-
- add a `open-next.config.ts` at the root of your project:
42-
43-
```ts
44-
import type { OpenNextConfig } from "open-next/types/open-next";
45-
46-
const config: OpenNextConfig = {
47-
default: {
48-
override: {
49-
wrapper: "cloudflare-node",
50-
converter: "edge",
51-
// Unused implementation
52-
incrementalCache: "dummy",
53-
tagCache: "dummy",
54-
queue: "dummy",
55-
},
56-
},
57-
58-
middleware: {
59-
external: true,
60-
override: {
61-
wrapper: "cloudflare-edge",
62-
converter: "edge",
63-
proxyExternalRequest: "fetch",
64-
},
65-
},
66-
};
67-
68-
export default config;
69-
```
70-
71-
## Known issues
72-
73-
- `▲ [WARNING] Suspicious assignment to defined constant "process.env.NODE_ENV" [assign-to-define]` can safely be ignored
74-
- Maybe more, still experimental...
9+
To get started with the adapter visit the [official get started documentation](https://opennext.js.org/cloudflare/get-started).
7510

7611
## Local development
7712

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import type { Context, RunningCodeOptions } from "node:vm";
2+
3+
declare global {
4+
interface CloudflareEnv {
5+
NEXT_CACHE_WORKERS_KV?: KVNamespace;
6+
ASSETS?: Fetcher;
7+
}
8+
}
9+
10+
export type CloudflareContext<
11+
CfProperties extends Record<string, unknown> = IncomingRequestCfProperties,
12+
Context = ExecutionContext,
13+
> = {
14+
/**
15+
* the worker's [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/)
16+
*/
17+
env: CloudflareEnv;
18+
/**
19+
* the request's [cf properties](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties)
20+
*/
21+
cf: CfProperties | undefined;
22+
/**
23+
* the current [execution context](https://developers.cloudflare.com/workers/runtime-apis/context)
24+
*/
25+
ctx: Context;
26+
};
27+
28+
/**
29+
* Symbol used as an index in the global scope to set and retrieve the Cloudflare context
30+
*
31+
* This is used both in production (in the actual built worker) and in development (`next dev`)
32+
*
33+
* Note: this symbol needs to be kept in sync with the one used in `src/cli/templates/worker.ts`
34+
*/
35+
const cloudflareContextSymbol = Symbol.for("__cloudflare-context__");
36+
37+
/**
38+
* `globalThis` override for internal usage (simply the standard `globalThis`) enhanced with
39+
* a property indexed by the `cloudflareContextSymbol`
40+
*/
41+
type InternalGlobalThis<
42+
CfProperties extends Record<string, unknown> = IncomingRequestCfProperties,
43+
Context = ExecutionContext,
44+
> = typeof globalThis & {
45+
[cloudflareContextSymbol]: CloudflareContext<CfProperties, Context> | undefined;
46+
};
47+
48+
/**
49+
* Utility to get the current Cloudflare context
50+
*
51+
* @returns the cloudflare context
52+
*/
53+
export function getCloudflareContext<
54+
CfProperties extends Record<string, unknown> = IncomingRequestCfProperties,
55+
Context = ExecutionContext,
56+
>(): CloudflareContext<CfProperties, Context> {
57+
const global = globalThis as InternalGlobalThis<CfProperties, Context>;
58+
59+
const cloudflareContext = global[cloudflareContextSymbol];
60+
61+
if (!cloudflareContext) {
62+
// the cloudflare context is initialized by the worker and is always present in production/preview
63+
// during local development (`next dev`) it might be missing only if the developers hasn't called
64+
// the `initOpenNextCloudflareForDev` function in their Next.js config file
65+
const getContextFunctionName = getCloudflareContext.name;
66+
const initFunctionName = initOpenNextCloudflareForDev.name;
67+
throw new Error(
68+
`\n\n\`${getContextFunctionName}\` has been called during development without having called` +
69+
` the \`${initFunctionName}\` function inside the Next.js config file.\n\n` +
70+
`In order to use \`${getContextFunctionName}\` import and call ${initFunctionName} in the Next.js config file.\n\n` +
71+
"Example: \n ```\n // next.config.mjs\n\n" +
72+
` import { ${initFunctionName} } from "@opennextjs/cloudflare";\n\n` +
73+
` ${initFunctionName}();\n\n` +
74+
" /** @type {import('next').NextConfig} */\n" +
75+
" const nextConfig = {};\n" +
76+
" export default nextConfig;\n" +
77+
" ```\n" +
78+
"\n(note: currently middlewares in Next.js are always run using the edge runtime)\n\n"
79+
);
80+
}
81+
82+
return cloudflareContext;
83+
}
84+
85+
/**
86+
* Performs some initial setup to integrate as best as possible the local Next.js dev server (run via `next dev`)
87+
* with the open-next Cloudflare adapter
88+
*
89+
* Note: this function should only be called inside the Next.js config file, and although async it doesn't need to be `await`ed
90+
*/
91+
export async function initOpenNextCloudflareForDev() {
92+
const context = await getCloudflareContextFromWrangler();
93+
94+
addCloudflareContextToNodejsGlobal(context);
95+
96+
await monkeyPatchVmModuleEdgeContext(context);
97+
}
98+
99+
/**
100+
* Adds the cloudflare context to the global scope in which the Next.js dev node.js process runs in, enabling
101+
* future calls to `getCloudflareContext` to retrieve and return such context
102+
*
103+
* @param cloudflareContext the cloudflare context to add to the node.sj global scope
104+
*/
105+
function addCloudflareContextToNodejsGlobal(cloudflareContext: CloudflareContext<CfProperties, Context>) {
106+
const global = globalThis as InternalGlobalThis<CfProperties, Context>;
107+
global[cloudflareContextSymbol] = cloudflareContext;
108+
}
109+
110+
/**
111+
* Next.js uses the Node.js vm module's `runInContext()` function to evaluate edge functions
112+
* in a runtime context that tries to simulate as accurately as possible the actual production runtime
113+
* behavior, see: https://github.com/vercel/next.js/blob/9a1cd3/packages/next/src/server/web/sandbox/context.ts#L525-L527
114+
*
115+
* This function monkey-patches the Node.js `vm` module to override the `runInContext()` function so that the
116+
* cloudflare context is added to the runtime context's global scope before edge functions are evaluated
117+
*
118+
* @param cloudflareContext the cloudflare context to patch onto the "edge" runtime context global scope
119+
*/
120+
async function monkeyPatchVmModuleEdgeContext(cloudflareContext: CloudflareContext<CfProperties, Context>) {
121+
const require = (
122+
await import(/* webpackIgnore: true */ `${"__module".replaceAll("_", "")}`)
123+
).default.createRequire(import.meta.url);
124+
125+
// eslint-disable-next-line unicorn/prefer-node-protocol -- the `next dev` compiler doesn't accept the node prefix
126+
const vmModule = require("vm");
127+
128+
const originalRunInContext = vmModule.runInContext.bind(vmModule);
129+
130+
vmModule.runInContext = (
131+
code: string,
132+
contextifiedObject: Context,
133+
options?: RunningCodeOptions | string
134+
) => {
135+
type RuntimeContext = Record<string, unknown> & {
136+
[cloudflareContextSymbol]?: CloudflareContext<CfProperties, Context>;
137+
};
138+
const runtimeContext = contextifiedObject as RuntimeContext;
139+
runtimeContext[cloudflareContextSymbol] ??= cloudflareContext;
140+
return originalRunInContext(code, contextifiedObject, options);
141+
};
142+
}
143+
144+
/**
145+
* Gets a cloudflare context object from wrangler
146+
*
147+
* @returns the cloudflare context ready for use
148+
*/
149+
async function getCloudflareContextFromWrangler<
150+
CfProperties extends Record<string, unknown> = IncomingRequestCfProperties,
151+
Context = ExecutionContext,
152+
>(): Promise<CloudflareContext<CfProperties, Context>> {
153+
// Note: we never want wrangler to be bundled in the Next.js app, that's why the import below looks like it does
154+
const { getPlatformProxy } = await import(/* webpackIgnore: true */ `${"__wrangler".replaceAll("_", "")}`);
155+
const { env, cf, ctx } = await getPlatformProxy({
156+
// This allows the selection of a wrangler environment while running in next dev mode
157+
environment: process.env.NEXT_DEV_WRANGLER_ENV,
158+
});
159+
return {
160+
env,
161+
cf: cf as unknown as CfProperties,
162+
ctx: ctx as Context,
163+
};
164+
}

‎packages/cloudflare/src/api/get-cloudflare-context.ts

-86
This file was deleted.

‎packages/cloudflare/src/api/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export * from "./get-cloudflare-context.js";
1+
export * from "./cloudflare-context.js";

‎packages/cloudflare/src/api/kvCache.ts

+21-30
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import type { KVNamespace } from "@cloudflare/workers-types";
21
import type { CacheValue, IncrementalCache, WithLastModified } from "@opennextjs/aws/types/overrides";
32
import { IgnorableError, RecoverableError } from "@opennextjs/aws/utils/error.js";
43

5-
import { getCloudflareContext } from "./get-cloudflare-context.js";
4+
import { getCloudflareContext } from "./cloudflare-context.js";
65

76
export const CACHE_ASSET_DIR = "cnd-cgi/_next_cache";
87

@@ -17,19 +16,16 @@ export const STATUS_DELETED = 1;
1716
*/
1817
class Cache implements IncrementalCache {
1918
readonly name = "cloudflare-kv";
20-
protected initialized = false;
21-
protected kv: KVNamespace | undefined;
22-
protected assets: Fetcher | undefined;
2319

2420
async get<IsFetch extends boolean = false>(
2521
key: string,
2622
isFetch?: IsFetch
2723
): Promise<WithLastModified<CacheValue<IsFetch>>> {
28-
if (!this.initialized) {
29-
await this.init();
30-
}
24+
const cfEnv = getCloudflareContext().env;
25+
const kv = cfEnv.NEXT_CACHE_WORKERS_KV;
26+
const assets = cfEnv.ASSETS;
3127

32-
if (!(this.kv || this.assets)) {
28+
if (!(kv || assets)) {
3329
throw new IgnorableError(`No KVNamespace nor Fetcher`);
3430
}
3531

@@ -42,19 +38,19 @@ class Cache implements IncrementalCache {
4238
status?: number;
4339
} | null = null;
4440

45-
if (this.kv) {
41+
if (kv) {
4642
this.debug(`- From KV`);
4743
const kvKey = this.getKVKey(key, isFetch);
48-
entry = await this.kv.get(kvKey, "json");
44+
entry = await kv.get(kvKey, "json");
4945
if (entry?.status === STATUS_DELETED) {
5046
return {};
5147
}
5248
}
5349

54-
if (!entry && this.assets) {
50+
if (!entry && assets) {
5551
this.debug(`- From Assets`);
5652
const url = this.getAssetUrl(key, isFetch);
57-
const response = await this.assets.fetch(url);
53+
const response = await assets.fetch(url);
5854
if (response.ok) {
5955
// TODO: consider populating KV with the asset value if faster.
6056
// This could be optional as KV writes are $$.
@@ -78,19 +74,20 @@ class Cache implements IncrementalCache {
7874
value: CacheValue<IsFetch>,
7975
isFetch?: IsFetch
8076
): Promise<void> {
81-
if (!this.initialized) {
82-
await this.init();
83-
}
84-
if (!this.kv) {
77+
const kv = getCloudflareContext().env.NEXT_CACHE_WORKERS_KV;
78+
79+
if (!kv) {
8580
throw new IgnorableError(`No KVNamespace`);
8681
}
82+
8783
this.debug(`Set ${key}`);
84+
8885
try {
8986
const kvKey = this.getKVKey(key, isFetch);
9087
// Note: We can not set a TTL as we might fallback to assets,
9188
// still removing old data (old BUILD_ID) could help avoiding
9289
// the cache growing too big.
93-
await this.kv.put(
90+
await kv.put(
9491
kvKey,
9592
JSON.stringify({
9693
value,
@@ -105,17 +102,18 @@ class Cache implements IncrementalCache {
105102
}
106103

107104
async delete(key: string): Promise<void> {
108-
if (!this.initialized) {
109-
await this.init();
110-
}
111-
if (!this.kv) {
105+
const kv = getCloudflareContext().env.NEXT_CACHE_WORKERS_KV;
106+
107+
if (!kv) {
112108
throw new IgnorableError(`No KVNamespace`);
113109
}
110+
114111
this.debug(`Delete ${key}`);
112+
115113
try {
116114
const kvKey = this.getKVKey(key, /* isFetch= */ false);
117115
// Do not delete the key as we would then fallback to the assets.
118-
await this.kv.put(kvKey, JSON.stringify({ status: STATUS_DELETED }));
116+
await kv.put(kvKey, JSON.stringify({ status: STATUS_DELETED }));
119117
} catch {
120118
throw new RecoverableError(`Failed to delete cache [${key}]`);
121119
}
@@ -140,13 +138,6 @@ class Cache implements IncrementalCache {
140138
protected getBuildId() {
141139
return process.env.NEXT_BUILD_ID ?? "no-build-id";
142140
}
143-
144-
protected async init() {
145-
const env = (await getCloudflareContext()).env;
146-
this.kv = env.NEXT_CACHE_WORKERS_KV;
147-
this.assets = env.ASSETS;
148-
this.initialized = true;
149-
}
150141
}
151142

152143
export default new Cache();

‎pnpm-lock.yaml

+11-11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎pnpm-workspace.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ catalog:
3333
typescript-eslint: ^8.7.0
3434
typescript: ^5.7.3
3535
vitest: ^2.1.1
36-
wrangler: ^3.103.0
36+
wrangler: ^3.105.0
3737

3838
# e2e tests
3939
catalogs:

0 commit comments

Comments
 (0)
Please sign in to comment.