Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3adbe20
Remove `getLocalWorkerdCompatibilityDate` from workers-utils and re-e…
dario-piotrowicz Feb 3, 2026
6ebf636
add code comment as suggested
dario-piotrowicz Mar 24, 2026
a702c3f
remove unnecessary * import from miniflare
dario-piotrowicz Mar 24, 2026
5e49112
add missing @returns to jsdoc comment
dario-piotrowicz Mar 24, 2026
1e60ce9
add todo comment
dario-piotrowicz Mar 24, 2026
c91c2cb
fix formatting
dario-piotrowicz Mar 24, 2026
ec884f0
remove inline comments
dario-piotrowicz Mar 24, 2026
22b2b2d
Update packages/miniflare/src/runtime/compatibility-date.ts
dario-piotrowicz Mar 24, 2026
befed3d
update changeset
dario-piotrowicz Mar 24, 2026
8c0addc
update changeset
dario-piotrowicz Mar 24, 2026
de99ecb
Update packages/miniflare/src/runtime/compatibility-date.ts
dario-piotrowicz Mar 24, 2026
c01cf7c
improve changesets
dario-piotrowicz Mar 24, 2026
f3031b5
address issue of older version of wrangler
dario-piotrowicz Mar 24, 2026
8b4ba49
bump fallback date
dario-piotrowicz Mar 24, 2026
9eb1dd3
Update .changeset/quiet-roses-help.md
dario-piotrowicz Mar 24, 2026
7214732
Update .changeset/quiet-roses-help.md
dario-piotrowicz Mar 24, 2026
3aa2610
Update .changeset/dirty-moments-join.md
dario-piotrowicz Mar 24, 2026
490a0d1
Apply suggestions from code review
dario-piotrowicz Mar 24, 2026
af2445e
update changeset to major
dario-piotrowicz Mar 24, 2026
cb41c70
add reverse date check
dario-piotrowicz Mar 24, 2026
3402f7a
avoid re-exporting * from compatibility-date
dario-piotrowicz Mar 24, 2026
81b5623
compare date strings
dario-piotrowicz Mar 24, 2026
ae72d62
update README
dario-piotrowicz Mar 24, 2026
a86d1e0
add check in c3 e2es for compatibility date
dario-piotrowicz Mar 24, 2026
edf4d29
fix formatting
dario-piotrowicz Mar 24, 2026
2997ba3
fix typo
dario-piotrowicz Mar 24, 2026
c9ccae5
revert changeset to `minor`
dario-piotrowicz Mar 24, 2026
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
7 changes: 7 additions & 0 deletions .changeset/dirty-moments-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wrangler": minor
---

Re-export `supportedCompatibilityDate` from miniflare

The re-exports contains a recent and safe compatibility date from the local version of `workerd`.
7 changes: 7 additions & 0 deletions .changeset/long-colts-occur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@cloudflare/vite-plugin": patch
---

Use `supportedCompatibilityDate` value from `miniflare` instead of getting the date from `@cloudflare/workers-utils`

`miniflare` exports a recent safe compatibility date as `supportedCompatibilityDate`, and that is the value the package now uses as the latest supported workerd compatibility date. This doesn't have any specific user-facing effect (besides potentially making the function more reliable).
9 changes: 9 additions & 0 deletions .changeset/quiet-roses-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"create-cloudflare": patch
---

Avoid resorting to the fallback compatibility date when running via `pnpm`

Previously, when running `create-cloudflare` via `pnpm` the resolution of the local `miniflare` package failed and the fallback date was always used instead of the actual `workerd` compatibility date.

The fix switches to using `supportedCompatibilityDate` from `wrangler` directly, which does not depend on the `miniflare` resolution and works reliably across all package managers.
12 changes: 12 additions & 0 deletions .changeset/smart-buckets-wonder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@cloudflare/workers-utils": minor
---

Remove the `getLocalWorkerdCompatibilityDate` utility from the package
Comment thread
dario-piotrowicz marked this conversation as resolved.

This utility has been removed because its implementation relied on retrieving the compat date from `workerd` by resolving to the `miniflare` dependency, which was unreliable in certain environments (e.g. when using `pnpm`). The functionality is now provided more reliably as a static export from `wrangler`.

Consumers should migrate to one of the following alternatives:

