From 3adbe209cf5e93022d7f414075b795729da6633f Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 3 Feb 2026 16:01:25 +0000 Subject: [PATCH 01/27] Remove `getLocalWorkerdCompatibilityDate` from workers-utils and re-export `supportedCompatibilityDate` from `miniflare` instead --- .changeset/dirty-moments-join.md | 7 ++ .changeset/long-colts-occur.md | 7 ++ .changeset/quiet-roses-help.md | 5 + .changeset/smart-buckets-wonder.md | 5 + .../src/helpers/__tests__/compatDate.test.ts | 25 +++-- .../src/helpers/compatDate.ts | 29 +++-- .../create-cloudflare/src/wrangler/config.ts | 11 +- packages/miniflare/README.md | 4 + .../src/runtime/compatibility-date.ts | 55 +++++++++ packages/miniflare/src/runtime/index.ts | 19 +--- .../test/runtime/compatibility-date.spec.ts | 105 ++++++++++++++++++ packages/vite-plugin-cloudflare/src/index.ts | 23 +++- .../src/plugin-config.ts | 8 +- .../workers-utils/src/compatibility-date.ts | 102 ----------------- .../src/construct-wrangler-config.ts | 3 +- packages/workers-utils/src/index.ts | 7 -- .../tests/compatibility-date.test.ts | 80 ------------- packages/wrangler/e2e/pages-dev.test.ts | 2 +- .../src/__tests__/autoconfig/run.test.ts | 9 +- packages/wrangler/src/__tests__/dev.test.ts | 13 +-- packages/wrangler/src/api/index.ts | 3 + .../src/api/integrations/platform/index.ts | 18 +-- .../api/startDevWorker/ConfigController.ts | 14 +-- .../src/autoconfig/frameworks/analog.ts | 8 +- .../src/autoconfig/frameworks/solid-start.ts | 8 +- packages/wrangler/src/autoconfig/run.ts | 13 +-- packages/wrangler/src/cli.ts | 3 + packages/wrangler/src/deploy/deploy.ts | 2 +- packages/wrangler/src/deploy/index.ts | 2 +- .../wrangler/src/deployment-bundle/entry.ts | 2 +- packages/wrangler/src/pages/dev.ts | 2 +- .../wrangler/src/pages/download-config.ts | 3 +- packages/wrangler/src/versions/upload.ts | 2 +- 33 files changed, 297 insertions(+), 302 deletions(-) create mode 100644 .changeset/dirty-moments-join.md create mode 100644 .changeset/long-colts-occur.md create mode 100644 .changeset/quiet-roses-help.md create mode 100644 .changeset/smart-buckets-wonder.md create mode 100644 packages/miniflare/src/runtime/compatibility-date.ts create mode 100644 packages/miniflare/test/runtime/compatibility-date.spec.ts delete mode 100644 packages/workers-utils/src/compatibility-date.ts delete mode 100644 packages/workers-utils/tests/compatibility-date.test.ts diff --git a/.changeset/dirty-moments-join.md b/.changeset/dirty-moments-join.md new file mode 100644 index 0000000000..03416854d7 --- /dev/null +++ b/.changeset/dirty-moments-join.md @@ -0,0 +1,7 @@ +--- +"wrangler": minor +--- + +Re-export `supportedCompatibilityDate` from miniflare + +Re-exports the `supportedCompatibilityDate` value form miniflare that contains the latest supported compatibility date by the local version of `workerd` diff --git a/.changeset/long-colts-occur.md b/.changeset/long-colts-occur.md new file mode 100644 index 0000000000..ebd6f8ff07 --- /dev/null +++ b/.changeset/long-colts-occur.md @@ -0,0 +1,7 @@ +--- +"@cloudflare/vite-plugin": patch +--- + +Use `supportedCompatibilityDate` value from `miniflare` instead of getting the date from `@cloudflare/workers-utils` + +`miniflare` exports the latest compatibility date `supportedCompatibilityDate` as, and that is the value that now the package uses as the latest supported workerd compatibility date. This doesn't have any specific user-facing effect (besides potentially making the function more reliable). diff --git a/.changeset/quiet-roses-help.md b/.changeset/quiet-roses-help.md new file mode 100644 index 0000000000..a0b5a916c5 --- /dev/null +++ b/.changeset/quiet-roses-help.md @@ -0,0 +1,5 @@ +--- +"create-cloudflare": patch +--- + +Fix the fallback compatibility date being always incorrectly used when running the CLI via `pnpm` diff --git a/.changeset/smart-buckets-wonder.md b/.changeset/smart-buckets-wonder.md new file mode 100644 index 0000000000..0207d28f06 --- /dev/null +++ b/.changeset/smart-buckets-wonder.md @@ -0,0 +1,5 @@ +--- +"@cloudflare/workers-utils": minor +--- + +Remove the `getLocalWorkerdCompatibilityDate` utility from the package (a utility of the same name is now exported by `miniflare` and `wrangler` instead) diff --git a/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts b/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts index a3bb9d5c35..0b2480a266 100644 --- a/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts +++ b/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts @@ -1,4 +1,4 @@ -import { getLocalWorkerdCompatibilityDate } from "@cloudflare/workers-utils"; +import { createRequire } from "node:module"; import { getLatestTypesEntrypoint, getWorkerdCompatibilityDate, @@ -10,7 +10,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; @@ -25,10 +25,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, + ); const date = getWorkerdCompatibilityDate("./my-app"); @@ -40,15 +42,14 @@ 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"); - const fallbackDate = "2025-09-27"; + const fallbackDate = "2026-03-03"; expect(date).toBe(fallbackDate); expect(spinner.start).toHaveBeenCalled(); expect(spinner.stop).toHaveBeenCalledWith( diff --git a/packages/create-cloudflare/src/helpers/compatDate.ts b/packages/create-cloudflare/src/helpers/compatDate.ts index 6ccff0bd26..3b316d8ae7 100644 --- a/packages/create-cloudflare/src/helpers/compatDate.ts +++ b/packages/create-cloudflare/src/helpers/compatDate.ts @@ -1,31 +1,42 @@ 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"; /** * Retrieves the latest workerd compatibility date * * @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 = + projectRequire("wrangler"); + const { supportedCompatibilityDate } = wrangler; - if (source === "fallback") { + s.stop( + `${brandColor("compatibility date")} ${dim(supportedCompatibilityDate)}` + ); + return supportedCompatibilityDate; + } catch { + // 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/supported-compatibility-date.txt) + const fallbackDate = "2026-03-03"; s.stop( `${brandColor("compatibility date")} ${dim( - ` Could not find workerd date, falling back to ${date}` + `Could not find workerd date, falling back to "${fallbackDate}"` )}` ); - } else { - s.stop(`${brandColor("compatibility date")} ${dim(date)}`); + return fallbackDate; } - return date; } /** diff --git a/packages/create-cloudflare/src/wrangler/config.ts b/packages/create-cloudflare/src/wrangler/config.ts index 24a214fc7a..c3af492224 100644 --- a/packages/create-cloudflare/src/wrangler/config.ts +++ b/packages/create-cloudflare/src/wrangler/config.ts @@ -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 { @@ -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: @@ -216,11 +216,14 @@ export const addVscodeConfig = (ctx: C3Context) => { async function getCompatibilityDate( tentativeDate: unknown, projectPath: string -): Promise { - if (typeof tentativeDate === "string" && isCompatDate(tentativeDate)) { +): Promise { + if ( + typeof tentativeDate === "string" && + /^\d{4}-\d{2}-\d{2}$/.test(tentativeDate) + ) { // 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); diff --git a/packages/miniflare/README.md b/packages/miniflare/README.md index f6f2408a5e..a4f4abc7c7 100644 --- a/packages/miniflare/README.md +++ b/packages/miniflare/README.md @@ -933,6 +933,10 @@ 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` + +Exported value containing the latest [compatibility date](https://developers.cloudflare.com/workers/platform/compatibility-dates/) supported by the locally installed `workerd` package. If the `workerd` package's compatibility date is in the future, the value contains today's date instead. The value is a string in the `YYYY-MM-DD` format (e.g. `"2025-09-27"`). + ## Configuration ### Local `workerd` diff --git a/packages/miniflare/src/runtime/compatibility-date.ts b/packages/miniflare/src/runtime/compatibility-date.ts new file mode 100644 index 0000000000..f11937fd12 --- /dev/null +++ b/packages/miniflare/src/runtime/compatibility-date.ts @@ -0,0 +1,55 @@ +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 (a string following the format `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. + */ +function getSafeCompatibilityDate(): CompatDate { + // The compatibility data from workerd follows the CompatDate format + assert(isCompatDate(workerdCompatibilityDate)); + + const today = formatCompatibilityDate(new Date()); + + if (workerdCompatibilityDate > today) { + return today; + } + + return workerdCompatibilityDate; +} + +const supportedCompatibilityDate = getSafeCompatibilityDate(); + +export { supportedCompatibilityDate }; diff --git a/packages/miniflare/src/runtime/index.ts b/packages/miniflare/src/runtime/index.ts index aad29fc22d..c4388de0e8 100644 --- a/packages/miniflare/src/runtime/index.ts +++ b/packages/miniflare/src/runtime/index.ts @@ -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"; @@ -350,17 +348,4 @@ 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 * from "./compatibility-date"; diff --git a/packages/miniflare/test/runtime/compatibility-date.spec.ts b/packages/miniflare/test/runtime/compatibility-date.spec.ts new file mode 100644 index 0000000000..026cc9f2e3 --- /dev/null +++ b/packages/miniflare/test/runtime/compatibility-date.spec.ts @@ -0,0 +1,105 @@ +import { + formatCompatibilityDate, + isCompatDate, + supportedCompatibilityDate, +} from "miniflare"; +import { describe, test } from "vitest"; + +describe("isCompatDate", () => { + test("returns true for valid date strings", ({ expect }) => { + expect(isCompatDate("2024-01-15")).toBe(true); + expect(isCompatDate("2023-12-31")).toBe(true); + expect(isCompatDate("2000-01-01")).toBe(true); + expect(isCompatDate("9999-12-31")).toBe(true); + }); + + test("returns false for invalid date formats", ({ expect }) => { + // Missing leading zeros + expect(isCompatDate("2024-1-15")).toBe(false); + expect(isCompatDate("2024-01-5")).toBe(false); + + // Wrong separators + expect(isCompatDate("2024/01/15")).toBe(false); + expect(isCompatDate("2024.01.15")).toBe(false); + + // Wrong length + expect(isCompatDate("24-01-15")).toBe(false); + expect(isCompatDate("2024-001-15")).toBe(false); + + // Invalid strings + expect(isCompatDate("not-a-date")).toBe(false); + expect(isCompatDate("")).toBe(false); + expect(isCompatDate("2024")).toBe(false); + expect(isCompatDate("2024-01")).toBe(false); + + // Extra characters + expect(isCompatDate("2024-01-15T00:00:00")).toBe(false); + expect(isCompatDate(" 2024-01-15")).toBe(false); + expect(isCompatDate("2024-01-15 ")).toBe(false); + }); +}); + +describe("formatCompatibilityDate", () => { + test("returns correctly formatted date string", ({ expect }) => { + // Use UTC dates to avoid timezone issues + const date = new Date(Date.UTC(2024, 0, 15)); // January 15, 2024 + expect(formatCompatibilityDate(date)).toBe("2024-01-15"); + }); + + test("pads single-digit months and days with zeros", ({ expect }) => { + const date1 = new Date(Date.UTC(2024, 0, 1)); // January 1, 2024 + expect(formatCompatibilityDate(date1)).toBe("2024-01-01"); + + const date2 = new Date(Date.UTC(2024, 8, 5)); // September 5, 2024 + expect(formatCompatibilityDate(date2)).toBe("2024-09-05"); + }); + + test("handles year boundaries correctly", ({ expect }) => { + const date1 = new Date(Date.UTC(2023, 11, 31)); // December 31, 2023 + expect(formatCompatibilityDate(date1)).toBe("2023-12-31"); + + const date2 = new Date(Date.UTC(2024, 0, 1)); // January 1, 2024 + expect(formatCompatibilityDate(date2)).toBe("2024-01-01"); + }); + + test("handles various years correctly", ({ expect }) => { + const date1 = new Date(Date.UTC(2000, 5, 15)); + expect(formatCompatibilityDate(date1)).toBe("2000-06-15"); + + const date2 = new Date(Date.UTC(2099, 11, 31)); + expect(formatCompatibilityDate(date2)).toBe("2099-12-31"); + }); +}); + +describe("supportedCompatibilityDate", () => { + test("returns a valid compat date string", ({ expect }) => { + expect(isCompatDate(supportedCompatibilityDate)).toBe(true); + }); + + test("should be parseable as a date", ({ expect }) => { + const parsed = new Date(supportedCompatibilityDate); + expect(parsed.toString()).not.toBe("Invalid Date"); + }); + + test("should not be in the future", ({ expect }) => { + const supportedCompatibilityDateAsDate = new Date( + supportedCompatibilityDate + ); + const now = new Date(); + + // The returned date should not be after today (in UTC) + // We compare timestamps at midnight UTC for both dates + const resultTimestamp = Date.UTC( + supportedCompatibilityDateAsDate.getUTCFullYear(), + supportedCompatibilityDateAsDate.getUTCMonth(), + supportedCompatibilityDateAsDate.getUTCDate() + ); + const todayTimestamp = Date.UTC( + now.getUTCFullYear(), + now.getUTCMonth(), + now.getUTCDate() + ); + + expect(resultTimestamp).toBeLessThanOrEqual(todayTimestamp); + }); +}); diff --git a/packages/vite-plugin-cloudflare/src/index.ts b/packages/vite-plugin-cloudflare/src/index.ts index 515f3a963b..cbb59dfe01 100644 --- a/packages/vite-plugin-cloudflare/src/index.ts +++ b/packages/vite-plugin-cloudflare/src/index.ts @@ -1,3 +1,4 @@ +import { supportedCompatibilityDate } from "miniflare"; import { assertWranglerVersion } from "./assert-wrangler-version"; import { PluginContext } from "./context"; import { resolvePluginConfig } from "./plugin-config"; @@ -23,9 +24,29 @@ import { wasmHelperPlugin } from "./plugins/wasm"; import { debuglog } from "./utils"; import type { SharedContext } from "./context"; import type { PluginConfig } from "./plugin-config"; +import type { CompatDate } from "miniflare"; import type * as vite from "vite"; -export { getLocalWorkerdCompatibilityDate } from "@cloudflare/workers-utils"; +/** + * Gets the compatibility date from the local workerd version. + * + * Note: the function's signature is as is because it needs to be backward compatibly with + * a previous iteration of this, it will be simplified in the next major version of this package. + * + * @param _options Unused argument (present only for backward compatibility) + * @returns Object containing the compatibility date (this is not the date directly for backward compatibility) + */ +export function getLocalWorkerdCompatibilityDate(_options?: { + projectPath?: string; +}): { + date: CompatDate; + source: "workerd"; +} { + return { + date: supportedCompatibilityDate, + source: "workerd", + }; +} export type { PluginConfig } from "./plugin-config"; export type { WorkerConfig } from "./workers-configs"; diff --git a/packages/vite-plugin-cloudflare/src/plugin-config.ts b/packages/vite-plugin-cloudflare/src/plugin-config.ts index e86e928e06..8400c560ed 100644 --- a/packages/vite-plugin-cloudflare/src/plugin-config.ts +++ b/packages/vite-plugin-cloudflare/src/plugin-config.ts @@ -1,7 +1,7 @@ import * as path from "node:path"; import { parseStaticRouting } from "@cloudflare/workers-shared/utils/configuration/parseStaticRouting"; -import { getLocalWorkerdCompatibilityDate } from "@cloudflare/workers-utils"; import { defu } from "defu"; +import { supportedCompatibilityDate } from "miniflare"; import * as vite from "vite"; import * as wrangler from "wrangler"; import { getWorkerConfigs } from "./deploy-config"; @@ -244,11 +244,7 @@ function resolveWorkerConfig( configCustomizer: options.configCustomizer, }); - const { date } = getLocalWorkerdCompatibilityDate({ - projectPath: options.root, - }); - - workerConfig.compatibility_date ??= date; + workerConfig.compatibility_date ??= supportedCompatibilityDate; if (isEntryWorker) { workerConfig.name ??= wrangler.unstable_getWorkerNameFromProject( diff --git a/packages/workers-utils/src/compatibility-date.ts b/packages/workers-utils/src/compatibility-date.ts deleted file mode 100644 index a025a8f29b..0000000000 --- a/packages/workers-utils/src/compatibility-date.ts +++ /dev/null @@ -1,102 +0,0 @@ -import assert from "node:assert"; -import module from "node:module"; -import path from "node:path"; - -type YYYY = `${number}${number}${number}${number}`; -type MM = `${number}${number}`; -type DD = `${number}${number}`; - -/** - * Represents a valid compatibility date, a string such as `2025-09-27` - */ -export type CompatDate = `${YYYY}-${MM}-${DD}`; - -type GetCompatDateOptions = { - projectPath?: string; -}; - -type GetCompatDateResult = { - date: CompatDate; - source: "workerd" | "fallback"; -}; - -/** - * Gets the compatibility date of the locally installed workerd package. - * - * If the package is not found the fallback date of 2025-09-27 is returned instead. - * - * Additionally, if the workerd date is set to the future then the current date is returned instead. - * - * @param options.projectPath the path to the project - * @returns an object including the compatibility date and its source - */ -export function getLocalWorkerdCompatibilityDate({ - projectPath = process.cwd(), -}: GetCompatDateOptions = {}): GetCompatDateResult { - try { - // Note: createRequire expects a filename, not a directory. When given a directory, - // Node.js looks for node_modules in the parent directory instead of the given directory. - // Appending package.json ensures resolution starts from the correct location. - const projectRequire = module.createRequire( - path.join(projectPath, "package.json") - ); - const miniflareEntry = projectRequire.resolve("miniflare"); - const miniflareRequire = module.createRequire(miniflareEntry); - const miniflareWorkerd = miniflareRequire("workerd") as { - compatibilityDate: string; - }; - const workerdDate = miniflareWorkerd.compatibilityDate; - return { - date: toSafeCompatibilityDate(new Date(workerdDate)), - source: "workerd", - }; - } catch {} - - return { - date: "2025-09-27", - source: "fallback", - }; -} - -/** - * Workerd releases often have a date for the following day. - * Unfortunately, Workers deployments will fail if they specify a compatibility date in the future. This means that most - * who create a new project on the same day as a workerd release will have their deployments fail until they - * manually adjust the compatibility date. - * - * To work around this, we must manually ensure that the compat date is not on a future UTC day when there was a recent workerd release. - * - * This function is the used to convert potential future dates to safe compatibility dates. - * - * @param date The local workerd date to check and convert - * @returns A compat date created using today's date if the local workerd date is in the future, one using the local workerd date otherwise - */ -function toSafeCompatibilityDate(date: Date): CompatDate { - if (date.getTime() > Date.now()) { - return formatCompatibilityDate(new Date()); - } - - return formatCompatibilityDate(date); -} - -/** - * 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 (a string following the format `YYYY-MM-DD`) - */ -export function formatCompatibilityDate(date: Date): CompatDate { - const compatDate = date.toISOString().slice(0, 10); - assert(isCompatDate(compatDate)); - return compatDate; -} diff --git a/packages/workers-utils/src/construct-wrangler-config.ts b/packages/workers-utils/src/construct-wrangler-config.ts index 1fc29d267a..0c824cf104 100644 --- a/packages/workers-utils/src/construct-wrangler-config.ts +++ b/packages/workers-utils/src/construct-wrangler-config.ts @@ -1,4 +1,3 @@ -import { formatCompatibilityDate } from "./compatibility-date"; import { ENVIRONMENT_TAG_PREFIX, SERVICE_TAG_PREFIX } from "./constants"; import { mapWorkerMetadataBindings } from "./map-worker-metadata-bindings"; import type { RawConfig } from "./config"; @@ -79,7 +78,7 @@ function convertWorkerToWranglerConfig(config: APIWorkerConfig): RawConfig { workers_dev: config.subdomain.enabled, preview_urls: config.subdomain.previews_enabled, compatibility_date: - config.compatibility_date ?? formatCompatibilityDate(new Date()), + config.compatibility_date ?? new Date().toISOString().slice(0, 10), compatibility_flags: config.compatibility_flags, ...(allRoutes.length ? { routes: allRoutes } : {}), placement: diff --git a/packages/workers-utils/src/index.ts b/packages/workers-utils/src/index.ts index 939387290e..a120ca25e9 100644 --- a/packages/workers-utils/src/index.ts +++ b/packages/workers-utils/src/index.ts @@ -85,13 +85,6 @@ export * from "./environment-variables/misc-variables"; export { getGlobalWranglerConfigPath } from "./global-wrangler-config-path"; -export { - getLocalWorkerdCompatibilityDate, - formatCompatibilityDate, - isCompatDate, -} from "./compatibility-date"; -export type { CompatDate } from "./compatibility-date"; - export { isDockerfile } from "./config/validation"; export { isDirectory, removeDir, removeDirSync } from "./fs-helpers"; diff --git a/packages/workers-utils/tests/compatibility-date.test.ts b/packages/workers-utils/tests/compatibility-date.test.ts deleted file mode 100644 index f258df9866..0000000000 --- a/packages/workers-utils/tests/compatibility-date.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import module from "node:module"; -import path from "node:path"; -import { beforeEach, describe, it, vi } from "vitest"; -import { getLocalWorkerdCompatibilityDate } from "../src/compatibility-date"; - -describe("getLocalWorkerdCompatibilityDate", () => { - beforeEach(() => { - vi.setSystemTime(vi.getRealSystemTime()); - }); - - it("should successfully get the local latest compatibility date from the local workerd instance", ({ - expect, - }) => { - const createRequireSpy = vi - .spyOn(module, "createRequire") - .mockImplementation(() => { - const mockedRequire = ((pkg: string) => { - if (pkg === "workerd") { - return { compatibilityDate: "2025-01-10" }; - } - return {}; - }) as NodeJS.Require; - mockedRequire.resolve = (() => "") as unknown as NodeJS.RequireResolve; - return mockedRequire; - }); - const { date, source } = getLocalWorkerdCompatibilityDate({ - projectPath: "/test/project", - }); - expect(date).toBe("2025-01-10"); - expect(source).toEqual("workerd"); - // Verify createRequire is called with a file path (package.json), not just a directory - expect(createRequireSpy).toHaveBeenCalledWith( - path.join("/test/project", "package.json") - ); - }); - - it("should fallback to the fallback date if it fails to get the date from a local workerd instance", ({ - expect, - }) => { - const createRequireSpy = vi - .spyOn(module, "createRequire") - .mockImplementation( - // This breaks the require function that createRequire generate, causing us not to find - // the local miniflare/workerd instance - () => ({}) as NodeJS.Require - ); - const { date, source } = getLocalWorkerdCompatibilityDate({ - projectPath: "/test/project", - }); - const fallbackCompatDate = "2025-09-27"; - expect(date).toEqual(fallbackCompatDate); - expect(source).toEqual("fallback"); - // Verify createRequire is called with a file path even when resolution fails - expect(createRequireSpy).toHaveBeenCalledWith( - path.join("/test/project", "package.json") - ); - }); - - it("should use today's date if the local workerd's date is in the future", async ({ - expect, - }) => { - vi.setSystemTime("2025-01-09T23:59:59.999Z"); - vi.spyOn(module, "createRequire").mockImplementation(() => { - const mockedRequire = ((pkg: string) => { - if (pkg === "workerd") { - return { compatibilityDate: "2025-01-10" }; - } - return {}; - }) as NodeJS.Require; - mockedRequire.resolve = (() => "") as unknown as NodeJS.RequireResolve; - return mockedRequire; - }); - const { date, source } = getLocalWorkerdCompatibilityDate({ - projectPath: "/test/project", - }); - const todaysDate = "2025-01-09"; - expect(date).toEqual(todaysDate); - expect(source).toEqual("workerd"); - }); -}); diff --git a/packages/wrangler/e2e/pages-dev.test.ts b/packages/wrangler/e2e/pages-dev.test.ts index 1d5989edbc..b6ce8548d4 100644 --- a/packages/wrangler/e2e/pages-dev.test.ts +++ b/packages/wrangler/e2e/pages-dev.test.ts @@ -1,7 +1,7 @@ import { existsSync } from "node:fs"; import path from "node:path"; -import { formatCompatibilityDate } from "@cloudflare/workers-utils"; import getPort from "get-port"; +import { formatCompatibilityDate } from "miniflare"; import dedent from "ts-dedent"; import { fetch } from "undici"; import { describe, it } from "vitest"; diff --git a/packages/wrangler/src/__tests__/autoconfig/run.test.ts b/packages/wrangler/src/__tests__/autoconfig/run.test.ts index 3916a1bf90..35e30450bd 100644 --- a/packages/wrangler/src/__tests__/autoconfig/run.test.ts +++ b/packages/wrangler/src/__tests__/autoconfig/run.test.ts @@ -26,16 +26,13 @@ import { writeWorkerSource } from "../helpers/write-worker-source"; import type { Framework } from "../../autoconfig/frameworks"; import type { MockInstance } from "vitest"; -vi.mock("@cloudflare/workers-utils", async (importOriginal) => { +vi.mock("miniflare", async (importOriginal) => { const originalModule = // eslint-disable-next-line @typescript-eslint/consistent-type-imports - await importOriginal>(); + await importOriginal>(); return { ...originalModule, - getLocalWorkerdCompatibilityDate: vi.fn(() => ({ - date: "2000-01-01", - source: "workerd", - })), + supportedCompatibilityDate: "2000-01-01", }; }); diff --git a/packages/wrangler/src/__tests__/dev.test.ts b/packages/wrangler/src/__tests__/dev.test.ts index 802b303021..0fab0e7f22 100644 --- a/packages/wrangler/src/__tests__/dev.test.ts +++ b/packages/wrangler/src/__tests__/dev.test.ts @@ -2,7 +2,6 @@ import * as fs from "node:fs"; import { COMPLIANCE_REGION_CONFIG_UNKNOWN, FatalError, - getLocalWorkerdCompatibilityDate, } from "@cloudflare/workers-utils"; import { writeWranglerConfig } from "@cloudflare/workers-utils/test-helpers"; import ci from "ci-info"; @@ -47,6 +46,8 @@ import type { import type { RawConfig } from "@cloudflare/workers-utils"; import type { Mock, MockInstance } from "vitest"; +import { supportedCompatibilityDate } from "../api"; + vi.mock("../api/startDevWorker/ConfigController", (importOriginal) => importOriginal() ); @@ -278,11 +279,7 @@ describe.sequential("wrangler dev", () => { fs.writeFileSync("index.js", `export default {};`); await runWranglerUntilConfig("dev"); - // Use getLocalWorkerdCompatibilityDate() which applies the same safe date - // conversion as wrangler does (converting future dates to today's date) - const { date: currentDate } = getLocalWorkerdCompatibilityDate(); - - expect(std.warn.replaceAll(currentDate, "")) + expect(std.warn.replaceAll(supportedCompatibilityDate, "")) .toMatchInlineSnapshot(` "▲ [WARNING] No compatibility_date was specified. Using the installed Workers runtime's latest supported date: . @@ -1662,7 +1659,7 @@ describe.sequential("wrangler dev", () => { binding ): binding is [ string, - Extract, + Extract ] => binding[1].type === "plain_text" || binding[1].type === "secret_text" @@ -1712,7 +1709,7 @@ describe.sequential("wrangler dev", () => { binding ): binding is [ string, - Extract, + Extract ] => binding[1].type === "plain_text" || binding[1].type === "secret_text" diff --git a/packages/wrangler/src/api/index.ts b/packages/wrangler/src/api/index.ts index d0f0937f5d..d12d4ba0eb 100644 --- a/packages/wrangler/src/api/index.ts +++ b/packages/wrangler/src/api/index.ts @@ -71,8 +71,11 @@ export type { PlatformProxy, SourcelessWorkerOptions, Unstable_MiniflareWorkerOptions, + CompatDate, } from "./integrations"; +export { supportedCompatibilityDate, isCompatDate } from "./integrations"; + // Exports from ./remoteBindings export { startRemoteProxySession, diff --git a/packages/wrangler/src/api/integrations/platform/index.ts b/packages/wrangler/src/api/integrations/platform/index.ts index 3f3f0cc617..64009008fc 100644 --- a/packages/wrangler/src/api/integrations/platform/index.ts +++ b/packages/wrangler/src/api/integrations/platform/index.ts @@ -1,10 +1,7 @@ import { resolveDockerHost } from "@cloudflare/containers-shared"; -import { - getDockerPath, - getLocalWorkerdCompatibilityDate, - getRegistryPath, -} from "@cloudflare/workers-utils"; +import { getDockerPath, getRegistryPath } from "@cloudflare/workers-utils"; import { kCurrentWorker, Miniflare } from "miniflare"; +import * as miniflareModule from "miniflare"; import { getAssetsOptions, NonExistentAssetsDirError } from "../../../assets"; import { readConfig } from "../../../config"; import { partitionDurableObjectBindings } from "../../../deployment-bundle/entry"; @@ -46,14 +43,21 @@ export { readConfig as unstable_readConfig }; export { getDurableObjectClassNameToUseSQLiteMap as unstable_getDurableObjectClassNameToUseSQLiteMap }; /** - * @deprecated use `getLocalWorkerdCompatibilityDate` from "@cloudflare/workers-utils" instead. + * @deprecated use `supportedCompatibilityDate` instead. * * We're keeping this function only not to break the vite plugin that relies on it, we should remove it as soon as possible. */ export function unstable_getDevCompatibilityDate() { - return getLocalWorkerdCompatibilityDate().date; + return miniflareModule.supportedCompatibilityDate; } +export { + supportedCompatibilityDate, + isCompatDate, + formatCompatibilityDate, +} from "miniflare"; +export type { CompatDate } from "miniflare"; + export { getWorkerNameFromProject as unstable_getWorkerNameFromProject } from "../../../autoconfig/details"; export type { Config as Unstable_Config, diff --git a/packages/wrangler/src/api/startDevWorker/ConfigController.ts b/packages/wrangler/src/api/startDevWorker/ConfigController.ts index e200ef3583..6236b916c5 100644 --- a/packages/wrangler/src/api/startDevWorker/ConfigController.ts +++ b/packages/wrangler/src/api/startDevWorker/ConfigController.ts @@ -5,11 +5,10 @@ import { configFileName, getDisableConfigWatching, getDockerPath, - getLocalWorkerdCompatibilityDate, UserError, } from "@cloudflare/workers-utils"; import { watch } from "chokidar"; -import { getWorkerRegistry } from "miniflare"; +import { getWorkerRegistry, supportedCompatibilityDate } from "miniflare"; import { getAssetsOptions, validateAssetsArgsAndConfig } from "../../assets"; import { fillOpenAPIConfiguration } from "../../cloudchamber/common"; import { readConfig } from "../../config"; @@ -314,11 +313,7 @@ async function resolveConfig( previousName ?? crypto.randomUUID(), config: config.configPath, - compatibilityDate: getDevCompatibilityDate( - entry.projectRoot, - config, - input.compatibilityDate - ), + compatibilityDate: getDevCompatibilityDate(config, input.compatibilityDate), compatibilityFlags: input.compatibilityFlags ?? config.compatibility_flags, complianceRegion: input.complianceRegion ?? config.compliance_region, pythonModules: { @@ -473,13 +468,10 @@ async function resolveConfig( * @returns the compatibility date to use in development */ function getDevCompatibilityDate( - projectPath: string, config: Config | undefined, compatibilityDate = config?.compatibility_date ): string { - const { date: workerdDate } = getLocalWorkerdCompatibilityDate({ - projectPath, - }); + const workerdDate = supportedCompatibilityDate; if (config?.configPath && compatibilityDate === undefined) { logger.warn( diff --git a/packages/wrangler/src/autoconfig/frameworks/analog.ts b/packages/wrangler/src/autoconfig/frameworks/analog.ts index 28ae2a3889..ddedd8a450 100644 --- a/packages/wrangler/src/autoconfig/frameworks/analog.ts +++ b/packages/wrangler/src/autoconfig/frameworks/analog.ts @@ -3,7 +3,7 @@ import { existsSync } from "node:fs"; import { join } from "node:path"; import { updateStatus } from "@cloudflare/cli"; import { blue } from "@cloudflare/cli/colors"; -import { getLocalWorkerdCompatibilityDate } from "@cloudflare/workers-utils"; +import { supportedCompatibilityDate } from "miniflare"; import * as recast from "recast"; import semiver from "semiver"; import { mergeObjectProperties, transformFile } from "../c3-vendor/codemod"; @@ -48,10 +48,6 @@ async function updateViteConfig(projectPath: string) { throw new Error("Could not find Vite config file to modify"); } - const { date: compatDate } = getLocalWorkerdCompatibilityDate({ - projectPath, - }); - updateStatus(`Updating configuration in ${blue(viteConfigPath)}`); transformFile(viteConfigPath, { @@ -73,7 +69,7 @@ async function updateViteConfig(projectPath: string) { ), b.objectProperty( b.identifier("compatibilityDate"), - b.stringLiteral(compatDate) + b.stringLiteral(supportedCompatibilityDate) ), ]) ), diff --git a/packages/wrangler/src/autoconfig/frameworks/solid-start.ts b/packages/wrangler/src/autoconfig/frameworks/solid-start.ts index ff777b0f29..6b9f18692a 100644 --- a/packages/wrangler/src/autoconfig/frameworks/solid-start.ts +++ b/packages/wrangler/src/autoconfig/frameworks/solid-start.ts @@ -1,7 +1,7 @@ import assert from "node:assert"; import { updateStatus } from "@cloudflare/cli"; import { blue } from "@cloudflare/cli/colors"; -import { getLocalWorkerdCompatibilityDate } from "@cloudflare/workers-utils"; +import { supportedCompatibilityDate } from "miniflare"; import * as recast from "recast"; import semiver from "semiver"; import { mergeObjectProperties, transformFile } from "../c3-vendor/codemod"; @@ -88,10 +88,6 @@ function updateViteConfigFile(projectPath: string): void { function updateAppConfigFile(projectPath: string): void { const filePath = `app.config.${usesTypescript(projectPath) ? "ts" : "js"}`; - const { date: compatDate } = getLocalWorkerdCompatibilityDate({ - projectPath, - }); - updateStatus(`Updating configuration in ${blue(filePath)}`); transformFile(filePath, { @@ -115,7 +111,7 @@ function updateAppConfigFile(projectPath: string): void { ), b.objectProperty( b.identifier("compatibilityDate"), - b.stringLiteral(compatDate) + b.stringLiteral(supportedCompatibilityDate) ), ]) ), diff --git a/packages/wrangler/src/autoconfig/run.ts b/packages/wrangler/src/autoconfig/run.ts index be68fbfb1d..c69a2be472 100644 --- a/packages/wrangler/src/autoconfig/run.ts +++ b/packages/wrangler/src/autoconfig/run.ts @@ -2,11 +2,8 @@ import assert from "node:assert"; import { existsSync } from "node:fs"; import { readFile, writeFile } from "node:fs/promises"; import { resolve } from "node:path"; -import { - FatalError, - getLocalWorkerdCompatibilityDate, - parseJSONC, -} from "@cloudflare/workers-utils"; +import { FatalError, parseJSONC } from "@cloudflare/workers-utils"; +import { supportedCompatibilityDate } from "../api/integrations"; import { runCommand } from "../deployment-bundle/run-custom-build"; import { confirm } from "../dialogs"; import { logger } from "../logger"; @@ -88,14 +85,10 @@ export async function runAutoConfig( "The Output Directory is unexpectedly missing" ); - const { date: compatibilityDate } = getLocalWorkerdCompatibilityDate({ - projectPath: autoConfigDetails.projectPath, - }); - const wranglerConfig: RawConfig = { $schema: "node_modules/wrangler/config-schema.json", name: autoConfigDetails.workerName, - compatibility_date: compatibilityDate, + compatibility_date: supportedCompatibilityDate, observability: { enabled: true, }, diff --git a/packages/wrangler/src/cli.ts b/packages/wrangler/src/cli.ts index bfb4c98185..4d49a518cc 100644 --- a/packages/wrangler/src/cli.ts +++ b/packages/wrangler/src/cli.ts @@ -85,6 +85,9 @@ export type { Unstable_MiniflareWorkerOptions, }; +export { supportedCompatibilityDate, isCompatDate } from "./api"; +export type { CompatDate } from "./api"; + export { printBindings as unstable_printBindings } from "./utils/print-bindings"; // Export internal APIs required by the Vitest integration as `unstable_` diff --git a/packages/wrangler/src/deploy/deploy.ts b/packages/wrangler/src/deploy/deploy.ts index 15885a7b61..ff8d3a5ebe 100644 --- a/packages/wrangler/src/deploy/deploy.ts +++ b/packages/wrangler/src/deploy/deploy.ts @@ -8,12 +8,12 @@ import { APIError, configFileName, experimental_patchConfig, - formatCompatibilityDate, formatConfigSnippet, getDockerPath, parseNonHyphenedUuid, UserError, } from "@cloudflare/workers-utils"; +import { formatCompatibilityDate } from "miniflare"; import PQueue from "p-queue"; import { Response } from "undici"; import { syncAssets } from "../assets"; diff --git a/packages/wrangler/src/deploy/index.ts b/packages/wrangler/src/deploy/index.ts index c1cc519cc6..67c12da5b4 100644 --- a/packages/wrangler/src/deploy/index.ts +++ b/packages/wrangler/src/deploy/index.ts @@ -3,11 +3,11 @@ import { statSync, writeFileSync } from "node:fs"; import path from "node:path"; import { configFileName, - formatCompatibilityDate, getCIOverrideName, UserError, } from "@cloudflare/workers-utils"; import chalk from "chalk"; +import { formatCompatibilityDate } from "miniflare"; import { getAssetsOptions, validateAssetsArgsAndConfig } from "../assets"; import { getDetailsForAutoConfig } from "../autoconfig/details"; import { runAutoConfig } from "../autoconfig/run"; diff --git a/packages/wrangler/src/deployment-bundle/entry.ts b/packages/wrangler/src/deployment-bundle/entry.ts index 3e34ef0551..89a430d809 100644 --- a/packages/wrangler/src/deployment-bundle/entry.ts +++ b/packages/wrangler/src/deployment-bundle/entry.ts @@ -1,10 +1,10 @@ import path from "node:path"; import { configFileName, - formatCompatibilityDate, formatConfigSnippet, UserError, } from "@cloudflare/workers-utils"; +import { formatCompatibilityDate } from "miniflare"; import dedent from "ts-dedent"; import { sniffUserAgent } from "../package-manager"; import { guessWorkerFormat } from "./guess-worker-format"; diff --git a/packages/wrangler/src/pages/dev.ts b/packages/wrangler/src/pages/dev.ts index 4c958cfa40..11cee94b26 100644 --- a/packages/wrangler/src/pages/dev.ts +++ b/packages/wrangler/src/pages/dev.ts @@ -6,11 +6,11 @@ import { setTimeout } from "node:timers/promises"; import { configFileName, FatalError, - formatCompatibilityDate, UserError, } from "@cloudflare/workers-utils"; import { watch } from "chokidar"; import * as esbuild from "esbuild"; +import { formatCompatibilityDate } from "miniflare"; import { readConfig } from "../config"; import { getConfigCache } from "../config-cache"; import { createCommand } from "../core/create-command"; diff --git a/packages/wrangler/src/pages/download-config.ts b/packages/wrangler/src/pages/download-config.ts index 25ad2c9df0..4ca2ad7ec3 100644 --- a/packages/wrangler/src/pages/download-config.ts +++ b/packages/wrangler/src/pages/download-config.ts @@ -3,10 +3,9 @@ import { writeFile } from "node:fs/promises"; import { COMPLIANCE_REGION_CONFIG_PUBLIC, FatalError, - formatCompatibilityDate, } from "@cloudflare/workers-utils"; import chalk from "chalk"; -import { supportedCompatibilityDate } from "miniflare"; +import { formatCompatibilityDate, supportedCompatibilityDate } from "miniflare"; import TOML from "smol-toml"; import { fetchResult } from "../cfetch"; import { getConfigCache } from "../config-cache"; diff --git a/packages/wrangler/src/versions/upload.ts b/packages/wrangler/src/versions/upload.ts index a08416f3d6..69a79db05b 100644 --- a/packages/wrangler/src/versions/upload.ts +++ b/packages/wrangler/src/versions/upload.ts @@ -6,7 +6,6 @@ import path from "node:path"; import { blue, gray } from "@cloudflare/cli/colors"; import { configFileName, - formatCompatibilityDate, formatConfigSnippet, getCIGeneratePreviewAlias, getCIOverrideName, @@ -14,6 +13,7 @@ import { ParseError, UserError, } from "@cloudflare/workers-utils"; +import { formatCompatibilityDate } from "miniflare"; import { Response } from "undici"; import { getAssetsOptions, From 6ebf636a3f8627c62552d7b2afe994c52fd67644 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 10:20:01 +0000 Subject: [PATCH 02/27] add code comment as suggested --- packages/miniflare/src/runtime/compatibility-date.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/miniflare/src/runtime/compatibility-date.ts b/packages/miniflare/src/runtime/compatibility-date.ts index f11937fd12..fc3b1e6761 100644 --- a/packages/miniflare/src/runtime/compatibility-date.ts +++ b/packages/miniflare/src/runtime/compatibility-date.ts @@ -50,6 +50,7 @@ function getSafeCompatibilityDate(): CompatDate { return workerdCompatibilityDate; } +/** `YYYY-MM-DD` compatibility date */ const supportedCompatibilityDate = getSafeCompatibilityDate(); export { supportedCompatibilityDate }; From a702c3f4d8da18c092cc1da06bd0a4267e0ab9f6 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 10:22:03 +0000 Subject: [PATCH 03/27] remove unnecessary * import from miniflare --- .../src/api/integrations/platform/index.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/wrangler/src/api/integrations/platform/index.ts b/packages/wrangler/src/api/integrations/platform/index.ts index 64009008fc..4ef77f6692 100644 --- a/packages/wrangler/src/api/integrations/platform/index.ts +++ b/packages/wrangler/src/api/integrations/platform/index.ts @@ -1,7 +1,10 @@ import { resolveDockerHost } from "@cloudflare/containers-shared"; import { getDockerPath, getRegistryPath } from "@cloudflare/workers-utils"; -import { kCurrentWorker, Miniflare } from "miniflare"; -import * as miniflareModule from "miniflare"; +import { + kCurrentWorker, + Miniflare, + supportedCompatibilityDate, +} from "miniflare"; import { getAssetsOptions, NonExistentAssetsDirError } from "../../../assets"; import { readConfig } from "../../../config"; import { partitionDurableObjectBindings } from "../../../deployment-bundle/entry"; @@ -48,7 +51,7 @@ export { getDurableObjectClassNameToUseSQLiteMap as unstable_getDurableObjectCla * We're keeping this function only not to break the vite plugin that relies on it, we should remove it as soon as possible. */ export function unstable_getDevCompatibilityDate() { - return miniflareModule.supportedCompatibilityDate; + return supportedCompatibilityDate; } export { @@ -116,7 +119,7 @@ export type GetPlatformProxyOptions = { */ export type PlatformProxy< Env = Record, - CfProperties extends Record = IncomingRequestCfProperties, + CfProperties extends Record = IncomingRequestCfProperties > = { /** * Environment object containing the various Cloudflare bindings @@ -150,7 +153,7 @@ export type PlatformProxy< */ export async function getPlatformProxy< Env = Record, - CfProperties extends Record = IncomingRequestCfProperties, + CfProperties extends Record = IncomingRequestCfProperties >( options: GetPlatformProxyOptions = {} ): Promise> { @@ -493,7 +496,7 @@ export function unstable_getMiniflareWorkerOptions( doClassName: binding.class_name, containerDOClassNames, containerBuildId: options?.containerBuildId, - }) + }) : undefined, } satisfies DurableObjectDefinition, ]; @@ -519,7 +522,7 @@ export function unstable_getMiniflareWorkerOptions( compatibilityFlags: config.compatibility_flags, modulesRules, containerEngine: useContainers - ? (config.dev.container_engine ?? resolveDockerHost(getDockerPath())) + ? config.dev.container_engine ?? resolveDockerHost(getDockerPath()) : undefined, ...bindingOptions, From 5e49112f536f2287e7bdd1e93d22ae654ce79e26 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 10:23:54 +0000 Subject: [PATCH 04/27] add missing @returns to jsdoc comment --- packages/wrangler/src/api/integrations/platform/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/wrangler/src/api/integrations/platform/index.ts b/packages/wrangler/src/api/integrations/platform/index.ts index 4ef77f6692..f789315f11 100644 --- a/packages/wrangler/src/api/integrations/platform/index.ts +++ b/packages/wrangler/src/api/integrations/platform/index.ts @@ -49,6 +49,8 @@ export { getDurableObjectClassNameToUseSQLiteMap as unstable_getDurableObjectCla * @deprecated use `supportedCompatibilityDate` instead. * * We're keeping this function only not to break the vite plugin that relies on it, we should remove it as soon as possible. + * + * @returns YYYY-MM-DD compatibility date */ export function unstable_getDevCompatibilityDate() { return supportedCompatibilityDate; From 1e60ce959d595f0797adb729f9a28d28bbdc77b4 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 10:35:23 +0000 Subject: [PATCH 05/27] add todo comment --- packages/vite-plugin-cloudflare/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vite-plugin-cloudflare/src/index.ts b/packages/vite-plugin-cloudflare/src/index.ts index cbb59dfe01..99ae64f1db 100644 --- a/packages/vite-plugin-cloudflare/src/index.ts +++ b/packages/vite-plugin-cloudflare/src/index.ts @@ -27,6 +27,7 @@ import type { PluginConfig } from "./plugin-config"; import type { CompatDate } from "miniflare"; import type * as vite from "vite"; +// TODO: simplify this function in the next major release (DEVX-2533) /** * Gets the compatibility date from the local workerd version. * From c91c2cb41281500743a1678916086acbd619b2e9 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 10:37:41 +0000 Subject: [PATCH 06/27] fix formatting --- .../src/helpers/__tests__/compatDate.test.ts | 2 +- packages/wrangler/src/__tests__/dev.test.ts | 7 +++---- packages/wrangler/src/api/integrations/platform/index.ts | 8 ++++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts b/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts index 0b2480a266..d319b89816 100644 --- a/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts +++ b/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts @@ -29,7 +29,7 @@ describe("Compatibility Date Helpers", () => { supportedCompatibilityDate: "2025-01-10", }; vi.mocked(createRequire).mockReturnValue( - (() => mockWrangler) as unknown as NodeJS.Require, + (() => mockWrangler) as unknown as NodeJS.Require ); const date = getWorkerdCompatibilityDate("./my-app"); diff --git a/packages/wrangler/src/__tests__/dev.test.ts b/packages/wrangler/src/__tests__/dev.test.ts index 0fab0e7f22..33c0f982a0 100644 --- a/packages/wrangler/src/__tests__/dev.test.ts +++ b/packages/wrangler/src/__tests__/dev.test.ts @@ -18,6 +18,7 @@ import { it, vi, } from "vitest"; +import { supportedCompatibilityDate } from "../api"; /* eslint-enable no-restricted-imports */ import { ConfigController } from "../api/startDevWorker/ConfigController"; import { unwrapHook } from "../api/startDevWorker/utils"; @@ -46,8 +47,6 @@ import type { import type { RawConfig } from "@cloudflare/workers-utils"; import type { Mock, MockInstance } from "vitest"; -import { supportedCompatibilityDate } from "../api"; - vi.mock("../api/startDevWorker/ConfigController", (importOriginal) => importOriginal() ); @@ -1659,7 +1658,7 @@ describe.sequential("wrangler dev", () => { binding ): binding is [ string, - Extract + Extract, ] => binding[1].type === "plain_text" || binding[1].type === "secret_text" @@ -1709,7 +1708,7 @@ describe.sequential("wrangler dev", () => { binding ): binding is [ string, - Extract + Extract, ] => binding[1].type === "plain_text" || binding[1].type === "secret_text" diff --git a/packages/wrangler/src/api/integrations/platform/index.ts b/packages/wrangler/src/api/integrations/platform/index.ts index f789315f11..88701f7b70 100644 --- a/packages/wrangler/src/api/integrations/platform/index.ts +++ b/packages/wrangler/src/api/integrations/platform/index.ts @@ -121,7 +121,7 @@ export type GetPlatformProxyOptions = { */ export type PlatformProxy< Env = Record, - CfProperties extends Record = IncomingRequestCfProperties + CfProperties extends Record = IncomingRequestCfProperties, > = { /** * Environment object containing the various Cloudflare bindings @@ -155,7 +155,7 @@ export type PlatformProxy< */ export async function getPlatformProxy< Env = Record, - CfProperties extends Record = IncomingRequestCfProperties + CfProperties extends Record = IncomingRequestCfProperties, >( options: GetPlatformProxyOptions = {} ): Promise> { @@ -498,7 +498,7 @@ export function unstable_getMiniflareWorkerOptions( doClassName: binding.class_name, containerDOClassNames, containerBuildId: options?.containerBuildId, - }) + }) : undefined, } satisfies DurableObjectDefinition, ]; @@ -524,7 +524,7 @@ export function unstable_getMiniflareWorkerOptions( compatibilityFlags: config.compatibility_flags, modulesRules, containerEngine: useContainers - ? config.dev.container_engine ?? resolveDockerHost(getDockerPath()) + ? (config.dev.container_engine ?? resolveDockerHost(getDockerPath())) : undefined, ...bindingOptions, From ec884f06de4c616102978b828c900740e8801697 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 10:43:15 +0000 Subject: [PATCH 07/27] remove inline comments --- .../miniflare/test/runtime/compatibility-date.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/miniflare/test/runtime/compatibility-date.spec.ts b/packages/miniflare/test/runtime/compatibility-date.spec.ts index 026cc9f2e3..55067f10c7 100644 --- a/packages/miniflare/test/runtime/compatibility-date.spec.ts +++ b/packages/miniflare/test/runtime/compatibility-date.spec.ts @@ -42,23 +42,23 @@ describe("isCompatDate", () => { describe("formatCompatibilityDate", () => { test("returns correctly formatted date string", ({ expect }) => { // Use UTC dates to avoid timezone issues - const date = new Date(Date.UTC(2024, 0, 15)); // January 15, 2024 + const date = new Date(Date.UTC(2024, 0, 15)); expect(formatCompatibilityDate(date)).toBe("2024-01-15"); }); test("pads single-digit months and days with zeros", ({ expect }) => { - const date1 = new Date(Date.UTC(2024, 0, 1)); // January 1, 2024 + const date1 = new Date(Date.UTC(2024, 0, 1)); expect(formatCompatibilityDate(date1)).toBe("2024-01-01"); - const date2 = new Date(Date.UTC(2024, 8, 5)); // September 5, 2024 + const date2 = new Date(Date.UTC(2024, 8, 5)); expect(formatCompatibilityDate(date2)).toBe("2024-09-05"); }); test("handles year boundaries correctly", ({ expect }) => { - const date1 = new Date(Date.UTC(2023, 11, 31)); // December 31, 2023 + const date1 = new Date(Date.UTC(2023, 11, 31)); expect(formatCompatibilityDate(date1)).toBe("2023-12-31"); - const date2 = new Date(Date.UTC(2024, 0, 1)); // January 1, 2024 + const date2 = new Date(Date.UTC(2024, 0, 1)); expect(formatCompatibilityDate(date2)).toBe("2024-01-01"); }); From 22b2b2df151fc1a0e9673e691be7ce1858f0c8af Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 10:44:54 +0000 Subject: [PATCH 08/27] Update packages/miniflare/src/runtime/compatibility-date.ts Co-authored-by: Victor Berchet --- packages/miniflare/src/runtime/compatibility-date.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/miniflare/src/runtime/compatibility-date.ts b/packages/miniflare/src/runtime/compatibility-date.ts index fc3b1e6761..c126b0aa2a 100644 --- a/packages/miniflare/src/runtime/compatibility-date.ts +++ b/packages/miniflare/src/runtime/compatibility-date.ts @@ -36,6 +36,8 @@ export function formatCompatibilityDate(date: Date): 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. + * + * @return The compatibility Date (`YYYY-MM-DD`) */ function getSafeCompatibilityDate(): CompatDate { // The compatibility data from workerd follows the CompatDate format From befed3d657d1a04d389786a631b56dd6efa18a62 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 11:06:17 +0000 Subject: [PATCH 09/27] update changeset --- .changeset/dirty-moments-join.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/dirty-moments-join.md b/.changeset/dirty-moments-join.md index 03416854d7..cf97e4186e 100644 --- a/.changeset/dirty-moments-join.md +++ b/.changeset/dirty-moments-join.md @@ -4,4 +4,4 @@ Re-export `supportedCompatibilityDate` from miniflare -Re-exports the `supportedCompatibilityDate` value form miniflare that contains the latest supported compatibility date by the local version of `workerd` +Re-exports the `supportedCompatibilityDate` value form miniflare that contains a recent and safe compatibility date from the local version of `workerd` From 8c0addcc1a50d04582153708ef056aab8cc33bf9 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 11:07:36 +0000 Subject: [PATCH 10/27] update changeset --- .changeset/smart-buckets-wonder.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/smart-buckets-wonder.md b/.changeset/smart-buckets-wonder.md index 0207d28f06..c18461d783 100644 --- a/.changeset/smart-buckets-wonder.md +++ b/.changeset/smart-buckets-wonder.md @@ -2,4 +2,4 @@ "@cloudflare/workers-utils": minor --- -Remove the `getLocalWorkerdCompatibilityDate` utility from the package (a utility of the same name is now exported by `miniflare` and `wrangler` instead) +Remove the `getLocalWorkerdCompatibilityDate` utility from the package (`supportedCompatibilityDate` from `miniflare` and `wrangler` should be used instead) From de99ecbd0cb4df071b958503ca54c79fb564e4db Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 11:11:09 +0000 Subject: [PATCH 11/27] Update packages/miniflare/src/runtime/compatibility-date.ts Co-authored-by: Victor Berchet --- packages/miniflare/src/runtime/compatibility-date.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/miniflare/src/runtime/compatibility-date.ts b/packages/miniflare/src/runtime/compatibility-date.ts index c126b0aa2a..93260e9306 100644 --- a/packages/miniflare/src/runtime/compatibility-date.ts +++ b/packages/miniflare/src/runtime/compatibility-date.ts @@ -24,7 +24,7 @@ export function isCompatDate(str: string): str is CompatDate { * Returns the date formatted as a compatibility date * * @param date The target date to convert - * @returns The date as a CompatDate string (a string following the format `YYYY-MM-DD`) + * @returns The date as a CompatDate string (`YYYY-MM-DD`) */ export function formatCompatibilityDate(date: Date): CompatDate { const compatDate = date.toISOString().slice(0, 10); From c01cf7c26d1d4863a0bf5e5ea5012dc45e45e715 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 11:26:01 +0000 Subject: [PATCH 12/27] improve changesets --- .changeset/dirty-moments-join.md | 2 +- .changeset/long-colts-occur.md | 2 +- .changeset/quiet-roses-help.md | 2 ++ .changeset/smart-buckets-wonder.md | 8 +++++++- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.changeset/dirty-moments-join.md b/.changeset/dirty-moments-join.md index cf97e4186e..29df101df2 100644 --- a/.changeset/dirty-moments-join.md +++ b/.changeset/dirty-moments-join.md @@ -4,4 +4,4 @@ Re-export `supportedCompatibilityDate` from miniflare -Re-exports the `supportedCompatibilityDate` value form miniflare that contains a recent and safe compatibility date from the local version of `workerd` +Re-exports the `supportedCompatibilityDate` value from miniflare that contains a recent and safe compatibility date from the local version of `workerd` diff --git a/.changeset/long-colts-occur.md b/.changeset/long-colts-occur.md index ebd6f8ff07..2bd261bb79 100644 --- a/.changeset/long-colts-occur.md +++ b/.changeset/long-colts-occur.md @@ -4,4 +4,4 @@ Use `supportedCompatibilityDate` value from `miniflare` instead of getting the date from `@cloudflare/workers-utils` -`miniflare` exports the latest compatibility date `supportedCompatibilityDate` as, and that is the value that now the package uses as the latest supported workerd compatibility date. This doesn't have any specific user-facing effect (besides potentially making the function more reliable). +`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). diff --git a/.changeset/quiet-roses-help.md b/.changeset/quiet-roses-help.md index a0b5a916c5..5326a457a0 100644 --- a/.changeset/quiet-roses-help.md +++ b/.changeset/quiet-roses-help.md @@ -3,3 +3,5 @@ --- Fix the fallback compatibility date being always incorrectly used when running the CLI via `pnpm` + +Previously, `create-cloudflare` used `getLocalWorkerdCompatibilityDate` from `@cloudflare/workers-utils`, which attempted to get the compatibility date from `workerd` via `miniflare`. When running via `pnpm`, the resolution to `miniflare` would however fail 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. diff --git a/.changeset/smart-buckets-wonder.md b/.changeset/smart-buckets-wonder.md index c18461d783..6f782a9c43 100644 --- a/.changeset/smart-buckets-wonder.md +++ b/.changeset/smart-buckets-wonder.md @@ -2,4 +2,10 @@ "@cloudflare/workers-utils": minor --- -Remove the `getLocalWorkerdCompatibilityDate` utility from the package (`supportedCompatibilityDate` from `miniflare` and `wrangler` should be used instead) +Remove the `getLocalWorkerdCompatibilityDate` utility from the package + +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 From f3031b5cec9530f8c1b51a5187e11121dddd17ed Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 11:32:46 +0000 Subject: [PATCH 13/27] address issue of older version of wrangler --- .../src/helpers/__tests__/compatDate.test.ts | 19 +++++++++++++++++++ .../src/helpers/compatDate.ts | 9 +++++++++ 2 files changed, 28 insertions(+) diff --git a/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts b/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts index d319b89816..29a8a7342f 100644 --- a/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts +++ b/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts @@ -56,6 +56,25 @@ describe("Compatibility Date Helpers", () => { expect.stringContaining(fallbackDate) ); }); + + 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 = "2026-03-03"; + expect(date).toBe(fallbackDate); + expect(spinner.start).toHaveBeenCalled(); + expect(spinner.stop).toHaveBeenCalledWith( + expect.stringContaining(fallbackDate) + ); + }); }); describe("getLatestTypesEntrypoint", () => { diff --git a/packages/create-cloudflare/src/helpers/compatDate.ts b/packages/create-cloudflare/src/helpers/compatDate.ts index 3b316d8ae7..1391995093 100644 --- a/packages/create-cloudflare/src/helpers/compatDate.ts +++ b/packages/create-cloudflare/src/helpers/compatDate.ts @@ -22,6 +22,15 @@ export function getWorkerdCompatibilityDate(projectPath: string): CompatDate { 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" + ); + } + s.stop( `${brandColor("compatibility date")} ${dim(supportedCompatibilityDate)}` ); From 8b4ba49cff571d3a50899875830373f43103bf21 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 11:36:38 +0000 Subject: [PATCH 14/27] bump fallback date --- .../src/helpers/__tests__/compatDate.test.ts | 4 ++-- packages/create-cloudflare/src/helpers/compatDate.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts b/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts index 29a8a7342f..457dd11acd 100644 --- a/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts +++ b/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts @@ -49,7 +49,7 @@ describe("Compatibility Date Helpers", () => { const date = getWorkerdCompatibilityDate("./my-app"); - const fallbackDate = "2026-03-03"; + const fallbackDate = "2026-03-24"; expect(date).toBe(fallbackDate); expect(spinner.start).toHaveBeenCalled(); expect(spinner.stop).toHaveBeenCalledWith( @@ -68,7 +68,7 @@ describe("Compatibility Date Helpers", () => { const date = getWorkerdCompatibilityDate("./my-app"); - const fallbackDate = "2026-03-03"; + const fallbackDate = "2026-03-24"; expect(date).toBe(fallbackDate); expect(spinner.start).toHaveBeenCalled(); expect(spinner.stop).toHaveBeenCalledWith( diff --git a/packages/create-cloudflare/src/helpers/compatDate.ts b/packages/create-cloudflare/src/helpers/compatDate.ts index 1391995093..567bfe96f2 100644 --- a/packages/create-cloudflare/src/helpers/compatDate.ts +++ b/packages/create-cloudflare/src/helpers/compatDate.ts @@ -37,8 +37,8 @@ export function getWorkerdCompatibilityDate(projectPath: string): CompatDate { return supportedCompatibilityDate; } catch { // 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/supported-compatibility-date.txt) - const fallbackDate = "2026-03-03"; + // (source: https://github.com/cloudflare/workerd/blob/main/src/workerd/io/release-version.txt#L1) + const fallbackDate = "2026-03-24"; s.stop( `${brandColor("compatibility date")} ${dim( `Could not find workerd date, falling back to "${fallbackDate}"` From 9eb1dd3974bdfc878dd99bdf4f70e776a0297321 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 14:17:44 +0000 Subject: [PATCH 15/27] Update .changeset/quiet-roses-help.md Co-authored-by: Pete Bacon Darwin --- .changeset/quiet-roses-help.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.changeset/quiet-roses-help.md b/.changeset/quiet-roses-help.md index 5326a457a0..fb0deebac3 100644 --- a/.changeset/quiet-roses-help.md +++ b/.changeset/quiet-roses-help.md @@ -4,4 +4,6 @@ Fix the fallback compatibility date being always incorrectly used when running the CLI via `pnpm` -Previously, `create-cloudflare` used `getLocalWorkerdCompatibilityDate` from `@cloudflare/workers-utils`, which attempted to get the compatibility date from `workerd` via `miniflare`. When running via `pnpm`, the resolution to `miniflare` would however fail 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. +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. From 72147322cfd2000f4cb400be4a7506f4160c8a07 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 14:17:58 +0000 Subject: [PATCH 16/27] Update .changeset/quiet-roses-help.md Co-authored-by: Pete Bacon Darwin --- .changeset/quiet-roses-help.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/quiet-roses-help.md b/.changeset/quiet-roses-help.md index fb0deebac3..43a44b31cd 100644 --- a/.changeset/quiet-roses-help.md +++ b/.changeset/quiet-roses-help.md @@ -2,7 +2,7 @@ "create-cloudflare": patch --- -Fix the fallback compatibility date being always incorrectly used when running the CLI via `pnpm` +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. From 3aa261093e4f7927ffdeb80a1f5b78072dd76df6 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 14:18:41 +0000 Subject: [PATCH 17/27] Update .changeset/dirty-moments-join.md Co-authored-by: Pete Bacon Darwin --- .changeset/dirty-moments-join.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/dirty-moments-join.md b/.changeset/dirty-moments-join.md index 29df101df2..8e840e22b4 100644 --- a/.changeset/dirty-moments-join.md +++ b/.changeset/dirty-moments-join.md @@ -4,4 +4,4 @@ Re-export `supportedCompatibilityDate` from miniflare -Re-exports the `supportedCompatibilityDate` value from miniflare that contains a recent and safe compatibility date from the local version of `workerd` +The re-exports contains a recent and safe compatibility date from the local version of `workerd`. From 490a0d11f5d5dc2a007324d2bea7fa048dad60a2 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 14:48:30 +0000 Subject: [PATCH 18/27] Apply suggestions from code review Co-authored-by: Pete Bacon Darwin --- packages/wrangler/src/api/index.ts | 2 +- packages/wrangler/src/autoconfig/run.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/wrangler/src/api/index.ts b/packages/wrangler/src/api/index.ts index d12d4ba0eb..5da40c0426 100644 --- a/packages/wrangler/src/api/index.ts +++ b/packages/wrangler/src/api/index.ts @@ -74,7 +74,7 @@ export type { CompatDate, } from "./integrations"; -export { supportedCompatibilityDate, isCompatDate } from "./integrations"; +export { supportedCompatibilityDate, isCompatDate } from "miniflare"; // Exports from ./remoteBindings export { diff --git a/packages/wrangler/src/autoconfig/run.ts b/packages/wrangler/src/autoconfig/run.ts index c69a2be472..2e8c212cee 100644 --- a/packages/wrangler/src/autoconfig/run.ts +++ b/packages/wrangler/src/autoconfig/run.ts @@ -3,7 +3,7 @@ import { existsSync } from "node:fs"; import { readFile, writeFile } from "node:fs/promises"; import { resolve } from "node:path"; import { FatalError, parseJSONC } from "@cloudflare/workers-utils"; -import { supportedCompatibilityDate } from "../api/integrations"; +import { supportedCompatibilityDate } from "miniflare"; import { runCommand } from "../deployment-bundle/run-custom-build"; import { confirm } from "../dialogs"; import { logger } from "../logger"; From af2445ed22160c6469f93d9b315382adaecf2503 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 14:50:13 +0000 Subject: [PATCH 19/27] update changeset to major --- .changeset/smart-buckets-wonder.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/smart-buckets-wonder.md b/.changeset/smart-buckets-wonder.md index 6f782a9c43..645ec07c9f 100644 --- a/.changeset/smart-buckets-wonder.md +++ b/.changeset/smart-buckets-wonder.md @@ -1,5 +1,5 @@ --- -"@cloudflare/workers-utils": minor +"@cloudflare/workers-utils": major --- Remove the `getLocalWorkerdCompatibilityDate` utility from the package From cb41c70c825750586518028b02c235a756a552e7 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 14:55:44 +0000 Subject: [PATCH 20/27] add reverse date check --- packages/miniflare/test/runtime/compatibility-date.spec.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/miniflare/test/runtime/compatibility-date.spec.ts b/packages/miniflare/test/runtime/compatibility-date.spec.ts index 55067f10c7..a1971861ef 100644 --- a/packages/miniflare/test/runtime/compatibility-date.spec.ts +++ b/packages/miniflare/test/runtime/compatibility-date.spec.ts @@ -36,6 +36,9 @@ describe("isCompatDate", () => { expect(isCompatDate("2024-01-15T00:00:00")).toBe(false); expect(isCompatDate(" 2024-01-15")).toBe(false); expect(isCompatDate("2024-01-15 ")).toBe(false); + + // Reverse Date + expect(isCompatDate("01-01-2000")).toBe(false); }); }); From 3402f7ab57b72d93314301315d8992f8ca307624 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 15:02:53 +0000 Subject: [PATCH 21/27] avoid re-exporting * from compatibility-date --- packages/miniflare/src/runtime/index.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/miniflare/src/runtime/index.ts b/packages/miniflare/src/runtime/index.ts index c4388de0e8..b7ff5dd4ff 100644 --- a/packages/miniflare/src/runtime/index.ts +++ b/packages/miniflare/src/runtime/index.ts @@ -348,4 +348,12 @@ export class Runtime { export * from "./config"; -export * from "./compatibility-date"; +export { + supportedCompatibilityDate, + formatCompatibilityDate, + isCompatDate, + type CompatDate, + type YYYY, + type MM, + type DD, +} from "./compatibility-date"; From 81b56234ada292a8096a3e176c8f1fd656c93eab Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 15:10:12 +0000 Subject: [PATCH 22/27] compare date strings --- .../test/runtime/compatibility-date.spec.ts | 23 ++++--------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/packages/miniflare/test/runtime/compatibility-date.spec.ts b/packages/miniflare/test/runtime/compatibility-date.spec.ts index a1971861ef..2e344b5698 100644 --- a/packages/miniflare/test/runtime/compatibility-date.spec.ts +++ b/packages/miniflare/test/runtime/compatibility-date.spec.ts @@ -85,24 +85,9 @@ describe("supportedCompatibilityDate", () => { }); test("should not be in the future", ({ expect }) => { - const supportedCompatibilityDateAsDate = new Date( - supportedCompatibilityDate - ); - const now = new Date(); - - // The returned date should not be after today (in UTC) - // We compare timestamps at midnight UTC for both dates - const resultTimestamp = Date.UTC( - supportedCompatibilityDateAsDate.getUTCFullYear(), - supportedCompatibilityDateAsDate.getUTCMonth(), - supportedCompatibilityDateAsDate.getUTCDate() - ); - const todayTimestamp = Date.UTC( - now.getUTCFullYear(), - now.getUTCMonth(), - now.getUTCDate() - ); - - expect(resultTimestamp).toBeLessThanOrEqual(todayTimestamp); + // supportedCompatibilityDate is already a YYYY-MM-DD string, which sorts + // lexicographically as a date. Compare directly against today's date. + const todayDate = formatCompatibilityDate(new Date()); + expect(supportedCompatibilityDate <= todayDate).toBe(true); }); }); From ae72d62872fe0cf41ddcedb5890f86bfa14a3602 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 15:12:55 +0000 Subject: [PATCH 23/27] update README --- packages/miniflare/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/miniflare/README.md b/packages/miniflare/README.md index a4f4abc7c7..77892c01d0 100644 --- a/packages/miniflare/README.md +++ b/packages/miniflare/README.md @@ -935,7 +935,10 @@ defined at the top-level. ### `supportedCompatibilityDate` -Exported value containing the latest [compatibility date](https://developers.cloudflare.com/workers/platform/compatibility-dates/) supported by the locally installed `workerd` package. If the `workerd` package's compatibility date is in the future, the value contains today's date instead. The value is a string in the `YYYY-MM-DD` format (e.g. `"2025-09-27"`). +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 From a86d1e0221b4b9b2d6e158cd676325bbde511357 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 16:01:14 +0000 Subject: [PATCH 24/27] add check in c3 e2es for compatibility date --- .../e2e/tests/workers/workers.test.ts | 23 +++++++++++++++++-- .../src/helpers/__tests__/compatDate.test.ts | 11 ++++----- .../src/helpers/compatDate.ts | 15 ++++++++---- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/packages/create-cloudflare/e2e/tests/workers/workers.test.ts b/packages/create-cloudflare/e2e/tests/workers/workers.test.ts index 5a1be7e019..36dbc64eb5 100644 --- a/packages/create-cloudflare/e2e/tests/workers/workers.test.ts +++ b/packages/create-cloudflare/e2e/tests/workers/workers.test.ts @@ -19,6 +19,7 @@ import { } from "../../helpers/workers-helpers"; import { getWorkerTests } from "./test-config"; import type { RunnerTestSuite } from "vitest"; +import { FALLBACK_COMPAT_DATE } from "helpers/compatDate"; const workerTests = getWorkerTests(); @@ -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 not the hardcoded fallback. + expect(config.compatibility_date).toMatch(/^\d{4}-\d{2}-\d{2}$/); + expect(config.compatibility_date).not.toEqual( + FALLBACK_COMPAT_DATE + ); } 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 not 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.` diff --git a/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts b/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts index 457dd11acd..1e7e095235 100644 --- a/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts +++ b/packages/create-cloudflare/src/helpers/__tests__/compatDate.test.ts @@ -1,5 +1,6 @@ import { createRequire } from "node:module"; import { + FALLBACK_COMPAT_DATE, getLatestTypesEntrypoint, getWorkerdCompatibilityDate, } from "helpers/compatDate"; @@ -49,11 +50,10 @@ describe("Compatibility Date Helpers", () => { const date = getWorkerdCompatibilityDate("./my-app"); - const fallbackDate = "2026-03-24"; - 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) ); }); @@ -68,11 +68,10 @@ describe("Compatibility Date Helpers", () => { const date = getWorkerdCompatibilityDate("./my-app"); - const fallbackDate = "2026-03-24"; - 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) ); }); }); diff --git a/packages/create-cloudflare/src/helpers/compatDate.ts b/packages/create-cloudflare/src/helpers/compatDate.ts index 567bfe96f2..c3ee91d041 100644 --- a/packages/create-cloudflare/src/helpers/compatDate.ts +++ b/packages/create-cloudflare/src/helpers/compatDate.ts @@ -6,6 +6,14 @@ import { spinner } from "@cloudflare/cli/interactive"; 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 * @@ -36,15 +44,12 @@ export function getWorkerdCompatibilityDate(projectPath: string): CompatDate { ); return supportedCompatibilityDate; } catch { - // 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) - const fallbackDate = "2026-03-24"; s.stop( `${brandColor("compatibility date")} ${dim( - `Could not find workerd date, falling back to "${fallbackDate}"` + `Could not find workerd date, falling back to "${FALLBACK_COMPAT_DATE}"` )}` ); - return fallbackDate; + return FALLBACK_COMPAT_DATE; } } From edf4d292223c6f76fe3b48b00a0d5b1233b0df9e Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 16:01:49 +0000 Subject: [PATCH 25/27] fix formatting --- .changeset/smart-buckets-wonder.md | 1 + packages/create-cloudflare/e2e/tests/workers/workers.test.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.changeset/smart-buckets-wonder.md b/.changeset/smart-buckets-wonder.md index 645ec07c9f..f2bef3c643 100644 --- a/.changeset/smart-buckets-wonder.md +++ b/.changeset/smart-buckets-wonder.md @@ -7,5 +7,6 @@ Remove the `getLocalWorkerdCompatibilityDate` utility from the package 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 diff --git a/packages/create-cloudflare/e2e/tests/workers/workers.test.ts b/packages/create-cloudflare/e2e/tests/workers/workers.test.ts index 36dbc64eb5..d35c45bf4a 100644 --- a/packages/create-cloudflare/e2e/tests/workers/workers.test.ts +++ b/packages/create-cloudflare/e2e/tests/workers/workers.test.ts @@ -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"; @@ -19,7 +20,6 @@ import { } from "../../helpers/workers-helpers"; import { getWorkerTests } from "./test-config"; import type { RunnerTestSuite } from "vitest"; -import { FALLBACK_COMPAT_DATE } from "helpers/compatDate"; const workerTests = getWorkerTests(); From 2997ba347542594f7d81febda4648812305a8d35 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 16:03:21 +0000 Subject: [PATCH 26/27] fix typo --- packages/create-cloudflare/e2e/tests/workers/workers.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/create-cloudflare/e2e/tests/workers/workers.test.ts b/packages/create-cloudflare/e2e/tests/workers/workers.test.ts index d35c45bf4a..dd31c809cc 100644 --- a/packages/create-cloudflare/e2e/tests/workers/workers.test.ts +++ b/packages/create-cloudflare/e2e/tests/workers/workers.test.ts @@ -72,7 +72,7 @@ describe expect(join(project.path, config.main)).toExist(); } // Verify the compatibility_date was resolved from the locally - // installed wrangler package and not the hardcoded fallback. + // 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 @@ -86,7 +86,7 @@ describe expect(join(project.path, config.main)).toExist(); } // Verify the compatibility_date was resolved from the locally - // installed wrangler package and not the hardcoded fallback. + // 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 From c9ccae57f39dae876be7bc855d4c2f258ef2c4f8 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 24 Mar 2026 16:34:29 +0000 Subject: [PATCH 27/27] revert changeset to `minor` --- .changeset/smart-buckets-wonder.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/smart-buckets-wonder.md b/.changeset/smart-buckets-wonder.md index f2bef3c643..6e84eaf29e 100644 --- a/.changeset/smart-buckets-wonder.md +++ b/.changeset/smart-buckets-wonder.md @@ -1,5 +1,5 @@ --- -"@cloudflare/workers-utils": major +"@cloudflare/workers-utils": minor --- Remove the `getLocalWorkerdCompatibilityDate` utility from the package