Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/funny-carrots-add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'astro': patch
'@astrojs/cloudflare': patch
---

Updates generated and default Cloudflare `compatibility_date` values to match the installed runtime and requires Wrangler `^4.125.0`
2 changes: 2 additions & 0 deletions knip.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ export default {
'rehype-toc',
'remark-code-titles',
'@types/http-cache-semantics',
// Resolved from the user's project by `astro add cloudflare`.
'@astrojs/cloudflare',
// Optional peer dep: dynamically imported in config validation for the legacy
// remark/rehype pipeline. Knip flags it because it's referenced from source.
'@astrojs/markdown-remark',
Expand Down
14 changes: 14 additions & 0 deletions packages/astro/src/cli/add/cloudflare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createRequire } from 'node:module';
import { pathToFileURL } from 'node:url';

interface CloudflareInfo {
getLocalWorkerdCompatibilityDate(): { date: string };
}

export async function getCloudflareCompatibilityDate(root: URL): Promise<string> {
const require = createRequire(root);
const infoPath = require.resolve('@astrojs/cloudflare/info');
const infoUrl = pathToFileURL(infoPath).toString();
const infoModule = (await import(infoUrl)) as CloudflareInfo;
return infoModule.getLocalWorkerdCompatibilityDate().date;
}
3 changes: 2 additions & 1 deletion packages/astro/src/cli/add/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { eventCliSession, telemetry } from '../../events/index.js';
import { exec } from '../exec.js';
import { createLoggerFromFlags, type Flags, flagsToAstroInlineConfig } from '../flags.js';
import { fetchPackageJson, fetchPackageVersions } from '../install-package.js';
import { getCloudflareCompatibilityDate } from './cloudflare.js';

const { bold, cyan, dim, green, magenta, red, yellow } = colors;