- `supportedCompatibilityDate` from `miniflare` — for direct miniflare users
- `supportedCompatibilityDate` re-exported from `wrangler` — for consumers of the wrangler programmatic API
23 changes: 21 additions & 2 deletions packages/create-cloudflare/e2e/tests/workers/workers.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { existsSync } from "node:fs";
import { join } from "node:path";
import { FALLBACK_COMPAT_DATE } from "helpers/compatDate";
import { readJSON, readToml } from "helpers/files";
import { beforeAll, describe } from "vitest";
import { deleteWorker } from "../../../scripts/common";
Expand Down Expand Up @@ -63,15 +64,33 @@ describe
const jsoncPath = join(project.path, "wrangler.jsonc");

if (existsSync(jsoncPath)) {
const config = readJSON(jsoncPath) as { main?: string };
const config = readJSON(jsoncPath) as {
main?: string;
compatibility_date?: string;
};
if (config.main) {
expect(join(project.path, config.main)).toExist();
}
// Verify the compatibility_date was resolved from the locally
// installed wrangler package and it isn't the hardcoded fallback.
expect(config.compatibility_date).toMatch(/^\d{4}-\d{2}-\d{2}$/);
expect(config.compatibility_date).not.toEqual(
FALLBACK_COMPAT_DATE
);
Comment thread
petebacondarwin marked this conversation as resolved.
} else if (existsSync(tomlPath)) {
const config = readToml(tomlPath) as { main?: string };
const config = readToml(tomlPath) as {
main?: string;
compatibility_date?: string;
};
if (config.main) {
expect(join(project.path, config.main)).toExist();
}
// Verify the compatibility_date was resolved from the locally
// installed wrangler package and it isn't the hardcoded fallback.
expect(config.compatibility_date).toMatch(/^\d{4}-\d{2}-\d{2}$/);
expect(config.compatibility_date).not.toEqual(
FALLBACK_COMPAT_DATE
);
} else {
expect.fail(
`Expected at least one of "${jsoncPath}" or "${tomlPath}" to exist.`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getLocalWorkerdCompatibilityDate } from "@cloudflare/workers-utils";
import { createRequire } from "node:module";
import {
FALLBACK_COMPAT_DATE,
getLatestTypesEntrypoint,
getWorkerdCompatibilityDate,
} from "helpers/compatDate";
Expand All @@ -10,7 +11,7 @@ import { mockSpinner, mockWorkersTypesDirectory } from "./mocks";
vi.mock("helpers/files");
vi.mock("fs");
vi.mock("@cloudflare/cli/interactive");
vi.mock("@cloudflare/workers-utils");
vi.mock("node:module");

describe("Compatibility Date Helpers", () => {
let spinner: ReturnType<typeof mockSpinner>;
Expand All @@ -25,10 +26,12 @@ describe("Compatibility Date Helpers", () => {

describe("getWorkerdCompatibilityDate()", () => {
test("normal flow", async ({ expect }) => {
vi.mocked(getLocalWorkerdCompatibilityDate).mockReturnValue({
date: "2025-01-10",
source: "workerd",
});
const mockWrangler = {
supportedCompatibilityDate: "2025-01-10",
};
vi.mocked(createRequire).mockReturnValue(
(() => mockWrangler) as unknown as NodeJS.Require
);
Comment thread
petebacondarwin marked this conversation as resolved.

const date = getWorkerdCompatibilityDate("./my-app");

Expand All @@ -40,19 +43,35 @@ describe("Compatibility Date Helpers", () => {
);
});

test("fallback result", async ({ expect }) => {
vi.mocked(getLocalWorkerdCompatibilityDate).mockReturnValue({
date: "2025-09-27",
source: "fallback",
});
test("fallback on error", async ({ expect }) => {
vi.mocked(createRequire).mockReturnValue((() => {
throw new Error("Cannot find module 'wrangler'");
}) as unknown as NodeJS.Require);

const date = getWorkerdCompatibilityDate("./my-app");

expect(date).toBe(FALLBACK_COMPAT_DATE);
expect(spinner.start).toHaveBeenCalled();
expect(spinner.stop).toHaveBeenCalledWith(
expect.stringContaining(FALLBACK_COMPAT_DATE)
);
});

test("fallback when wrangler does not export supportedCompatibilityDate (older version)", async ({
expect,
}) => {
// Simulate an older version of wrangler that doesn't have this export
const mockWrangler = {};
vi.mocked(createRequire).mockReturnValue(
(() => mockWrangler) as unknown as NodeJS.Require
);

const date = getWorkerdCompatibilityDate("./my-app");

const fallbackDate = "2025-09-27";
expect(date).toBe(fallbackDate);
expect(date).toBe(FALLBACK_COMPAT_DATE);
expect(spinner.start).toHaveBeenCalled();
expect(spinner.stop).toHaveBeenCalledWith(
expect.stringContaining(fallbackDate)
expect.stringContaining(FALLBACK_COMPAT_DATE)
);
});
});
Expand Down
43 changes: 34 additions & 9 deletions packages/create-cloudflare/src/helpers/compatDate.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,56 @@
import { readdirSync } from "node:fs";
import { resolve } from "node:path";
import { createRequire } from "node:module";
import { join, resolve } from "node:path";
import { brandColor, dim } from "@cloudflare/cli/colors";
import { spinner } from "@cloudflare/cli/interactive";
import { getLocalWorkerdCompatibilityDate } from "@cloudflare/workers-utils";
import type { C3Context } from "types";
import type { CompatDate } from "wrangler";

/**
* Compatibility date to fallback to if getting the compatibility date from wrangler fails for whatever reason.
*
* Note: this fallback date doesn't have any special meaning, it's simply the latest compatibility date at the time of writing
* (source: https://github.com/cloudflare/workerd/blob/main/src/workerd/io/release-version.txt#L1)
*/
export const FALLBACK_COMPAT_DATE = "2026-03-24";

/**
* Retrieves the latest workerd compatibility date
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you expand this JSDoc to describe that this function

  1. Try to get the latest compat date form local workerd (capped to today)
  2. fallback to today - 1 month if 1 fails.

*
* @returns The latest compatibility date for workerd in the form "YYYY-MM-DD"
*/
export function getWorkerdCompatibilityDate(projectPath: string) {
export function getWorkerdCompatibilityDate(projectPath: string): CompatDate {
const s = spinner();
s.start("Retrieving current workerd compatibility date");

const { date, source } = getLocalWorkerdCompatibilityDate({ projectPath });
try {
const projectRequire = createRequire(join(projectPath, "package.json"));
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
const wrangler: Awaited<typeof import("wrangler")> =
projectRequire("wrangler");
const { supportedCompatibilityDate } = wrangler;

if (
typeof supportedCompatibilityDate !== "string" ||
!/^\d{4}-\d{2}-\d{2}$/.test(supportedCompatibilityDate)
) {
throw new Error(
"wrangler does not export a valid supportedCompatibilityDate"
);
}

if (source === "fallback") {
s.stop(
`${brandColor("compatibility date")} ${dim(supportedCompatibilityDate)}`
);
return supportedCompatibilityDate;
Comment thread
dario-piotrowicz marked this conversation as resolved.
} catch {
s.stop(
`${brandColor("compatibility date")} ${dim(
` Could not find workerd date, falling back to ${date}`
`Could not find workerd date, falling back to "${FALLBACK_COMPAT_DATE}"`
)}`
);
} else {
s.stop(`${brandColor("compatibility date")} ${dim(date)}`);
return FALLBACK_COMPAT_DATE;
}
return date;
}

/**
Expand Down
11 changes: 7 additions & 4 deletions packages/create-cloudflare/src/wrangler/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { existsSync, mkdirSync } from "node:fs";
import { resolve } from "node:path";
import { isCompatDate } from "@cloudflare/workers-utils";
import { getWorkerdCompatibilityDate } from "helpers/compatDate";
import { readFile, writeFile, writeJSON } from "helpers/files";
import {
Expand All @@ -14,6 +13,7 @@ import TOML from "smol-toml";
import type { CommentObject, Reviver } from "comment-json";
import type { TomlTable } from "smol-toml";
import type { C3Context } from "types";
import type { CompatDate } from "wrangler";

/**
* Update the `wrangler.(toml|json|jsonc)` file for this project by:
Expand Down Expand Up @@ -216,11 +216,14 @@ export const addVscodeConfig = (ctx: C3Context) => {
async function getCompatibilityDate(
tentativeDate: unknown,
projectPath: string
): Promise<string> {
if (typeof tentativeDate === "string" && isCompatDate(tentativeDate)) {
): Promise<CompatDate> {
if (
typeof tentativeDate === "string" &&
/^\d{4}-\d{2}-\d{2}$/.test(tentativeDate)
Comment thread
petebacondarwin marked this conversation as resolved.
) {
// Use the tentative date when it is valid.
// It may be there for a specific compat reason
return tentativeDate;
return tentativeDate as CompatDate;
}
// Fallback to the latest workerd date
return getWorkerdCompatibilityDate(projectPath);
Expand Down
7 changes: 7 additions & 0 deletions packages/miniflare/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,13 @@ defined at the top-level.
Returns the same object returned from incoming `Request`'s `cf` property. This
object depends on the `cf` property from `SharedOptions`.

### `supportedCompatibilityDate`

Each workerd release exposes a supported compat date that it guarantees can be used with this version of the package. This date is up to 7 days after the release date of the version.
Normally this `supportedCompatibilityDate` is equal to the value exported by workerd, but if this supported compat date is in the future (because the workerd package was released in the last 7 days) then the value of this export is today.

This value is a string in the `YYYY-MM-DD` format (e.g. `"2025-09-27"`).

## Configuration

### Local `workerd`
Expand Down
58 changes: 58 additions & 0 deletions packages/miniflare/src/runtime/compatibility-date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import assert from "node:assert";
import { compatibilityDate as workerdCompatibilityDate } from "workerd";

export type YYYY = `${number}${number}${number}${number}`;
export type MM = `${number}${number}`;
export type DD = `${number}${number}`;

/**
* String representing a date following the Cloudflare compatibility date format, such as `2025-09-27`
*/
export type CompatDate = `${YYYY}-${MM}-${DD}`;

/**
* Discern whether a string represents a compatibility date (`YYYY-MM-DD`)
*
* @param str The target string
* @returns true if the string represents a compatibility date, false otherwise
*/
export function isCompatDate(str: string): str is CompatDate {
return /^\d{4}-\d{2}-\d{2}$/.test(str);
}

/**
* Returns the date formatted as a compatibility date
*
* @param date The target date to convert
* @returns The date as a CompatDate string (`YYYY-MM-DD`)
*/
export function formatCompatibilityDate(date: Date): CompatDate {
const compatDate = date.toISOString().slice(0, 10);
assert(isCompatDate(compatDate));
return compatDate;
}

/**
* Gets a safe compatibility date from workerd. If the workerd compatibility
* date is in the future, returns today's date instead. This handles the case
* where workerd releases set their compatibility date up to 7 days in the future.
Comment on lines +36 to +38
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Gets a safe compatibility date from workerd. If the workerd compatibility
* date is in the future, returns today's date instead. This handles the case
* where workerd releases set their compatibility date up to 7 days in the future.
* Gets a safe compatibility date from workerd. If the workerd compatibility
* date is in the future, returns today's date instead. workerd releases set their compatibility date to 7 days in the future.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we do need to write up the resolution logic carefully here. It is still confusing even to the ANT team members!

*
* @return The compatibility Date (`YYYY-MM-DD`)
*/
Comment thread
dario-piotrowicz marked this conversation as resolved.
function getSafeCompatibilityDate(): CompatDate {
// The compatibility data from workerd follows the CompatDate format
assert(isCompatDate(workerdCompatibilityDate));
Comment thread
dario-piotrowicz marked this conversation as resolved.

const today = formatCompatibilityDate(new Date());

if (workerdCompatibilityDate > today) {
return today;
}

return workerdCompatibilityDate;
}

/** `YYYY-MM-DD` compatibility date */
const supportedCompatibilityDate = getSafeCompatibilityDate();

export { supportedCompatibilityDate };
Comment thread
dario-piotrowicz marked this conversation as resolved.
27 changes: 10 additions & 17 deletions packages/miniflare/src/runtime/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import path from "node:path";
import rl from "node:readline";
import { Readable, Transform } from "node:stream";
import { $ as $colors, red } from "kleur/colors";
import workerdPath, {
compatibilityDate as workerdCompatibilityDate,
} from "workerd";
import workerdPath from "workerd";
import { z } from "zod";
import { SERVICE_LOOPBACK, SOCKET_ENTRY } from "../plugins";
import { MiniflareCoreError } from "../shared";
Expand Down Expand Up @@ -350,17 +348,12 @@ export class Runtime {

export * from "./config";

/**
* Gets a safe compatibility date from workerd. If the workerd compatibility
* date is in the future, returns today's date instead. This handles the case
* where workerd releases set their compatibility date up to 7 days in the future.
*/
function getSafeCompatibilityDate(): string {
const today = new Date().toISOString().slice(0, 10);
if (workerdCompatibilityDate > today) {
return today;
}
return workerdCompatibilityDate;
}

export const supportedCompatibilityDate = getSafeCompatibilityDate();
export {
supportedCompatibilityDate,
formatCompatibilityDate,
isCompatDate,
type CompatDate,
type YYYY,
type MM,
type DD,
} from "./compatibility-date";
Loading
Loading