From f43116682a2a08d47383be1125fd6f0b4ae3a0cc Mon Sep 17 00:00:00 2001 From: James Opstad <13586373+jamesopstad@users.noreply.github.com> Date: Tue, 18 Aug 2026 11:56:48 +0100 Subject: [PATCH 1/4] Prevent date-enabled Node.js compatibility from adding conflicting globals to generated runtime types (#15239) --- .changeset/tidy-dodos-rest.md | 8 +++++++ .../src/__tests__/runtime.test.ts | 15 ++++++++++++ packages/runtime-types/src/runtime.ts | 16 +++++++++---- packages/wrangler/e2e/types.test.ts | 23 +++++++++++++++++++ 4 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 .changeset/tidy-dodos-rest.md diff --git a/.changeset/tidy-dodos-rest.md b/.changeset/tidy-dodos-rest.md new file mode 100644 index 00000000000..d0caf67eef8 --- /dev/null +++ b/.changeset/tidy-dodos-rest.md @@ -0,0 +1,8 @@ +--- +"wrangler": patch +"@cloudflare/vite-plugin": patch +--- + +Prevent date-enabled Node.js compatibility from adding conflicting globals to generated runtime types + +Runtime type generation now treats Node.js compatibility enabled by a compatibility date the same way as an explicit `nodejs_compat` flag. Node.js globals continue to come from `@types/node` instead of being generated as `any` declarations that override those types. diff --git a/packages/runtime-types/src/__tests__/runtime.test.ts b/packages/runtime-types/src/__tests__/runtime.test.ts index 0ad2b924eb5..50498db232a 100644 --- a/packages/runtime-types/src/__tests__/runtime.test.ts +++ b/packages/runtime-types/src/__tests__/runtime.test.ts @@ -76,6 +76,21 @@ describe("generateRuntimeTypes", () => { ); }); + it("disables Node.js compatibility when it is enabled by the compatibility date", async ({ + expect, + }) => { + const result = await generateRuntimeTypes({ + compatibilityDate: "2026-08-04", + }); + + expect(dispatchFetchMock).toHaveBeenCalledWith( + "http://dummy.com/2026-08-04+no_nodejs_compat+no_nodejs_compat_v2" + ); + expect(result.runtimeHeader).toBe( + getRuntimeHeader(WORKERD_VERSION, "2026-08-04", []) + ); + }); + it("returns cached types when the header and marker match", async ({ expect, }) => { diff --git a/packages/runtime-types/src/runtime.ts b/packages/runtime-types/src/runtime.ts index dd46055c23b..c63b70660a6 100644 --- a/packages/runtime-types/src/runtime.ts +++ b/packages/runtime-types/src/runtime.ts @@ -1,5 +1,6 @@ import * as fsp from "node:fs/promises"; import { createRequire } from "node:module"; +import { isNodejsCompatDefaultOn } from "@cloudflare/workers-utils"; import { convertV4MiniflareOptions, Miniflare } from "miniflare"; import { version } from "workerd"; import { @@ -74,12 +75,19 @@ export async function generateRuntimeTypes({ } } + const runtimeCompatibilityFlags = compatibilityFlags.filter( + (flag) => !flag.includes("nodejs_compat") + ); + if (isNodejsCompatDefaultOn(compatibilityDate)) { + runtimeCompatibilityFlags.push("no_nodejs_compat", "no_nodejs_compat_v2"); + } + const types = await generate({ compatibilityDate, - // Ignore nodejs compat flags as there is currently no mechanism to generate these dynamically. - compatibilityFlags: compatibilityFlags.filter( - (flag) => !flag.includes("nodejs_compat") - ), + // Ignore Node.js compatibility as there is currently no mechanism to + // generate these types dynamically. Explicit opt-outs are needed once the + // compatibility date enables Node.js compatibility by default. + compatibilityFlags: runtimeCompatibilityFlags, }); return { runtimeHeader: header, runtimeTypes: types, isCached: false }; diff --git a/packages/wrangler/e2e/types.test.ts b/packages/wrangler/e2e/types.test.ts index cef6e7fc723..e1ac23c4592 100644 --- a/packages/wrangler/e2e/types.test.ts +++ b/packages/wrangler/e2e/types.test.ts @@ -44,6 +44,29 @@ describe("types", () => { expect(output.stdout).toContain("📖 Read about runtime types"); }); + it("should not generate Node.js globals when nodejs_compat is enabled by the compatibility date", async ({ + expect, + }) => { + const helper = new WranglerE2ETestHelper(); + await helper.seed({ + ...seed, + "wrangler.toml": dedent` + name = "test-worker" + main = "src/index.ts" + compatibility_date = "2026-08-04" + `, + }); + + await helper.run("wrangler types"); + + const file = readFileSync( + path.join(helper.tmpPath, "worker-configuration.d.ts"), + "utf8" + ); + expect(file).not.toMatch(/^declare (?:const|let|var) Buffer\b/m); + expect(file).not.toMatch(/^declare (?:const|let|var) process\b/m); + }); + it("should generate runtime types and env types in one file at the default path", async ({ expect, }) => { From 8ee43f60b279f2bd61228bb3d560d7b6e4b9856a Mon Sep 17 00:00:00 2001 From: ANT Bot <116369605+workers-devprod@users.noreply.github.com> Date: Tue, 18 Aug 2026 13:48:05 +0100 Subject: [PATCH 2/4] Version Packages (#15175) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/access-dev-vite-plugin.md | 5 - .changeset/access-dev-vitest-pool.md | 6 - ...rowser-rendering-partial-chrome-install.md | 9 -- .changeset/c3-frameworks-update-15217.md | 11 -- .changeset/c3-frameworks-update-15218.md | 11 -- .changeset/c3-frameworks-update-15219.md | 11 -- .changeset/c3-frameworks-update-15220.md | 11 -- .changeset/c3-frameworks-update-15221.md | 11 -- .changeset/c3-frameworks-update-15222.md | 11 -- .changeset/case-insensitive-env-stale-keys.md | 9 -- .changeset/config-export-container-field.md | 35 ----- .changeset/container-key-validation-crash.md | 9 -- .changeset/containers-attached-via-exports.md | 40 ------ .changeset/d1-crlf-remote-query.md | 7 - .changeset/d1-splitter-lowercase-end.md | 17 --- .changeset/dependabot-update-15231.md | 13 -- .changeset/dependabot-update-15248.md | 13 -- .../edge-preview-proxy-wshim-metrics.md | 7 - .changeset/fix-cjs-relative-require-spaces.md | 7 - .changeset/fresh-hats-test.md | 5 - .changeset/fuzzy-cats-dispose.md | 5 - .changeset/fuzzy-pears-repair.md | 5 - .changeset/lazy-donkeys-argue.md | 9 -- .changeset/msw-cloudflare-integration.md | 9 -- .changeset/nine-jars-shake.md | 7 - .../pages-project-commands-env-account-id.md | 7 - .changeset/queues-consumers-not-array.md | 7 - .changeset/quiet-access-caches.md | 5 - .changeset/restore-vitest-pool-inject-keys.md | 7 - .changeset/ripe-rice-hang.md | 6 - .changeset/small-oranges-warn.md | 5 - .changeset/tender-pears-listen.md | 12 -- .changeset/tidy-dodos-rest.md | 8 -- .changeset/tidy-falcons-comply.md | 8 -- .changeset/tidy-ravens-config.md | 7 - packages/autoconfig/CHANGELOG.md | 8 ++ packages/autoconfig/package.json | 2 +- packages/build-output-utils/CHANGELOG.md | 7 + packages/build-output-utils/package.json | 2 +- packages/cli/CHANGELOG.md | 7 + packages/cli/package.json | 2 +- packages/config/CHANGELOG.md | 36 +++++ packages/config/package.json | 2 +- packages/create-cloudflare/CHANGELOG.md | 60 +++++++++ packages/create-cloudflare/package.json | 2 +- packages/deploy-helpers/CHANGELOG.md | 9 ++ packages/deploy-helpers/package.json | 2 +- .../CHANGELOG.md | 8 ++ .../package.json | 2 +- packages/miniflare/CHANGELOG.md | 32 +++++ packages/miniflare/package.json | 2 +- packages/pages-shared/CHANGELOG.md | 7 + packages/pages-shared/package.json | 2 +- packages/remote-bindings/CHANGELOG.md | 11 ++ packages/remote-bindings/package.json | 2 +- packages/runtime-types/CHANGELOG.md | 7 + packages/runtime-types/package.json | 2 +- packages/vite-plugin-cloudflare/CHANGELOG.md | 64 +++++++++ packages/vite-plugin-cloudflare/package.json | 2 +- packages/vitest-pool-workers/CHANGELOG.md | 34 +++++ packages/vitest-pool-workers/package.json | 2 +- packages/workers-auth/CHANGELOG.md | 9 ++ packages/workers-auth/package.json | 2 +- packages/workers-utils/CHANGELOG.md | 14 ++ packages/workers-utils/package.json | 2 +- packages/wrangler/CHANGELOG.md | 123 ++++++++++++++++++ packages/wrangler/package.json | 2 +- 67 files changed, 452 insertions(+), 371 deletions(-) delete mode 100644 .changeset/access-dev-vite-plugin.md delete mode 100644 .changeset/access-dev-vitest-pool.md delete mode 100644 .changeset/browser-rendering-partial-chrome-install.md delete mode 100644 .changeset/c3-frameworks-update-15217.md delete mode 100644 .changeset/c3-frameworks-update-15218.md delete mode 100644 .changeset/c3-frameworks-update-15219.md delete mode 100644 .changeset/c3-frameworks-update-15220.md delete mode 100644 .changeset/c3-frameworks-update-15221.md delete mode 100644 .changeset/c3-frameworks-update-15222.md delete mode 100644 .changeset/case-insensitive-env-stale-keys.md delete mode 100644 .changeset/config-export-container-field.md delete mode 100644 .changeset/container-key-validation-crash.md delete mode 100644 .changeset/containers-attached-via-exports.md delete mode 100644 .changeset/d1-crlf-remote-query.md delete mode 100644 .changeset/d1-splitter-lowercase-end.md delete mode 100644 .changeset/dependabot-update-15231.md delete mode 100644 .changeset/dependabot-update-15248.md delete mode 100644 .changeset/edge-preview-proxy-wshim-metrics.md delete mode 100644 .changeset/fix-cjs-relative-require-spaces.md delete mode 100644 .changeset/fresh-hats-test.md delete mode 100644 .changeset/fuzzy-cats-dispose.md delete mode 100644 .changeset/fuzzy-pears-repair.md delete mode 100644 .changeset/lazy-donkeys-argue.md delete mode 100644 .changeset/msw-cloudflare-integration.md delete mode 100644 .changeset/nine-jars-shake.md delete mode 100644 .changeset/pages-project-commands-env-account-id.md delete mode 100644 .changeset/queues-consumers-not-array.md delete mode 100644 .changeset/quiet-access-caches.md delete mode 100644 .changeset/restore-vitest-pool-inject-keys.md delete mode 100644 .changeset/ripe-rice-hang.md delete mode 100644 .changeset/small-oranges-warn.md delete mode 100644 .changeset/tender-pears-listen.md delete mode 100644 .changeset/tidy-dodos-rest.md delete mode 100644 .changeset/tidy-falcons-comply.md delete mode 100644 .changeset/tidy-ravens-config.md diff --git a/.changeset/access-dev-vite-plugin.md b/.changeset/access-dev-vite-plugin.md deleted file mode 100644 index 812b295b651..00000000000 --- a/.changeset/access-dev-vite-plugin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@cloudflare/vite-plugin": patch ---- - -Honor `access.dev` when running Workers with `@cloudflare/vite-plugin`, so `ctx.access.getIdentity()` returns the configured identity. diff --git a/.changeset/access-dev-vitest-pool.md b/.changeset/access-dev-vitest-pool.md deleted file mode 100644 index a2b897207e1..00000000000 --- a/.changeset/access-dev-vitest-pool.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"wrangler": patch -"@cloudflare/vitest-pool-workers": patch ---- - -Honor `access.dev` when running Workers with `@cloudflare/vitest-pool-workers`, so `ctx.access.getIdentity()` returns the configured identity just as it does with `wrangler dev`. diff --git a/.changeset/browser-rendering-partial-chrome-install.md b/.changeset/browser-rendering-partial-chrome-install.md deleted file mode 100644 index a9bf7b9ec86..00000000000 --- a/.changeset/browser-rendering-partial-chrome-install.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"miniflare": patch ---- - -Recover automatically from a partially downloaded Chrome install for the Browser Run binding - -If the Chrome download for a `browser` binding was interrupted — a cancelled dev session, a killed test run, a machine going to sleep — the next launch could fail indefinitely with `Failed to launch the browser process!`, usually alongside a message about being unable to load `resources.pak`. `@puppeteer/browsers` treats an install as present as soon as the executable exists, and the Chrome archives extract alphabetically, so the executable is written long before the resources it needs. Every subsequent launch then reused the half-written directory, and the only way out was deleting the Chrome cache by hand. - -Miniflare now detects this: an install that Chrome has never successfully started from is cleared and re-downloaded on a failed launch, rather than reused forever. Overlapping launches also share a single download instead of racing to populate the same directory. diff --git a/.changeset/c3-frameworks-update-15217.md b/.changeset/c3-frameworks-update-15217.md deleted file mode 100644 index f754195103c..00000000000 --- a/.changeset/c3-frameworks-update-15217.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"create-cloudflare": patch ---- - -Update dependencies of "create-cloudflare" - -The following dependency versions have been updated: - -| Dependency | From | To | -| --------------- | ------ | ------ | -| @angular/create | 22.1.3 | 22.1.4 | diff --git a/.changeset/c3-frameworks-update-15218.md b/.changeset/c3-frameworks-update-15218.md deleted file mode 100644 index 2c9710c1b5b..00000000000 --- a/.changeset/c3-frameworks-update-15218.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"create-cloudflare": patch ---- - -Update dependencies of "create-cloudflare" - -The following dependency versions have been updated: - -| Dependency | From | To | -| ------------ | ----- | ------ | -| create-solid | 0.8.1 | 0.10.0 | diff --git a/.changeset/c3-frameworks-update-15219.md b/.changeset/c3-frameworks-update-15219.md deleted file mode 100644 index 3dc4abf0802..00000000000 --- a/.changeset/c3-frameworks-update-15219.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"create-cloudflare": patch ---- - -Update dependencies of "create-cloudflare" - -The following dependency versions have been updated: - -| Dependency | From | To | -| --------------- | ------ | ------ | -| create-next-app | 16.3.0 | 16.3.1 | diff --git a/.changeset/c3-frameworks-update-15220.md b/.changeset/c3-frameworks-update-15220.md deleted file mode 100644 index 9802f1d12ef..00000000000 --- a/.changeset/c3-frameworks-update-15220.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"create-cloudflare": patch ---- - -Update dependencies of "create-cloudflare" - -The following dependency versions have been updated: - -| Dependency | From | To | -| ----------------- | ------------ | ------------ | -| create-vinext-app | 1.0.0-beta.1 | 1.0.0-beta.2 | diff --git a/.changeset/c3-frameworks-update-15221.md b/.changeset/c3-frameworks-update-15221.md deleted file mode 100644 index bff890dafa1..00000000000 --- a/.changeset/c3-frameworks-update-15221.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"create-cloudflare": patch ---- - -Update dependencies of "create-cloudflare" - -The following dependency versions have been updated: - -| Dependency | From | To | -| ------------- | ----- | ----- | -| create-analog | 2.6.4 | 2.7.0 | diff --git a/.changeset/c3-frameworks-update-15222.md b/.changeset/c3-frameworks-update-15222.md deleted file mode 100644 index da6090b294d..00000000000 --- a/.changeset/c3-frameworks-update-15222.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"create-cloudflare": patch ---- - -Update dependencies of "create-cloudflare" - -The following dependency versions have been updated: - -| Dependency | From | To | -| ----------- | ------- | ------- | -| create-vike | 0.0.673 | 0.0.675 | diff --git a/.changeset/case-insensitive-env-stale-keys.md b/.changeset/case-insensitive-env-stale-keys.md deleted file mode 100644 index b20d045bed1..00000000000 --- a/.changeset/case-insensitive-env-stale-keys.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"wrangler": patch ---- - -Fix `.env` loading on Windows leaking stale, differently-cased duplicate keys - -On Windows, `wrangler` loads `.env` values through a case-insensitive `Proxy` wrapper so lookups like `env.PATH` and `env.Path` resolve to the same value, and this object is assigned directly to `process.env`. When a key was set again under a different casing (e.g. a value in `.env.local` overriding one from `.env` with different casing), the previous casing was never removed from the underlying object. `env.PATH`/`env.Path` still returned the correct, latest value, but anything that enumerates `process.env` — `Object.keys`, `for...in`, `JSON.stringify`, object spread, or a spawned subprocess inheriting the environment — would see both the stale and current key. - -Duplicate entries no longer appear, so environment variables passed to subprocesses and any code that lists the environment now see only the latest value for each variable. diff --git a/.changeset/config-export-container-field.md b/.changeset/config-export-container-field.md deleted file mode 100644 index 33a203445d6..00000000000 --- a/.changeset/config-export-container-field.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -"@cloudflare/config": minor ---- - -Add a `container` option to `exports.durableObject()` - -Live Durable Object exports can now attach a container by name, matching the new `container` field in the Wrangler configuration format: - -```typescript -import { defineWorker, exports } from "@cloudflare/config"; - -export default defineWorker({ - name: "my-worker", - compatibilityDate: "2026-07-01", - exports: { - MyContainerDO: exports.durableObject({ - storage: "sqlite", - container: "my-container", - }), - }, -}); -``` - -Containers are only supported on the SQLite storage engine, so `container` is only offered alongside `storage: "sqlite"`. Passing it with `storage: "legacy-kv"` is a type error rather than something only caught on deploy: - -```typescript -exports.durableObject({ - storage: "legacy-kv", - // Object literal may only specify known properties, - // and 'container' does not exist in type '{ storage: "legacy-kv" }' - container: "my-container", -}); -``` - -This is an experimental feature: containers themselves are not yet configurable from `cloudflare.config.ts`, so the field is only useful once they are. diff --git a/.changeset/container-key-validation-crash.md b/.changeset/container-key-validation-crash.md deleted file mode 100644 index f47673ea83e..00000000000 --- a/.changeset/container-key-validation-crash.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@cloudflare/workers-utils": patch ---- - -Report malformed container SSH keys and a non-object `containers.configuration` as config errors instead of crashing - -Previously, a `containers` entry with a malformed `authorized_keys` or `trusted_user_ca_keys` entry (a missing or non-string `public_key`, or an entry that is not an object), or a `containers.configuration` set to `null`, made Wrangler exit with a stack trace and "If you think this is a bug, please open an issue" rather than pointing at the field. - -These configurations now produce an ordinary configuration error naming the offending field and array index, such as `containers.authorized_keys[0].public_key must be a string`. A `public_key` that is not an ED25519 key is also now reported with correct grammar. diff --git a/.changeset/containers-attached-via-exports.md b/.changeset/containers-attached-via-exports.md deleted file mode 100644 index fe2bb02e6e5..00000000000 --- a/.changeset/containers-attached-via-exports.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -"wrangler": minor -"@cloudflare/vite-plugin": minor ---- - -Allow containers to be attached to a Durable Object from its `exports` entry - -A container can now be linked to its Durable Object from the export side, using a new `container` field that names an entry in the `containers` array. As a result `containers[].class_name` is now optional — a container that is referenced this way only needs a `name`: - -```jsonc -{ - "name": "my-worker", - "main": "worker.js", - "compatibility_date": "2026-07-01", - "containers": [ - { "name": "my-container", "image": "./Dockerfile", "max_instances": 1 }, - ], - "exports": { - "MyContainerDO": { - "type": "durable-object", - "storage": "sqlite", - "container": "my-container", - }, - }, -} -``` - -The existing `containers[].class_name` direction keeps working and either direction may be used, but the two must agree: a container that names its Durable Object cannot also be claimed by a different one. - -`container` is only valid on live `durable-object` exports (`created` and `expecting-transfer`) and requires `storage: "sqlite"`. Wrangler now also reports an error when: - -- a `container` reference names a container that does not exist -- two Durable Object exports claim the same container -- a container and a Durable Object export disagree about which one they are linked to -- a container ends up linked to no Durable Object at all -- two containers share a `name` -- a container's `class_name` names a Durable Object whose `storage` is `legacy-kv` -- two containers are attached to the same Durable Object - -That last case was previously accepted but could never work: workerd attaches a single container per Durable Object namespace, and in local development every container for a class builds into the same image tag, so one silently overwrote the other. If you have two containers on one `class_name`, give each its own Durable Object class. diff --git a/.changeset/d1-crlf-remote-query.md b/.changeset/d1-crlf-remote-query.md deleted file mode 100644 index 780b2a38585..00000000000 --- a/.changeset/d1-crlf-remote-query.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"wrangler": patch ---- - -Normalize structural CRLF line endings before sending D1 commands to the remote query API - -`wrangler d1 migrations apply --remote` and `wrangler d1 execute --remote --command` failed with `incomplete input: SQLITE_ERROR` when the SQL contained CRLF line endings inside a compound statement such as a `CREATE TRIGGER ... BEGIN ... END;` body. Structural line endings are now normalized to LF before the command is sent to the D1 query API, while CRLF inside quoted values and identifiers remains unchanged. diff --git a/.changeset/d1-splitter-lowercase-end.md b/.changeset/d1-splitter-lowercase-end.md deleted file mode 100644 index 7bf5bdc6434..00000000000 --- a/.changeset/d1-splitter-lowercase-end.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -"wrangler": patch ---- - -Fixes D1 SQL statements not handling lowercase `end`s correctly - -`wrangler d1 execute` and `wrangler d1 migrations apply` split a SQL file into statements before running them. A `BEGIN` or `CASE` block closed with a lowercase `end` was not recognised as closed, so every statement after it was folded into that block instead of being run on its own. SQLite accepts either case, so a file like this applied only the trigger and silently skipped the table: - -```sql -CREATE TRIGGER IF NOT EXISTS update_trigger AFTER UPDATE ON items -begin - DELETE FROM updates WHERE item_id=old.id; -end; -CREATE TABLE after_the_trigger (id TEXT PRIMARY KEY); -``` - -Files written with an uppercase `END` were unaffected. Both cases now behave the same. diff --git a/.changeset/dependabot-update-15231.md b/.changeset/dependabot-update-15231.md deleted file mode 100644 index 7c152b254fa..00000000000 --- a/.changeset/dependabot-update-15231.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"miniflare": patch -"wrangler": patch ---- - -Update dependencies of "miniflare", "wrangler" - -The following dependency versions have been updated: - -| Dependency | From | To | -| ------------------------- | ------------- | ------------- | -| @cloudflare/workers-types | ^5.20260811.1 | ^5.20260814.1 | -| workerd | 1.20260811.1 | 1.20260814.1 | diff --git a/.changeset/dependabot-update-15248.md b/.changeset/dependabot-update-15248.md deleted file mode 100644 index 5c9c312d745..00000000000 --- a/.changeset/dependabot-update-15248.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"miniflare": patch -"wrangler": patch ---- - -Update dependencies of "miniflare", "wrangler" - -The following dependency versions have been updated: - -| Dependency | From | To | -| ------------------------- | ------------- | ------------- | -| @cloudflare/workers-types | ^5.20260814.1 | ^5.20260815.1 | -| workerd | 1.20260814.1 | 1.20260815.1 | diff --git a/.changeset/edge-preview-proxy-wshim-metrics.md b/.changeset/edge-preview-proxy-wshim-metrics.md deleted file mode 100644 index c864d94de73..00000000000 --- a/.changeset/edge-preview-proxy-wshim-metrics.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@cloudflare/edge-preview-authenticated-proxy": patch ---- - -Fix missing request and error metrics for the edge preview proxy - -The proxy stopped reporting its request and error counters, so dashboards and alerts based on them had no data. Metrics reporting now works again. diff --git a/.changeset/fix-cjs-relative-require-spaces.md b/.changeset/fix-cjs-relative-require-spaces.md deleted file mode 100644 index 9069d6dfd10..00000000000 --- a/.changeset/fix-cjs-relative-require-spaces.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@cloudflare/vitest-pool-workers": patch ---- - -Fix module resolution for relative `require()` inside CJS deps when the project path contains spaces - -When a project lives under a directory with a space in its name, externalized CommonJS dependencies that use relative `require()` calls (e.g. `require("./lib/impl.js")`) would fail with "No such module" because `workerd` preserves URL encoding in the module name. Encoded module paths are now handled deterministically before CommonJS resolution without altering literal percent sequences. diff --git a/.changeset/fresh-hats-test.md b/.changeset/fresh-hats-test.md deleted file mode 100644 index 7c608ea443e..00000000000 --- a/.changeset/fresh-hats-test.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"create-cloudflare": patch ---- - -Update the Hello World templates to use the latest version of `@cloudflare/vitest-pool-workers`. diff --git a/.changeset/fuzzy-cats-dispose.md b/.changeset/fuzzy-cats-dispose.md deleted file mode 100644 index 055a5104dfb..00000000000 --- a/.changeset/fuzzy-cats-dispose.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"miniflare": patch ---- - -Prevent `workerd` from remaining running during Miniflare shutdown when browser or proxy cleanup is slow or fails. diff --git a/.changeset/fuzzy-pears-repair.md b/.changeset/fuzzy-pears-repair.md deleted file mode 100644 index abace30d820..00000000000 --- a/.changeset/fuzzy-pears-repair.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@cloudflare/vite-plugin": patch ---- - -Handle Worker-side request body cancellation without surfacing stream controller errors in local dev. diff --git a/.changeset/lazy-donkeys-argue.md b/.changeset/lazy-donkeys-argue.md deleted file mode 100644 index d4e731f31c3..00000000000 --- a/.changeset/lazy-donkeys-argue.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"wrangler": patch ---- - -Resolve `--latest` to the newest compatibility date supported by the installed runtime - -`wrangler deploy --latest` and `wrangler versions upload --latest` resolved the compatibility date to the current date, and `wrangler pages download config` did the same for projects configured to always use the latest compatibility date. Both write that date into a configuration file for subsequent commands to use, so a date that the installed `workerd` did not yet support left the project unable to run `wrangler dev`. - -These now resolve to the latest compatibility date supported by this version of Wrangler, which is the release date of the `workerd` it ships with. diff --git a/.changeset/msw-cloudflare-integration.md b/.changeset/msw-cloudflare-integration.md deleted file mode 100644 index fec8726de49..00000000000 --- a/.changeset/msw-cloudflare-integration.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@cloudflare/vitest-pool-workers": minor ---- - -Mocking requests with MSW in Worker tests now requires MSW >= 2.14 - -`@cloudflare/vitest-pool-workers` previously shipped internal shims to make MSW work inside the workerd runtime. MSW 2.14 added that support natively, so those shims have been removed. - -If you mock requests with MSW in your Worker tests, make sure you're on MSW `>= 2.14`; older versions will no longer intercept requests. You can keep using `setupServer()` from `msw/node`, or adopt the official [`@msw/cloudflare`](https://github.com/mswjs/cloudflare) integration via `setupNetwork()`. See the updated [`request-mocking` example fixture](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/request-mocking) for the recommended pattern. diff --git a/.changeset/nine-jars-shake.md b/.changeset/nine-jars-shake.md deleted file mode 100644 index e3654d9dca0..00000000000 --- a/.changeset/nine-jars-shake.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"wrangler": patch ---- - -Fix spurious `Trailing comma jsonc(519)` warnings for `wrangler.jsonc` in VS Code 1.131+ - -Trailing commas in `wrangler.jsonc` files that reference Wrangler's JSON schema are no longer reported as errors by recent versions of VS Code. Wrangler always accepted these files; only the editor warning was wrong. diff --git a/.changeset/pages-project-commands-env-account-id.md b/.changeset/pages-project-commands-env-account-id.md deleted file mode 100644 index 6541617637a..00000000000 --- a/.changeset/pages-project-commands-env-account-id.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"wrangler": patch ---- - -Respect `CLOUDFLARE_ACCOUNT_ID` in `wrangler pages project list`, `create` and `delete` - -These three commands could target a previously used account even when `CLOUDFLARE_ACCOUNT_ID` was set, failing with `Authentication error [code: 10000]` in setups with more than one account. They now use the account named by `CLOUDFLARE_ACCOUNT_ID`, matching the rest of `wrangler pages`. When the variable is unset, the previously used account is still selected, as before. diff --git a/.changeset/queues-consumers-not-array.md b/.changeset/queues-consumers-not-array.md deleted file mode 100644 index 35ba9a67268..00000000000 --- a/.changeset/queues-consumers-not-array.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@cloudflare/workers-utils": patch ---- - -Report an invalid `queues.consumers` value as a configuration error instead of crashing - -Previously, setting `queues.consumers` to something other than an array (for example `null` or a string) could crash Wrangler or produce a flood of confusing extra errors. You now get a single clear message telling you the field must be an array. diff --git a/.changeset/quiet-access-caches.md b/.changeset/quiet-access-caches.md deleted file mode 100644 index 799af465faa..00000000000 --- a/.changeset/quiet-access-caches.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@cloudflare/workers-auth": patch ---- - -Use the current Cloudflare Access service-token credentials after environment variables change. Interactive Access cookie caching is unchanged. diff --git a/.changeset/restore-vitest-pool-inject-keys.md b/.changeset/restore-vitest-pool-inject-keys.md deleted file mode 100644 index 79c0060153f..00000000000 --- a/.changeset/restore-vitest-pool-inject-keys.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@cloudflare/vitest-pool-workers": patch ---- - -Restore typed `inject()` keys in `cloudflareTest()` pool options - -`inject()` inside `cloudflareTest()` options again infers the value type from the keys you declare in your Vitest `ProvidedContext`, and reports misspelled keys. For keys that are only provided at runtime, pass an explicit type argument, e.g. `inject("myPort")`. diff --git a/.changeset/ripe-rice-hang.md b/.changeset/ripe-rice-hang.md deleted file mode 100644 index f9f580e8d2c..00000000000 --- a/.changeset/ripe-rice-hang.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"miniflare": patch -"@cloudflare/vitest-pool-workers": patch ---- - -Bump `capnp-es` to 0.0.16. diff --git a/.changeset/small-oranges-warn.md b/.changeset/small-oranges-warn.md deleted file mode 100644 index 7545af18a2e..00000000000 --- a/.changeset/small-oranges-warn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"wrangler": patch ---- - -Fix `wrangler preview base-config` commands showing an inherited `script` positional diff --git a/.changeset/tender-pears-listen.md b/.changeset/tender-pears-listen.md deleted file mode 100644 index 5fe39a67bf1..00000000000 --- a/.changeset/tender-pears-listen.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"wrangler": patch -"create-cloudflare": patch -"@cloudflare/vite-plugin": patch -"@cloudflare/vitest-pool-workers": patch ---- - -Use a fixed default compatibility date rather than the current date - -When no compatibility date was set, Wrangler, C3 and the Vitest pool all defaulted to the current date. `workerd` only accepts a compatibility date up to 7 days beyond its own release, so whenever a `workerd` release was delayed the default could get ahead of the runtime that had been installed, and local development would fail to start. - -The default is now fixed at the release date of the `workerd` version that ships with each release, which leaves a week of headroom and updates as `workerd` is upgraded. `@cloudflare/vite-plugin` previously inlined the date at which it was built. It now shares the same default. diff --git a/.changeset/tidy-dodos-rest.md b/.changeset/tidy-dodos-rest.md deleted file mode 100644 index d0caf67eef8..00000000000 --- a/.changeset/tidy-dodos-rest.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"wrangler": patch -"@cloudflare/vite-plugin": patch ---- - -Prevent date-enabled Node.js compatibility from adding conflicting globals to generated runtime types - -Runtime type generation now treats Node.js compatibility enabled by a compatibility date the same way as an explicit `nodejs_compat` flag. Node.js globals continue to come from `@types/node` instead of being generated as `any` declarations that override those types. diff --git a/.changeset/tidy-falcons-comply.md b/.changeset/tidy-falcons-comply.md deleted file mode 100644 index b97efae20c9..00000000000 --- a/.changeset/tidy-falcons-comply.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@cloudflare/vite-plugin": patch -"wrangler": patch ---- - -Use the FedRAMP High managed container registry when Wrangler targets the FedRAMP High compliance region - -Container builds, pushes, deployments, image commands, and local development now select the corresponding production or staging FedRAMP registry and API from either `compliance_region` or `CLOUDFLARE_COMPLIANCE_REGION`. diff --git a/.changeset/tidy-ravens-config.md b/.changeset/tidy-ravens-config.md deleted file mode 100644 index fe23b6d8325..00000000000 --- a/.changeset/tidy-ravens-config.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"wrangler": patch ---- - -Enable the new configuration format in the `cf-wrangler` dev delegate - -Projects started through `cf dev` now load `cloudflare.config.ts` and optional `wrangler.config.ts`, matching the configuration used by the delegate's build path. diff --git a/packages/autoconfig/CHANGELOG.md b/packages/autoconfig/CHANGELOG.md index 9e7ff399fb3..287512528ee 100644 --- a/packages/autoconfig/CHANGELOG.md +++ b/packages/autoconfig/CHANGELOG.md @@ -1,5 +1,13 @@ # @cloudflare/autoconfig +## 0.2.8 + +### Patch Changes + +- Updated dependencies [[`fb6b51b`](https://github.com/cloudflare/workers-sdk/commit/fb6b51b87bf73edca9866bdf2d0810d7bf491108), [`1b73c87`](https://github.com/cloudflare/workers-sdk/commit/1b73c879c168dcc78b0f2657d04bc784b8af7da3)]: + - @cloudflare/workers-utils@0.33.1 + - @cloudflare/cli-shared-helpers@0.1.24 + ## 0.2.7 ### Patch Changes diff --git a/packages/autoconfig/package.json b/packages/autoconfig/package.json index c075463dce6..f3a41e1bf81 100644 --- a/packages/autoconfig/package.json +++ b/packages/autoconfig/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflare/autoconfig", - "version": "0.2.7", + "version": "0.2.8", "description": "Framework autoconfig detection and configuration for Cloudflare Workers", "license": "MIT OR Apache-2.0", "repository": { diff --git a/packages/build-output-utils/CHANGELOG.md b/packages/build-output-utils/CHANGELOG.md index cad9997330c..95ff84a5647 100644 --- a/packages/build-output-utils/CHANGELOG.md +++ b/packages/build-output-utils/CHANGELOG.md @@ -1,5 +1,12 @@ # @cloudflare/build-output-utils +## 0.1.2 + +### Patch Changes + +- Updated dependencies [[`6529f0c`](https://github.com/cloudflare/workers-sdk/commit/6529f0ca5ecda93f67efbaa72a7f9a9f8fd814bf)]: + - @cloudflare/config@0.6.0 + ## 0.1.1 ### Patch Changes diff --git a/packages/build-output-utils/package.json b/packages/build-output-utils/package.json index 3c965986229..e181b9f6e5a 100644 --- a/packages/build-output-utils/package.json +++ b/packages/build-output-utils/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflare/build-output-utils", - "version": "0.1.1", + "version": "0.1.2", "description": "Utilities for reading and writing the Build Output Specification directory. This is not yet stable enough for external use — APIs may change without notice.", "homepage": "https://github.com/cloudflare/workers-sdk/tree/main/packages/build-output-utils#readme", "bugs": { diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 6883874a4f2..3adf856c259 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,12 @@ # @cloudflare/cli +## 0.1.24 + +### Patch Changes + +- Updated dependencies [[`fb6b51b`](https://github.com/cloudflare/workers-sdk/commit/fb6b51b87bf73edca9866bdf2d0810d7bf491108), [`1b73c87`](https://github.com/cloudflare/workers-sdk/commit/1b73c879c168dcc78b0f2657d04bc784b8af7da3)]: + - @cloudflare/workers-utils@0.33.1 + ## 0.1.23 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 0e0da643e7f..55f2ff21a75 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflare/cli-shared-helpers", - "version": "0.1.23", + "version": "0.1.24", "description": "Internal shared CLI helpers for workers-sdk. Not intended for external use — APIs may change without notice.", "keywords": [ "cli", diff --git a/packages/config/CHANGELOG.md b/packages/config/CHANGELOG.md index c48f8aac70c..572458dd208 100644 --- a/packages/config/CHANGELOG.md +++ b/packages/config/CHANGELOG.md @@ -1,5 +1,41 @@ # @cloudflare/config +## 0.6.0 + +### Minor Changes + +- [#15026](https://github.com/cloudflare/workers-sdk/pull/15026) [`6529f0c`](https://github.com/cloudflare/workers-sdk/commit/6529f0ca5ecda93f67efbaa72a7f9a9f8fd814bf) Thanks [@petebacondarwin](https://github.com/petebacondarwin)! - Add a `container` option to `exports.durableObject()` + + Live Durable Object exports can now attach a container by name, matching the new `container` field in the Wrangler configuration format: + + ```typescript + import { defineWorker, exports } from "@cloudflare/config"; + + export default defineWorker({ + name: "my-worker", + compatibilityDate: "2026-07-01", + exports: { + MyContainerDO: exports.durableObject({ + storage: "sqlite", + container: "my-container", + }), + }, + }); + ``` + + Containers are only supported on the SQLite storage engine, so `container` is only offered alongside `storage: "sqlite"`. Passing it with `storage: "legacy-kv"` is a type error rather than something only caught on deploy: + + ```typescript + exports.durableObject({ + storage: "legacy-kv", + // Object literal may only specify known properties, + // and 'container' does not exist in type '{ storage: "legacy-kv" }' + container: "my-container", + }); + ``` + + This is an experimental feature: containers themselves are not yet configurable from `cloudflare.config.ts`, so the field is only useful once they are. + ## 0.5.0 ### Minor Changes diff --git a/packages/config/package.json b/packages/config/package.json index 6dfecb2b591..45066252e92 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflare/config", - "version": "0.5.0", + "version": "0.6.0", "description": "Definitions and utilities for configuring Cloudflare Workers and related products. This is not yet stable enough for external use — APIs may change without notice.", "homepage": "https://github.com/cloudflare/workers-sdk/tree/main/packages/config#readme", "bugs": { diff --git a/packages/create-cloudflare/CHANGELOG.md b/packages/create-cloudflare/CHANGELOG.md index 414d6d3d281..833f76330f8 100644 --- a/packages/create-cloudflare/CHANGELOG.md +++ b/packages/create-cloudflare/CHANGELOG.md @@ -1,5 +1,65 @@ # create-cloudflare +## 2.72.1 + +### Patch Changes + +- [#15217](https://github.com/cloudflare/workers-sdk/pull/15217) [`1447950`](https://github.com/cloudflare/workers-sdk/commit/1447950241d4d3a1a28c9fe2ed85bea42023b202) Thanks [@dependabot](https://github.com/apps/dependabot)! - Update dependencies of "create-cloudflare" + + The following dependency versions have been updated: + + | Dependency | From | To | + | --------------- | ------ | ------ | + | @angular/create | 22.1.3 | 22.1.4 | + +- [#15218](https://github.com/cloudflare/workers-sdk/pull/15218) [`6d71eeb`](https://github.com/cloudflare/workers-sdk/commit/6d71eebfd07bc67dba4259b041bac73a13e8ccfe) Thanks [@dependabot](https://github.com/apps/dependabot)! - Update dependencies of "create-cloudflare" + + The following dependency versions have been updated: + + | Dependency | From | To | + | ------------ | ----- | ------ | + | create-solid | 0.8.1 | 0.10.0 | + +- [#15219](https://github.com/cloudflare/workers-sdk/pull/15219) [`ee15a07`](https://github.com/cloudflare/workers-sdk/commit/ee15a078c227951952ecbd2e70a1abfa7221c7f9) Thanks [@dependabot](https://github.com/apps/dependabot)! - Update dependencies of "create-cloudflare" + + The following dependency versions have been updated: + + | Dependency | From | To | + | --------------- | ------ | ------ | + | create-next-app | 16.3.0 | 16.3.1 | + +- [#15220](https://github.com/cloudflare/workers-sdk/pull/15220) [`1282749`](https://github.com/cloudflare/workers-sdk/commit/128274970c3c542b941b512980a2b2fc3af4136a) Thanks [@dependabot](https://github.com/apps/dependabot)! - Update dependencies of "create-cloudflare" + + The following dependency versions have been updated: + + | Dependency | From | To | + | ----------------- | ------------ | ------------ | + | create-vinext-app | 1.0.0-beta.1 | 1.0.0-beta.2 | + +- [#15221](https://github.com/cloudflare/workers-sdk/pull/15221) [`7af190a`](https://github.com/cloudflare/workers-sdk/commit/7af190a5d90113235978ef30e7da064a9aa13996) Thanks [@dependabot](https://github.com/apps/dependabot)! - Update dependencies of "create-cloudflare" + + The following dependency versions have been updated: + + | Dependency | From | To | + | ------------- | ----- | ----- | + | create-analog | 2.6.4 | 2.7.0 | + +- [#15222](https://github.com/cloudflare/workers-sdk/pull/15222) [`afe5788`](https://github.com/cloudflare/workers-sdk/commit/afe57886c67cff8163d6b5ffdba6e40e73754b5a) Thanks [@dependabot](https://github.com/apps/dependabot)! - Update dependencies of "create-cloudflare" + + The following dependency versions have been updated: + + | Dependency | From | To | + | ----------- | ------- | ------- | + | create-vike | 0.0.673 | 0.0.675 | + +- [#14953](https://github.com/cloudflare/workers-sdk/pull/14953) [`05959fb`](https://github.com/cloudflare/workers-sdk/commit/05959fb980f24ef8bea9d40fbef49ebc2631bf2a) Thanks [@edmundhung](https://github.com/edmundhung)! - Update the Hello World templates to use the latest version of `@cloudflare/vitest-pool-workers`. + +- [#15185](https://github.com/cloudflare/workers-sdk/pull/15185) [`1f79ace`](https://github.com/cloudflare/workers-sdk/commit/1f79ace67a81633e34dae47a666468fcdaf93f41) Thanks [@jamesopstad](https://github.com/jamesopstad)! - Use a fixed default compatibility date rather than the current date + + When no compatibility date was set, Wrangler, C3 and the Vitest pool all defaulted to the current date. `workerd` only accepts a compatibility date up to 7 days beyond its own release, so whenever a `workerd` release was delayed the default could get ahead of the runtime that had been installed, and local development would fail to start. + + The default is now fixed at the release date of the `workerd` version that ships with each release, which leaves a week of headroom and updates as `workerd` is upgraded. `@cloudflare/vite-plugin` previously inlined the date at which it was built. It now shares the same default. + ## 2.72.0 ### Minor Changes diff --git a/packages/create-cloudflare/package.json b/packages/create-cloudflare/package.json index 561672fcd4e..3ffbbe74060 100644 --- a/packages/create-cloudflare/package.json +++ b/packages/create-cloudflare/package.json @@ -1,6 +1,6 @@ { "name": "create-cloudflare", - "version": "2.72.0", + "version": "2.72.1", "description": "A CLI for creating and deploying new applications to Cloudflare.", "keywords": [ "cloudflare", diff --git a/packages/deploy-helpers/CHANGELOG.md b/packages/deploy-helpers/CHANGELOG.md index ff1f72b4f4b..94fdbb59041 100644 --- a/packages/deploy-helpers/CHANGELOG.md +++ b/packages/deploy-helpers/CHANGELOG.md @@ -1,5 +1,14 @@ # @cloudflare/deploy-helpers +## 0.7.1 + +### Patch Changes + +- Updated dependencies [[`1277a72`](https://github.com/cloudflare/workers-sdk/commit/1277a72e0d01325c37a05c0e8f5111a45100af77), [`fb6b51b`](https://github.com/cloudflare/workers-sdk/commit/fb6b51b87bf73edca9866bdf2d0810d7bf491108), [`4f922dc`](https://github.com/cloudflare/workers-sdk/commit/4f922dc19941db31394357f7e146af320ae1f3d9), [`4d74b8d`](https://github.com/cloudflare/workers-sdk/commit/4d74b8d8fd5c034c012fa13973ee20bedbc844c7), [`2e0c962`](https://github.com/cloudflare/workers-sdk/commit/2e0c962da0c57bdc79b5edcaa64c7b725c1524f0), [`1b73c87`](https://github.com/cloudflare/workers-sdk/commit/1b73c879c168dcc78b0f2657d04bc784b8af7da3), [`8777180`](https://github.com/cloudflare/workers-sdk/commit/8777180b8239d9df435acee465d02682477e93ea)]: + - miniflare@5.20260815.0-alpha + - @cloudflare/workers-utils@0.33.1 + - @cloudflare/cli-shared-helpers@0.1.24 + ## 0.7.0 ### Minor Changes diff --git a/packages/deploy-helpers/package.json b/packages/deploy-helpers/package.json index cb118d52207..39a6023e2a9 100644 --- a/packages/deploy-helpers/package.json +++ b/packages/deploy-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflare/deploy-helpers", - "version": "0.7.0", + "version": "0.7.1", "description": "Internal deploy helpers for workers-sdk. Not intended for external use — APIs may change without notice.", "homepage": "https://github.com/cloudflare/workers-sdk/tree/main/packages/deploy-helpers#readme", "bugs": { diff --git a/packages/edge-preview-authenticated-proxy/CHANGELOG.md b/packages/edge-preview-authenticated-proxy/CHANGELOG.md index f1e78696fad..94e580d0487 100644 --- a/packages/edge-preview-authenticated-proxy/CHANGELOG.md +++ b/packages/edge-preview-authenticated-proxy/CHANGELOG.md @@ -1,5 +1,13 @@ # edge-preview-authenticated-proxy +## 0.3.3 + +### Patch Changes + +- [#15224](https://github.com/cloudflare/workers-sdk/pull/15224) [`32782e4`](https://github.com/cloudflare/workers-sdk/commit/32782e45b4a49762d702df4e4fa1cad620430962) Thanks [@petebacondarwin](https://github.com/petebacondarwin)! - Fix missing request and error metrics for the edge preview proxy + + The proxy stopped reporting its request and error counters, so dashboards and alerts based on them had no data. Metrics reporting now works again. + ## 0.3.2 ### Patch Changes diff --git a/packages/edge-preview-authenticated-proxy/package.json b/packages/edge-preview-authenticated-proxy/package.json index e18cbb413a5..5d6ca7c9e23 100644 --- a/packages/edge-preview-authenticated-proxy/package.json +++ b/packages/edge-preview-authenticated-proxy/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflare/edge-preview-authenticated-proxy", - "version": "0.3.2", + "version": "0.3.3", "private": true, "scripts": { "check:type": "tsc", diff --git a/packages/miniflare/CHANGELOG.md b/packages/miniflare/CHANGELOG.md index cfd0c1c5447..265cfae1aa9 100644 --- a/packages/miniflare/CHANGELOG.md +++ b/packages/miniflare/CHANGELOG.md @@ -1,5 +1,37 @@ # miniflare +## 5.20260815.0-alpha + +### Patch Changes + +- [#15206](https://github.com/cloudflare/workers-sdk/pull/15206) [`1277a72`](https://github.com/cloudflare/workers-sdk/commit/1277a72e0d01325c37a05c0e8f5111a45100af77) Thanks [@petebacondarwin](https://github.com/petebacondarwin)! - Recover automatically from a partially downloaded Chrome install for the Browser Run binding + + If the Chrome download for a `browser` binding was interrupted — a cancelled dev session, a killed test run, a machine going to sleep — the next launch could fail indefinitely with `Failed to launch the browser process!`, usually alongside a message about being unable to load `resources.pak`. `@puppeteer/browsers` treats an install as present as soon as the executable exists, and the Chrome archives extract alphabetically, so the executable is written long before the resources it needs. Every subsequent launch then reused the half-written directory, and the only way out was deleting the Chrome cache by hand. + + Miniflare now detects this: an install that Chrome has never successfully started from is cleared and re-downloaded on a failed launch, rather than reused forever. Overlapping launches also share a single download instead of racing to populate the same directory. + +- [#15231](https://github.com/cloudflare/workers-sdk/pull/15231) [`4f922dc`](https://github.com/cloudflare/workers-sdk/commit/4f922dc19941db31394357f7e146af320ae1f3d9) Thanks [@dependabot](https://github.com/apps/dependabot)! - Update dependencies of "miniflare", "wrangler" + + The following dependency versions have been updated: + + | Dependency | From | To | + | ------------------------- | ------------- | ------------- | + | @cloudflare/workers-types | ^5.20260811.1 | ^5.20260814.1 | + | workerd | 1.20260811.1 | 1.20260814.1 | + +- [#15248](https://github.com/cloudflare/workers-sdk/pull/15248) [`4d74b8d`](https://github.com/cloudflare/workers-sdk/commit/4d74b8d8fd5c034c012fa13973ee20bedbc844c7) Thanks [@dependabot](https://github.com/apps/dependabot)! - Update dependencies of "miniflare", "wrangler" + + The following dependency versions have been updated: + + | Dependency | From | To | + | ------------------------- | ------------- | ------------- | + | @cloudflare/workers-types | ^5.20260814.1 | ^5.20260815.1 | + | workerd | 1.20260814.1 | 1.20260815.1 | + +- [#15143](https://github.com/cloudflare/workers-sdk/pull/15143) [`2e0c962`](https://github.com/cloudflare/workers-sdk/commit/2e0c962da0c57bdc79b5edcaa64c7b725c1524f0) Thanks [@teamleaderleo](https://github.com/teamleaderleo)! - Prevent `workerd` from remaining running during Miniflare shutdown when browser or proxy cleanup is slow or fails. + +- [#15232](https://github.com/cloudflare/workers-sdk/pull/15232) [`8777180`](https://github.com/cloudflare/workers-sdk/commit/8777180b8239d9df435acee465d02682477e93ea) Thanks [@vicb](https://github.com/vicb)! - Bump `capnp-es` to 0.0.16. + ## 5.20260811.1-alpha ### Minor Changes diff --git a/packages/miniflare/package.json b/packages/miniflare/package.json index fd470ef9f26..1319edf67ec 100644 --- a/packages/miniflare/package.json +++ b/packages/miniflare/package.json @@ -1,6 +1,6 @@ { "name": "miniflare", - "version": "5.20260811.1-alpha", + "version": "5.20260815.0-alpha", "description": "Fun, full-featured, fully-local simulator for Cloudflare Workers", "keywords": [ "cloudflare", diff --git a/packages/pages-shared/CHANGELOG.md b/packages/pages-shared/CHANGELOG.md index 48ba95d3353..f515d0624b5 100644 --- a/packages/pages-shared/CHANGELOG.md +++ b/packages/pages-shared/CHANGELOG.md @@ -1,5 +1,12 @@ # @cloudflare/pages-shared +## 0.13.170 + +### Patch Changes + +- Updated dependencies [[`1277a72`](https://github.com/cloudflare/workers-sdk/commit/1277a72e0d01325c37a05c0e8f5111a45100af77), [`4f922dc`](https://github.com/cloudflare/workers-sdk/commit/4f922dc19941db31394357f7e146af320ae1f3d9), [`4d74b8d`](https://github.com/cloudflare/workers-sdk/commit/4d74b8d8fd5c034c012fa13973ee20bedbc844c7), [`2e0c962`](https://github.com/cloudflare/workers-sdk/commit/2e0c962da0c57bdc79b5edcaa64c7b725c1524f0), [`8777180`](https://github.com/cloudflare/workers-sdk/commit/8777180b8239d9df435acee465d02682477e93ea)]: + - miniflare@5.20260815.0-alpha + ## 0.13.169 ### Patch Changes diff --git a/packages/pages-shared/package.json b/packages/pages-shared/package.json index 1c27845e3d3..8efc0f844cf 100644 --- a/packages/pages-shared/package.json +++ b/packages/pages-shared/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflare/pages-shared", - "version": "0.13.169", + "version": "0.13.170", "repository": { "type": "git", "url": "https://github.com/cloudflare/workers-sdk.git", diff --git a/packages/remote-bindings/CHANGELOG.md b/packages/remote-bindings/CHANGELOG.md index d4923801290..625ac6e7130 100644 --- a/packages/remote-bindings/CHANGELOG.md +++ b/packages/remote-bindings/CHANGELOG.md @@ -1,5 +1,16 @@ # @cloudflare/remote-bindings +## 0.0.13 + +### Patch Changes + +- Updated dependencies [[`1277a72`](https://github.com/cloudflare/workers-sdk/commit/1277a72e0d01325c37a05c0e8f5111a45100af77), [`fb6b51b`](https://github.com/cloudflare/workers-sdk/commit/fb6b51b87bf73edca9866bdf2d0810d7bf491108), [`4f922dc`](https://github.com/cloudflare/workers-sdk/commit/4f922dc19941db31394357f7e146af320ae1f3d9), [`4d74b8d`](https://github.com/cloudflare/workers-sdk/commit/4d74b8d8fd5c034c012fa13973ee20bedbc844c7), [`2e0c962`](https://github.com/cloudflare/workers-sdk/commit/2e0c962da0c57bdc79b5edcaa64c7b725c1524f0), [`1b73c87`](https://github.com/cloudflare/workers-sdk/commit/1b73c879c168dcc78b0f2657d04bc784b8af7da3), [`b6d00ed`](https://github.com/cloudflare/workers-sdk/commit/b6d00edc459a7ca2dda817d8263a33b326f083b8), [`8777180`](https://github.com/cloudflare/workers-sdk/commit/8777180b8239d9df435acee465d02682477e93ea)]: + - miniflare@5.20260815.0-alpha + - @cloudflare/workers-utils@0.33.1 + - @cloudflare/workers-auth@0.6.4 + - @cloudflare/deploy-helpers@0.7.1 + - @cloudflare/cli-shared-helpers@0.1.24 + ## 0.0.12 ### Patch Changes diff --git a/packages/remote-bindings/package.json b/packages/remote-bindings/package.json index 1e1773e90c1..cb5c588b3ff 100644 --- a/packages/remote-bindings/package.json +++ b/packages/remote-bindings/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflare/remote-bindings", - "version": "0.0.12", + "version": "0.0.13", "private": true, "homepage": "https://github.com/cloudflare/workers-sdk/tree/main/packages/remote-bindings#readme", "bugs": { diff --git a/packages/runtime-types/CHANGELOG.md b/packages/runtime-types/CHANGELOG.md index 504e0b8d2a4..495f1339843 100644 --- a/packages/runtime-types/CHANGELOG.md +++ b/packages/runtime-types/CHANGELOG.md @@ -1,5 +1,12 @@ # @cloudflare/runtime-types +## 0.0.15 + +### Patch Changes + +- Updated dependencies [[`1277a72`](https://github.com/cloudflare/workers-sdk/commit/1277a72e0d01325c37a05c0e8f5111a45100af77), [`4f922dc`](https://github.com/cloudflare/workers-sdk/commit/4f922dc19941db31394357f7e146af320ae1f3d9), [`4d74b8d`](https://github.com/cloudflare/workers-sdk/commit/4d74b8d8fd5c034c012fa13973ee20bedbc844c7), [`2e0c962`](https://github.com/cloudflare/workers-sdk/commit/2e0c962da0c57bdc79b5edcaa64c7b725c1524f0), [`8777180`](https://github.com/cloudflare/workers-sdk/commit/8777180b8239d9df435acee465d02682477e93ea)]: + - miniflare@5.20260815.0-alpha + ## 0.0.14 ### Patch Changes diff --git a/packages/runtime-types/package.json b/packages/runtime-types/package.json index cd200af43df..1067715f779 100644 --- a/packages/runtime-types/package.json +++ b/packages/runtime-types/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflare/runtime-types", - "version": "0.0.14", + "version": "0.0.15", "private": true, "homepage": "https://github.com/cloudflare/workers-sdk/tree/main/packages/runtime-types#readme", "bugs": { diff --git a/packages/vite-plugin-cloudflare/CHANGELOG.md b/packages/vite-plugin-cloudflare/CHANGELOG.md index 715939caa39..1aced1665ba 100644 --- a/packages/vite-plugin-cloudflare/CHANGELOG.md +++ b/packages/vite-plugin-cloudflare/CHANGELOG.md @@ -1,5 +1,69 @@ # @cloudflare/vite-plugin +## 1.53.0 + +### Minor Changes + +- [#15026](https://github.com/cloudflare/workers-sdk/pull/15026) [`6529f0c`](https://github.com/cloudflare/workers-sdk/commit/6529f0ca5ecda93f67efbaa72a7f9a9f8fd814bf) Thanks [@petebacondarwin](https://github.com/petebacondarwin)! - Allow containers to be attached to a Durable Object from its `exports` entry + + A container can now be linked to its Durable Object from the export side, using a new `container` field that names an entry in the `containers` array. As a result `containers[].class_name` is now optional — a container that is referenced this way only needs a `name`: + + ```jsonc + { + "name": "my-worker", + "main": "worker.js", + "compatibility_date": "2026-07-01", + "containers": [ + { "name": "my-container", "image": "./Dockerfile", "max_instances": 1 } + ], + "exports": { + "MyContainerDO": { + "type": "durable-object", + "storage": "sqlite", + "container": "my-container" + } + } + } + ``` + + The existing `containers[].class_name` direction keeps working and either direction may be used, but the two must agree: a container that names its Durable Object cannot also be claimed by a different one. + + `container` is only valid on live `durable-object` exports (`created` and `expecting-transfer`) and requires `storage: "sqlite"`. Wrangler now also reports an error when: + + - a `container` reference names a container that does not exist + - two Durable Object exports claim the same container + - a container and a Durable Object export disagree about which one they are linked to + - a container ends up linked to no Durable Object at all + - two containers share a `name` + - a container's `class_name` names a Durable Object whose `storage` is `legacy-kv` + - two containers are attached to the same Durable Object + + That last case was previously accepted but could never work: workerd attaches a single container per Durable Object namespace, and in local development every container for a class builds into the same image tag, so one silently overwrote the other. If you have two containers on one `class_name`, give each its own Durable Object class. + +### Patch Changes + +- [#15238](https://github.com/cloudflare/workers-sdk/pull/15238) [`3a4fc6b`](https://github.com/cloudflare/workers-sdk/commit/3a4fc6b2fce883248dac4734699d8df89dc7662a) Thanks [@jamesopstad](https://github.com/jamesopstad)! - Honor `access.dev` when running Workers with `@cloudflare/vite-plugin`, so `ctx.access.getIdentity()` returns the configured identity. + +- [#15028](https://github.com/cloudflare/workers-sdk/pull/15028) [`d4f441f`](https://github.com/cloudflare/workers-sdk/commit/d4f441fae6dd823364d57b0898031890357ee933) Thanks [@harshmathurx](https://github.com/harshmathurx)! - Handle Worker-side request body cancellation without surfacing stream controller errors in local dev. + +- [#15185](https://github.com/cloudflare/workers-sdk/pull/15185) [`1f79ace`](https://github.com/cloudflare/workers-sdk/commit/1f79ace67a81633e34dae47a666468fcdaf93f41) Thanks [@jamesopstad](https://github.com/jamesopstad)! - Use a fixed default compatibility date rather than the current date + + When no compatibility date was set, Wrangler, C3 and the Vitest pool all defaulted to the current date. `workerd` only accepts a compatibility date up to 7 days beyond its own release, so whenever a `workerd` release was delayed the default could get ahead of the runtime that had been installed, and local development would fail to start. + + The default is now fixed at the release date of the `workerd` version that ships with each release, which leaves a week of headroom and updates as `workerd` is upgraded. `@cloudflare/vite-plugin` previously inlined the date at which it was built. It now shares the same default. + +- [#15239](https://github.com/cloudflare/workers-sdk/pull/15239) [`f431166`](https://github.com/cloudflare/workers-sdk/commit/f43116682a2a08d47383be1125fd6f0b4ae3a0cc) Thanks [@jamesopstad](https://github.com/jamesopstad)! - Prevent date-enabled Node.js compatibility from adding conflicting globals to generated runtime types + + Runtime type generation now treats Node.js compatibility enabled by a compatibility date the same way as an explicit `nodejs_compat` flag. Node.js globals continue to come from `@types/node` instead of being generated as `any` declarations that override those types. + +- [#15196](https://github.com/cloudflare/workers-sdk/pull/15196) [`8fb2b87`](https://github.com/cloudflare/workers-sdk/commit/8fb2b8760b9ba23d1584156d25c02ca2db8d2d80) Thanks [@skepticfx](https://github.com/skepticfx)! - Use the FedRAMP High managed container registry when Wrangler targets the FedRAMP High compliance region + + Container builds, pushes, deployments, image commands, and local development now select the corresponding production or staging FedRAMP registry and API from either `compliance_region` or `CLOUDFLARE_COMPLIANCE_REGION`. + +- Updated dependencies [[`bc5726b`](https://github.com/cloudflare/workers-sdk/commit/bc5726bd0b88856f6781c62b4cb3c9c47b84eb07), [`1277a72`](https://github.com/cloudflare/workers-sdk/commit/1277a72e0d01325c37a05c0e8f5111a45100af77), [`ba54f0d`](https://github.com/cloudflare/workers-sdk/commit/ba54f0d39d1ba7eb5545d8bf9ba43624cfa211f9), [`6529f0c`](https://github.com/cloudflare/workers-sdk/commit/6529f0ca5ecda93f67efbaa72a7f9a9f8fd814bf), [`b7422b0`](https://github.com/cloudflare/workers-sdk/commit/b7422b0a8a2e74bba068a1924992dcfeff0bd126), [`186339c`](https://github.com/cloudflare/workers-sdk/commit/186339cf854cf3522614fb686ec66e6682c569b8), [`4f922dc`](https://github.com/cloudflare/workers-sdk/commit/4f922dc19941db31394357f7e146af320ae1f3d9), [`4d74b8d`](https://github.com/cloudflare/workers-sdk/commit/4d74b8d8fd5c034c012fa13973ee20bedbc844c7), [`2e0c962`](https://github.com/cloudflare/workers-sdk/commit/2e0c962da0c57bdc79b5edcaa64c7b725c1524f0), [`1f79ace`](https://github.com/cloudflare/workers-sdk/commit/1f79ace67a81633e34dae47a666468fcdaf93f41), [`49f73de`](https://github.com/cloudflare/workers-sdk/commit/49f73de207124171b3f8e9ffb182facb48727388), [`7cee278`](https://github.com/cloudflare/workers-sdk/commit/7cee2784a937c86db6a88455c6efe8e8660ae69c), [`8777180`](https://github.com/cloudflare/workers-sdk/commit/8777180b8239d9df435acee465d02682477e93ea), [`265256a`](https://github.com/cloudflare/workers-sdk/commit/265256a5619fbf82e12473f7bb7c7944bdd368b4), [`1f79ace`](https://github.com/cloudflare/workers-sdk/commit/1f79ace67a81633e34dae47a666468fcdaf93f41), [`f431166`](https://github.com/cloudflare/workers-sdk/commit/f43116682a2a08d47383be1125fd6f0b4ae3a0cc), [`8fb2b87`](https://github.com/cloudflare/workers-sdk/commit/8fb2b8760b9ba23d1584156d25c02ca2db8d2d80), [`75cf407`](https://github.com/cloudflare/workers-sdk/commit/75cf407fc0d7fa6f4d4d9848a9221e7b16930497)]: + - wrangler@4.124.0 + - miniflare@5.20260815.0-alpha + ## 1.52.1 ### Patch Changes diff --git a/packages/vite-plugin-cloudflare/package.json b/packages/vite-plugin-cloudflare/package.json index be4ba5e114a..edfce18d51f 100644 --- a/packages/vite-plugin-cloudflare/package.json +++ b/packages/vite-plugin-cloudflare/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflare/vite-plugin", - "version": "1.52.1", + "version": "1.53.0", "description": "Cloudflare plugin for Vite", "keywords": [ "cloudflare", diff --git a/packages/vitest-pool-workers/CHANGELOG.md b/packages/vitest-pool-workers/CHANGELOG.md index 7168b237e1f..0b365e51414 100644 --- a/packages/vitest-pool-workers/CHANGELOG.md +++ b/packages/vitest-pool-workers/CHANGELOG.md @@ -1,5 +1,39 @@ # @cloudflare/vitest-pool-workers +## 0.22.0 + +### Minor Changes + +- [#13830](https://github.com/cloudflare/workers-sdk/pull/13830) [`49d4e00`](https://github.com/cloudflare/workers-sdk/commit/49d4e0096802fb4d5ff2fe7277c893065dfa8c86) Thanks [@penalosa](https://github.com/penalosa)! - Mocking requests with MSW in Worker tests now requires MSW >= 2.14 + + `@cloudflare/vitest-pool-workers` previously shipped internal shims to make MSW work inside the workerd runtime. MSW 2.14 added that support natively, so those shims have been removed. + + If you mock requests with MSW in your Worker tests, make sure you're on MSW `>= 2.14`; older versions will no longer intercept requests. You can keep using `setupServer()` from `msw/node`, or adopt the official [`@msw/cloudflare`](https://github.com/mswjs/cloudflare) integration via `setupNetwork()`. See the updated [`request-mocking` example fixture](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/request-mocking) for the recommended pattern. + +### Patch Changes + +- [#15211](https://github.com/cloudflare/workers-sdk/pull/15211) [`bc5726b`](https://github.com/cloudflare/workers-sdk/commit/bc5726bd0b88856f6781c62b4cb3c9c47b84eb07) Thanks [@nithin42](https://github.com/nithin42)! - Honor `access.dev` when running Workers with `@cloudflare/vitest-pool-workers`, so `ctx.access.getIdentity()` returns the configured identity just as it does with `wrangler dev`. + +- [#15156](https://github.com/cloudflare/workers-sdk/pull/15156) [`3ddd3ce`](https://github.com/cloudflare/workers-sdk/commit/3ddd3ce5b871d40b20e58156eadbe6ea68a2f573) Thanks [@dario-piotrowicz](https://github.com/dario-piotrowicz)! - Fix module resolution for relative `require()` inside CJS deps when the project path contains spaces + + When a project lives under a directory with a space in its name, externalized CommonJS dependencies that use relative `require()` calls (e.g. `require("./lib/impl.js")`) would fail with "No such module" because `workerd` preserves URL encoding in the module name. Encoded module paths are now handled deterministically before CommonJS resolution without altering literal percent sequences. + +- [#15150](https://github.com/cloudflare/workers-sdk/pull/15150) [`2cf3143`](https://github.com/cloudflare/workers-sdk/commit/2cf314322c046a0e11ddedda0230cd44adda29a9) Thanks [@kkkhs](https://github.com/kkkhs)! - Restore typed `inject()` keys in `cloudflareTest()` pool options + + `inject()` inside `cloudflareTest()` options again infers the value type from the keys you declare in your Vitest `ProvidedContext`, and reports misspelled keys. For keys that are only provided at runtime, pass an explicit type argument, e.g. `inject("myPort")`. + +- [#15232](https://github.com/cloudflare/workers-sdk/pull/15232) [`8777180`](https://github.com/cloudflare/workers-sdk/commit/8777180b8239d9df435acee465d02682477e93ea) Thanks [@vicb](https://github.com/vicb)! - Bump `capnp-es` to 0.0.16. + +- [#15185](https://github.com/cloudflare/workers-sdk/pull/15185) [`1f79ace`](https://github.com/cloudflare/workers-sdk/commit/1f79ace67a81633e34dae47a666468fcdaf93f41) Thanks [@jamesopstad](https://github.com/jamesopstad)! - Use a fixed default compatibility date rather than the current date + + When no compatibility date was set, Wrangler, C3 and the Vitest pool all defaulted to the current date. `workerd` only accepts a compatibility date up to 7 days beyond its own release, so whenever a `workerd` release was delayed the default could get ahead of the runtime that had been installed, and local development would fail to start. + + The default is now fixed at the release date of the `workerd` version that ships with each release, which leaves a week of headroom and updates as `workerd` is upgraded. `@cloudflare/vite-plugin` previously inlined the date at which it was built. It now shares the same default. + +- Updated dependencies [[`bc5726b`](https://github.com/cloudflare/workers-sdk/commit/bc5726bd0b88856f6781c62b4cb3c9c47b84eb07), [`1277a72`](https://github.com/cloudflare/workers-sdk/commit/1277a72e0d01325c37a05c0e8f5111a45100af77), [`ba54f0d`](https://github.com/cloudflare/workers-sdk/commit/ba54f0d39d1ba7eb5545d8bf9ba43624cfa211f9), [`6529f0c`](https://github.com/cloudflare/workers-sdk/commit/6529f0ca5ecda93f67efbaa72a7f9a9f8fd814bf), [`b7422b0`](https://github.com/cloudflare/workers-sdk/commit/b7422b0a8a2e74bba068a1924992dcfeff0bd126), [`186339c`](https://github.com/cloudflare/workers-sdk/commit/186339cf854cf3522614fb686ec66e6682c569b8), [`4f922dc`](https://github.com/cloudflare/workers-sdk/commit/4f922dc19941db31394357f7e146af320ae1f3d9), [`4d74b8d`](https://github.com/cloudflare/workers-sdk/commit/4d74b8d8fd5c034c012fa13973ee20bedbc844c7), [`2e0c962`](https://github.com/cloudflare/workers-sdk/commit/2e0c962da0c57bdc79b5edcaa64c7b725c1524f0), [`1f79ace`](https://github.com/cloudflare/workers-sdk/commit/1f79ace67a81633e34dae47a666468fcdaf93f41), [`49f73de`](https://github.com/cloudflare/workers-sdk/commit/49f73de207124171b3f8e9ffb182facb48727388), [`7cee278`](https://github.com/cloudflare/workers-sdk/commit/7cee2784a937c86db6a88455c6efe8e8660ae69c), [`8777180`](https://github.com/cloudflare/workers-sdk/commit/8777180b8239d9df435acee465d02682477e93ea), [`265256a`](https://github.com/cloudflare/workers-sdk/commit/265256a5619fbf82e12473f7bb7c7944bdd368b4), [`1f79ace`](https://github.com/cloudflare/workers-sdk/commit/1f79ace67a81633e34dae47a666468fcdaf93f41), [`f431166`](https://github.com/cloudflare/workers-sdk/commit/f43116682a2a08d47383be1125fd6f0b4ae3a0cc), [`8fb2b87`](https://github.com/cloudflare/workers-sdk/commit/8fb2b8760b9ba23d1584156d25c02ca2db8d2d80), [`75cf407`](https://github.com/cloudflare/workers-sdk/commit/75cf407fc0d7fa6f4d4d9848a9221e7b16930497)]: + - wrangler@4.124.0 + - miniflare@5.20260815.0-alpha + ## 0.21.3 ### Patch Changes diff --git a/packages/vitest-pool-workers/package.json b/packages/vitest-pool-workers/package.json index b8f02907837..eda96833905 100644 --- a/packages/vitest-pool-workers/package.json +++ b/packages/vitest-pool-workers/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflare/vitest-pool-workers", - "version": "0.21.3", + "version": "0.22.0", "description": "Workers Vitest integration for writing Vitest unit and integration tests that run inside the Workers runtime", "keywords": [ "cloudflare", diff --git a/packages/workers-auth/CHANGELOG.md b/packages/workers-auth/CHANGELOG.md index 00d752bb97f..57cbb817e88 100644 --- a/packages/workers-auth/CHANGELOG.md +++ b/packages/workers-auth/CHANGELOG.md @@ -1,5 +1,14 @@ # @cloudflare/workers-auth +## 0.6.4 + +### Patch Changes + +- [#15080](https://github.com/cloudflare/workers-sdk/pull/15080) [`b6d00ed`](https://github.com/cloudflare/workers-sdk/commit/b6d00edc459a7ca2dda817d8263a33b326f083b8) Thanks [@teamleaderleo](https://github.com/teamleaderleo)! - Use the current Cloudflare Access service-token credentials after environment variables change. Interactive Access cookie caching is unchanged. + +- Updated dependencies [[`fb6b51b`](https://github.com/cloudflare/workers-sdk/commit/fb6b51b87bf73edca9866bdf2d0810d7bf491108), [`1b73c87`](https://github.com/cloudflare/workers-sdk/commit/1b73c879c168dcc78b0f2657d04bc784b8af7da3)]: + - @cloudflare/workers-utils@0.33.1 + ## 0.6.3 ### Patch Changes diff --git a/packages/workers-auth/package.json b/packages/workers-auth/package.json index 7d5bc9e517d..502b7a47bb2 100644 --- a/packages/workers-auth/package.json +++ b/packages/workers-auth/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflare/workers-auth", - "version": "0.6.3", + "version": "0.6.4", "description": "Internal OAuth 2.0 + PKCE flow for Cloudflare CLIs. Not intended for external use — APIs may change without notice.", "homepage": "https://github.com/cloudflare/workers-sdk/tree/main/packages/workers-auth#readme", "bugs": { diff --git a/packages/workers-utils/CHANGELOG.md b/packages/workers-utils/CHANGELOG.md index 573c8259528..e2f05d76ed1 100644 --- a/packages/workers-utils/CHANGELOG.md +++ b/packages/workers-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @cloudflare/workers-utils +## 0.33.1 + +### Patch Changes + +- [#15088](https://github.com/cloudflare/workers-sdk/pull/15088) [`fb6b51b`](https://github.com/cloudflare/workers-sdk/commit/fb6b51b87bf73edca9866bdf2d0810d7bf491108) Thanks [@Neal006](https://github.com/Neal006)! - Report malformed container SSH keys and a non-object `containers.configuration` as config errors instead of crashing + + Previously, a `containers` entry with a malformed `authorized_keys` or `trusted_user_ca_keys` entry (a missing or non-string `public_key`, or an entry that is not an object), or a `containers.configuration` set to `null`, made Wrangler exit with a stack trace and "If you think this is a bug, please open an issue" rather than pointing at the field. + + These configurations now produce an ordinary configuration error naming the offending field and array index, such as `containers.authorized_keys[0].public_key must be a string`. A `public_key` that is not an ED25519 key is also now reported with correct grammar. + +- [#15010](https://github.com/cloudflare/workers-sdk/pull/15010) [`1b73c87`](https://github.com/cloudflare/workers-sdk/commit/1b73c879c168dcc78b0f2657d04bc784b8af7da3) Thanks [@LeSingh1](https://github.com/LeSingh1)! - Report an invalid `queues.consumers` value as a configuration error instead of crashing + + Previously, setting `queues.consumers` to something other than an array (for example `null` or a string) could crash Wrangler or produce a flood of confusing extra errors. You now get a single clear message telling you the field must be an array. + ## 0.33.0 ### Minor Changes diff --git a/packages/workers-utils/package.json b/packages/workers-utils/package.json index d9481edee7a..7373d06a2e8 100644 --- a/packages/workers-utils/package.json +++ b/packages/workers-utils/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflare/workers-utils", - "version": "0.33.0", + "version": "0.33.1", "description": "Internal utility package for workers-sdk. Not intended for external use — APIs may change without notice.", "homepage": "https://github.com/cloudflare/workers-sdk/tree/main/packages/workers-utils#readme", "bugs": { diff --git a/packages/wrangler/CHANGELOG.md b/packages/wrangler/CHANGELOG.md index 44943575f0c..af3581d47e7 100644 --- a/packages/wrangler/CHANGELOG.md +++ b/packages/wrangler/CHANGELOG.md @@ -1,5 +1,128 @@ # wrangler +## 4.124.0 + +### Minor Changes + +- [#15026](https://github.com/cloudflare/workers-sdk/pull/15026) [`6529f0c`](https://github.com/cloudflare/workers-sdk/commit/6529f0ca5ecda93f67efbaa72a7f9a9f8fd814bf) Thanks [@petebacondarwin](https://github.com/petebacondarwin)! - Allow containers to be attached to a Durable Object from its `exports` entry + + A container can now be linked to its Durable Object from the export side, using a new `container` field that names an entry in the `containers` array. As a result `containers[].class_name` is now optional — a container that is referenced this way only needs a `name`: + + ```jsonc + { + "name": "my-worker", + "main": "worker.js", + "compatibility_date": "2026-07-01", + "containers": [ + { "name": "my-container", "image": "./Dockerfile", "max_instances": 1 } + ], + "exports": { + "MyContainerDO": { + "type": "durable-object", + "storage": "sqlite", + "container": "my-container" + } + } + } + ``` + + The existing `containers[].class_name` direction keeps working and either direction may be used, but the two must agree: a container that names its Durable Object cannot also be claimed by a different one. + + `container` is only valid on live `durable-object` exports (`created` and `expecting-transfer`) and requires `storage: "sqlite"`. Wrangler now also reports an error when: + + - a `container` reference names a container that does not exist + - two Durable Object exports claim the same container + - a container and a Durable Object export disagree about which one they are linked to + - a container ends up linked to no Durable Object at all + - two containers share a `name` + - a container's `class_name` names a Durable Object whose `storage` is `legacy-kv` + - two containers are attached to the same Durable Object + + That last case was previously accepted but could never work: workerd attaches a single container per Durable Object namespace, and in local development every container for a class builds into the same image tag, so one silently overwrote the other. If you have two containers on one `class_name`, give each its own Durable Object class. + +### Patch Changes + +- [#15211](https://github.com/cloudflare/workers-sdk/pull/15211) [`bc5726b`](https://github.com/cloudflare/workers-sdk/commit/bc5726bd0b88856f6781c62b4cb3c9c47b84eb07) Thanks [@nithin42](https://github.com/nithin42)! - Honor `access.dev` when running Workers with `@cloudflare/vitest-pool-workers`, so `ctx.access.getIdentity()` returns the configured identity just as it does with `wrangler dev`. + +- [#14999](https://github.com/cloudflare/workers-sdk/pull/14999) [`ba54f0d`](https://github.com/cloudflare/workers-sdk/commit/ba54f0d39d1ba7eb5545d8bf9ba43624cfa211f9) Thanks [@mittalpk](https://github.com/mittalpk)! - Fix `.env` loading on Windows leaking stale, differently-cased duplicate keys + + On Windows, `wrangler` loads `.env` values through a case-insensitive `Proxy` wrapper so lookups like `env.PATH` and `env.Path` resolve to the same value, and this object is assigned directly to `process.env`. When a key was set again under a different casing (e.g. a value in `.env.local` overriding one from `.env` with different casing), the previous casing was never removed from the underlying object. `env.PATH`/`env.Path` still returned the correct, latest value, but anything that enumerates `process.env` — `Object.keys`, `for...in`, `JSON.stringify`, object spread, or a spawned subprocess inheriting the environment — would see both the stale and current key. + + Duplicate entries no longer appear, so environment variables passed to subprocesses and any code that lists the environment now see only the latest value for each variable. + +- [#15044](https://github.com/cloudflare/workers-sdk/pull/15044) [`b7422b0`](https://github.com/cloudflare/workers-sdk/commit/b7422b0a8a2e74bba068a1924992dcfeff0bd126) Thanks [@stareezy-1](https://github.com/stareezy-1)! - Normalize structural CRLF line endings before sending D1 commands to the remote query API + + `wrangler d1 migrations apply --remote` and `wrangler d1 execute --remote --command` failed with `incomplete input: SQLITE_ERROR` when the SQL contained CRLF line endings inside a compound statement such as a `CREATE TRIGGER ... BEGIN ... END;` body. Structural line endings are now normalized to LF before the command is sent to the D1 query API, while CRLF inside quoted values and identifiers remains unchanged. + +- [#15046](https://github.com/cloudflare/workers-sdk/pull/15046) [`186339c`](https://github.com/cloudflare/workers-sdk/commit/186339cf854cf3522614fb686ec66e6682c569b8) Thanks [@erwinzhang7](https://github.com/erwinzhang7)! - Fixes D1 SQL statements not handling lowercase `end`s correctly + + `wrangler d1 execute` and `wrangler d1 migrations apply` split a SQL file into statements before running them. A `BEGIN` or `CASE` block closed with a lowercase `end` was not recognised as closed, so every statement after it was folded into that block instead of being run on its own. SQLite accepts either case, so a file like this applied only the trigger and silently skipped the table: + + ```sql + CREATE TRIGGER IF NOT EXISTS update_trigger AFTER UPDATE ON items + begin + DELETE FROM updates WHERE item_id=old.id; + end; + CREATE TABLE after_the_trigger (id TEXT PRIMARY KEY); + ``` + + Files written with an uppercase `END` were unaffected. Both cases now behave the same. + +- [#15231](https://github.com/cloudflare/workers-sdk/pull/15231) [`4f922dc`](https://github.com/cloudflare/workers-sdk/commit/4f922dc19941db31394357f7e146af320ae1f3d9) Thanks [@dependabot](https://github.com/apps/dependabot)! - Update dependencies of "miniflare", "wrangler" + + The following dependency versions have been updated: + + | Dependency | From | To | + | ------------------------- | ------------- | ------------- | + | @cloudflare/workers-types | ^5.20260811.1 | ^5.20260814.1 | + | workerd | 1.20260811.1 | 1.20260814.1 | + +- [#15248](https://github.com/cloudflare/workers-sdk/pull/15248) [`4d74b8d`](https://github.com/cloudflare/workers-sdk/commit/4d74b8d8fd5c034c012fa13973ee20bedbc844c7) Thanks [@dependabot](https://github.com/apps/dependabot)! - Update dependencies of "miniflare", "wrangler" + + The following dependency versions have been updated: + + | Dependency | From | To | + | ------------------------- | ------------- | ------------- | + | @cloudflare/workers-types | ^5.20260814.1 | ^5.20260815.1 | + | workerd | 1.20260814.1 | 1.20260815.1 | + +- [#15185](https://github.com/cloudflare/workers-sdk/pull/15185) [`1f79ace`](https://github.com/cloudflare/workers-sdk/commit/1f79ace67a81633e34dae47a666468fcdaf93f41) Thanks [@jamesopstad](https://github.com/jamesopstad)! - Resolve `--latest` to the newest compatibility date supported by the installed runtime + + `wrangler deploy --latest` and `wrangler versions upload --latest` resolved the compatibility date to the current date, and `wrangler pages download config` did the same for projects configured to always use the latest compatibility date. Both write that date into a configuration file for subsequent commands to use, so a date that the installed `workerd` did not yet support left the project unable to run `wrangler dev`. + + These now resolve to the latest compatibility date supported by this version of Wrangler, which is the release date of the `workerd` it ships with. + +- [#15151](https://github.com/cloudflare/workers-sdk/pull/15151) [`49f73de`](https://github.com/cloudflare/workers-sdk/commit/49f73de207124171b3f8e9ffb182facb48727388) Thanks [@maximilliangrand](https://github.com/maximilliangrand)! - Fix spurious `Trailing comma jsonc(519)` warnings for `wrangler.jsonc` in VS Code 1.131+ + + Trailing commas in `wrangler.jsonc` files that reference Wrangler's JSON schema are no longer reported as errors by recent versions of VS Code. Wrangler always accepted these files; only the editor warning was wrong. + +- [#14983](https://github.com/cloudflare/workers-sdk/pull/14983) [`7cee278`](https://github.com/cloudflare/workers-sdk/commit/7cee2784a937c86db6a88455c6efe8e8660ae69c) Thanks [@kdelay](https://github.com/kdelay)! - Respect `CLOUDFLARE_ACCOUNT_ID` in `wrangler pages project list`, `create` and `delete` + + These three commands could target a previously used account even when `CLOUDFLARE_ACCOUNT_ID` was set, failing with `Authentication error [code: 10000]` in setups with more than one account. They now use the account named by `CLOUDFLARE_ACCOUNT_ID`, matching the rest of `wrangler pages`. When the variable is unset, the previously used account is still selected, as before. + +- [#15153](https://github.com/cloudflare/workers-sdk/pull/15153) [`265256a`](https://github.com/cloudflare/workers-sdk/commit/265256a5619fbf82e12473f7bb7c7944bdd368b4) Thanks [@podonnell-dev](https://github.com/podonnell-dev)! - Fix `wrangler preview base-config` commands showing an inherited `script` positional + +- [#15185](https://github.com/cloudflare/workers-sdk/pull/15185) [`1f79ace`](https://github.com/cloudflare/workers-sdk/commit/1f79ace67a81633e34dae47a666468fcdaf93f41) Thanks [@jamesopstad](https://github.com/jamesopstad)! - Use a fixed default compatibility date rather than the current date + + When no compatibility date was set, Wrangler, C3 and the Vitest pool all defaulted to the current date. `workerd` only accepts a compatibility date up to 7 days beyond its own release, so whenever a `workerd` release was delayed the default could get ahead of the runtime that had been installed, and local development would fail to start. + + The default is now fixed at the release date of the `workerd` version that ships with each release, which leaves a week of headroom and updates as `workerd` is upgraded. `@cloudflare/vite-plugin` previously inlined the date at which it was built. It now shares the same default. + +- [#15239](https://github.com/cloudflare/workers-sdk/pull/15239) [`f431166`](https://github.com/cloudflare/workers-sdk/commit/f43116682a2a08d47383be1125fd6f0b4ae3a0cc) Thanks [@jamesopstad](https://github.com/jamesopstad)! - Prevent date-enabled Node.js compatibility from adding conflicting globals to generated runtime types + + Runtime type generation now treats Node.js compatibility enabled by a compatibility date the same way as an explicit `nodejs_compat` flag. Node.js globals continue to come from `@types/node` instead of being generated as `any` declarations that override those types. + +- [#15196](https://github.com/cloudflare/workers-sdk/pull/15196) [`8fb2b87`](https://github.com/cloudflare/workers-sdk/commit/8fb2b8760b9ba23d1584156d25c02ca2db8d2d80) Thanks [@skepticfx](https://github.com/skepticfx)! - Use the FedRAMP High managed container registry when Wrangler targets the FedRAMP High compliance region + + Container builds, pushes, deployments, image commands, and local development now select the corresponding production or staging FedRAMP registry and API from either `compliance_region` or `CLOUDFLARE_COMPLIANCE_REGION`. + +- [#15082](https://github.com/cloudflare/workers-sdk/pull/15082) [`75cf407`](https://github.com/cloudflare/workers-sdk/commit/75cf407fc0d7fa6f4d4d9848a9221e7b16930497) Thanks [@penalosa](https://github.com/penalosa)! - Enable the new configuration format in the `cf-wrangler` dev delegate + + Projects started through `cf dev` now load `cloudflare.config.ts` and optional `wrangler.config.ts`, matching the configuration used by the delegate's build path. + +- Updated dependencies [[`1277a72`](https://github.com/cloudflare/workers-sdk/commit/1277a72e0d01325c37a05c0e8f5111a45100af77), [`4f922dc`](https://github.com/cloudflare/workers-sdk/commit/4f922dc19941db31394357f7e146af320ae1f3d9), [`4d74b8d`](https://github.com/cloudflare/workers-sdk/commit/4d74b8d8fd5c034c012fa13973ee20bedbc844c7), [`2e0c962`](https://github.com/cloudflare/workers-sdk/commit/2e0c962da0c57bdc79b5edcaa64c7b725c1524f0), [`8777180`](https://github.com/cloudflare/workers-sdk/commit/8777180b8239d9df435acee465d02682477e93ea)]: + - miniflare@5.20260815.0-alpha + ## 4.123.0 ### Minor Changes diff --git a/packages/wrangler/package.json b/packages/wrangler/package.json index 09ae3d8aa7b..ac325f85bb9 100644 --- a/packages/wrangler/package.json +++ b/packages/wrangler/package.json @@ -1,6 +1,6 @@ { "name": "wrangler", - "version": "4.123.0", + "version": "4.124.0", "description": "Command-line interface for all things Cloudflare Workers", "keywords": [ "assembly", From 8a04946b4b1ac41d1fbde7badc2615130b9c1544 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Tue, 18 Aug 2026 14:22:22 +0100 Subject: [PATCH 3/4] [workers-auth] Recover from a partially installed keyring native binding (#15223) --- .changeset/keyring-partial-native-install.md | 9 ++ packages/mock-npm-registry/src/index.ts | 59 ++++++- .../key-providers/lazy-installer.ts | 92 +++++++++-- .../key-providers/lazy-installer.test.ts | 149 +++++++++++++++++- .../tests/credential-store/resolver.test.ts | 80 ++++++++++ 5 files changed, 370 insertions(+), 19 deletions(-) create mode 100644 .changeset/keyring-partial-native-install.md diff --git a/.changeset/keyring-partial-native-install.md b/.changeset/keyring-partial-native-install.md new file mode 100644 index 00000000000..446d8cd0fc6 --- /dev/null +++ b/.changeset/keyring-partial-native-install.md @@ -0,0 +1,9 @@ +--- +"@cloudflare/workers-auth": patch +--- + +Recover from a partially installed keyring backend on Windows + +Choosing to keep your credentials in the OS keyring on Windows installs a native backend the first time you opt in. An install interrupted part-way through — by a dropped connection, a full disk, or an npm told to skip optional packages — could leave a broken backend behind that was nonetheless treated as working. Every login, token refresh, and credential read from then on failed with an internal error, and because the broken state was never re-examined, no amount of retrying would clear it. + +A broken backend is now spotted and reinstalled automatically. If the reinstall still cannot produce a working one, you get a single explanation of how to install it by hand and fall back to the plaintext credentials file for the rest of the session, rather than sitting through a fresh install attempt on every credential access. diff --git a/packages/mock-npm-registry/src/index.ts b/packages/mock-npm-registry/src/index.ts index f793f4573d3..8ffa876193a 100644 --- a/packages/mock-npm-registry/src/index.ts +++ b/packages/mock-npm-registry/src/index.ts @@ -14,6 +14,12 @@ export { overrideConfigEnv } from "./config-env.js"; const debugLog = util.debuglog("mock-npm-registry"); const repoRoot = path.resolve(__dirname, "../../.."); +/** + * How long to wait for Verdaccio to actually exit after it has been killed, + * before giving up and letting teardown continue regardless. + */ +const EXIT_TIMEOUT = 10_000; + /** * Start a mock local npm registry (using verdaccio) to host local copies of packages under test. * @@ -289,6 +295,13 @@ async function startVerdaccioServer(configPath: string) { require.resolve("verdaccio/bin/verdaccio"), ["-c", configPath] ); + + // Attached before anything can stop the server, so the event can never + // be missed by a listener registered too late to see it. + const exited = new Promise((res) => { + server.once("exit", () => res()); + }); + server.on("error", reject); server.on("disconnect", reject); @@ -302,14 +315,46 @@ async function startVerdaccioServer(configPath: string) { } }); - function stop() { - return new Promise((res) => { - if (server?.pid) { - treeKill(server.pid, () => res()); - } else { - res(); - } + /** + * Stop the server, resolving only once it has really exited. + * + * `treeKill`'s callback fires once the signals have been *delivered*, + * not once the process tree has finished exiting. Callers remove the + * registry's storage directory immediately afterwards and may restart + * on the same port, both of which race a Verdaccio that is still + * shutting down — producing `ENOTEMPTY`/`EBUSY` on directory removal + * (particularly on Windows, where an open file blocks deletion) and + * `EADDRINUSE` on restart. So wait for the real `exit` event. + * + * Bounded, because a process wedged in an uninterruptible state must + * not hang test teardown forever: after `EXIT_TIMEOUT` we proceed and + * let the caller's own error handling deal with the consequences, + * which is still strictly better than not waiting at all. + */ + async function stop() { + const pid = server.pid; + if (pid === undefined) { + return; + } + await new Promise((res) => { + // Errors are ignored: the usual one is "no such process", + // meaning it has already exited, which is what we want anyway. + treeKill(pid, () => res()); }); + let timer: NodeJS.Timeout | undefined; + try { + await Promise.race([ + exited, + new Promise((res) => { + timer = setTimeout(res, EXIT_TIMEOUT); + // Don't hold the event loop open just to await a + // timeout we may never need. + timer.unref(); + }), + ]); + } finally { + clearTimeout(timer); + } } }); } diff --git a/packages/workers-auth/src/credential-store/key-providers/lazy-installer.ts b/packages/workers-auth/src/credential-store/key-providers/lazy-installer.ts index 17169822cc7..cdf7c1ba61c 100644 --- a/packages/workers-auth/src/credential-store/key-providers/lazy-installer.ts +++ b/packages/workers-auth/src/credential-store/key-providers/lazy-installer.ts @@ -111,6 +111,37 @@ function getInstalledBindingPath(installDir: string): string { return path.join(installDir, "node_modules", "@napi-rs", "keyring"); } +/** + * Whether `candidate` is a binding this process can actually load. + * + * The presence of `index.js` is *not* sufficient evidence of a working + * install. `@napi-rs/keyring`'s `index.js` `require`s a platform-specific + * `.node` binary, which is a separate optional dependency: an `npm install` + * that is interrupted, runs out of disk, or fails to fetch the platform + * package can leave `index.js` on disk with no loadable binary beside it. + * + * Probing with a real `require` is the only honest test, because that is + * exactly what {@link resolveKeyringEntryFactory} does moments later. Without + * it, a half-installed binding is reported as "available" forever: the + * resolver never re-runs the install, and every credential read and write + * throws a raw module-resolution error instead. + * + * `require` caches on success, so the load is not repeated when the factory + * subsequently resolves the same path. This only runs on the Windows keyring + * opt-in path, so it never `dlopen`s a native binary for users who have not + * opted in. + */ +function canLoadKeyringBinding(candidate: string): boolean { + try { + createRequire(candidate)(candidate); + return true; + } catch { + // Missing or unloadable native binary: treat the install as absent so + // the caller reinstalls rather than failing on every credential access. + return false; + } +} + /** * Locate an installed `@napi-rs/keyring` binding usable from this process. * @@ -120,6 +151,11 @@ function getInstalledBindingPath(installDir: string): string { * @napi-rs/keyring` works in CI environments where the lazy install * path is unavailable. * + * A candidate only counts when it actually loads (see + * {@link canLoadKeyringBinding}) — a half-installed binding is reported as + * absent so the caller reinstalls it, rather than being trusted forever on the + * strength of an `index.js` that cannot be required. + * * Returns `null` when no binding is available; callers handle the missing * binding case explicitly so they can surface remediation instructions. * @@ -135,7 +171,7 @@ export function findKeyringBinding(installDir: string): string | null { return cached; } const lazy = getInstalledBindingPath(installDir); - if (existsSync(path.join(lazy, "index.js"))) { + if (existsSync(path.join(lazy, "index.js")) && canLoadKeyringBinding(lazy)) { cachedBindingPathByDir.set(installDir, lazy); return lazy; } @@ -143,7 +179,10 @@ export function findKeyringBinding(installDir: string): string | null { const r = npmRunner(["root", "-g"]); if (r.status === 0) { const global = path.join(r.stdout.trim(), "@napi-rs", "keyring"); - if (existsSync(path.join(global, "index.js"))) { + if ( + existsSync(path.join(global, "index.js")) && + canLoadKeyringBinding(global) + ) { cachedBindingPathByDir.set(installDir, global); return global; } @@ -160,9 +199,10 @@ export function findKeyringBinding(installDir: string): string | null { * Install `@napi-rs/keyring` into the private install dir using the user's * `npm`. * - * Throws a {@link UserError} when `npm` cannot be spawned or returns a - * non-zero exit code, so callers can surface actionable remediation hints - * rather than a raw stack trace. + * Throws a {@link UserError} when `npm` cannot be spawned, returns a non-zero + * exit code, or exits cleanly without leaving a loadable binding behind, so + * callers can surface actionable remediation hints rather than a raw stack + * trace. */ export function installKeyringBindingSync(installDir: string): void { const dir = installDir; @@ -213,10 +253,32 @@ export function installKeyringBindingSync(installDir: string): void { { telemetryMessage: "workers-auth keyring npm install failed" } ); } - // Fresh install landed on disk — invalidate the cached "not found" - // result for this install dir so the next `findKeyringBinding()` call - // picks the new path up immediately instead of returning the stale `null`. + // Something landed on disk — invalidate the cached "not found" result for + // this install dir so the verification below, and every later + // `findKeyringBinding()` call, re-probes instead of returning the stale + // `null`. cachedBindingPathByDir.delete(installDir); + + // A zero exit code is not evidence of a usable binding. The + // platform-specific `.node` binary ships as an *optional* dependency of + // `@napi-rs/keyring`, and npm exits 0 when an optional dependency cannot + // be fetched — as it also does when configured to skip optional + // dependencies entirely. Reporting success here would leave the resolver + // on the `needs-install` branch, which only latches its session failure + // flag when the install *throws*: it would therefore respawn this whole + // install on every credential read and write, and still fail at the end + // of each one. Failing loudly instead bounds the retries to one per + // session and routes the user through the normal remediation/fallback + // path. + if (findKeyringBinding(installDir) === null) { + throw new UserError( + `\`npm\` reported success but the \`@napi-rs/keyring\` native binding still cannot be loaded.\n\n` + + `Its platform-specific binary ships as a separate optional package, so a download that was skipped or interrupted leaves the binding unusable.\n\n` + + `Retry once the underlying issue is fixed, or install the binding globally:\n` + + ` npm install -g @napi-rs/keyring@${PINNED_KEYRING_VERSION}`, + { telemetryMessage: "workers-auth keyring install incomplete" } + ); + } } /** @@ -261,6 +323,13 @@ interface KeyringModule { /** * Resolve the active keyring entry factory, loading the lazy-installed * binding from `installDir` when no test override is registered. + * + * Callers are expected to have resolved the binding's availability first, so a + * missing binding here means it went away between resolution and use (an + * uninstall, or a directory cleaned up mid-session). That is rare enough to be + * close to an internal invariant violation, but it is still the user's machine + * that has to be fixed, so it carries the same remediation hint as the + * installer rather than a bare stack trace. */ export function resolveKeyringEntryFactory( installDir: string @@ -270,8 +339,11 @@ export function resolveKeyringEntryFactory( } const bindingPath = findKeyringBinding(installDir); if (bindingPath === null) { - throw new Error( - "`@napi-rs/keyring` binding not found. Call `installKeyringBindingSync()` first." + throw new UserError( + `The \`@napi-rs/keyring\` native binding is not available.\n\n` + + `Retry the command, or install the binding globally:\n` + + ` npm install -g @napi-rs/keyring@${PINNED_KEYRING_VERSION}`, + { telemetryMessage: "workers-auth keyring binding not loadable" } ); } const mod = createRequire(bindingPath)(bindingPath) as KeyringModule; diff --git a/packages/workers-auth/tests/credential-store/key-providers/lazy-installer.test.ts b/packages/workers-auth/tests/credential-store/key-providers/lazy-installer.test.ts index ff85ce9886f..8c9c2cfa10e 100644 --- a/packages/workers-auth/tests/credential-store/key-providers/lazy-installer.test.ts +++ b/packages/workers-auth/tests/credential-store/key-providers/lazy-installer.test.ts @@ -1,6 +1,6 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import path from "node:path"; -import { getGlobalConfigPath } from "@cloudflare/workers-utils"; +import { getGlobalConfigPath, UserError } from "@cloudflare/workers-utils"; import { runInTempDir } from "@cloudflare/workers-utils/test-helpers"; import { afterEach, beforeEach, describe, it } from "vitest"; import { @@ -8,6 +8,7 @@ import { getKeyringInstallDir, installKeyringBindingSync, PINNED_KEYRING_VERSION, + resolveKeyringEntryFactory, setNpmRunner, } from "../../../src/credential-store/key-providers/lazy-installer"; import type { SpawnSyncReturns } from "node:child_process"; @@ -98,6 +99,78 @@ describe("lazy keyring installer", () => { expect(findKeyringBinding(installDir())).toBeNull(); }); + it("treats a binding whose native binary is missing as absent", ({ + expect, + }) => { + // Regression: an interrupted `npm install` can leave `index.js` on + // disk without the platform-specific `.node` binary it requires. + // Trusting `index.js` alone reported that wreck as "available" + // forever — the install was never retried and every credential + // read threw a raw module-resolution error instead. + const dir = path.join( + installDir(), + "node_modules", + "@napi-rs", + "keyring" + ); + mkdirSync(dir, { recursive: true }); + writeFileSync( + path.join(dir, "index.js"), + // Exactly what the real binding does, minus the binary. + 'require("./keyring.win32-x64-msvc.node");' + ); + setNpmRunner((args) => { + lastInvocation = args; + return mockResult({ stdout: "/nonexistent/global/root\n" }); + }); + + expect(findKeyringBinding(installDir())).toBeNull(); + // Having rejected the local wreck, it still probes the global root. + expect(lastInvocation).toEqual(["root", "-g"]); + }); + + it("ignores a global binding that cannot be loaded", ({ expect }) => { + const globalRoot = path.join(getGlobalConfigPath(), "global-npm-root"); + const bindingDir = path.join(globalRoot, "@napi-rs", "keyring"); + mkdirSync(bindingDir, { recursive: true }); + writeFileSync( + path.join(bindingDir, "index.js"), + 'require("./keyring.win32-x64-msvc.node");' + ); + setNpmRunner(() => mockResult({ stdout: globalRoot + "\n" })); + expect(findKeyringBinding(installDir())).toBeNull(); + }); + + it("recovers when a reinstall repairs a half-installed binding", ({ + expect, + }) => { + // The end-to-end recovery this is all for: a broken install must be + // reported absent (so the resolver reinstalls), and the repaired + // install must then be picked up. + const dir = path.join( + installDir(), + "node_modules", + "@napi-rs", + "keyring" + ); + mkdirSync(dir, { recursive: true }); + writeFileSync( + path.join(dir, "index.js"), + 'require("./keyring.win32-x64-msvc.node");' + ); + setNpmRunner((args) => { + if (args[0] === "root") { + return mockResult({ stdout: "/nonexistent/global/root\n" }); + } + writeFileSync(path.join(dir, "index.js"), "module.exports = {};"); + return mockResult({}); + }); + + expect(findKeyringBinding(installDir())).toBeNull(); + installKeyringBindingSync(installDir()); + expect(findKeyringBinding(installDir())).toBe(dir); + }); + it("memoizes the result so repeated calls do not re-spawn npm", ({ expect, }) => { @@ -121,8 +194,21 @@ describe("lazy keyring installer", () => { it("creates a private host package.json before invoking npm install", ({ expect, }) => { + const bindingDir = path.join( + installDir(), + "node_modules", + "@napi-rs", + "keyring" + ); setNpmRunner((args) => { lastInvocation = args; + // The install verifies its own work, so the runner has to + // actually land a loadable binding to count as a success. + mkdirSync(bindingDir, { recursive: true }); + writeFileSync( + path.join(bindingDir, "index.js"), + "module.exports = {};" + ); return mockResult({}); }); installKeyringBindingSync(installDir()); @@ -161,13 +247,56 @@ describe("lazy keyring installer", () => { ); }); + it("throws a UserError when npm exits 0 without leaving a loadable binding", ({ + expect, + }) => { + // npm exits 0 when an optional dependency cannot be fetched, and + // the platform-specific `.node` binary is exactly that. Reporting + // success here would leave the resolver on its `needs-install` + // branch — which only latches its session failure flag when the + // install throws — so the whole install would be respawned on + // every single credential read and write. + const bindingDir = path.join( + installDir(), + "node_modules", + "@napi-rs", + "keyring" + ); + setNpmRunner((args) => { + if (args[0] === "root") { + return mockResult({ stdout: "/nonexistent/global/root\n" }); + } + mkdirSync(bindingDir, { recursive: true }); + writeFileSync( + path.join(bindingDir, "index.js"), + 'require("./keyring.win32-x64-msvc.node");' + ); + return mockResult({}); + }); + + // A `UserError` specifically: the resolver reports it verbatim when + // keyring storage was forced via `CLOUDFLARE_AUTH_USE_KEYRING`. + expect(() => installKeyringBindingSync(installDir())).toThrow(UserError); + expect(() => installKeyringBindingSync(installDir())).toThrow( + /`npm` reported success but the `@napi-rs\/keyring` native binding still cannot be loaded[\s\S]*npm install -g @napi-rs\/keyring@\d+\.\d+\.\d+/ + ); + }); + it("does not overwrite an existing host package.json", ({ expect }) => { const dir = installDir(); mkdirSync(dir, { recursive: true }); const hostPkgJson = path.join(dir, "package.json"); writeFileSync(hostPkgJson, '{"customMarker":true}'); - setNpmRunner(() => mockResult({})); + const bindingDir = path.join(dir, "node_modules", "@napi-rs", "keyring"); + setNpmRunner(() => { + mkdirSync(bindingDir, { recursive: true }); + writeFileSync( + path.join(bindingDir, "index.js"), + "module.exports = {};" + ); + return mockResult({}); + }); installKeyringBindingSync(installDir()); expect(readFileSync(hostPkgJson, "utf-8")).toContain("customMarker"); }); @@ -213,4 +342,20 @@ describe("lazy keyring installer", () => { expect(findKeyringBinding(installDir())).toBe(bindingDir); }); }); + + describe("resolveKeyringEntryFactory", () => { + it("throws a UserError with remediation when no binding is available", ({ + expect, + }) => { + // Callers resolve availability before getting here, so this is + // close to an internal invariant violation — but it is still the + // user's machine that needs fixing, so it must not surface as a + // bare module-resolution stack trace. + setNpmRunner(() => mockResult({ stdout: "/nonexistent/global/root\n" })); + expect(() => resolveKeyringEntryFactory(installDir())).toThrow(UserError); + expect(() => resolveKeyringEntryFactory(installDir())).toThrow( + /native binding is not available[\s\S]*npm install -g @napi-rs\/keyring@\d+\.\d+\.\d+/ + ); + }); + }); }); diff --git a/packages/workers-auth/tests/credential-store/resolver.test.ts b/packages/workers-auth/tests/credential-store/resolver.test.ts index aab10a0c40b..af669b1f0b5 100644 --- a/packages/workers-auth/tests/credential-store/resolver.test.ts +++ b/packages/workers-auth/tests/credential-store/resolver.test.ts @@ -396,6 +396,86 @@ describe("createCredentialStorageContext — resolver", () => { resolveStore({ isKeyringEnabled: true, isNonInteractiveOrCI: false }) ).toThrow(); }); + + it("stops retrying when npm exits 0 but the binding is still unloadable", ({ + expect, + }) => { + // npm exits 0 when the platform-specific optional package cannot be + // fetched, leaving an `index.js` that cannot be required. Unless + // that counts as an install failure the session latch never gets + // set, so the multi-second install is respawned on every single + // credential read and write — and still fails at the end of each. + stubPlatform("win32"); + + let installCalls = 0; + const bindingDir = path.join( + getKeyringInstallDir(getGlobalConfigPath()), + "node_modules", + "@napi-rs", + "keyring" + ); + setNpmRunner((args) => { + if (args[0] === "install") { + installCalls += 1; + mkdirSync(bindingDir, { recursive: true }); + writeFileSync( + path.join(bindingDir, "index.js"), + 'require("./keyring.win32-x64-msvc.node");' + ); + return mockResult({}); + } + return mockResult({ status: 0, stdout: "/nowhere\n" }); + }); + + const { getActiveStore } = createCredentialStorageContext({ + serviceName: "wrangler", + getConfigPath: () => getGlobalConfigPath(), + isKeyringEnabled: () => true, + logger: silentLogger, + isNonInteractiveOrCI: () => false, + loginCommand: "wrangler login", + }); + const kinds = [ + getActiveStore().kind, + getActiveStore().kind, + getActiveStore().kind, + ]; + expect(installCalls).toBe(1); + expect(kinds).toEqual(["file", "file", "file"]); + expect(warn).toHaveBeenCalledWith( + expect.stringContaining("still cannot be loaded") + ); + }); + + it("hard-errors when CLOUDFLARE_AUTH_USE_KEYRING=true and the install lands no loadable binding", ({ + expect, + }) => { + stubPlatform("win32"); + vi.stubEnv("CLOUDFLARE_AUTH_USE_KEYRING", "true"); + const bindingDir = path.join( + getKeyringInstallDir(getGlobalConfigPath()), + "node_modules", + "@napi-rs", + "keyring" + ); + setNpmRunner((args) => { + if (args[0] === "install") { + mkdirSync(bindingDir, { recursive: true }); + writeFileSync( + path.join(bindingDir, "index.js"), + 'require("./keyring.win32-x64-msvc.node");' + ); + return mockResult({}); + } + return mockResult({ status: 0, stdout: "/nowhere\n" }); + }); + + // Reported verbatim rather than silently downgraded to the + // plaintext file, because the user demanded keyring storage. + expect(() => + resolveStore({ isKeyringEnabled: true, isNonInteractiveOrCI: false }) + ).toThrow(/still cannot be loaded/); + }); }); describe("unsupported platform", () => { From d81fae7487abee539d985c348dddf39bca3196f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Somhairle=20MacLe=C3=B2id?= Date: Tue, 18 Aug 2026 14:49:47 +0100 Subject: [PATCH 4/4] [codemod] Add migration CLI (#14690) --- .changeset/curly-codemods-juggle.md | 8 + packages/autoconfig/package.json | 2 +- packages/autoconfig/src/frameworks/analog.ts | 5 +- packages/autoconfig/src/frameworks/astro.ts | 5 +- packages/autoconfig/src/frameworks/nuxt.ts | 5 +- packages/autoconfig/src/frameworks/qwik.ts | 2 +- .../autoconfig/src/frameworks/react-router.ts | 2 +- .../autoconfig/src/frameworks/solid-start.ts | 5 +- .../src/frameworks/utils/vite-config.ts | 2 +- packages/autoconfig/src/frameworks/vike.ts | 2 +- packages/autoconfig/src/frameworks/waku.ts | 2 +- packages/codemods/README.md | 19 + packages/codemods/package.json | 54 ++ packages/codemods/src/bin.ts | 80 ++ .../codemods/src/codemods/vitest-v3-to-v4.ts | 257 ++++++ packages/codemods/src/codemods/vitest.ts | 179 +++++ packages/codemods/src/files.ts | 59 ++ packages/codemods/src/runner.ts | 52 ++ packages/codemods/src/types.ts | 22 + packages/codemods/test/runner.test.ts | 378 +++++++++ .../tests => codemods/test}/tsconfig.json | 0 .../codemods/test/vitest-v3-to-v4.test.ts | 135 ++++ packages/codemods/tsconfig.json | 10 + packages/codemods/tsup.config.ts | 21 + packages/{codemod => codemods}/turbo.json | 0 packages/codemods/vitest.config.mts | 12 + packages/create-cloudflare/package.json | 2 +- .../create-cloudflare/src/helpers/codemod.ts | 2 +- .../create-cloudflare/templates/analog/c3.ts | 5 +- .../templates/astro/pages/c3.ts | 2 +- .../templates/nuxt/pages/c3.ts | 5 +- .../templates/nuxt/workers/c3.ts | 5 +- .../templates/qwik/pages/c3.ts | 2 +- .../templates/qwik/workers/c3.ts | 2 +- .../templates/react/workers/c3.ts | 2 +- .../create-cloudflare/templates/solid/c3.ts | 5 +- .../templates/svelte/pages/c3.ts | 2 +- .../templates/svelte/workers/c3.ts | 2 +- .../.gitignore | 0 .../CHANGELOG.md | 2 +- .../README.md | 31 +- .../dev-snippets/test.ts | 0 .../package.json | 6 +- .../src/dev.ts | 0 .../src/index.ts | 29 +- .../tests/index.test.ts | 0 .../shared-ast-primitives/tests/tsconfig.json | 9 + .../tsconfig.json | 0 .../tsup.config.ts | 0 packages/shared-ast-primitives/turbo.json | 16 + .../vitest.config.mts | 0 packages/vitest-pool-workers/AGENTS.md | 3 +- packages/vitest-pool-workers/package.json | 5 - .../src/codemods/vitest-v3-to-v4.ts | 271 ------- .../vitest-pool-workers/test/codemods.test.ts | 206 ----- packages/vitest-pool-workers/tsdown.config.ts | 11 - packages/wrangler/package.json | 2 +- pnpm-lock.yaml | 732 ++---------------- .../__tests__/publish-packages.test.ts | 1 + .../__tests__/validate-changesets.test.ts | 3 +- 60 files changed, 1476 insertions(+), 1205 deletions(-) create mode 100644 .changeset/curly-codemods-juggle.md create mode 100644 packages/codemods/README.md create mode 100644 packages/codemods/package.json create mode 100644 packages/codemods/src/bin.ts create mode 100644 packages/codemods/src/codemods/vitest-v3-to-v4.ts create mode 100644 packages/codemods/src/codemods/vitest.ts create mode 100644 packages/codemods/src/files.ts create mode 100644 packages/codemods/src/runner.ts create mode 100644 packages/codemods/src/types.ts create mode 100644 packages/codemods/test/runner.test.ts rename packages/{codemod/tests => codemods/test}/tsconfig.json (100%) create mode 100644 packages/codemods/test/vitest-v3-to-v4.test.ts create mode 100644 packages/codemods/tsconfig.json create mode 100644 packages/codemods/tsup.config.ts rename packages/{codemod => codemods}/turbo.json (100%) create mode 100644 packages/codemods/vitest.config.mts rename packages/{codemod => shared-ast-primitives}/.gitignore (100%) rename packages/{codemod => shared-ast-primitives}/CHANGELOG.md (92%) rename packages/{codemod => shared-ast-primitives}/README.md (58%) rename packages/{codemod => shared-ast-primitives}/dev-snippets/test.ts (100%) rename packages/{codemod => shared-ast-primitives}/package.json (85%) rename packages/{codemod => shared-ast-primitives}/src/dev.ts (100%) rename packages/{codemod => shared-ast-primitives}/src/index.ts (83%) rename packages/{codemod => shared-ast-primitives}/tests/index.test.ts (100%) create mode 100644 packages/shared-ast-primitives/tests/tsconfig.json rename packages/{codemod => shared-ast-primitives}/tsconfig.json (100%) rename packages/{codemod => shared-ast-primitives}/tsup.config.ts (100%) create mode 100644 packages/shared-ast-primitives/turbo.json rename packages/{codemod => shared-ast-primitives}/vitest.config.mts (100%) delete mode 100644 packages/vitest-pool-workers/src/codemods/vitest-v3-to-v4.ts delete mode 100644 packages/vitest-pool-workers/test/codemods.test.ts diff --git a/.changeset/curly-codemods-juggle.md b/.changeset/curly-codemods-juggle.md new file mode 100644 index 00000000000..08b64994013 --- /dev/null +++ b/.changeset/curly-codemods-juggle.md @@ -0,0 +1,8 @@ +--- +"@cloudflare/codemods": minor +"@cloudflare/vitest-pool-workers": minor +--- + +Add a central CLI for Cloudflare codemods + +Run a codemod by name, e.g. `npx @cloudflare/codemods vitest:v3-to-v4`. The initial migrations cover Vitest v3 to v4 configuration (`vitest:v3-to-v4`) and the `@cloudflare/vitest-pool-workers` to `@cloudflare/vitest-plugin` v1 rename (`vitest:pool-workers-to-vitest-plugin`). The existing Vitest transform now lives in this dedicated package. diff --git a/packages/autoconfig/package.json b/packages/autoconfig/package.json index f3a41e1bf81..09921015e43 100644 --- a/packages/autoconfig/package.json +++ b/packages/autoconfig/package.json @@ -33,7 +33,7 @@ "@cloudflare/workers-utils": "workspace:*" }, "devDependencies": { - "@cloudflare/codemod": "workspace:*", + "@cloudflare/shared-ast-primitives": "workspace:*", "@cloudflare/workers-tsconfig": "workspace:*", "@netlify/build-info": "^10.5.1", "@types/esprima": "^4.0.3", diff --git a/packages/autoconfig/src/frameworks/analog.ts b/packages/autoconfig/src/frameworks/analog.ts index e4b9a8b6991..c7932bd35c2 100644 --- a/packages/autoconfig/src/frameworks/analog.ts +++ b/packages/autoconfig/src/frameworks/analog.ts @@ -2,7 +2,10 @@ import { existsSync } from "node:fs"; import { join } from "node:path"; import { updateStatus } from "@cloudflare/cli-shared-helpers"; import { blue } from "@cloudflare/cli-shared-helpers/colors"; -import { mergeObjectProperties, transformFile } from "@cloudflare/codemod"; +import { + mergeObjectProperties, + transformFile, +} from "@cloudflare/shared-ast-primitives"; import { DEFAULT_COMPAT_DATE } from "@cloudflare/workers-utils"; import * as recast from "recast"; import { Framework } from "./framework-class"; diff --git a/packages/autoconfig/src/frameworks/astro.ts b/packages/autoconfig/src/frameworks/astro.ts index cbc986f1e91..0c04ce98e98 100644 --- a/packages/autoconfig/src/frameworks/astro.ts +++ b/packages/autoconfig/src/frameworks/astro.ts @@ -8,7 +8,10 @@ import { updateStatus } from "@cloudflare/cli-shared-helpers"; import { blue, brandColor, dim } from "@cloudflare/cli-shared-helpers/colors"; import { runCommand } from "@cloudflare/cli-shared-helpers/command"; import { installPackages } from "@cloudflare/cli-shared-helpers/packages"; -import { mergeObjectProperties, transformFile } from "@cloudflare/codemod"; +import { + mergeObjectProperties, + transformFile, +} from "@cloudflare/shared-ast-primitives"; import { parseJSONC } from "@cloudflare/workers-utils"; import * as recast from "recast"; import semiver from "semiver"; diff --git a/packages/autoconfig/src/frameworks/nuxt.ts b/packages/autoconfig/src/frameworks/nuxt.ts index 4720ea34272..aa536529ad4 100644 --- a/packages/autoconfig/src/frameworks/nuxt.ts +++ b/packages/autoconfig/src/frameworks/nuxt.ts @@ -1,7 +1,10 @@ import path from "node:path"; import { brandColor, dim } from "@cloudflare/cli-shared-helpers/colors"; import { installPackages } from "@cloudflare/cli-shared-helpers/packages"; -import { mergeObjectProperties, transformFile } from "@cloudflare/codemod"; +import { + mergeObjectProperties, + transformFile, +} from "@cloudflare/shared-ast-primitives"; import * as recast from "recast"; import { Framework } from "./framework-class"; import type { diff --git a/packages/autoconfig/src/frameworks/qwik.ts b/packages/autoconfig/src/frameworks/qwik.ts index 698ec69049e..bcc07802cae 100644 --- a/packages/autoconfig/src/frameworks/qwik.ts +++ b/packages/autoconfig/src/frameworks/qwik.ts @@ -5,7 +5,7 @@ import { runCommand, } from "@cloudflare/cli-shared-helpers/command"; import { spinner } from "@cloudflare/cli-shared-helpers/interactive"; -import { transformFile } from "@cloudflare/codemod"; +import { transformFile } from "@cloudflare/shared-ast-primitives"; import * as recast from "recast"; import * as typescriptParser from "recast/parsers/typescript"; import { usesTypescript } from "../uses-typescript"; diff --git a/packages/autoconfig/src/frameworks/react-router.ts b/packages/autoconfig/src/frameworks/react-router.ts index 449e6e58e78..a4cddf5059b 100644 --- a/packages/autoconfig/src/frameworks/react-router.ts +++ b/packages/autoconfig/src/frameworks/react-router.ts @@ -3,7 +3,7 @@ import { existsSync, mkdirSync, writeFileSync } from "node:fs"; import path from "node:path"; import { brandColor, dim } from "@cloudflare/cli-shared-helpers/colors"; import { installPackages } from "@cloudflare/cli-shared-helpers/packages"; -import { parseFile, transformFile } from "@cloudflare/codemod"; +import { parseFile, transformFile } from "@cloudflare/shared-ast-primitives"; import * as recast from "recast"; import semiver from "semiver"; import dedent from "ts-dedent"; diff --git a/packages/autoconfig/src/frameworks/solid-start.ts b/packages/autoconfig/src/frameworks/solid-start.ts index 30e04925eac..064106b7e32 100644 --- a/packages/autoconfig/src/frameworks/solid-start.ts +++ b/packages/autoconfig/src/frameworks/solid-start.ts @@ -1,6 +1,9 @@ import { updateStatus } from "@cloudflare/cli-shared-helpers"; import { blue } from "@cloudflare/cli-shared-helpers/colors"; -import { mergeObjectProperties, transformFile } from "@cloudflare/codemod"; +import { + mergeObjectProperties, + transformFile, +} from "@cloudflare/shared-ast-primitives"; import { DEFAULT_COMPAT_DATE } from "@cloudflare/workers-utils"; import * as recast from "recast"; import semiver from "semiver"; diff --git a/packages/autoconfig/src/frameworks/utils/vite-config.ts b/packages/autoconfig/src/frameworks/utils/vite-config.ts index 42867b1dae0..851ae0ccd4f 100644 --- a/packages/autoconfig/src/frameworks/utils/vite-config.ts +++ b/packages/autoconfig/src/frameworks/utils/vite-config.ts @@ -1,6 +1,6 @@ import { existsSync } from "node:fs"; import path from "node:path"; -import { transformFile } from "@cloudflare/codemod"; +import { transformFile } from "@cloudflare/shared-ast-primitives"; import { UserError } from "@cloudflare/workers-utils"; import * as recast from "recast"; import dedent from "ts-dedent"; diff --git a/packages/autoconfig/src/frameworks/vike.ts b/packages/autoconfig/src/frameworks/vike.ts index eb9ae0ad932..eccb0bcaeaf 100644 --- a/packages/autoconfig/src/frameworks/vike.ts +++ b/packages/autoconfig/src/frameworks/vike.ts @@ -3,7 +3,7 @@ import { existsSync } from "node:fs"; import path from "node:path"; import { brandColor } from "@cloudflare/cli-shared-helpers/colors"; import { installPackages } from "@cloudflare/cli-shared-helpers/packages"; -import { transformFile } from "@cloudflare/codemod"; +import { transformFile } from "@cloudflare/shared-ast-primitives"; import * as recast from "recast"; import { Framework } from "./framework-class"; import { isPackageInstalled } from "./utils/packages"; diff --git a/packages/autoconfig/src/frameworks/waku.ts b/packages/autoconfig/src/frameworks/waku.ts index 9eac04a1a68..08b5a241db4 100644 --- a/packages/autoconfig/src/frameworks/waku.ts +++ b/packages/autoconfig/src/frameworks/waku.ts @@ -5,7 +5,7 @@ import { join } from "node:path"; import { updateStatus } from "@cloudflare/cli-shared-helpers"; import { blue, brandColor } from "@cloudflare/cli-shared-helpers/colors"; import { installPackages } from "@cloudflare/cli-shared-helpers/packages"; -import { transformFile } from "@cloudflare/codemod"; +import { transformFile } from "@cloudflare/shared-ast-primitives"; import * as recast from "recast"; import dedent from "ts-dedent"; import { Framework } from "./framework-class"; diff --git a/packages/codemods/README.md b/packages/codemods/README.md new file mode 100644 index 00000000000..4a8901abcb6 --- /dev/null +++ b/packages/codemods/README.md @@ -0,0 +1,19 @@ +# `@cloudflare/codemods` + +Codemods for upgrading Cloudflare developer projects. + +Run a codemod by name: + +```sh +npx @cloudflare/codemods vitest:v3-to-v4 +``` + +Available codemods: + +- `vitest:v3-to-v4` — migrate `@cloudflare/vitest-pool-workers` configuration from + Vitest v3 to v4 +- `vitest:pool-workers-to-vitest-plugin` — rename `@cloudflare/vitest-pool-workers` + to `@cloudflare/vitest-plugin` v1 + +Use `--dry-run` to list changes without writing them, `--cwd ` to target +another project, or repeat `--files ` to restrict the files considered. diff --git a/packages/codemods/package.json b/packages/codemods/package.json new file mode 100644 index 00000000000..770f588f6aa --- /dev/null +++ b/packages/codemods/package.json @@ -0,0 +1,54 @@ +{ + "name": "@cloudflare/codemods", + "version": "0.0.0", + "description": "Codemods for upgrading Cloudflare developer projects", + "keywords": [ + "cloudflare", + "codemod", + "migration", + "workers" + ], + "homepage": "https://github.com/cloudflare/workers-sdk/tree/main/packages/codemods#readme", + "bugs": { + "url": "https://github.com/cloudflare/workers-sdk/issues" + }, + "license": "MIT OR Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/cloudflare/workers-sdk.git", + "directory": "packages/codemods" + }, + "bin": { + "cloudflare-codemods": "./dist/bin.mjs" + }, + "files": [ + "dist" + ], + "type": "module", + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsup", + "check:type": "tsc -p ./tsconfig.json", + "deploy": "echo 'no deploy'", + "test": "vitest", + "test:ci": "vitest run", + "type:tests": "tsc -p ./test/tsconfig.json" + }, + "devDependencies": { + "@cloudflare/shared-ast-primitives": "workspace:*", + "@cloudflare/workers-tsconfig": "workspace:*", + "@types/node": "catalog:default", + "tinyglobby": "catalog:default", + "tsup": "8.3.0", + "typescript": "catalog:default", + "vitest": "catalog:default" + }, + "volta": { + "extends": "../../package.json" + }, + "workers-sdk": { + "prerelease": true + } +} diff --git a/packages/codemods/src/bin.ts b/packages/codemods/src/bin.ts new file mode 100644 index 00000000000..fc50693f7fa --- /dev/null +++ b/packages/codemods/src/bin.ts @@ -0,0 +1,80 @@ +#!/usr/bin/env node + +import path from "node:path"; +import { parseArgs } from "node:util"; +import { availableCodemods, runCodemod } from "./runner"; + +/** Prints command usage and the available codemods. */ +function printHelp(): void { + console.log(`Usage: cloudflare-codemods [options] + +Run a codemod by name: + npx @cloudflare/codemods vitest:v3-to-v4 + +Options: + --cwd Project directory (default: current directory) + --files Restrict files considered; may be repeated + --dry-run List changes without writing files + --help Show this help + +Available Codemods: +${availableCodemods.map((codemod) => ` ${codemod.name}\n ${codemod.description}`).join("\n")}`); +} + +/** + * Runs the codemod CLI. + * + * @param args Command-line arguments excluding the executable and script paths. + */ +export async function main(args = process.argv.slice(2)): Promise { + const { values, positionals } = parseArgs({ + args, + allowPositionals: true, + options: { + cwd: { type: "string" }, + files: { type: "string", multiple: true }, + "dry-run": { type: "boolean", default: false }, + help: { type: "boolean", short: "h", default: false }, + }, + }); + + if (values.help || positionals.length === 0) { + printHelp(); + return; + } + if (positionals.length > 1) { + throw new Error("Expected a single codemod name"); + } + + const name = positionals[0]; + if (!name) { + throw new Error("Expected a codemod name"); + } + const cwd = path.resolve(values.cwd ?? process.cwd()); + const result = await runCodemod(name, { + cwd, + dryRun: values["dry-run"], + files: values.files, + }); + + if (result.changedFiles.length > 0) { + console.log(`${name}: ${result.changedFiles.length} file(s)`); + for (const changedFile of result.changedFiles) { + console.log(` ${changedFile}`); + } + } + console.log( + result.changedFiles.length === 0 + ? "Project is already up to date." + : values["dry-run"] + ? `Would update ${result.changedFiles.length} file(s).` + : `Updated ${result.changedFiles.length} file(s). Run your package manager's install command to refresh its lockfile.` + ); +} + +try { + await main(); +} catch (error) { + console.error(error instanceof Error ? error.message : error); + process.exitCode = 1; +} diff --git a/packages/codemods/src/codemods/vitest-v3-to-v4.ts b/packages/codemods/src/codemods/vitest-v3-to-v4.ts new file mode 100644 index 00000000000..c1e63e01979 --- /dev/null +++ b/packages/codemods/src/codemods/vitest-v3-to-v4.ts @@ -0,0 +1,257 @@ +/** + * Migrates @cloudflare/vitest-pool-workers configuration from Vitest v3 to v4. + * + * Transforms a `defineWorkersProject({ test: { poolOptions: { workers } } })` + * or `defineWorkersConfig(...)` config into + * `defineConfig({ plugins: [cloudflareTest(workers)], test })`, rewriting the + * relevant imports. + */ +import { parseTs, print, types } from "@cloudflare/shared-ast-primitives"; + +const b = types.builders; +const n = types.namedTypes; +const { visit } = types; + +type Node = types.ASTNode; +type ObjectExpression = types.namedTypes.ObjectExpression; +type ObjectProperty = types.namedTypes.ObjectProperty; +type Property = types.namedTypes.Property; +type ImportDeclaration = types.namedTypes.ImportDeclaration; +type NamedProp = ObjectProperty | Property; + +const CONFIG_HELPERS = ["defineWorkersConfig", "defineWorkersProject"]; +const CONFIG_PACKAGES = [ + "@cloudflare/vitest-plugin", + "@cloudflare/vitest-pool-workers", +]; + +/** Returns whether an AST property has the requested identifier key. */ +function isNamedProp(prop: Node, name: string): prop is NamedProp { + return ( + (n.ObjectProperty.check(prop) || n.Property.check(prop)) && + n.Identifier.check(prop.key) && + prop.key.name === name + ); +} + +/** Finds an AST property by identifier key. */ +function findNamedProp( + properties: Node[], + name: string +): NamedProp | undefined { + return properties.find((prop): prop is NamedProp => isNamedProp(prop, name)); +} + +/** Finds a named import specifier on an import declaration. */ +function importSpecifierNamed(imp: ImportDeclaration, imported: string) { + return (imp.specifiers ?? []).find( + (candidate) => + n.ImportSpecifier.check(candidate) && + n.Identifier.check(candidate.imported) && + candidate.imported.name === imported + ); +} + +/** Migrates a Workers Vitest v3 configuration source to the v4 plugin API. */ +export default function transform(source: string): string { + const ast = parseTs(source); + const body = ast.program.body; + + const importDeclarations = body.filter((node): node is ImportDeclaration => + n.ImportDeclaration.check(node) + ); + + const configImports = importDeclarations.flatMap((imp) => + CONFIG_PACKAGES.flatMap((packageName) => + n.StringLiteral.check(imp.source) && + imp.source.value === `${packageName}/config` + ? [{ imp, packageName }] + : [] + ) + ); + + const matchingImports = configImports.flatMap(({ imp, packageName }) => + CONFIG_HELPERS.flatMap((helperName) => { + const specifier = importSpecifierNamed(imp, helperName); + if (!specifier || !n.ImportSpecifier.check(specifier)) { + return []; + } + const localName = n.Identifier.check(specifier.local) + ? specifier.local.name + : helperName; + + // Collect matching call expressions across the whole tree. + const calls: types.namedTypes.CallExpression[] = []; + visit(ast, { + visitCallExpression(path) { + const callee = path.node.callee; + if (n.Identifier.check(callee) && callee.name === localName) { + calls.push(path.node); + } + this.traverse(path); + }, + }); + + return calls.length === 0 + ? [] + : [{ imp, packageName, helperName, localName, calls }]; + }) + ); + + if (matchingImports.length === 0) { + return source; + } + if (matchingImports.length > 1) { + throw new Error("Multiple Workers config helpers are not supported"); + } + + const [{ imp: configImport, packageName, helperName, localName, calls }] = + matchingImports; + + // Resolve the local name for `cloudflareTest`, importing it if absent. + const rootPackageImports = importDeclarations.filter( + (imp) => + n.StringLiteral.check(imp.source) && imp.source.value === packageName + ); + let cloudflareTestName: string | undefined; + for (const imp of rootPackageImports) { + const specifier = importSpecifierNamed(imp, "cloudflareTest"); + if (specifier && n.ImportSpecifier.check(specifier)) { + cloudflareTestName = n.Identifier.check(specifier.local) + ? specifier.local.name + : "cloudflareTest"; + break; + } + } + + if (!cloudflareTestName) { + cloudflareTestName = "cloudflareTest"; + configImport.specifiers = [ + b.importSpecifier(b.identifier(cloudflareTestName)), + ...(configImport.specifiers ?? []), + ]; + } + configImport.source = b.stringLiteral(packageName); + configImport.specifiers = (configImport.specifiers ?? []).filter( + (specifier) => + !( + n.ImportSpecifier.check(specifier) && + n.Identifier.check(specifier.imported) && + specifier.imported.name === helperName + ) + ); + + // Resolve `defineConfig`, reusing the helper from Vite or Vitest if present. + const defineConfigImports = importDeclarations.filter( + (imp) => + n.StringLiteral.check(imp.source) && + (imp.source.value === "vitest/config" || imp.source.value === "vite") + ); + let defineConfigName = "defineConfig"; + let hasDefineConfigImport = false; + for (const imp of defineConfigImports) { + const specifier = importSpecifierNamed(imp, "defineConfig"); + if (specifier && n.ImportSpecifier.check(specifier)) { + defineConfigName = n.Identifier.check(specifier.local) + ? specifier.local.name + : "defineConfig"; + hasDefineConfigImport = true; + break; + } + } + + if (!hasDefineConfigImport) { + const target = defineConfigImports.find( + (imp) => + n.StringLiteral.check(imp.source) && + imp.source.value === "vitest/config" + ); + if (target) { + target.specifiers = [ + ...(target.specifiers ?? []), + b.importSpecifier(b.identifier(defineConfigName)), + ]; + } else { + const lastImportIndex = body.reduce( + (acc, node, index) => (n.ImportDeclaration.check(node) ? index : acc), + -1 + ); + body.splice( + lastImportIndex + 1, + 0, + b.importDeclaration( + [b.importSpecifier(b.identifier(defineConfigName))], + b.stringLiteral("vitest/config") + ) + ); + } + } + + for (const call of calls) { + if (!n.Identifier.check(call.callee) || call.callee.name !== localName) { + continue; + } + call.callee.name = defineConfigName; + + const config = call.arguments[0]; + if (!n.ObjectExpression.check(config)) { + throw new Error( + `${helperName}() is called with a function and not an object, ` + + "and so is too complex to apply a codemod to. " + + "Please refer to the migration docs to perform the migration manually." + ); + } + + const testProp = findNamedProp(config.properties, "test"); + if (!testProp || !n.ObjectExpression.check(testProp.value)) { + throw new Error("Could not find `test` property in config"); + } + const testObj: ObjectExpression = testProp.value; + + const poolOptionsProp = findNamedProp(testObj.properties, "poolOptions"); + if (!poolOptionsProp || !n.ObjectExpression.check(poolOptionsProp.value)) { + throw new Error("Could not find `test.poolOptions` property in config"); + } + const poolOptionsObj: ObjectExpression = poolOptionsProp.value; + + const workersProp = findNamedProp(poolOptionsObj.properties, "workers"); + if ( + !workersProp || + !( + n.ObjectExpression.check(workersProp.value) || + n.FunctionExpression.check(workersProp.value) || + n.ArrowFunctionExpression.check(workersProp.value) + ) + ) { + throw new Error( + "Could not find `test.poolOptions.workers` property in config" + ); + } + + const pluginCall = b.callExpression(b.identifier(cloudflareTestName), [ + workersProp.value, + ]); + const pluginsProp = findNamedProp(config.properties, "plugins"); + if (pluginsProp && n.ArrayExpression.check(pluginsProp.value)) { + pluginsProp.value.elements.unshift(pluginCall); + } else if (pluginsProp) { + throw new Error( + "`plugins` is not an inline array and so is too complex to apply a codemod to. " + + "Please refer to the migration docs to perform the migration manually." + ); + } else { + config.properties.unshift( + b.objectProperty( + b.identifier("plugins"), + b.arrayExpression([pluginCall]) + ) + ); + } + + testObj.properties = testObj.properties.filter( + (prop) => !isNamedProp(prop, "poolOptions") + ) as ObjectExpression["properties"]; + } + + return print(ast).code; +} diff --git a/packages/codemods/src/codemods/vitest.ts b/packages/codemods/src/codemods/vitest.ts new file mode 100644 index 00000000000..d90e8b12e34 --- /dev/null +++ b/packages/codemods/src/codemods/vitest.ts @@ -0,0 +1,179 @@ +import { transformFiles } from "../files"; +import transformV3ToV4 from "./vitest-v3-to-v4"; +import type { Codemod } from "../types"; + +const OLD_PACKAGE = "@cloudflare/vitest-pool-workers"; +const NEW_PACKAGE = "@cloudflare/vitest-plugin"; +const V1_RANGE = "^1.0.0"; +const SOURCE_PATTERNS = ["**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx,json,jsonc}"]; +const PRESERVED_PROTOCOLS = /^(?:(?:workspace|catalog|link|file|npm):|\$)/; + +/** Returns the v1 range while preserving package-manager protocol references. */ +function getV1Specifier(specifier: string): string { + return PRESERVED_PROTOCOLS.test(specifier) ? specifier : V1_RANGE; +} + +/** Renames a package selector and updates any plain version range it contains. */ +function renamePackageSelector(selector: string): string { + const packageIndex = selector.indexOf(OLD_PACKAGE); + if (packageIndex === -1) { + return selector; + } + const versionIndex = packageIndex + OLD_PACKAGE.length; + return selector[versionIndex] === "@" + ? `${selector.slice(0, packageIndex)}${NEW_PACKAGE}@${getV1Specifier(selector.slice(versionIndex + 1))}` + : selector.replaceAll(OLD_PACKAGE, NEW_PACKAGE); +} + +/** + * Recursively migrates package selectors and ranges in an override-style map. + * + * @returns Whether the map was changed. + */ +function renameVersionMap(value: unknown): boolean { + if (!value || typeof value !== "object" || Array.isArray(value)) { + return false; + } + + const entries = Object.entries(value); + let changed = false; + for (const [key, entryValue] of entries) { + const renamedKey = renamePackageSelector(key); + if ( + renamedKey !== key && + entryValue && + typeof entryValue === "object" && + !Array.isArray(entryValue) && + typeof (entryValue as Record)["."] === "string" + ) { + (entryValue as Record)["."] = getV1Specifier( + (entryValue as Record)["."] + ); + changed = true; + } + const renamedValue = + renamedKey !== key && typeof entryValue === "string" + ? getV1Specifier(entryValue) + : entryValue; + changed = renameVersionMap(renamedValue) || changed; + if (renamedKey !== key || renamedValue !== entryValue) { + delete (value as Record)[key]; + (value as Record)[renamedKey] = renamedValue; + changed = true; + } + } + return changed; +} + +/** Renames the Vitest package and updates version-bearing package.json fields. */ +function renamePackageDependency(source: string): string { + let packageJson: { + dependencies?: Record; + devDependencies?: Record; + peerDependencies?: Record; + optionalDependencies?: Record; + overrides?: Record; + resolutions?: Record; + pnpm?: { + overrides?: Record; + packageExtensions?: Record; + }; + }; + try { + packageJson = JSON.parse(source) as typeof packageJson; + } catch { + return source.replaceAll(OLD_PACKAGE, NEW_PACKAGE); + } + + const dependencyGroups = [ + packageJson.dependencies, + packageJson.devDependencies, + packageJson.peerDependencies, + packageJson.optionalDependencies, + ]; + const hasOldDeclaration = dependencyGroups.some( + (dependencies) => dependencies && OLD_PACKAGE in dependencies + ); + const hasNewDeclaration = dependencyGroups.some( + (dependencies) => dependencies && NEW_PACKAGE in dependencies + ); + if (hasOldDeclaration && hasNewDeclaration) { + throw new Error( + `Cannot migrate a package.json with conflicting ${OLD_PACKAGE} and ${NEW_PACKAGE} dependency declarations` + ); + } + + let changed = false; + for (const dependencies of dependencyGroups) { + if (!dependencies || !(OLD_PACKAGE in dependencies)) { + continue; + } + + const entries = Object.entries(dependencies).map(([name, version]) => + name === OLD_PACKAGE + ? [NEW_PACKAGE, getV1Specifier(version)] + : [name, version] + ); + for (const key of Object.keys(dependencies)) { + delete dependencies[key]; + } + Object.assign(dependencies, Object.fromEntries(entries)); + changed = true; + } + for (const versionMap of [ + packageJson.overrides, + packageJson.resolutions, + packageJson.pnpm?.overrides, + packageJson.pnpm?.packageExtensions, + ]) { + changed = renameVersionMap(versionMap) || changed; + } + + if (!changed) { + return source.replaceAll(OLD_PACKAGE, NEW_PACKAGE); + } + + const indent = source.match(/\n([\t ]+)"/)?.[1] ?? "\t"; + const trailingNewline = source.endsWith("\n") ? "\n" : ""; + // Re-serialise the rewritten dependency ranges, then sweep any remaining + // textual references (scripts, `pnpm.overrides`, `resolutions`, custom config + // keys) so nothing is left pointing at the removed package. + return ( + JSON.stringify(packageJson, null, indent).replaceAll( + OLD_PACKAGE, + NEW_PACKAGE + ) + trailingNewline + ); +} + +export const vitestCodemods: Codemod[] = [ + { + name: "vitest:v3-to-v4", + aliases: ["vitest v3 to v4"], + description: "Migrate Workers Vitest configuration from Vitest v3 to v4", + async run(context) { + const changedFiles = await transformFiles( + context, + ["**/vitest.config.{js,cjs,mjs,ts,cts,mts}"], + (source) => transformV3ToV4(source) + ); + return { changedFiles }; + }, + }, + { + name: "vitest:pool-workers-to-vitest-plugin", + aliases: ["vitest pool workers to vitest plugin", "vitest v1"], + description: `Rename ${OLD_PACKAGE} to ${NEW_PACKAGE} v1`, + async run(context) { + const changedFiles = await transformFiles( + context, + SOURCE_PATTERNS, + (source, filePath) => + filePath.endsWith("package.json") + ? renamePackageDependency(source) + : source.replaceAll(OLD_PACKAGE, NEW_PACKAGE) + ); + return { changedFiles }; + }, + }, +]; diff --git a/packages/codemods/src/files.ts b/packages/codemods/src/files.ts new file mode 100644 index 00000000000..5dda67e0f9c --- /dev/null +++ b/packages/codemods/src/files.ts @@ -0,0 +1,59 @@ +import { readFile } from "node:fs/promises"; +import path from "node:path"; +import { glob } from "tinyglobby"; +import type { RunContext } from "./types"; + +const DEFAULT_IGNORES = [ + "**/.git/**", + "**/node_modules/**", + "**/dist/**", + "**/build/**", + "**/.wrangler/**", + "**/package-lock.json", + "**/npm-shrinkwrap.json", +]; + +/** + * Applies a transform to matching files and stages changed outputs in memory. + * + * @param context Shared state and file restrictions for the codemod run. + * @param patterns Glob patterns defining the codemod's file scope. + * @param transform Function that transforms one file's source. + * @returns Paths changed by the transform, relative to the working directory. + */ +export async function transformFiles( + context: RunContext, + patterns: string[], + transform: (source: string, filePath: string) => string +): Promise { + const globOptions = { + cwd: context.cwd, + absolute: true, + dot: true, + ignore: DEFAULT_IGNORES, + } as const; + const filePaths = await glob(patterns, globOptions); + const restrictedPaths = context.files + ? new Set(await glob(context.files, globOptions)) + : undefined; + const changes: Array<{ filePath: string; output: string }> = []; + + for (const filePath of filePaths + .filter((candidate) => !restrictedPaths || restrictedPaths.has(candidate)) + .sort()) { + const source = + context.stagedFiles.get(filePath) ?? (await readFile(filePath, "utf8")); + const output = transform(source, filePath); + if (output === source) { + continue; + } + + changes.push({ filePath, output }); + } + + for (const { filePath, output } of changes) { + context.stagedFiles.set(filePath, output); + } + + return changes.map(({ filePath }) => path.relative(context.cwd, filePath)); +} diff --git a/packages/codemods/src/runner.ts b/packages/codemods/src/runner.ts new file mode 100644 index 00000000000..ecd4557fa3a --- /dev/null +++ b/packages/codemods/src/runner.ts @@ -0,0 +1,52 @@ +import { readFile, writeFile } from "node:fs/promises"; +import { vitestCodemods } from "./codemods/vitest"; +import type { Codemod, CodemodContext, CodemodResult } from "./types"; + +export const availableCodemods: Codemod[] = [...vitestCodemods]; + +/** Returns a canonical form used to compare codemod names and aliases. */ +function normaliseName(value: string): string { + return value + .toLowerCase() + .trim() + .replace(/[^a-z0-9]+/g, "-"); +} + +/** Returns the codemod matching a name or human-readable alias. */ +export function getCodemod(name: string): Codemod | undefined { + return availableCodemods.find((codemod) => + [codemod.name, ...(codemod.aliases ?? [])].some( + (candidate) => normaliseName(candidate) === normaliseName(name) + ) + ); +} + +/** + * Runs a named codemod and writes its staged outputs unless this is a dry run. + * + * @param name Codemod name or alias. + * @param context Working directory, dry-run mode, and optional file restrictions. + * @returns The files changed by the codemod. + */ +export async function runCodemod( + name: string, + context: CodemodContext +): Promise { + const codemod = getCodemod(name); + if (!codemod) { + throw new Error(`Unknown codemod: ${name}`); + } + + const stagedFiles = new Map(); + const result = await codemod.run({ ...context, stagedFiles }); + if (!context.dryRun) { + await Promise.all( + [...stagedFiles].map(async ([filePath, output]) => { + if ((await readFile(filePath, "utf8")) !== output) { + await writeFile(filePath, output); + } + }) + ); + } + return result; +} diff --git a/packages/codemods/src/types.ts b/packages/codemods/src/types.ts new file mode 100644 index 00000000000..9c93af7d468 --- /dev/null +++ b/packages/codemods/src/types.ts @@ -0,0 +1,22 @@ +export interface CodemodContext { + cwd: string; + dryRun: boolean; + files?: string[]; +} + +/** Context for a single codemod within an ordered run. */ +export interface RunContext extends CodemodContext { + /** In-memory writes shared by an ordered codemod run. */ + stagedFiles: Map; +} + +export interface CodemodResult { + changedFiles: string[]; +} + +export interface Codemod { + name: string; + aliases?: string[]; + description: string; + run(context: RunContext): Promise; +} diff --git a/packages/codemods/test/runner.test.ts b/packages/codemods/test/runner.test.ts new file mode 100644 index 00000000000..8a19a967357 --- /dev/null +++ b/packages/codemods/test/runner.test.ts @@ -0,0 +1,378 @@ +import { + mkdtemp, + readdir, + readFile, + rmdir, + unlink, + writeFile, +} from "node:fs/promises"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { afterEach, describe, it } from "vitest"; +import { transformFiles } from "../src/files"; +import { availableCodemods, runCodemod } from "../src/runner"; + +const temporaryDirectories: string[] = []; + +async function createProject(files: Record): Promise { + const directory = await mkdtemp(path.join(tmpdir(), "cloudflare-codemods-")); + temporaryDirectories.push(directory); + for (const [filePath, contents] of Object.entries(files)) { + await writeFile(path.join(directory, filePath), contents); + } + return directory; +} + +afterEach(async () => { + for (const directory of temporaryDirectories.splice(0)) { + for (const fileName of await readdir(directory)) { + await unlink(path.join(directory, fileName)); + } + await rmdir(directory); + } +}); + +describe("codemod runner", () => { + it("runs the Vitest migrations manually, in sequence", async ({ expect }) => { + const cwd = await createProject({ + "package.json": `${JSON.stringify( + { + devDependencies: { + "@cloudflare/vitest-pool-workers": "^0.13.0", + }, + }, + null, + 2 + )}\n`, + "vitest.config.ts": ` +import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config"; +export default defineWorkersProject({ + test: { poolOptions: { workers: { wrangler: { configPath: "./wrangler.jsonc" } } } }, +});`, + "tsconfig.json": JSON.stringify({ + compilerOptions: { + types: ["@cloudflare/vitest-pool-workers/types"], + }, + }), + }); + + await runCodemod("vitest:v3-to-v4", { cwd, dryRun: false }); + await runCodemod("vitest:pool-workers-to-vitest-plugin", { + cwd, + dryRun: false, + }); + + const packageJson = JSON.parse( + await readFile(path.join(cwd, "package.json"), "utf8") + ) as { devDependencies: Record }; + const config = await readFile(path.join(cwd, "vitest.config.ts"), "utf8"); + const tsconfig = await readFile(path.join(cwd, "tsconfig.json"), "utf8"); + + expect(packageJson.devDependencies).toEqual({ + "@cloudflare/vitest-plugin": "^1.0.0", + }); + expect(config).toContain('from "@cloudflare/vitest-plugin"'); + expect(config).toContain("cloudflareTest"); + expect(config).not.toContain("defineWorkersProject"); + expect(tsconfig).toContain("@cloudflare/vitest-plugin/types"); + }); + + it("runs a codemod by its human-readable alias", async ({ expect }) => { + const cwd = await createProject({ + "vitest.config.ts": + 'import { cloudflareTest } from "@cloudflare/vitest-pool-workers";', + }); + + const result = await runCodemod("vitest v1", { cwd, dryRun: false }); + + expect(result.changedFiles).toEqual(["vitest.config.ts"]); + expect( + await readFile(path.join(cwd, "vitest.config.ts"), "utf8") + ).toContain('from "@cloudflare/vitest-plugin"'); + }); + + it("renames the package in package.json outside dependency groups", async ({ + expect, + }) => { + const cwd = await createProject({ + "package.json": `${JSON.stringify( + { + scripts: { + test: "vitest && echo @cloudflare/vitest-pool-workers", + }, + devDependencies: { + "@cloudflare/vitest-pool-workers": "^0.13.0", + }, + pnpm: { + overrides: { + "@cloudflare/vitest-pool-workers": "^0.13.0", + }, + }, + }, + null, + 2 + )}\n`, + }); + + await runCodemod("vitest:pool-workers-to-vitest-plugin", { + cwd, + dryRun: false, + }); + + const output = await readFile(path.join(cwd, "package.json"), "utf8"); + expect(output).not.toContain("@cloudflare/vitest-pool-workers"); + const packageJson = JSON.parse(output) as { + scripts: Record; + devDependencies: Record; + pnpm: { overrides: Record }; + }; + expect(packageJson.devDependencies).toEqual({ + "@cloudflare/vitest-plugin": "^1.0.0", + }); + expect(packageJson.scripts.test).toContain("@cloudflare/vitest-plugin"); + expect(packageJson.pnpm.overrides).toEqual({ + "@cloudflare/vitest-plugin": "^1.0.0", + }); + }); + + it("updates package versions in override maps", async ({ expect }) => { + const cwd = await createProject({ + "package.json": JSON.stringify({ + overrides: { + "@cloudflare/vitest-pool-workers": { ".": "^0.13.0" }, + }, + resolutions: { + "**/@cloudflare/vitest-pool-workers": "~0.18.0", + }, + pnpm: { + overrides: { + "@cloudflare/vitest-pool-workers": "^0.20.0", + }, + packageExtensions: { + "@cloudflare/vitest-pool-workers@^0.18.0": { + dependencies: { + "@cloudflare/vitest-pool-workers": "^0.18.0", + }, + }, + }, + }, + }), + }); + + await runCodemod("vitest:pool-workers-to-vitest-plugin", { + cwd, + dryRun: false, + }); + + const packageJson = JSON.parse( + await readFile(path.join(cwd, "package.json"), "utf8") + ) as { + overrides: Record; + resolutions: Record; + pnpm: { + overrides: Record; + packageExtensions: Record< + string, + { dependencies: Record } + >; + }; + }; + expect(packageJson.overrides).toEqual({ + "@cloudflare/vitest-plugin": { ".": "^1.0.0" }, + }); + expect(packageJson.resolutions).toEqual({ + "**/@cloudflare/vitest-plugin": "^1.0.0", + }); + expect(packageJson.pnpm.overrides).toEqual({ + "@cloudflare/vitest-plugin": "^1.0.0", + }); + expect(packageJson.pnpm.packageExtensions).toEqual({ + "@cloudflare/vitest-plugin@^1.0.0": { + dependencies: { + "@cloudflare/vitest-plugin": "^1.0.0", + }, + }, + }); + }); + + it("renames the package in multiple dependency groups", async ({ + expect, + }) => { + const cwd = await createProject({ + "package.json": JSON.stringify({ + devDependencies: { + "@cloudflare/vitest-pool-workers": "^0.13.0", + }, + peerDependencies: { + "@cloudflare/vitest-pool-workers": "^0.18.0", + }, + }), + }); + + await runCodemod("vitest:pool-workers-to-vitest-plugin", { + cwd, + dryRun: false, + }); + + const packageJson = JSON.parse( + await readFile(path.join(cwd, "package.json"), "utf8") + ) as { + devDependencies: Record; + peerDependencies: Record; + }; + expect(packageJson.devDependencies).toEqual({ + "@cloudflare/vitest-plugin": "^1.0.0", + }); + expect(packageJson.peerDependencies).toEqual({ + "@cloudflare/vitest-plugin": "^1.0.0", + }); + }); + + it("preserves protocol-based dependency specifiers", async ({ expect }) => { + const cwd = await createProject({ + "package.json": JSON.stringify({ + dependencies: { + "@cloudflare/vitest-pool-workers": "workspace:*", + }, + devDependencies: { + "@cloudflare/vitest-pool-workers": "catalog:default", + }, + peerDependencies: { + "@cloudflare/vitest-pool-workers": "link:../vitest-plugin", + }, + optionalDependencies: { + "@cloudflare/vitest-pool-workers": "file:../vitest-plugin", + }, + }), + }); + + await runCodemod("vitest:pool-workers-to-vitest-plugin", { + cwd, + dryRun: false, + }); + + const packageJson = JSON.parse( + await readFile(path.join(cwd, "package.json"), "utf8") + ) as Record>; + expect(packageJson.dependencies?.["@cloudflare/vitest-plugin"]).toBe( + "workspace:*" + ); + expect(packageJson.devDependencies?.["@cloudflare/vitest-plugin"]).toBe( + "catalog:default" + ); + expect(packageJson.peerDependencies?.["@cloudflare/vitest-plugin"]).toBe( + "link:../vitest-plugin" + ); + expect( + packageJson.optionalDependencies?.["@cloudflare/vitest-plugin"] + ).toBe("file:../vitest-plugin"); + }); + + it("throws for an unknown codemod", async ({ expect }) => { + const cwd = await createProject({}); + await expect( + runCodemod("does-not-exist", { cwd, dryRun: false }) + ).rejects.toThrow("Unknown codemod"); + }); + + it("does not write files in dry-run mode", async ({ expect }) => { + const source = + 'import { cloudflareTest } from "@cloudflare/vitest-pool-workers";'; + const cwd = await createProject({ "vitest.config.ts": source }); + + const result = await runCodemod("vitest v1", { cwd, dryRun: true }); + + expect(result.changedFiles).toEqual(["vitest.config.ts"]); + expect(await readFile(path.join(cwd, "vitest.config.ts"), "utf8")).toBe( + source + ); + }); + + it("is a no-op for an up-to-date project", async ({ expect }) => { + const cwd = await createProject({ + "package.json": JSON.stringify({ + devDependencies: { "@cloudflare/vitest-plugin": "^1.0.0" }, + }), + }); + + const result = await runCodemod("vitest:pool-workers-to-vitest-plugin", { + cwd, + dryRun: false, + }); + + expect(result.changedFiles).toEqual([]); + }); + + it("does not make partial changes when a codemod fails", async ({ + expect, + }) => { + const config = ` +import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config"; +export default defineWorkersProject({ + test: { poolOptions: { workers: {} } }, +});`; + const cwd = await createProject({ + "package.json": JSON.stringify({ + devDependencies: { + "@cloudflare/vitest-plugin": "^1.0.0", + "@cloudflare/vitest-pool-workers": "^0.18.0", + }, + }), + "vitest.config.ts": config, + }); + + await expect( + runCodemod("vitest:pool-workers-to-vitest-plugin", { + cwd, + dryRun: false, + }) + ).rejects.toThrow("conflicting"); + expect(await readFile(path.join(cwd, "vitest.config.ts"), "utf8")).toBe( + config + ); + }); + + it("does not flush staged outputs when a codemod throws", async ({ + expect, + }) => { + const cwd = await createProject({ "input.txt": "before" }); + const initialLength = availableCodemods.length; + availableCodemods.push({ + name: "transaction-test", + description: "stage a change, then reject it", + async run(context) { + await transformFiles(context, ["input.txt"], (source) => + source.replace("before", "after") + ); + throw new Error("staged output rejected"); + }, + }); + + try { + await expect( + runCodemod("transaction-test", { cwd, dryRun: false }) + ).rejects.toThrow("staged output rejected"); + expect(await readFile(path.join(cwd, "input.txt"), "utf8")).toBe( + "before" + ); + } finally { + availableCodemods.splice(initialLength); + } + }); + + it("intersects file restrictions with the codemod's scope", async ({ + expect, + }) => { + const source = "@cloudflare/vitest-pool-workers"; + const cwd = await createProject({ "notes.txt": source }); + + const result = await runCodemod("vitest v1", { + cwd, + dryRun: false, + files: ["**/*.txt"], + }); + + expect(result.changedFiles).toEqual([]); + expect(await readFile(path.join(cwd, "notes.txt"), "utf8")).toBe(source); + }); +}); diff --git a/packages/codemod/tests/tsconfig.json b/packages/codemods/test/tsconfig.json similarity index 100% rename from packages/codemod/tests/tsconfig.json rename to packages/codemods/test/tsconfig.json diff --git a/packages/codemods/test/vitest-v3-to-v4.test.ts b/packages/codemods/test/vitest-v3-to-v4.test.ts new file mode 100644 index 00000000000..720285b3de0 --- /dev/null +++ b/packages/codemods/test/vitest-v3-to-v4.test.ts @@ -0,0 +1,135 @@ +import { describe, it } from "vitest"; +import transform from "../src/codemods/vitest-v3-to-v4"; + +function run(source: string): string { + return transform(source); +} + +describe("vitest-v3-to-v4 codemod", () => { + it("transforms a basic defineWorkersProject config", ({ expect }) => { + const output = run(` +import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config"; + +export default defineWorkersProject({ + test: { + poolOptions: { + workers: { + wrangler: { configPath: "./wrangler.jsonc" }, + }, + }, + include: ["tests/**/*.test.ts"], + }, +});`); + + expect(output).toContain( + 'import { cloudflareTest } from "@cloudflare/vitest-pool-workers";' + ); + expect(output).toContain('import { defineConfig } from "vitest/config";'); + expect(output).toContain("plugins: [cloudflareTest({"); + expect(output).toContain('include: ["tests/**/*.test.ts"]'); + expect(output).not.toContain("poolOptions"); + }); + + it("transforms a defineWorkersConfig config", ({ expect }) => { + const output = run(` +import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config"; + +export default defineWorkersConfig({ + test: { poolOptions: { workers: {} } }, +});`); + + expect(output).toContain("export default defineConfig({"); + expect(output).toContain("plugins: [cloudflareTest({})]"); + expect(output).not.toContain("defineWorkersConfig"); + }); + + it("returns source unchanged without a matching import", ({ expect }) => { + const input = `import { defineConfig } from "vitest/config";`; + expect(run(input)).toBe(input); + }); + + it("throws when the config is too complex to migrate", ({ expect }) => { + const input = ` +import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config"; +export default defineWorkersProject(() => ({ test: {} }));`; + + expect(() => run(input)).toThrow("too complex to apply a codemod to"); + }); + + it("preserves existing plugins and config helpers", ({ expect }) => { + const output = run(` +import { defineWorkersProject, readD1Migrations } from "@cloudflare/vitest-pool-workers/config"; +export default defineWorkersProject({ + plugins: [somePlugin()], + test: { poolOptions: { workers: { miniflare: { kvNamespaces: ["KV"] } } } }, +});`); + + expect(output).toContain("cloudflareTest, readD1Migrations"); + expect(output).toContain("}), somePlugin()]"); + }); + + it("throws when plugins are not an inline array", ({ expect }) => { + const input = ` +import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config"; +export default defineWorkersProject({ + plugins: getPlugins(), + test: { poolOptions: { workers: {} } }, +});`; + + expect(() => run(input)).toThrow("`plugins` is not an inline array"); + }); + + it("supports an aliased defineWorkersProject import", ({ expect }) => { + const output = run(` +import { defineWorkersProject as defineProject } from "@cloudflare/vitest-pool-workers/config"; +export default defineProject({ + test: { poolOptions: { workers: {} } }, +});`); + + expect(output).toContain("export default defineConfig({"); + expect(output).not.toContain("defineProject"); + }); + + it("reuses an existing defineConfig import", ({ expect }) => { + const output = run(` +import { defineConfig } from "vitest/config"; +import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config"; +export default defineWorkersProject({ + test: { poolOptions: { workers: {} } }, +});`); + + expect(output.match(/import \{ defineConfig \}/g)).toHaveLength(1); + }); + + it("reuses defineConfig imported from Vite", ({ expect }) => { + const output = run(` +import { defineConfig } from "vite"; +import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config"; +export default defineWorkersProject({ + test: { poolOptions: { workers: {} } }, +});`); + + expect(output).toContain('import { defineConfig } from "vite";'); + expect(output).not.toContain('from "vitest/config"'); + expect(output).toContain("export default defineConfig({"); + }); + + it("transforms configuration after the package rename", ({ expect }) => { + const output = run(` +import { defineWorkersProject } from "@cloudflare/vitest-plugin/config"; +export default defineWorkersProject({ + test: { poolOptions: { workers: {} } }, +});`); + + expect(output).toContain( + 'import { cloudflareTest } from "@cloudflare/vitest-plugin";' + ); + expect(output).toContain("export default defineConfig({"); + }); + + it("does not change helper-only imports", ({ expect }) => { + const input = + 'import { readD1Migrations } from "@cloudflare/vitest-pool-workers/config";'; + expect(run(input)).toBe(input); + }); +}); diff --git a/packages/codemods/tsconfig.json b/packages/codemods/tsconfig.json new file mode 100644 index 00000000000..7e86dbb4629 --- /dev/null +++ b/packages/codemods/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@cloudflare/workers-tsconfig/tsconfig.json", + "compilerOptions": { + "module": "esnext", + "types": ["node"], + "tsBuildInfoFile": ".tsbuildinfo" + }, + "include": ["**/*.ts", "**/*.js"], + "exclude": ["dist", "node_modules", "test/**/*.test.ts"] +} diff --git a/packages/codemods/tsup.config.ts b/packages/codemods/tsup.config.ts new file mode 100644 index 00000000000..38549d4eb44 --- /dev/null +++ b/packages/codemods/tsup.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from "tsup"; + +export default defineConfig(() => [ + { + treeshake: true, + keepNames: true, + entry: ["src/bin.ts"], + platform: "node", + format: "esm", + outExtension: () => ({ js: ".mjs" }), + dts: false, + outDir: "dist", + tsconfig: "tsconfig.json", + metafile: true, + sourcemap: process.env.SOURCEMAPS !== "false", + banner: { + js: 'import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);', + }, + noExternal: [/.*/], + }, +]); diff --git a/packages/codemod/turbo.json b/packages/codemods/turbo.json similarity index 100% rename from packages/codemod/turbo.json rename to packages/codemods/turbo.json diff --git a/packages/codemods/vitest.config.mts b/packages/codemods/vitest.config.mts new file mode 100644 index 00000000000..351ab707464 --- /dev/null +++ b/packages/codemods/vitest.config.mts @@ -0,0 +1,12 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + testTimeout: 15_000, + pool: "forks", + include: ["**/test/**/*.test.ts"], + reporters: ["default"], + unstubEnvs: true, + mockReset: true, + }, +}); diff --git a/packages/create-cloudflare/package.json b/packages/create-cloudflare/package.json index 3ffbbe74060..9185c1da613 100644 --- a/packages/create-cloudflare/package.json +++ b/packages/create-cloudflare/package.json @@ -41,8 +41,8 @@ "@babel/types": "^7.21.4", "@clack/prompts": "^1.2.0", "@cloudflare/cli-shared-helpers": "workspace:*", - "@cloudflare/codemod": "workspace:*", "@cloudflare/mock-npm-registry": "workspace:*", + "@cloudflare/shared-ast-primitives": "workspace:*", "@cloudflare/vite-plugin": "workspace:*", "@cloudflare/workers-tsconfig": "workspace:*", "@cloudflare/workers-types": "catalog:default", diff --git a/packages/create-cloudflare/src/helpers/codemod.ts b/packages/create-cloudflare/src/helpers/codemod.ts index 1ec4045cba6..ba175ba78c4 100644 --- a/packages/create-cloudflare/src/helpers/codemod.ts +++ b/packages/create-cloudflare/src/helpers/codemod.ts @@ -1,6 +1,6 @@ import { lstatSync, readdirSync } from "node:fs"; import { extname, join } from "node:path"; -import { parseFile } from "@cloudflare/codemod"; +import { parseFile } from "@cloudflare/shared-ast-primitives"; import { getTemplatePath } from "../templates"; import type * as recast from "recast"; import type { C3Context } from "types"; diff --git a/packages/create-cloudflare/templates/analog/c3.ts b/packages/create-cloudflare/templates/analog/c3.ts index b2ff764a002..ff3d99d168f 100644 --- a/packages/create-cloudflare/templates/analog/c3.ts +++ b/packages/create-cloudflare/templates/analog/c3.ts @@ -1,6 +1,9 @@ import { logRaw, updateStatus } from "@cloudflare/cli-shared-helpers"; import { blue } from "@cloudflare/cli-shared-helpers/colors"; -import { mergeObjectProperties, transformFile } from "@cloudflare/codemod"; +import { + mergeObjectProperties, + transformFile, +} from "@cloudflare/shared-ast-primitives"; import { runFrameworkGenerator } from "frameworks/index"; import { getWorkerdCompatibilityDate } from "helpers/compatDate"; import { usesTypescript } from "helpers/files"; diff --git a/packages/create-cloudflare/templates/astro/pages/c3.ts b/packages/create-cloudflare/templates/astro/pages/c3.ts index 8781a687f9e..2242faf5c8b 100644 --- a/packages/create-cloudflare/templates/astro/pages/c3.ts +++ b/packages/create-cloudflare/templates/astro/pages/c3.ts @@ -1,7 +1,7 @@ import { logRaw, updateStatus } from "@cloudflare/cli-shared-helpers"; import { blue, brandColor, dim } from "@cloudflare/cli-shared-helpers/colors"; import { runCommand } from "@cloudflare/cli-shared-helpers/command"; -import { transformFile } from "@cloudflare/codemod"; +import { transformFile } from "@cloudflare/shared-ast-primitives"; import { runFrameworkGenerator } from "frameworks/index"; import { usesTypescript } from "helpers/files"; import { detectPackageManager } from "helpers/packageManagers"; diff --git a/packages/create-cloudflare/templates/nuxt/pages/c3.ts b/packages/create-cloudflare/templates/nuxt/pages/c3.ts index 1f88bcb3ffa..1cfa39f5b04 100644 --- a/packages/create-cloudflare/templates/nuxt/pages/c3.ts +++ b/packages/create-cloudflare/templates/nuxt/pages/c3.ts @@ -1,7 +1,10 @@ import { logRaw } from "@cloudflare/cli-shared-helpers"; import { brandColor, dim } from "@cloudflare/cli-shared-helpers/colors"; import { spinner } from "@cloudflare/cli-shared-helpers/interactive"; -import { mergeObjectProperties, transformFile } from "@cloudflare/codemod"; +import { + mergeObjectProperties, + transformFile, +} from "@cloudflare/shared-ast-primitives"; import { runFrameworkGenerator } from "frameworks/index"; import { writeFile } from "helpers/files"; import { detectPackageManager } from "helpers/packageManagers"; diff --git a/packages/create-cloudflare/templates/nuxt/workers/c3.ts b/packages/create-cloudflare/templates/nuxt/workers/c3.ts index 0113664a457..cc8516da714 100644 --- a/packages/create-cloudflare/templates/nuxt/workers/c3.ts +++ b/packages/create-cloudflare/templates/nuxt/workers/c3.ts @@ -1,7 +1,10 @@ import { logRaw } from "@cloudflare/cli-shared-helpers"; import { brandColor, dim } from "@cloudflare/cli-shared-helpers/colors"; import { spinner } from "@cloudflare/cli-shared-helpers/interactive"; -import { mergeObjectProperties, transformFile } from "@cloudflare/codemod"; +import { + mergeObjectProperties, + transformFile, +} from "@cloudflare/shared-ast-primitives"; import { runFrameworkGenerator } from "frameworks/index"; import { writeFile } from "helpers/files"; import { detectPackageManager } from "helpers/packageManagers"; diff --git a/packages/create-cloudflare/templates/qwik/pages/c3.ts b/packages/create-cloudflare/templates/qwik/pages/c3.ts index dfe2de11417..27f27a5b4e7 100644 --- a/packages/create-cloudflare/templates/qwik/pages/c3.ts +++ b/packages/create-cloudflare/templates/qwik/pages/c3.ts @@ -5,7 +5,7 @@ import { runCommand, } from "@cloudflare/cli-shared-helpers/command"; import { spinner } from "@cloudflare/cli-shared-helpers/interactive"; -import { transformFile } from "@cloudflare/codemod"; +import { transformFile } from "@cloudflare/shared-ast-primitives"; import { runFrameworkGenerator } from "frameworks/index"; import { loadTemplateSnippets } from "helpers/codemod"; import { usesTypescript } from "helpers/files"; diff --git a/packages/create-cloudflare/templates/qwik/workers/c3.ts b/packages/create-cloudflare/templates/qwik/workers/c3.ts index ea61589fa9c..84892805dcc 100644 --- a/packages/create-cloudflare/templates/qwik/workers/c3.ts +++ b/packages/create-cloudflare/templates/qwik/workers/c3.ts @@ -5,7 +5,7 @@ import { runCommand, } from "@cloudflare/cli-shared-helpers/command"; import { spinner } from "@cloudflare/cli-shared-helpers/interactive"; -import { transformFile } from "@cloudflare/codemod"; +import { transformFile } from "@cloudflare/shared-ast-primitives"; import { runFrameworkGenerator } from "frameworks/index"; import { loadTemplateSnippets } from "helpers/codemod"; import { usesTypescript } from "helpers/files"; diff --git a/packages/create-cloudflare/templates/react/workers/c3.ts b/packages/create-cloudflare/templates/react/workers/c3.ts index 26024d1eb4a..4f21225a343 100644 --- a/packages/create-cloudflare/templates/react/workers/c3.ts +++ b/packages/create-cloudflare/templates/react/workers/c3.ts @@ -5,7 +5,7 @@ import { inputPrompt, spinner, } from "@cloudflare/cli-shared-helpers/interactive"; -import { transformFile } from "@cloudflare/codemod"; +import { transformFile } from "@cloudflare/shared-ast-primitives"; import { runFrameworkGenerator } from "frameworks/index"; import { readJSON, usesTypescript, writeJSON } from "helpers/files"; import { detectPackageManager } from "helpers/packageManagers"; diff --git a/packages/create-cloudflare/templates/solid/c3.ts b/packages/create-cloudflare/templates/solid/c3.ts index 3ffd823127e..d6e39229ecd 100644 --- a/packages/create-cloudflare/templates/solid/c3.ts +++ b/packages/create-cloudflare/templates/solid/c3.ts @@ -1,6 +1,9 @@ import { logRaw, updateStatus } from "@cloudflare/cli-shared-helpers"; import { blue } from "@cloudflare/cli-shared-helpers/colors"; -import { mergeObjectProperties, transformFile } from "@cloudflare/codemod"; +import { + mergeObjectProperties, + transformFile, +} from "@cloudflare/shared-ast-primitives"; import { runFrameworkGenerator } from "frameworks/index"; import { usesTypescript } from "helpers/files"; import { detectPackageManager } from "helpers/packageManagers"; diff --git a/packages/create-cloudflare/templates/svelte/pages/c3.ts b/packages/create-cloudflare/templates/svelte/pages/c3.ts index d66555f761c..21ea7482bfb 100644 --- a/packages/create-cloudflare/templates/svelte/pages/c3.ts +++ b/packages/create-cloudflare/templates/svelte/pages/c3.ts @@ -1,7 +1,7 @@ import { existsSync } from "node:fs"; import { logRaw, updateStatus } from "@cloudflare/cli-shared-helpers"; import { blue, brandColor, dim } from "@cloudflare/cli-shared-helpers/colors"; -import { transformFile } from "@cloudflare/codemod"; +import { transformFile } from "@cloudflare/shared-ast-primitives"; import { runFrameworkGenerator } from "frameworks/index"; import { usesTypescript } from "helpers/files"; import { detectPackageManager } from "helpers/packageManagers"; diff --git a/packages/create-cloudflare/templates/svelte/workers/c3.ts b/packages/create-cloudflare/templates/svelte/workers/c3.ts index 7e71e4ca2a7..38ce6b3dd38 100644 --- a/packages/create-cloudflare/templates/svelte/workers/c3.ts +++ b/packages/create-cloudflare/templates/svelte/workers/c3.ts @@ -1,6 +1,6 @@ import { logRaw, updateStatus } from "@cloudflare/cli-shared-helpers"; import { blue, brandColor, dim } from "@cloudflare/cli-shared-helpers/colors"; -import { transformFile } from "@cloudflare/codemod"; +import { transformFile } from "@cloudflare/shared-ast-primitives"; import { runFrameworkGenerator } from "frameworks/index"; import { usesTypescript } from "helpers/files"; import { detectPackageManager } from "helpers/packageManagers"; diff --git a/packages/codemod/.gitignore b/packages/shared-ast-primitives/.gitignore similarity index 100% rename from packages/codemod/.gitignore rename to packages/shared-ast-primitives/.gitignore diff --git a/packages/codemod/CHANGELOG.md b/packages/shared-ast-primitives/CHANGELOG.md similarity index 92% rename from packages/codemod/CHANGELOG.md rename to packages/shared-ast-primitives/CHANGELOG.md index 9f917d1e2f7..c52d968af70 100644 --- a/packages/codemod/CHANGELOG.md +++ b/packages/shared-ast-primitives/CHANGELOG.md @@ -1,4 +1,4 @@ -# @cloudflare/codemod +# @cloudflare/shared-ast-primitives ## 1.1.0 diff --git a/packages/codemod/README.md b/packages/shared-ast-primitives/README.md similarity index 58% rename from packages/codemod/README.md rename to packages/shared-ast-primitives/README.md index 6c3352b2cf7..1d9156d5705 100644 --- a/packages/codemod/README.md +++ b/packages/shared-ast-primitives/README.md @@ -1,8 +1,12 @@ -# @cloudflare/codemod +# `@cloudflare/shared-ast-primitives` -Internal codemod utilities for the Cloudflare Workers SDK. +Internal AST-manipulation primitives shared across Cloudflare Workers SDK codemods. -## Developing Codemods +This package wraps [`recast`](https://github.com/benjamn/recast) with a small set of +helpers (`parseJs`, `parseTs`, `parseFile`, `transformFile`, `mergeObjectProperties`) +used by C3 and autoconfig to transform user project files. It is private and not published. + +## Developing Writing codemods often requires trial and error. The package ships a dedicated dev workflow so you can iterate on transforms in isolation without writing throw-away scripts. @@ -32,14 +36,6 @@ Writing codemods often requires trial and error. The package ships a dedicated d 3. **Run `pnpm dev`** — the transformed code is printed to the console and written to `dev-snippets-outputs/test.ts` (gitignored). Inspect the output file to verify the transform behaves as expected. -### Key Files - -| File | Purpose | -| ----------------------- | -------------------------------------------------------------------------------------------- | -| `src/dev.ts` | Dev entry point; exports `testTransform()` and contains the editable `testCodemod()` sandbox | -| `dev-snippets/test.ts` | Default sample input — replace its contents freely | -| `dev-snippets-outputs/` | Auto-generated transform output, gitignored — safe to inspect, never committed | - ### `testTransform(filePath, methods)` Mirrors the production `transformFile()` API but instead of silently writing in place it: @@ -48,16 +44,3 @@ Mirrors the production `transformFile()` API but instead of silently writing in - Writes the result to the corresponding path under `dev-snippets-outputs/` The `filePath` argument must point to a file inside `dev-snippets/`; paths outside that directory are rejected. - -### Inspecting the AST - -`src/dev.ts` includes a commented-out `_printSnippet()` helper. Uncomment its call at the bottom of the file to log the AST of an arbitrary snippet to the console — useful when you need to know the exact node shape to target in a visitor: - -```ts -const _printSnippet = () => { - const snippet = `if (true) { console.log("potato"); }`; - const program = parseTs(snippet).program; - console.log(program.body[0]); -}; -_printSnippet(); // uncomment to run -``` diff --git a/packages/codemod/dev-snippets/test.ts b/packages/shared-ast-primitives/dev-snippets/test.ts similarity index 100% rename from packages/codemod/dev-snippets/test.ts rename to packages/shared-ast-primitives/dev-snippets/test.ts diff --git a/packages/codemod/package.json b/packages/shared-ast-primitives/package.json similarity index 85% rename from packages/codemod/package.json rename to packages/shared-ast-primitives/package.json index e424ec94a6d..71d065321ea 100644 --- a/packages/codemod/package.json +++ b/packages/shared-ast-primitives/package.json @@ -1,8 +1,8 @@ { - "name": "@cloudflare/codemod", + "name": "@cloudflare/shared-ast-primitives", "version": "1.1.0", "private": true, - "description": "Internal codemod utilities", + "description": "Internal AST-manipulation primitives shared across Cloudflare Workers SDK codemods", "homepage": "https://github.com/cloudflare/workers-sdk#readme", "bugs": { "url": "https://github.com/cloudflare/workers-sdk/issues" @@ -12,7 +12,7 @@ "repository": { "type": "git", "url": "https://github.com/cloudflare/workers-sdk.git", - "directory": "packages/codemod" + "directory": "packages/shared-ast-primitives" }, "files": [ "dist" diff --git a/packages/codemod/src/dev.ts b/packages/shared-ast-primitives/src/dev.ts similarity index 100% rename from packages/codemod/src/dev.ts rename to packages/shared-ast-primitives/src/dev.ts diff --git a/packages/codemod/src/index.ts b/packages/shared-ast-primitives/src/index.ts similarity index 83% rename from packages/codemod/src/index.ts rename to packages/shared-ast-primitives/src/index.ts index 65fcaeddf2f..06f71f62247 100644 --- a/packages/codemod/src/index.ts +++ b/packages/shared-ast-primitives/src/index.ts @@ -5,6 +5,13 @@ import * as esprimaParser from "recast/parsers/esprima"; import * as typescriptParser from "recast/parsers/typescript"; import type { Program } from "esprima"; +// Re-export the recast primitives needed to author string-in/string-out +// transforms without taking a direct dependency on `recast`: `print` (AST -> +// string) and the `ast-types` namespace `types` (node builders, `.check()` +// guards, and `types.visit`). Re-exported directly so `types` keeps its +// namespace and can be used in type positions. +export { print, types } from "recast"; + /* CODEMOD TIPS & TRICKS ===================== @@ -24,9 +31,14 @@ import type { Program } from "esprima"; */ -// Parse an input string as javascript and return an ast -export function parseJs(src: string) { - src = src.trim(); +/** + * Parses JavaScript while preserving the source verbatim so transforms can + * print unchanged files byte-for-byte. + * + * @param src JavaScript source to parse. + * @returns The parsed AST. + */ +export function parseJs(src: string): recast.types.namedTypes.File { try { return recast.parse(src, { parser: esprimaParser }); } catch { @@ -34,9 +46,14 @@ export function parseJs(src: string) { } } -// Parse an input string as typescript and return an ast -export function parseTs(src: string) { - src = src.trim(); +/** + * Parses TypeScript while preserving the source verbatim so transforms can + * print unchanged files byte-for-byte. + * + * @param src TypeScript source to parse. + * @returns The parsed AST. + */ +export function parseTs(src: string): recast.types.namedTypes.File { try { return recast.parse(src, { parser: typescriptParser }); } catch { diff --git a/packages/codemod/tests/index.test.ts b/packages/shared-ast-primitives/tests/index.test.ts similarity index 100% rename from packages/codemod/tests/index.test.ts rename to packages/shared-ast-primitives/tests/index.test.ts diff --git a/packages/shared-ast-primitives/tests/tsconfig.json b/packages/shared-ast-primitives/tests/tsconfig.json new file mode 100644 index 00000000000..cfc97eec044 --- /dev/null +++ b/packages/shared-ast-primitives/tests/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "types": ["node"], + "tsBuildInfoFile": "tsconfig.tsbuildinfo" + }, + "include": ["**/*.ts"], + "exclude": [] +} diff --git a/packages/codemod/tsconfig.json b/packages/shared-ast-primitives/tsconfig.json similarity index 100% rename from packages/codemod/tsconfig.json rename to packages/shared-ast-primitives/tsconfig.json diff --git a/packages/codemod/tsup.config.ts b/packages/shared-ast-primitives/tsup.config.ts similarity index 100% rename from packages/codemod/tsup.config.ts rename to packages/shared-ast-primitives/tsup.config.ts diff --git a/packages/shared-ast-primitives/turbo.json b/packages/shared-ast-primitives/turbo.json new file mode 100644 index 00000000000..a90e902eb80 --- /dev/null +++ b/packages/shared-ast-primitives/turbo.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://turbo.build/schema.json", + "extends": ["//"], + "tasks": { + "build": { + "inputs": ["$TURBO_DEFAULT$", "!**/__tests__/**"], + "outputs": ["dist/**"], + "env": ["SOURCEMAPS"], + "passThroughEnv": ["PWD"] + }, + "test:ci": { + "dependsOn": ["build"], + "env": ["LC_ALL", "TZ"] + } + } +} diff --git a/packages/codemod/vitest.config.mts b/packages/shared-ast-primitives/vitest.config.mts similarity index 100% rename from packages/codemod/vitest.config.mts rename to packages/shared-ast-primitives/vitest.config.mts diff --git a/packages/vitest-pool-workers/AGENTS.md b/packages/vitest-pool-workers/AGENTS.md index 7cc0903c479..e010bf37fb5 100644 --- a/packages/vitest-pool-workers/AGENTS.md +++ b/packages/vitest-pool-workers/AGENTS.md @@ -27,11 +27,10 @@ ## BUILD -`tsdown.config.ts` defines 3 separate builds (all ESM): +`tsdown.config.ts` defines 2 separate builds (all ESM): 1. pool — `src/pool/index.ts` → `dist/pool` (emits type declarations) 2. worker + libs — `src/worker/index.ts` plus `src/worker/lib` and `src/worker/node` → `dist/worker` -3. codemods — `src/codemods/vitest-v3-to-v4.ts` → `dist/codemods` Types entry `types/cloudflare-test.d.ts` is hand-written (NOT generated from source). diff --git a/packages/vitest-pool-workers/package.json b/packages/vitest-pool-workers/package.json index eda96833905..b9a640a4f94 100644 --- a/packages/vitest-pool-workers/package.json +++ b/packages/vitest-pool-workers/package.json @@ -39,9 +39,6 @@ }, "./types": { "types": "./types/cloudflare-test.d.ts" - }, - "./codemods/vitest-v3-to-v4": { - "import": "./dist/codemods/vitest-v3-to-v4.mjs" } }, "scripts": { @@ -66,7 +63,6 @@ "@cloudflare/workers-types": "catalog:default", "@cloudflare/workers-utils": "workspace:*", "@cloudflare/workflows-shared": "workspace:*", - "@types/jscodeshift": "^17.3.0", "@types/node": "catalog:default", "@types/semver": "^7.5.1", "@vitest/runner": "catalog:default", @@ -75,7 +71,6 @@ "capnp-es": "catalog:default", "devalue": "^5.6.3", "get-port": "^7.1.0", - "jscodeshift": "^17.3.0", "semver": "^7.7.1", "tree-kill": "catalog:default", "ts-dedent": "^2.2.0", diff --git a/packages/vitest-pool-workers/src/codemods/vitest-v3-to-v4.ts b/packages/vitest-pool-workers/src/codemods/vitest-v3-to-v4.ts deleted file mode 100644 index 5463a4ba8f0..00000000000 --- a/packages/vitest-pool-workers/src/codemods/vitest-v3-to-v4.ts +++ /dev/null @@ -1,271 +0,0 @@ -/** - * jscodeshift codemod: migrate @cloudflare/vitest-pool-workers config from v3 to v4. - * - * Transforms: - * import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config"; - * export default defineWorkersProject({ test: { poolOptions: { workers: { ... } } } }); - * - * Into: - * import { cloudflareTest } from "@cloudflare/vitest-pool-workers"; - * import { defineConfig } from "vitest/config"; - * export default defineConfig({ plugins: [cloudflareTest({ ... })], test: { ... } }); - * - * Usage: - * npx jscodeshift -t node_modules/@cloudflare/vitest-pool-workers/dist/codemods/vitest-v3-to-v4.mjs vitest.config.ts - */ - -// Minimal jscodeshift types — avoids requiring @types/jscodeshift as a dependency. -interface FileInfo { - path: string; - source: string; -} -interface JSCodeshift { - (source: string): Collection; - ImportDeclaration: ASTType; - CallExpression: ASTType; - ObjectExpression: { check(node: unknown): node is ObjectExpressionNode }; - ObjectProperty: { check(node: unknown): node is PropertyNode }; - Property: { check(node: unknown): node is PropertyNode }; - Identifier: { check(node: unknown): node is IdentifierNode }; - FunctionExpression: { check(node: unknown): boolean }; - ArrowFunctionExpression: { check(node: unknown): boolean }; - ArrayExpression: { check(node: unknown): node is ArrayExpressionNode }; - importDeclaration( - specifiers: ImportSpecifierNode[], - source: LiteralNode - ): ImportDeclarationNode; - importSpecifier( - imported: IdentifierNode, - local?: IdentifierNode - ): ImportSpecifierNode; - identifier(name: string): IdentifierNode; - stringLiteral(value: string): LiteralNode; - callExpression( - callee: IdentifierNode, - args: ExpressionNode[] - ): CallExpressionNode; - arrayExpression(elements: ExpressionNode[]): ArrayExpressionNode; - objectProperty(key: IdentifierNode, value: ExpressionNode): PropertyNode; -} - -interface ASTType { - name: string; -} - -interface ASTNode { - type: string; -} - -interface IdentifierNode extends ASTNode { - type: "Identifier"; - name: string; -} - -interface LiteralNode extends ASTNode { - value: string; -} - -interface ImportSpecifierNode extends ASTNode { - type: "ImportSpecifier"; - imported: IdentifierNode; - local?: IdentifierNode; -} - -interface ImportDeclarationNode extends ASTNode { - type: "ImportDeclaration"; - source: LiteralNode; - specifiers: ImportSpecifierNode[]; -} - -interface PropertyNode extends ASTNode { - key: ASTNode; - value: ASTNode; -} - -interface ObjectExpressionNode extends ASTNode { - type: "ObjectExpression"; - properties: PropertyNode[]; -} - -interface ArrayExpressionNode extends ASTNode { - type: "ArrayExpression"; - elements: ExpressionNode[]; -} - -interface CallExpressionNode extends ASTNode { - type: "CallExpression"; - callee: ASTNode; - arguments: ASTNode[]; -} - -type ExpressionNode = ASTNode; - -interface NodePath { - node: T; - parent: NodePath; - insertAfter(node: ASTNode): void; -} - -interface Collection { - find(type: ASTType, filter?: Record): Collection; - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- jscodeshift Collection.forEach accepts callbacks with narrowed NodePath types - forEach(callback: (path: NodePath) => void): Collection; - at(index: number): Collection; - paths(): NodePath[]; - length: number; - toSource(): string; -} - -interface API { - jscodeshift: JSCodeshift; -} - -function isNamedProp( - j: JSCodeshift, - prop: ASTNode, - name: string -): prop is PropertyNode { - return ( - (j.Property.check(prop) || j.ObjectProperty.check(prop)) && - j.Identifier.check(prop.key) && - (prop.key as IdentifierNode).name === name - ); -} - -function findNamedProp( - j: JSCodeshift, - properties: PropertyNode[], - name: string -): PropertyNode | undefined { - return properties.find((prop) => isNamedProp(j, prop, name)); -} - -export default function transform(fileInfo: FileInfo, api: API): string { - const j = api.jscodeshift; - const root = j(fileInfo.source); - - // 1. Find the import of @cloudflare/vitest-pool-workers/config with defineWorkersProject - const configImports = root.find(j.ImportDeclaration, { - source: { value: "@cloudflare/vitest-pool-workers/config" }, - }); - - if (configImports.length === 0) { - // Nothing to transform - return fileInfo.source; - } - - // Update the import: change source and swap defineWorkersProject → cloudflareTest - configImports.forEach((path: NodePath) => { - path.node.source.value = "@cloudflare/vitest-pool-workers"; - path.node.specifiers = [ - j.importSpecifier(j.identifier("cloudflareTest")), - ...(path.node.specifiers?.filter( - (s) => - !( - s.type === "ImportSpecifier" && - s.imported?.name === "defineWorkersProject" - ) - ) ?? []), - ]; - }); - - // 2. Add `import { defineConfig } from "vitest/config"` after the last import - const allImports = root.find(j.ImportDeclaration); - if (allImports.length > 0) { - const lastImport = allImports.at(-1); - lastImport.forEach((path: NodePath) => { - path.insertAfter( - j.importDeclaration( - [j.importSpecifier(j.identifier("defineConfig"))], - j.stringLiteral("vitest/config") - ) - ); - }); - } - - // 3. Transform defineWorkersProject() → defineConfig() with plugins - root - .find(j.CallExpression, { - callee: { name: "defineWorkersProject" }, - }) - .forEach((path: NodePath) => { - // Rename callee - (path.node.callee as IdentifierNode).name = "defineConfig"; - - const config = path.node.arguments[0]; - if (!j.ObjectExpression.check(config)) { - throw new Error( - "defineWorkersProject() is called with a function and not an object, " + - "and so is too complex to apply a codemod to. " + - "Please refer to the migration docs to perform the migration manually." - ); - } - - // Find test.poolOptions.workers - const testProp = findNamedProp(j, config.properties, "test"); - if (!testProp || !j.ObjectExpression.check(testProp.value)) { - throw new Error("Could not find `test` property in config"); - } - const testObj = testProp.value as ObjectExpressionNode; - - const poolOptionsProp = findNamedProp( - j, - testObj.properties, - "poolOptions" - ); - if ( - !poolOptionsProp || - !j.ObjectExpression.check(poolOptionsProp.value) - ) { - throw new Error("Could not find `test.poolOptions` property in config"); - } - const poolOptionsObj = poolOptionsProp.value as ObjectExpressionNode; - - const workersProp = findNamedProp( - j, - poolOptionsObj.properties, - "workers" - ); - if ( - !workersProp || - !( - j.ObjectExpression.check(workersProp.value) || - j.FunctionExpression.check(workersProp.value) || - j.ArrowFunctionExpression.check(workersProp.value) - ) - ) { - throw new Error( - "Could not find `test.poolOptions.workers` property in config" - ); - } - - // Create plugins: [cloudflareTest()] - const pluginCall = j.callExpression(j.identifier("cloudflareTest"), [ - workersProp.value as ExpressionNode, - ]); - - const pluginsProp = findNamedProp(j, config.properties, "plugins"); - if (pluginsProp && j.ArrayExpression.check(pluginsProp.value)) { - // Prepend to existing plugins array - (pluginsProp.value as ArrayExpressionNode).elements.unshift(pluginCall); - } else { - // Create new plugins property at the start - config.properties.unshift( - j.objectProperty( - j.identifier("plugins"), - j.arrayExpression([pluginCall]) - ) - ); - } - - // Remove poolOptions from test - testObj.properties = testObj.properties.filter( - (prop) => !isNamedProp(j, prop, "poolOptions") - ); - }); - - return root.toSource(); -} - -// Tell jscodeshift to use the TypeScript parser -export const parser = "ts"; diff --git a/packages/vitest-pool-workers/test/codemods.test.ts b/packages/vitest-pool-workers/test/codemods.test.ts deleted file mode 100644 index d1d61337649..00000000000 --- a/packages/vitest-pool-workers/test/codemods.test.ts +++ /dev/null @@ -1,206 +0,0 @@ -import jscodeshift from "jscodeshift"; -import { describe, it } from "vitest"; -import transform from "../src/codemods/vitest-v3-to-v4"; - -function run(source: string): string { - return transform( - { path: "vitest.config.ts", source }, - // Cast needed: @types/jscodeshift's full JSCodeshift type is structurally - // incompatible with the codemod's minimal JSCodeshift interface, but they - // are compatible at runtime. - { jscodeshift: jscodeshift.withParser("ts") as never } - ); -} - -describe("vitest-v3-to-v4 codemod", () => { - it("transforms a basic defineWorkersProject config", ({ expect }) => { - const input = ` -import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config"; - -export default defineWorkersProject({ - test: { - poolOptions: { - workers: { - wrangler: { configPath: "./wrangler.toml" }, - }, - }, - }, -});`; - - const output = run(input); - - expect(output).toMatchInlineSnapshot(` - " - import { cloudflareTest } from "@cloudflare/vitest-pool-workers"; - - import { defineConfig } from "vitest/config"; - - export default defineConfig({ - plugins: [cloudflareTest({ - wrangler: { configPath: "./wrangler.toml" }, - })], - - test: {} - });" - `); - }); - - it("preserves non-poolOptions test properties", ({ expect }) => { - const input = ` -import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config"; - -export default defineWorkersProject({ - test: { - poolOptions: { - workers: { - wrangler: { configPath: "./wrangler.toml" }, - }, - }, - include: ["tests/**/*.test.ts"], - }, -});`; - - const output = run(input); - - expect(output).toMatchInlineSnapshot(` - " - import { cloudflareTest } from "@cloudflare/vitest-pool-workers"; - - import { defineConfig } from "vitest/config"; - - export default defineConfig({ - plugins: [cloudflareTest({ - wrangler: { configPath: "./wrangler.toml" }, - })], - - test: { - include: ["tests/**/*.test.ts"] - } - });" - `); - }); - - it("preserves miniflare options inside workers", ({ expect }) => { - const input = ` -import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config"; - -export default defineWorkersProject({ - test: { - poolOptions: { - workers: { - wrangler: { configPath: "./wrangler.toml" }, - miniflare: { d1Databases: ["DB"] }, - }, - }, - }, -});`; - - const output = run(input); - - expect(output).toMatchInlineSnapshot(` - " - import { cloudflareTest } from "@cloudflare/vitest-pool-workers"; - - import { defineConfig } from "vitest/config"; - - export default defineConfig({ - plugins: [cloudflareTest({ - wrangler: { configPath: "./wrangler.toml" }, - miniflare: { d1Databases: ["DB"] }, - })], - - test: {} - });" - `); - }); - - it("returns source unchanged if no matching import", ({ expect }) => { - const input = ` -import { defineConfig } from "vitest/config"; - -export default defineConfig({ test: {} });`; - - expect(run(input)).toBe(input); - }); - - it("throws for function argument to defineWorkersProject", ({ expect }) => { - const input = ` -import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config"; - -export default defineWorkersProject(() => ({ - test: { - poolOptions: { - workers: { wrangler: { configPath: "./wrangler.toml" } }, - }, - }, -}));`; - - expect(() => run(input)).toThrow("too complex to apply a codemod to"); - }); - - it("prepends to existing plugins array", ({ expect }) => { - const input = ` -import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config"; - -export default defineWorkersProject({ - plugins: [somePlugin()], - test: { - poolOptions: { - workers: { - wrangler: { configPath: "./wrangler.toml" }, - }, - }, - }, -});`; - - const output = run(input); - - expect(output).toMatchInlineSnapshot(` - " - import { cloudflareTest } from "@cloudflare/vitest-pool-workers"; - - import { defineConfig } from "vitest/config"; - - export default defineConfig({ - plugins: [cloudflareTest({ - wrangler: { configPath: "./wrangler.toml" }, - }), somePlugin()], - test: {}, - });" - `); - }); - - it("preserves other import specifiers from the config module", ({ - expect, - }) => { - const input = ` -import { defineWorkersProject, readD1Migrations } from "@cloudflare/vitest-pool-workers/config"; - -export default defineWorkersProject({ - test: { - poolOptions: { - workers: { - wrangler: { configPath: "./wrangler.toml" }, - }, - }, - }, -});`; - - const output = run(input); - - expect(output).toMatchInlineSnapshot(` - " - import { cloudflareTest, readD1Migrations } from "@cloudflare/vitest-pool-workers"; - - import { defineConfig } from "vitest/config"; - - export default defineConfig({ - plugins: [cloudflareTest({ - wrangler: { configPath: "./wrangler.toml" }, - })], - - test: {} - });" - `); - }); -}); diff --git a/packages/vitest-pool-workers/tsdown.config.ts b/packages/vitest-pool-workers/tsdown.config.ts index f04e98c0072..31df47f2992 100644 --- a/packages/vitest-pool-workers/tsdown.config.ts +++ b/packages/vitest-pool-workers/tsdown.config.ts @@ -91,16 +91,5 @@ export default defineConfig(async () => { JSON.stringify(builtinModules), }, }, - // Codemods — standalone jscodeshift-compatible transforms - { - ...commonOptions, - entry: path.join(pkgRoot, "src", "codemods", "vitest-v3-to-v4.ts"), - outDir: path.join(pkgRoot, "dist", "codemods"), - dts: false, - // Codemods run in the user's Node.js environment via jscodeshift, - // which provides its own AST manipulation API at runtime. - // No bundled dependencies needed. - external: [/./], - }, ]; }); diff --git a/packages/wrangler/package.json b/packages/wrangler/package.json index ac325f85bb9..867d7113085 100644 --- a/packages/wrangler/package.json +++ b/packages/wrangler/package.json @@ -93,7 +93,6 @@ "@cloudflare/autoconfig": "workspace:*", "@cloudflare/build-output-utils": "workspace:*", "@cloudflare/cli-shared-helpers": "workspace:*", - "@cloudflare/codemod": "workspace:*", "@cloudflare/config": "workspace:*", "@cloudflare/containers-shared": "workspace:*", "@cloudflare/deploy-helpers": "workspace:*", @@ -101,6 +100,7 @@ "@cloudflare/pages-shared": "workspace:^", "@cloudflare/remote-bindings": "workspace:*", "@cloudflare/runtime-types": "workspace:*", + "@cloudflare/shared-ast-primitives": "workspace:*", "@cloudflare/types": "6.18.4", "@cloudflare/workers-auth": "workspace:*", "@cloudflare/workers-shared": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ada0630a075..3a473fcdac0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1696,9 +1696,9 @@ importers: specifier: workspace:* version: link:../workers-utils devDependencies: - '@cloudflare/codemod': + '@cloudflare/shared-ast-primitives': specifier: workspace:* - version: link:../codemod + version: link:../shared-ast-primitives '@cloudflare/workers-tsconfig': specifier: workspace:* version: link:../workers-tsconfig @@ -1737,7 +1737,7 @@ importers: version: 5.8.3 vitest: specifier: catalog:default - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.14.6(@types/node@22.15.17)(typescript@5.8.3))(vite@8.2.0(@types/node@22.15.17)(esbuild@0.23.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.14.6(@types/node@22.15.17)(typescript@5.8.3))(vite@8.2.0(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) packages/build-output-utils: dependencies: @@ -1801,38 +1801,29 @@ importers: specifier: ^0.15.9 version: 0.15.9(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(typescript@5.9.3) - packages/codemod: + packages/codemods: devDependencies: + '@cloudflare/shared-ast-primitives': + specifier: workspace:* + version: link:../shared-ast-primitives '@cloudflare/workers-tsconfig': specifier: workspace:* version: link:../workers-tsconfig - '@types/esprima': - specifier: ^4.0.3 - version: 4.0.3 '@types/node': specifier: 22.15.17 version: 22.15.17 - '@vitest/ui': + tinyglobby: specifier: catalog:default - version: 4.1.0(vitest@4.1.0) - concurrently: - specifier: ^8.2.2 - version: 8.2.2 - recast: - specifier: ^0.23.11 - version: 0.23.11 + version: 0.2.16 tsup: specifier: 8.3.0 - version: 8.3.0(@microsoft/api-extractor@7.52.8(@types/node@22.15.17))(jiti@2.6.1)(postcss@8.5.23)(tsx@3.12.10)(typescript@5.8.3)(yaml@2.9.0) - tsx: - specifier: ^3.12.8 - version: 3.12.10 + version: 8.3.0(@microsoft/api-extractor@7.52.8(@types/node@22.15.17))(jiti@2.6.1)(postcss@8.5.23)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0) typescript: specifier: catalog:default version: 5.8.3 vitest: specifier: catalog:default - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.14.6(@types/node@22.15.17)(typescript@5.8.3))(vite@8.2.0(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@3.12.10)(yaml@2.9.0)) + version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.14.6(@types/node@22.15.17)(typescript@5.8.3))(vite@8.2.0(@types/node@22.15.17)(esbuild@0.23.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) packages/config: dependencies: @@ -1894,12 +1885,12 @@ importers: '@cloudflare/cli-shared-helpers': specifier: workspace:* version: link:../cli - '@cloudflare/codemod': - specifier: workspace:* - version: link:../codemod '@cloudflare/mock-npm-registry': specifier: workspace:* version: link:../mock-npm-registry + '@cloudflare/shared-ast-primitives': + specifier: workspace:* + version: link:../shared-ast-primitives '@cloudflare/vite-plugin': specifier: workspace:* version: link:../vite-plugin-cloudflare @@ -2762,6 +2753,39 @@ importers: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.14.6(@types/node@22.15.17)(typescript@5.8.3))(vite@8.2.0(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) + packages/shared-ast-primitives: + devDependencies: + '@cloudflare/workers-tsconfig': + specifier: workspace:* + version: link:../workers-tsconfig + '@types/esprima': + specifier: ^4.0.3 + version: 4.0.3 + '@types/node': + specifier: 22.15.17 + version: 22.15.17 + '@vitest/ui': + specifier: catalog:default + version: 4.1.0(vitest@4.1.0) + concurrently: + specifier: ^8.2.2 + version: 8.2.2 + recast: + specifier: ^0.23.11 + version: 0.23.11 + tsup: + specifier: 8.3.0 + version: 8.3.0(@microsoft/api-extractor@7.52.8(@types/node@22.15.17))(jiti@2.6.1)(postcss@8.5.23)(tsx@3.12.10)(typescript@5.8.3)(yaml@2.9.0) + tsx: + specifier: ^3.12.8 + version: 3.12.10 + typescript: + specifier: catalog:default + version: 5.8.3 + vitest: + specifier: catalog:default + version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.14.6(@types/node@22.15.17)(typescript@5.8.3))(vite@8.2.0(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@3.12.10)(yaml@2.9.0)) + packages/solarflare-theme: {} packages/turbo-r2-archive: @@ -4109,9 +4133,6 @@ importers: '@cloudflare/workflows-shared': specifier: workspace:* version: link:../workflows-shared - '@types/jscodeshift': - specifier: ^17.3.0 - version: 17.3.0 '@types/node': specifier: 22.15.17 version: 22.15.17 @@ -4136,9 +4157,6 @@ importers: get-port: specifier: ^7.1.0 version: 7.1.0 - jscodeshift: - specifier: ^17.3.0 - version: 17.3.0 semver: specifier: ^7.7.1 version: 7.7.1 @@ -4558,9 +4576,6 @@ importers: '@cloudflare/cli-shared-helpers': specifier: workspace:* version: link:../cli - '@cloudflare/codemod': - specifier: workspace:* - version: link:../codemod '@cloudflare/config': specifier: workspace:* version: link:../config @@ -4582,6 +4597,9 @@ importers: '@cloudflare/runtime-types': specifier: workspace:* version: link:../runtime-types + '@cloudflare/shared-ast-primitives': + specifier: workspace:* + version: link:../shared-ast-primitives '@cloudflare/types': specifier: 6.18.4 version: 6.18.4(react@19.2.4) @@ -5194,10 +5212,6 @@ packages: resolution: {integrity: sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==} engines: {node: ^22.18.0 || >=24.11.0} - '@babel/helper-annotate-as-pure@7.27.3': - resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} - engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@8.0.0': resolution: {integrity: sha512-NSpMkMsvvZqzThJ0p1B02cbtA2ObEyfBvq950bmNkyxsxvcxwhvvCB036rKhlEnuBBo30bOrk13u3FzlKSoRrw==} engines: {node: ^22.18.0 || >=24.11.0} @@ -5210,12 +5224,6 @@ packages: resolution: {integrity: sha512-JwculLABZvyPvyLBpwU/E/IbH2uM3mnxNtIJpxnIfb24y1PrdVxK5Dqjle4DpgqpGRnwgC7G8IkzPdSXZrO1Ew==} engines: {node: ^22.18.0 || >=24.11.0} - '@babel/helper-create-class-features-plugin@7.28.6': - resolution: {integrity: sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/helper-create-class-features-plugin@8.0.1': resolution: {integrity: sha512-++t3ZktzlLmASAxIlxeXQK9Z2YwUafYGYcvGBFevqOqt16HozVHStUoQvWD09fzAZOb/uJGpUTBuGK41AJAuOA==} engines: {node: ^22.18.0 || >=24.11.0} @@ -5230,10 +5238,6 @@ packages: resolution: {integrity: sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==} engines: {node: ^22.18.0 || >=24.11.0} - '@babel/helper-member-expression-to-functions@7.28.5': - resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} - engines: {node: '>=6.9.0'} - '@babel/helper-member-expression-to-functions@8.0.0': resolution: {integrity: sha512-xkXrMbtk87Gk7+oKBVmBc6EORg/Qwx++AHESldmHkpvG8wgccdhJJFwrzqlF382Fk8wfXhJHWE/g/43QvEGNPQ==} engines: {node: ^22.18.0 || >=24.11.0} @@ -5248,10 +5252,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-optimise-call-expression@7.27.1': - resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} - engines: {node: '>=6.9.0'} - '@babel/helper-optimise-call-expression@8.0.0': resolution: {integrity: sha512-3W6satvtPuCUkUx63S2jMoW9EQNYkADgs1HTfufmL7gCmAulHMKupA/12WNz4A0GMMFn/YnWWwqOT9IZrJHQjg==} engines: {node: ^22.18.0 || >=24.11.0} @@ -5266,22 +5266,12 @@ packages: peerDependencies: '@babel/core': ^8.0.0 - '@babel/helper-replace-supers@7.28.6': - resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/helper-replace-supers@8.0.1': resolution: {integrity: sha512-B1SZADIcy3tmH8CmWvj4SHi/oAPom4UL3uknTc2QRNsPVLFk/sPnZvQL/8kj7Y5omvjMqie0vklvs6XM4OLW5Q==} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': - resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} - engines: {node: '>=6.9.0'} - '@babel/helper-skip-transparent-expression-wrappers@8.0.0': resolution: {integrity: sha512-xmCA9kP3IhySsqhzwIdWGlDN/1A4cCKNBO/uwZx/3YzmDoMePwno2Q5/Bq0q+tYaKbeF940YiKV/kaW8Mzvpjw==} engines: {node: ^22.18.0 || >=24.11.0} @@ -5353,12 +5343,6 @@ packages: peerDependencies: '@babel/core': ^8.0.0 - '@babel/plugin-syntax-flow@7.28.6': - resolution: {integrity: sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-jsx@7.28.6': resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} engines: {node: '>=6.9.0'} @@ -5371,42 +5355,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-properties@7.28.6': - resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-flow-strip-types@7.27.1': - resolution: {integrity: sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-modules-commonjs@7.28.6': - resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-nullish-coalescing-operator@7.28.6': - resolution: {integrity: sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-optional-chaining@7.28.6': - resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-private-methods@7.28.6': - resolution: {integrity: sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-self@7.27.1': resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} engines: {node: '>=6.9.0'} @@ -5419,30 +5367,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.28.6': - resolution: {integrity: sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/preset-flow@7.27.1': - resolution: {integrity: sha512-ez3a2it5Fn6P54W8QkbfIyyIbxlXvcxyWHHvno1Wg0Ej5eiJY5hBb8ExttoIOJJk7V2dZE6prP7iby5q2aQ0Lg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/preset-typescript@7.28.5': - resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/register@7.28.6': - resolution: {integrity: sha512-pgcbbEl/dWQYb6L6Yew6F94rdwygfuv+vJ/tXfwIOYAfPB6TNWpXUMEtEq3YuTeHRdvMIhvz13bkT9CNaS+wqA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/runtime-corejs3@7.27.0': resolution: {integrity: sha512-UWjX6t+v+0ckwZ50Y5ShZLnlk95pP5MyW/pon9tiYzl3+18pkTHTFNTKr7rQbfRXPkowt2QAn30o1b6oswszew==} engines: {node: '>=6.9.0'} @@ -5857,7 +5781,7 @@ packages: react: ^15.0.0-0 || ^16.0.0-0 || ^17.0.0-0 '@cloudflare/intl-types@1.5.7': - resolution: {integrity: sha512-5p+NqAoM3rOMsZsAS6RMWvClhuxWA3YqRkfIxkTcc6uYNsays90GuyzdXmN/v+T7UiSkmzRa7Atu75tD/245MQ==} + resolution: {integrity: sha512-5p+NqAoM3rOMsZsAS6RMWvClhuxWA3YqRkfIxkTcc6uYNsays90GuyzdXmN/v+T7UiSkmzRa7Atu75tD/245MQ==, tarball: https://registry.npmjs.org/@cloudflare/intl-types/-/intl-types-1.5.7.tgz} peerDependencies: react: ^15.0.0-0 || ^16.0.0-0 || ^17.0.0-0 @@ -5908,7 +5832,7 @@ packages: react: ^15.0.0-0 || ^16.0.0-0 || ^17.0.0-0 '@cloudflare/style-const@6.1.3': - resolution: {integrity: sha512-kwKNttljHfLMY9iVY4r4P00L9TrIc3xWvFoFte/ImLIOq13MH1sDXkQbA8nLC1qcFq7Liv/gbGrtgouPW2lO5Q==} + resolution: {integrity: sha512-kwKNttljHfLMY9iVY4r4P00L9TrIc3xWvFoFte/ImLIOq13MH1sDXkQbA8nLC1qcFq7Liv/gbGrtgouPW2lO5Q==, tarball: https://registry.npmjs.org/@cloudflare/style-const/-/style-const-6.1.3.tgz} peerDependencies: react: ^15.0.0-0 || ^16.0.0-0 || ^17.0.0-0 @@ -5936,7 +5860,7 @@ packages: react: ^15.0.0-0 || ^16.0.0-0 || ^17.0.0-0 '@cloudflare/types@6.29.1': - resolution: {integrity: sha512-3AfpWx3G47NWgrkTMMIxcDxl/JpS8K4a5w28+4afK8Eyzd2Mnh7+JRB3C59A6mUjo6e+KTJ/cEvyVIHYO6FQDA==} + resolution: {integrity: sha512-3AfpWx3G47NWgrkTMMIxcDxl/JpS8K4a5w28+4afK8Eyzd2Mnh7+JRB3C59A6mUjo6e+KTJ/cEvyVIHYO6FQDA==, tarball: https://registry.npmjs.org/@cloudflare/types/-/types-6.29.1.tgz} peerDependencies: react: ^15.0.0-0 || ^16.0.0-0 || ^17.0.0-0 @@ -5953,7 +5877,7 @@ packages: resolution: {integrity: sha512-qdCFf90hoZzT4o4xEmxOKUf9+bEJNGh4ANnRYApo6BMyVnHoHEHAQ3nWmGSHBmo+W9hOk2Ik7r1oHLbI0O/RRg==} '@cloudflare/util-en-garde@8.0.15': - resolution: {integrity: sha512-wFs0Ug1TGhGcYZZ2JfPM4g0fNTIkBKSp8XRdZHZ3iq+2B/RdP7fYbzZxWpdUKzphVt+S2gVpMbo3nwn+dRhw+g==} + resolution: {integrity: sha512-wFs0Ug1TGhGcYZZ2JfPM4g0fNTIkBKSp8XRdZHZ3iq+2B/RdP7fYbzZxWpdUKzphVt+S2gVpMbo3nwn+dRhw+g==, tarball: https://registry.npmjs.org/@cloudflare/util-en-garde/-/util-en-garde-8.0.15.tgz} '@cloudflare/util-hooks@1.3.1': resolution: {integrity: sha512-gIsPlzgUbMswIE1h8vGK6LZr/Io5yocUl01WCLy5fxEajhCQ0mNLixkD2Uqne+WPTfqzu4jgC5NxYXgl+Hf6yQ==} @@ -5971,91 +5895,91 @@ packages: vitest: ^4.1.0 '@cloudflare/workerd-darwin-64@1.20260317.1': - resolution: {integrity: sha512-8hjh3sPMwY8M/zedq3/sXoA2Q4BedlGufn3KOOleIG+5a4ReQKLlUah140D7J6zlKmYZAFMJ4tWC7hCuI/s79g==} + resolution: {integrity: sha512-8hjh3sPMwY8M/zedq3/sXoA2Q4BedlGufn3KOOleIG+5a4ReQKLlUah140D7J6zlKmYZAFMJ4tWC7hCuI/s79g==, tarball: https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260317.1.tgz} engines: {node: '>=16'} cpu: [x64] os: [darwin] '@cloudflare/workerd-darwin-64@1.20260423.1': - resolution: {integrity: sha512-+1vfsTa/fyE/9GCrNHBkWdIYj4gXSjWSH7ATdy7/FHs07iB8Mapuk8/sW8Fxs2oXpaNeJ3CN83Ux5i4nQDlwNw==} + resolution: {integrity: sha512-+1vfsTa/fyE/9GCrNHBkWdIYj4gXSjWSH7ATdy7/FHs07iB8Mapuk8/sW8Fxs2oXpaNeJ3CN83Ux5i4nQDlwNw==, tarball: https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260423.1.tgz} engines: {node: '>=16'} cpu: [x64] os: [darwin] '@cloudflare/workerd-darwin-64@1.20260815.1': - resolution: {integrity: sha512-7PsLdcz6pT9EMd1EJGZEgMyYRfs0CHxGs62PS2L1w3s6+xGmQcRXKm/zoMftmqZF45JBa4MzFeownRKbRt/x5g==} + resolution: {integrity: sha512-7PsLdcz6pT9EMd1EJGZEgMyYRfs0CHxGs62PS2L1w3s6+xGmQcRXKm/zoMftmqZF45JBa4MzFeownRKbRt/x5g==, tarball: https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260815.1.tgz} engines: {node: '>=16'} cpu: [x64] os: [darwin] '@cloudflare/workerd-darwin-arm64@1.20260317.1': - resolution: {integrity: sha512-M/MnNyvO5HMgoIdr3QHjdCj2T1ki9gt0vIUnxYxBu9ISXS/jgtMl6chUVPJ7zHYBn9MyYr8ByeN6frjYxj0MGg==} + resolution: {integrity: sha512-M/MnNyvO5HMgoIdr3QHjdCj2T1ki9gt0vIUnxYxBu9ISXS/jgtMl6chUVPJ7zHYBn9MyYr8ByeN6frjYxj0MGg==, tarball: https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260317.1.tgz} engines: {node: '>=16'} cpu: [arm64] os: [darwin] '@cloudflare/workerd-darwin-arm64@1.20260423.1': - resolution: {integrity: sha512-plI6RlUg+hPZ83DUbeWCNG+JNeVHLysinowtYT8O02LKm9Dy2GMYgnosOe3xDFhKE7ou+9SI8KJiNfSnwgHSIg==} + resolution: {integrity: sha512-plI6RlUg+hPZ83DUbeWCNG+JNeVHLysinowtYT8O02LKm9Dy2GMYgnosOe3xDFhKE7ou+9SI8KJiNfSnwgHSIg==, tarball: https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260423.1.tgz} engines: {node: '>=16'} cpu: [arm64] os: [darwin] '@cloudflare/workerd-darwin-arm64@1.20260815.1': - resolution: {integrity: sha512-60wtg8ng7FVWeOg/UMbZ9Ye0sslpRRAKoftPbdtuH2volq676quxVr6Zm2EjVULH/JFZeCn72dbLlrnbh0Mpcw==} + resolution: {integrity: sha512-60wtg8ng7FVWeOg/UMbZ9Ye0sslpRRAKoftPbdtuH2volq676quxVr6Zm2EjVULH/JFZeCn72dbLlrnbh0Mpcw==, tarball: https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260815.1.tgz} engines: {node: '>=16'} cpu: [arm64] os: [darwin] '@cloudflare/workerd-linux-64@1.20260317.1': - resolution: {integrity: sha512-1ltuEjkRcS3fsVF7CxsKlWiRmzq2ZqMfqDN0qUOgbUwkpXsLVJsXmoblaLf5OP00ELlcgF0QsN0p2xPEua4Uug==} + resolution: {integrity: sha512-1ltuEjkRcS3fsVF7CxsKlWiRmzq2ZqMfqDN0qUOgbUwkpXsLVJsXmoblaLf5OP00ELlcgF0QsN0p2xPEua4Uug==, tarball: https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260317.1.tgz} engines: {node: '>=16'} cpu: [x64] os: [linux] '@cloudflare/workerd-linux-64@1.20260423.1': - resolution: {integrity: sha512-Ud5xtpXhCB3/XtgJK4ac6VYlQAb/LPOflvhi0/ncndx8dSyJ8iXHGXd7VOOikGXGWdRsttGpstlXYUEN6hU8TQ==} + resolution: {integrity: sha512-Ud5xtpXhCB3/XtgJK4ac6VYlQAb/LPOflvhi0/ncndx8dSyJ8iXHGXd7VOOikGXGWdRsttGpstlXYUEN6hU8TQ==, tarball: https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260423.1.tgz} engines: {node: '>=16'} cpu: [x64] os: [linux] '@cloudflare/workerd-linux-64@1.20260815.1': - resolution: {integrity: sha512-MuqKIHPo0Qyo8MZMmy0lP2B5PeAL7f4T9Fu4Usk3QdbV4JIrKG/OoybN3Ign7m/Dff+L1Oo/ZHydB+hEg1ueFw==} + resolution: {integrity: sha512-MuqKIHPo0Qyo8MZMmy0lP2B5PeAL7f4T9Fu4Usk3QdbV4JIrKG/OoybN3Ign7m/Dff+L1Oo/ZHydB+hEg1ueFw==, tarball: https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260815.1.tgz} engines: {node: '>=16'} cpu: [x64] os: [linux] '@cloudflare/workerd-linux-arm64@1.20260317.1': - resolution: {integrity: sha512-3QrNnPF1xlaNwkHpasvRvAMidOvQs2NhXQmALJrEfpIJ/IDL2la8g499yXp3eqhG3hVMCB07XVY149GTs42Xtw==} + resolution: {integrity: sha512-3QrNnPF1xlaNwkHpasvRvAMidOvQs2NhXQmALJrEfpIJ/IDL2la8g499yXp3eqhG3hVMCB07XVY149GTs42Xtw==, tarball: https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260317.1.tgz} engines: {node: '>=16'} cpu: [arm64] os: [linux] '@cloudflare/workerd-linux-arm64@1.20260423.1': - resolution: {integrity: sha512-xI2SqbkRDOwPQUUGd8N6qb2wuxFlu6GWi7qz6OFolZIDGu6m5Q/oMzMtV0txkXVClw1puLWYlc/wcURxAi+qsg==} + resolution: {integrity: sha512-xI2SqbkRDOwPQUUGd8N6qb2wuxFlu6GWi7qz6OFolZIDGu6m5Q/oMzMtV0txkXVClw1puLWYlc/wcURxAi+qsg==, tarball: https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260423.1.tgz} engines: {node: '>=16'} cpu: [arm64] os: [linux] '@cloudflare/workerd-linux-arm64@1.20260815.1': - resolution: {integrity: sha512-XNFtJ5rIqJxnY6ISjkfbhT/ODiWJ6LcBvNbntuPD6I/F2k7aZeKgPaXrvWvKde66LXyzFKzc8Hn+Ydx4shevQg==} + resolution: {integrity: sha512-XNFtJ5rIqJxnY6ISjkfbhT/ODiWJ6LcBvNbntuPD6I/F2k7aZeKgPaXrvWvKde66LXyzFKzc8Hn+Ydx4shevQg==, tarball: https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260815.1.tgz} engines: {node: '>=16'} cpu: [arm64] os: [linux] '@cloudflare/workerd-windows-64@1.20260317.1': - resolution: {integrity: sha512-MfZTz+7LfuIpMGTa3RLXHX8Z/pnycZLItn94WRdHr8LPVet+C5/1Nzei399w/jr3+kzT4pDKk26JF/tlI5elpQ==} + resolution: {integrity: sha512-MfZTz+7LfuIpMGTa3RLXHX8Z/pnycZLItn94WRdHr8LPVet+C5/1Nzei399w/jr3+kzT4pDKk26JF/tlI5elpQ==, tarball: https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260317.1.tgz} engines: {node: '>=16'} cpu: [x64] os: [win32] '@cloudflare/workerd-windows-64@1.20260423.1': - resolution: {integrity: sha512-3ZhiwG/MSCF9YFxSOkfbXWM2yEIoMKWGnnZMZklY6jnNRTQIGqjvVBdzPYZyLiUqTRV5L+1W7Mvb7tg/merhiQ==} + resolution: {integrity: sha512-3ZhiwG/MSCF9YFxSOkfbXWM2yEIoMKWGnnZMZklY6jnNRTQIGqjvVBdzPYZyLiUqTRV5L+1W7Mvb7tg/merhiQ==, tarball: https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260423.1.tgz} engines: {node: '>=16'} cpu: [x64] os: [win32] '@cloudflare/workerd-windows-64@1.20260815.1': - resolution: {integrity: sha512-PiIUWrhbMg3quolwjgMvPOd75vKESjT4aDm7nL6mSjL5IOgmpO/zKstXnYfnEH3pq7sC0UCvKlF8ZPcfsh8NMw==} + resolution: {integrity: sha512-PiIUWrhbMg3quolwjgMvPOd75vKESjT4aDm7nL6mSjL5IOgmpO/zKstXnYfnEH3pq7sC0UCvKlF8ZPcfsh8NMw==, tarball: https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260815.1.tgz} engines: {node: '>=16'} cpu: [x64] os: [win32] @@ -9043,11 +8967,6 @@ packages: cpu: [arm] os: [android] - '@rollup/rollup-android-arm-eabi@4.44.1': - resolution: {integrity: sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w==} - cpu: [arm] - os: [android] - '@rollup/rollup-android-arm-eabi@4.57.1': resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==} cpu: [arm] @@ -9058,11 +8977,6 @@ packages: cpu: [arm64] os: [android] - '@rollup/rollup-android-arm64@4.44.1': - resolution: {integrity: sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ==} - cpu: [arm64] - os: [android] - '@rollup/rollup-android-arm64@4.57.1': resolution: {integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==} cpu: [arm64] @@ -9073,11 +8987,6 @@ packages: cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-arm64@4.44.1': - resolution: {integrity: sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg==} - cpu: [arm64] - os: [darwin] - '@rollup/rollup-darwin-arm64@4.57.1': resolution: {integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==} cpu: [arm64] @@ -9088,11 +8997,6 @@ packages: cpu: [x64] os: [darwin] - '@rollup/rollup-darwin-x64@4.44.1': - resolution: {integrity: sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw==} - cpu: [x64] - os: [darwin] - '@rollup/rollup-darwin-x64@4.57.1': resolution: {integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==} cpu: [x64] @@ -9103,11 +9007,6 @@ packages: cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-arm64@4.44.1': - resolution: {integrity: sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA==} - cpu: [arm64] - os: [freebsd] - '@rollup/rollup-freebsd-arm64@4.57.1': resolution: {integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==} cpu: [arm64] @@ -9118,11 +9017,6 @@ packages: cpu: [x64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.44.1': - resolution: {integrity: sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw==} - cpu: [x64] - os: [freebsd] - '@rollup/rollup-freebsd-x64@4.57.1': resolution: {integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==} cpu: [x64] @@ -9134,12 +9028,6 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-gnueabihf@4.44.1': - resolution: {integrity: sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ==} - cpu: [arm] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm-gnueabihf@4.57.1': resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==} cpu: [arm] @@ -9152,12 +9040,6 @@ packages: os: [linux] libc: [musl] - '@rollup/rollup-linux-arm-musleabihf@4.44.1': - resolution: {integrity: sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw==} - cpu: [arm] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-arm-musleabihf@4.57.1': resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==} cpu: [arm] @@ -9170,12 +9052,6 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-gnu@4.44.1': - resolution: {integrity: sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ==} - cpu: [arm64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm64-gnu@4.57.1': resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==} cpu: [arm64] @@ -9188,12 +9064,6 @@ packages: os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-musl@4.44.1': - resolution: {integrity: sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g==} - cpu: [arm64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-arm64-musl@4.57.1': resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==} cpu: [arm64] @@ -9218,24 +9088,12 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-loongarch64-gnu@4.44.1': - resolution: {integrity: sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew==} - cpu: [loong64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-powerpc64le-gnu@4.44.1': - resolution: {integrity: sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-ppc64-gnu@4.57.1': resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==} cpu: [ppc64] @@ -9254,24 +9112,12 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.44.1': - resolution: {integrity: sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.57.1': resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.44.1': - resolution: {integrity: sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg==} - cpu: [riscv64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-riscv64-musl@4.57.1': resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==} cpu: [riscv64] @@ -9284,12 +9130,6 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-s390x-gnu@4.44.1': - resolution: {integrity: sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw==} - cpu: [s390x] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-s390x-gnu@4.57.1': resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==} cpu: [s390x] @@ -9302,12 +9142,6 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.44.1': - resolution: {integrity: sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw==} - cpu: [x64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.57.1': resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==} cpu: [x64] @@ -9320,12 +9154,6 @@ packages: os: [linux] libc: [musl] - '@rollup/rollup-linux-x64-musl@4.44.1': - resolution: {integrity: sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g==} - cpu: [x64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-x64-musl@4.57.1': resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==} cpu: [x64] @@ -9347,11 +9175,6 @@ packages: cpu: [arm64] os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.44.1': - resolution: {integrity: sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg==} - cpu: [arm64] - os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.57.1': resolution: {integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==} cpu: [arm64] @@ -9362,11 +9185,6 @@ packages: cpu: [ia32] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.44.1': - resolution: {integrity: sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A==} - cpu: [ia32] - os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.57.1': resolution: {integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==} cpu: [ia32] @@ -9382,11 +9200,6 @@ packages: cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.44.1': - resolution: {integrity: sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug==} - cpu: [x64] - os: [win32] - '@rollup/rollup-win32-x64-msvc@4.57.1': resolution: {integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==} cpu: [x64] @@ -10199,9 +10012,6 @@ packages: '@types/jest@29.5.14': resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} - '@types/jscodeshift@17.3.0': - resolution: {integrity: sha512-ogvGG8VQQqAQQ096uRh+d6tBHrYuZjsumHirKtvBa5qEyTMN3IQJ7apo+sw9lxaB/iKWIhbbLlF3zmAWk9XQIg==} - '@types/jsesc@2.5.1': resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} @@ -11292,10 +11102,6 @@ packages: resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} engines: {node: '>=20'} - clone-deep@4.0.1: - resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} - engines: {node: '>=6'} - clone@2.1.2: resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} engines: {node: '>=0.8'} @@ -12464,14 +12270,6 @@ packages: resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} engines: {node: '>= 18.0.0'} - find-cache-dir@2.1.0: - resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==} - engines: {node: '>=6'} - - find-up@3.0.0: - resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} - engines: {node: '>=6'} - find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -12494,10 +12292,6 @@ packages: flatted@3.4.1: resolution: {integrity: sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==} - flow-parser@0.304.0: - resolution: {integrity: sha512-JjHRBxQX5b5pAn0nwr/U29U+uodOQzIyAKRAFEaXpB2BUkNyW76IRRD0eCEKvZGj23sJ+zDyPuwGGRGNwWW5vQ==} - engines: {node: '>=0.4.0'} - for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -13114,10 +12908,6 @@ packages: resolution: {integrity: sha512-CQpnWPrDwmP1+SMHXZhtLtJv90yiyVfluGsX5iNCVkrhQtU3TQHsUWPG9wkdk9Lgd5yNpAg9jQEo90CBaXgWMA==} engines: {node: '>=4'} - is-plain-object@2.0.4: - resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} - engines: {node: '>=0.10.0'} - is-promise@2.2.2: resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} @@ -13312,16 +13102,6 @@ packages: jsbn@1.1.0: resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - jscodeshift@17.3.0: - resolution: {integrity: sha512-LjFrGOIORqXBU+jwfC9nbkjmQfFldtMIoS6d9z2LG/lkmyNXsJAySPT+2SWXJEoE68/bCWcxKpXH37npftgmow==} - engines: {node: '>=16'} - hasBin: true - peerDependencies: - '@babel/preset-env': ^7.1.6 - peerDependenciesMeta: - '@babel/preset-env': - optional: true - jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -13410,10 +13190,6 @@ packages: resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} engines: {node: '>=0.10.0'} - kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -13610,10 +13386,6 @@ packages: resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} engines: {node: '>=14'} - locate-path@3.0.0: - resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} - engines: {node: '>=6'} - locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -13746,10 +13518,6 @@ packages: magic-string@1.1.0: resolution: {integrity: sha512-kS3VHe0nEPST2saQV4Rbkchcd3UBRkVTQHo1D3h/ZTwFDhai/mfKkmtPAtD129EOI7K3HlHIsFOt0WrI2/oU9g==} - make-dir@2.1.0: - resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} - engines: {node: '>=6'} - marked@0.3.19: resolution: {integrity: sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==} engines: {node: '>=0.10.0'} @@ -14348,10 +14116,6 @@ packages: resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - p-locate@3.0.0: - resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} - engines: {node: '>=6'} - p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -14465,10 +14229,6 @@ packages: path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - path-exists@3.0.0: - resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} - engines: {node: '>=4'} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -14626,10 +14386,6 @@ packages: resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} engines: {node: '>=16.20.0'} - pkg-dir@3.0.0: - resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==} - engines: {node: '>=6'} - pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -15053,10 +14809,6 @@ packages: pumpify@1.5.1: resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==} - punycode@2.1.1: - resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} - engines: {node: '>=6'} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -15443,11 +15195,6 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rollup@4.44.1: - resolution: {integrity: sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - rollup@4.57.1: resolution: {integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -15523,10 +15270,6 @@ packages: resolution: {integrity: sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==} engines: {node: '>=6'} - semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true - semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -15619,10 +15362,6 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - shallow-clone@3.0.1: - resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} - engines: {node: '>=8'} - shallow-equal@1.2.1: resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==} @@ -16139,10 +15878,6 @@ packages: resolution: {integrity: sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==} hasBin: true - tmp@0.2.5: - resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} - engines: {node: '>=14.14'} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -16989,10 +16724,6 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - write-file-atomic@5.0.1: - resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - ws@8.18.0: resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} @@ -17935,10 +17666,6 @@ snapshots: '@types/jsesc': 2.5.1 jsesc: 3.1.0 - '@babel/helper-annotate-as-pure@7.27.3': - dependencies: - '@babel/types': 7.29.0 - '@babel/helper-annotate-as-pure@8.0.0': dependencies: '@babel/types': 8.0.4 @@ -17959,19 +17686,6 @@ snapshots: lru-cache: 11.5.2 semver: 7.8.5 - '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-member-expression-to-functions': 7.28.5 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.29.0 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - '@babel/helper-create-class-features-plugin@8.0.1(@babel/core@8.0.1)': dependencies: '@babel/core': 8.0.1 @@ -17987,13 +17701,6 @@ snapshots: '@babel/helper-globals@8.0.0': {} - '@babel/helper-member-expression-to-functions@7.28.5': - dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - transitivePeerDependencies: - - supports-color - '@babel/helper-member-expression-to-functions@8.0.0': dependencies: '@babel/traverse': 8.0.4 @@ -18015,10 +17722,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-optimise-call-expression@7.27.1': - dependencies: - '@babel/types': 7.29.0 - '@babel/helper-optimise-call-expression@8.0.0': dependencies: '@babel/types': 8.0.4 @@ -18029,15 +17732,6 @@ snapshots: dependencies: '@babel/core': 8.0.1 - '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-member-expression-to-functions': 7.28.5 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.29.0 - transitivePeerDependencies: - - supports-color - '@babel/helper-replace-supers@8.0.1(@babel/core@8.0.1)': dependencies: '@babel/core': 8.0.1 @@ -18045,13 +17739,6 @@ snapshots: '@babel/helper-optimise-call-expression': 8.0.0 '@babel/traverse': 8.0.4 - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': - dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - transitivePeerDependencies: - - supports-color - '@babel/helper-skip-transparent-expression-wrappers@8.0.0': dependencies: '@babel/traverse': 8.0.4 @@ -18107,11 +17794,6 @@ snapshots: '@babel/core': 8.0.1 '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.1) - '@babel/plugin-syntax-flow@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -18122,49 +17804,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-flow-strip-types@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-flow': 7.28.6(@babel/core@7.29.0) - - '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - transitivePeerDependencies: - - supports-color - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -18175,44 +17814,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) - transitivePeerDependencies: - - supports-color - - '@babel/preset-flow@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0) - - '@babel/preset-typescript@7.28.5(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) - transitivePeerDependencies: - - supports-color - - '@babel/register@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - clone-deep: 4.0.1 - find-cache-dir: 2.1.0 - make-dir: 2.1.0 - pirates: 4.0.6 - source-map-support: 0.5.21 - '@babel/runtime-corejs3@7.27.0': dependencies: core-js-pure: 3.41.0 @@ -21494,90 +21095,60 @@ snapshots: '@rollup/rollup-android-arm-eabi@4.30.1': optional: true - '@rollup/rollup-android-arm-eabi@4.44.1': - optional: true - '@rollup/rollup-android-arm-eabi@4.57.1': optional: true '@rollup/rollup-android-arm64@4.30.1': optional: true - '@rollup/rollup-android-arm64@4.44.1': - optional: true - '@rollup/rollup-android-arm64@4.57.1': optional: true '@rollup/rollup-darwin-arm64@4.30.1': optional: true - '@rollup/rollup-darwin-arm64@4.44.1': - optional: true - '@rollup/rollup-darwin-arm64@4.57.1': optional: true '@rollup/rollup-darwin-x64@4.30.1': optional: true - '@rollup/rollup-darwin-x64@4.44.1': - optional: true - '@rollup/rollup-darwin-x64@4.57.1': optional: true '@rollup/rollup-freebsd-arm64@4.30.1': optional: true - '@rollup/rollup-freebsd-arm64@4.44.1': - optional: true - '@rollup/rollup-freebsd-arm64@4.57.1': optional: true '@rollup/rollup-freebsd-x64@4.30.1': optional: true - '@rollup/rollup-freebsd-x64@4.44.1': - optional: true - '@rollup/rollup-freebsd-x64@4.57.1': optional: true '@rollup/rollup-linux-arm-gnueabihf@4.30.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.44.1': - optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.57.1': optional: true '@rollup/rollup-linux-arm-musleabihf@4.30.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.44.1': - optional: true - '@rollup/rollup-linux-arm-musleabihf@4.57.1': optional: true '@rollup/rollup-linux-arm64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.44.1': - optional: true - '@rollup/rollup-linux-arm64-gnu@4.57.1': optional: true '@rollup/rollup-linux-arm64-musl@4.30.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.44.1': - optional: true - '@rollup/rollup-linux-arm64-musl@4.57.1': optional: true @@ -21590,15 +21161,9 @@ snapshots: '@rollup/rollup-linux-loongarch64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.44.1': - optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.44.1': - optional: true - '@rollup/rollup-linux-ppc64-gnu@4.57.1': optional: true @@ -21608,42 +21173,27 @@ snapshots: '@rollup/rollup-linux-riscv64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.44.1': - optional: true - '@rollup/rollup-linux-riscv64-gnu@4.57.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.44.1': - optional: true - '@rollup/rollup-linux-riscv64-musl@4.57.1': optional: true '@rollup/rollup-linux-s390x-gnu@4.30.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.44.1': - optional: true - '@rollup/rollup-linux-s390x-gnu@4.57.1': optional: true '@rollup/rollup-linux-x64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.44.1': - optional: true - '@rollup/rollup-linux-x64-gnu@4.57.1': optional: true '@rollup/rollup-linux-x64-musl@4.30.1': optional: true - '@rollup/rollup-linux-x64-musl@4.44.1': - optional: true - '@rollup/rollup-linux-x64-musl@4.57.1': optional: true @@ -21656,18 +21206,12 @@ snapshots: '@rollup/rollup-win32-arm64-msvc@4.30.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.44.1': - optional: true - '@rollup/rollup-win32-arm64-msvc@4.57.1': optional: true '@rollup/rollup-win32-ia32-msvc@4.30.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.44.1': - optional: true - '@rollup/rollup-win32-ia32-msvc@4.57.1': optional: true @@ -21677,9 +21221,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.30.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.44.1': - optional: true - '@rollup/rollup-win32-x64-msvc@4.57.1': optional: true @@ -22677,11 +22218,6 @@ snapshots: expect: 29.7.0 pretty-format: 29.7.0 - '@types/jscodeshift@17.3.0': - dependencies: - ast-types: 0.16.1 - recast: 0.23.11 - '@types/jsesc@2.5.1': {} '@types/json-diff@1.0.3': {} @@ -23920,7 +23456,7 @@ snapshots: citty@0.1.6: dependencies: - consola: 3.3.3 + consola: 3.4.2 citty@0.2.0: {} @@ -23958,12 +23494,6 @@ snapshots: strip-ansi: 7.1.0 wrap-ansi: 9.0.0 - clone-deep@4.0.1: - dependencies: - is-plain-object: 2.0.4 - kind-of: 6.0.3 - shallow-clone: 3.0.1 - clone@2.1.2: {} cloudflare@5.2.0(encoding@0.1.13): @@ -24291,7 +23821,7 @@ snapshots: date-fns@2.30.0: dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.7 date-fns@4.1.0: {} @@ -25337,16 +24867,6 @@ snapshots: transitivePeerDependencies: - supports-color - find-cache-dir@2.1.0: - dependencies: - commondir: 1.0.1 - make-dir: 2.1.0 - pkg-dir: 3.0.0 - - find-up@3.0.0: - dependencies: - locate-path: 3.0.0 - find-up@4.1.0: dependencies: locate-path: 5.0.0 @@ -25372,8 +24892,6 @@ snapshots: flatted@3.4.1: {} - flow-parser@0.304.0: {} - for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -25982,10 +25500,6 @@ snapshots: dependencies: is-number: 6.0.0 - is-plain-object@2.0.4: - dependencies: - isobject: 3.0.1 - is-promise@2.2.2: {} is-promise@4.0.0: {} @@ -26169,29 +25683,6 @@ snapshots: jsbn@1.1.0: {} - jscodeshift@17.3.0: - dependencies: - '@babel/core': 7.29.0 - '@babel/parser': 7.29.0 - '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) - '@babel/preset-flow': 7.27.1(@babel/core@7.29.0) - '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@babel/register': 7.28.6(@babel/core@7.29.0) - flow-parser: 0.304.0 - graceful-fs: 4.2.11 - micromatch: 4.0.8 - neo-async: 2.6.2 - picocolors: 1.1.1 - recast: 0.23.11 - tmp: 0.2.5 - write-file-atomic: 5.0.1 - transitivePeerDependencies: - - supports-color - jsesc@3.1.0: {} json-bigint@1.0.0: @@ -26293,8 +25784,6 @@ snapshots: dependencies: is-buffer: 1.1.6 - kind-of@6.0.3: {} - kleur@3.0.3: {} kleur@4.1.5: {} @@ -26425,11 +25914,6 @@ snapshots: mlly: 1.7.4 pkg-types: 1.3.1 - locate-path@3.0.0: - dependencies: - p-locate: 3.0.0 - path-exists: 3.0.0 - locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -26546,11 +26030,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - make-dir@2.1.0: - dependencies: - pify: 4.0.1 - semver: 5.7.2 - marked@0.3.19: {} match-sorter@6.3.1: @@ -27142,10 +26621,6 @@ snapshots: dependencies: yocto-queue: 1.0.0 - p-locate@3.0.0: - dependencies: - p-limit: 2.3.0 - p-locate@4.1.0: dependencies: p-limit: 2.3.0 @@ -27256,8 +26731,6 @@ snapshots: path-browserify@1.0.1: {} - path-exists@3.0.0: {} - path-exists@4.0.0: {} path-exists@5.0.0: {} @@ -27396,10 +26869,6 @@ snapshots: pkce-challenge@5.0.1: {} - pkg-dir@3.0.0: - dependencies: - find-up: 3.0.0 - pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -27833,8 +27302,6 @@ snapshots: inherits: 2.0.4 pump: 2.0.1 - punycode@2.1.1: {} - punycode@2.3.1: {} pure-rand@6.1.0: {} @@ -28366,32 +27833,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.30.1 fsevents: 2.3.3 - rollup@4.44.1: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.44.1 - '@rollup/rollup-android-arm64': 4.44.1 - '@rollup/rollup-darwin-arm64': 4.44.1 - '@rollup/rollup-darwin-x64': 4.44.1 - '@rollup/rollup-freebsd-arm64': 4.44.1 - '@rollup/rollup-freebsd-x64': 4.44.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.44.1 - '@rollup/rollup-linux-arm-musleabihf': 4.44.1 - '@rollup/rollup-linux-arm64-gnu': 4.44.1 - '@rollup/rollup-linux-arm64-musl': 4.44.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.44.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.44.1 - '@rollup/rollup-linux-riscv64-gnu': 4.44.1 - '@rollup/rollup-linux-riscv64-musl': 4.44.1 - '@rollup/rollup-linux-s390x-gnu': 4.44.1 - '@rollup/rollup-linux-x64-gnu': 4.44.1 - '@rollup/rollup-linux-x64-musl': 4.44.1 - '@rollup/rollup-win32-arm64-msvc': 4.44.1 - '@rollup/rollup-win32-ia32-msvc': 4.44.1 - '@rollup/rollup-win32-x64-msvc': 4.44.1 - fsevents: 2.3.3 - rollup@4.57.1: dependencies: '@types/estree': 1.0.8 @@ -28497,8 +27938,6 @@ snapshots: semiver@1.1.0: {} - semver@5.7.2: {} - semver@6.3.1: {} semver@7.5.4: @@ -28628,10 +28067,6 @@ snapshots: setprototypeof@1.2.0: {} - shallow-clone@3.0.1: - dependencies: - kind-of: 6.0.3 - shallow-equal@1.2.1: {} sharp@0.34.5: @@ -29243,8 +28678,6 @@ snapshots: dependencies: tldts-core: 7.0.19 - tmp@0.2.5: {} - to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -29271,7 +28704,7 @@ snapshots: tr46@1.0.1: dependencies: - punycode: 2.1.1 + punycode: 2.3.1 tr46@5.1.1: dependencies: @@ -29408,10 +28841,10 @@ snapshots: picocolors: 1.1.1 postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.23)(tsx@4.21.0)(yaml@2.8.1) resolve-from: 5.0.0 - rollup: 4.44.1 + rollup: 4.57.1 source-map: 0.8.0-beta.0 sucrase: 3.35.0 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 tree-kill: 1.2.2 optionalDependencies: '@microsoft/api-extractor': 7.52.8(@types/node@22.15.17) @@ -29436,10 +28869,10 @@ snapshots: picocolors: 1.1.1 postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.23)(tsx@3.12.10)(yaml@2.9.0) resolve-from: 5.0.0 - rollup: 4.44.1 + rollup: 4.57.1 source-map: 0.8.0-beta.0 sucrase: 3.35.0 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 tree-kill: 1.2.2 optionalDependencies: '@microsoft/api-extractor': 7.52.8(@types/node@22.15.17) @@ -29464,10 +28897,10 @@ snapshots: picocolors: 1.1.1 postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.23)(tsx@4.21.0)(yaml@2.9.0) resolve-from: 5.0.0 - rollup: 4.44.1 + rollup: 4.57.1 source-map: 0.8.0-beta.0 sucrase: 3.35.0 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 tree-kill: 1.2.2 optionalDependencies: '@microsoft/api-extractor': 7.52.8(@types/node@22.15.17) @@ -29492,10 +28925,10 @@ snapshots: picocolors: 1.1.1 postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.23)(tsx@4.21.0)(yaml@2.9.0) resolve-from: 5.0.0 - rollup: 4.44.1 + rollup: 4.57.1 source-map: 0.8.0-beta.0 sucrase: 3.35.0 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 tree-kill: 1.2.2 optionalDependencies: '@microsoft/api-extractor': 7.52.8(@types/node@22.15.17) @@ -30451,11 +29884,6 @@ snapshots: wrappy@1.0.2: {} - write-file-atomic@5.0.1: - dependencies: - imurmurhash: 0.1.4 - signal-exit: 4.1.0 - ws@8.18.0: {} ws@8.21.0: {} diff --git a/tools/deployments/__tests__/publish-packages.test.ts b/tools/deployments/__tests__/publish-packages.test.ts index 9e043ad6e38..4c3a70f7654 100644 --- a/tools/deployments/__tests__/publish-packages.test.ts +++ b/tools/deployments/__tests__/publish-packages.test.ts @@ -266,6 +266,7 @@ describe("the real workspace", () => { expect(tierNames(tiers)).toMatchInlineSnapshot(` [ [ + "@cloudflare/codemods", "@cloudflare/config", "@cloudflare/kv-asset-handler", "@cloudflare/pages-functions", diff --git a/tools/deployments/__tests__/validate-changesets.test.ts b/tools/deployments/__tests__/validate-changesets.test.ts index 326b4b9d00f..eb1696fb6d3 100644 --- a/tools/deployments/__tests__/validate-changesets.test.ts +++ b/tools/deployments/__tests__/validate-changesets.test.ts @@ -26,7 +26,7 @@ describe("findPackageNames()", () => { "@cloudflare/build-output-utils", "@cloudflare/chrome-devtools-patches", "@cloudflare/cli-shared-helpers", - "@cloudflare/codemod", + "@cloudflare/codemods", "@cloudflare/config", "@cloudflare/containers-shared", "@cloudflare/deploy-helpers", @@ -40,6 +40,7 @@ describe("findPackageNames()", () => { "@cloudflare/pages-shared", "@cloudflare/playground-preview-worker", "@cloudflare/quick-edit", + "@cloudflare/shared-ast-primitives", "@cloudflare/turbo-r2-archive", "@cloudflare/unenv-preset", "@cloudflare/vite-plugin",