Expand Down Expand Up @@ -200,7 +201,7 @@ export async function add(names: string[], { flags }: AddOptions) {

if (await askToContinue({ flags, logger })) {
const data = await getPackageJson();
let compatibilityDate = new Date().toISOString().slice(0, 10);
const compatibilityDate = await getCloudflareCompatibilityDate(root);

await fs.writeFile(
wranglerConfigURL,
Expand Down
39 changes: 39 additions & 0 deletions packages/astro/test/units/cli/add-cloudflare.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import assert from 'node:assert/strict';
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { describe, it } from 'node:test';
import { pathToFileURL } from 'node:url';
import { getCloudflareCompatibilityDate } from '../../../dist/cli/add/cloudflare.js';

describe('astro add cloudflare', () => {
it('uses the compatibility date exported by the installed adapter', async () => {
const projectDir = await mkdtemp(join(tmpdir(), 'astro-add-cloudflare-'));
const adapterDir = join(projectDir, 'node_modules', '@astrojs', 'cloudflare');

try {
await mkdir(adapterDir, { recursive: true });
await writeFile(
join(adapterDir, 'package.json'),
JSON.stringify({
name: '@astrojs/cloudflare',
type: 'module',
exports: { './info': './info.js' },
}),
);
await writeFile(
join(adapterDir, 'info.js'),
`export function getLocalWorkerdCompatibilityDate() {
return { date: '2026-08-15', source: 'workerd' };
}
`,
);

const date = await getCloudflareCompatibilityDate(pathToFileURL(`${projectDir}/`));

assert.equal(date, '2026-08-15');
} finally {
await rm(projectDir, { recursive: true, force: true });
}
});
});
9 changes: 5 additions & 4 deletions packages/integrations/cloudflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"homepage": "https://docs.astro.build/en/guides/integrations-guide/cloudflare/",
"exports": {
".": "./dist/index.js",
"./info": "./dist/info.js",
"./entrypoints/server": "./dist/entrypoints/server.js",
"./entrypoints/preview": "./dist/entrypoints/preview.js",
"./entrypoints/server.js": "./dist/entrypoints/server.js",
Expand Down Expand Up @@ -46,16 +47,16 @@
"dependencies": {
"@astrojs/internal-helpers": "workspace:*",
"@astrojs/underscore-redirects": "workspace:*",
"@cloudflare/vite-plugin": "^1.39.0",
"@cloudflare/vite-plugin": "^1.53.0",
"piccolore": "^0.1.3",
"vite": "^8.0.13"
},
"peerDependencies": {
"astro": "^7.2.0",
"wrangler": "^4.83.0"
"wrangler": "^4.125.0"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20260526.1",
"@cloudflare/workers-types": "^5.20260820.1",
"@types/node": "^22.10.6",
"@types/prismjs": "1.26.6",
"astro": "workspace:*",
Expand All @@ -64,7 +65,7 @@
"devalue": "^5.8.1",
"prismjs": "^1.30.0",
"tinyglobby": "^0.2.15",
"wrangler": "^4.83.0"
"wrangler": "^4.125.0"
Comment thread
matthewp marked this conversation as resolved.
},
"publishConfig": {
"provenance": true
Expand Down
2 changes: 1 addition & 1 deletion packages/integrations/cloudflare/src/hono.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function getFetchState(context: HonoCloudflareContextLike): FetchState {
export function cf(): HonoMiddlewareHandler {
return async (context, next) => {
const state = getFetchState(context);
const asset = await cfFetch(state, context.env, context.executionCtx);
const asset = await cfFetch(state, context.env, context.executionCtx as ExecutionContext);
if (asset) return asset;
await next();
};
Expand Down
1 change: 1 addition & 0 deletions packages/integrations/cloudflare/src/info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { getLocalWorkerdCompatibilityDate } from '@cloudflare/vite-plugin';
8 changes: 2 additions & 6 deletions packages/integrations/cloudflare/src/wrangler.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import type { PluginConfig, WorkerConfig } from '@cloudflare/vite-plugin';
import { getLocalWorkerdCompatibilityDate } from './info.js';

export const DEFAULT_SESSION_KV_BINDING_NAME = 'SESSION';
export const DEFAULT_IMAGES_BINDING_NAME = 'IMAGES';
export const DEFAULT_ASSETS_BINDING_NAME = 'ASSETS';

// Default compatibility date used when the user doesn't set one in their wrangler config.
// The @cloudflare/vite-plugin falls back to today's date, but that can exceed the maximum
// date supported by the bundled workerd binary (which has a ~7 day buffer from its build date),
// causing ERR_RUNTIME_FAILURE. A hard-coded date avoids this issue.
// This should be updated when upgrading wrangler/workerd dependencies.
const DEFAULT_COMPATIBILITY_DATE = '2026-04-15';
const DEFAULT_COMPATIBILITY_DATE = getLocalWorkerdCompatibilityDate().date;

interface CloudflareConfigOptions {
sessionKVBindingName?: string | undefined;
Expand Down
17 changes: 17 additions & 0 deletions packages/integrations/cloudflare/test/wrangler.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import { getLocalWorkerdCompatibilityDate } from '../dist/info.js';
import {
cloudflareConfigCustomizer,
DEFAULT_ASSETS_BINDING_NAME,
Expand All @@ -8,6 +9,22 @@ import {
} from '../dist/wrangler.js';

describe('cloudflareConfigCustomizer', () => {
describe('compatibility date', () => {
it('uses the date supported by the installed workerd', () => {
const customizer = cloudflareConfigCustomizer();
const result = customizer({});

assert.equal(result.compatibility_date, getLocalWorkerdCompatibilityDate().date);
});

it('preserves the user compatibility date', () => {
const customizer = cloudflareConfigCustomizer();
const result = customizer({ compatibility_date: '2025-01-01' });

assert.equal(result.compatibility_date, '2025-01-01');
});
});

describe('main entrypoint', () => {
it('sets main to the server entrypoint when none exists', () => {
const customizer = cloudflareConfigCustomizer();
Expand Down
Loading
Loading