diff --git a/.changeset/astro-v6-workers-support.md b/.changeset/astro-v6-workers-support.md new file mode 100644 index 000000000000..cfd5651dd95f --- /dev/null +++ b/.changeset/astro-v6-workers-support.md @@ -0,0 +1,9 @@ +--- +"create-cloudflare": patch +--- + +Update Astro Workers template for Astro v6 + +The Astro Workers template now scaffolds projects using Astro v6. The adapter uses the Cloudflare Vite plugin under the hood, so `astro dev` runs on the workerd runtime locally and `wrangler.jsonc` fields like `main` and `assets` are no longer needed in the template. + +For existing projects, see the [Astro Cloudflare adapter migration guide](https://docs.astro.build/en/guides/integrations-guide/cloudflare/#upgrading-to-v13-and-astro-6). diff --git a/.changeset/brave-mails-take.md b/.changeset/brave-mails-take.md deleted file mode 100644 index b9cca3e1ba01..000000000000 --- a/.changeset/brave-mails-take.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@cloudflare/local-explorer-ui": patch ---- - -Fix local explorer's sidebar header link to point to the correct `/cdn-cgi/explorer/` path rather than `/`. diff --git a/.changeset/bumpy-suns-press.md b/.changeset/bumpy-suns-press.md deleted file mode 100644 index 21ce7c8bfbab..000000000000 --- a/.changeset/bumpy-suns-press.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@cloudflare/vite-plugin": minor ---- - -Support local explorer `/cdn-cgi/` routes - -The local explorer UI can now be accessed at `/cdn-cgi/explorer`. diff --git a/.changeset/c3-frameworks-update-12805.md b/.changeset/c3-frameworks-update-12805.md deleted file mode 100644 index e074d7ae8be9..000000000000 --- a/.changeset/c3-frameworks-update-12805.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 | 21.2.0 | 21.2.1 | diff --git a/.changeset/c3-frameworks-update-12806.md b/.changeset/c3-frameworks-update-12806.md deleted file mode 100644 index c65ff9e3a4a1..000000000000 --- a/.changeset/c3-frameworks-update-12806.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.591 | 0.0.592 | diff --git a/.changeset/dependabot-update-12861.md b/.changeset/dependabot-update-12861.md deleted file mode 100644 index 61c8bc4ed273..000000000000 --- a/.changeset/dependabot-update-12861.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"miniflare": patch -"wrangler": patch ---- - -Update dependencies of "miniflare", "wrangler" - -The following dependency versions have been updated: - -| Dependency | From | To | -| ---------- | ------------ | ------------ | -| workerd | 1.20260310.1 | 1.20260312.1 | diff --git a/.changeset/deprecate-ssh-passthrough-flags.md b/.changeset/deprecate-ssh-passthrough-flags.md deleted file mode 100644 index 777a7a842734..000000000000 --- a/.changeset/deprecate-ssh-passthrough-flags.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"wrangler": minor ---- - -Deprecate SSH passthrough flags in `wrangler containers ssh` - -The `--cipher`, `--log-file`, `--escape-char`, `--config-file`, `--pkcs11`, `--identity-file`, `--mac-spec`, `--option`, and `--tag` flags are now deprecated. These flags expose OpenSSH-specific options that are tied to the current implementation. A future release will replace the underlying SSH transport, at which point these flags will be removed. They still function for now. diff --git a/.changeset/eight-melons-rule.md b/.changeset/eight-melons-rule.md deleted file mode 100644 index 7b2546676407..000000000000 --- a/.changeset/eight-melons-rule.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -"wrangler": patch ---- - -Add back support for wrangler d1 exports with multiple tables. - -Example: - -```bash -# All tables (default) -wrangler d1 export db --output all-tables.sql - -# Single table (unchanged) -wrangler d1 export db --output single-table.sql --table foo - -# Multiple tables (new) -wrangler d1 export db --output multiple-tables.sql --table foo --table bar -``` diff --git a/.changeset/eleven-ways-go.md b/.changeset/eleven-ways-go.md new file mode 100644 index 000000000000..ba3ca7249d55 --- /dev/null +++ b/.changeset/eleven-ways-go.md @@ -0,0 +1,9 @@ +--- +"miniflare": patch +--- + +Local explorer: validate host and origin headers before Miniflare modifies them + +If `routes` are set, Miniflare will alter the host and origin headers to match, causing the local explorer to mistakenly identify and block same-origin requests. + +Note the local explorer is a WIP experimental feature. diff --git a/.changeset/fast-escape-keypress.md b/.changeset/fast-escape-keypress.md new file mode 100644 index 000000000000..f521659d2815 --- /dev/null +++ b/.changeset/fast-escape-keypress.md @@ -0,0 +1,7 @@ +--- +"wrangler": patch +--- + +Add `escapeCodeTimeout` option to `onKeyPress` utility for faster Esc key detection + +The `onKeyPress` utility now accepts an optional `escapeCodeTimeout` parameter that controls how long readline waits to disambiguate a standalone Esc press from multi-byte escape sequences (e.g. arrow keys). The default remains readline's built-in 500ms, but callers can pass a lower value (e.g. 25ms) for near-instant Esc handling in interactive prompts. diff --git a/.changeset/fix-c3-dashboard-url.md b/.changeset/fix-c3-dashboard-url.md deleted file mode 100644 index 6e726e18cf88..000000000000 --- a/.changeset/fix-c3-dashboard-url.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"create-cloudflare": patch ---- - -Fix C3 success summary dashboard link to point to Workers service production view - -The "Dash:" URL now includes `/production` so it opens the correct Workers & Pages service view in the Cloudflare dashboard. diff --git a/.changeset/floppy-webs-smell.md b/.changeset/floppy-webs-smell.md deleted file mode 100644 index cb0abe279a41..000000000000 --- a/.changeset/floppy-webs-smell.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@cloudflare/vite-plugin": patch ---- - -Warn when the `assets` field is provided for auxiliary Workers - -Auxiliary Workers do not support static assets. Previously, the `assets` field was silently ignored but we now warn if it is used. diff --git a/.changeset/green-buses-melt.md b/.changeset/green-buses-melt.md new file mode 100644 index 000000000000..137c3e21e3ff --- /dev/null +++ b/.changeset/green-buses-melt.md @@ -0,0 +1,7 @@ +--- +"@cloudflare/vite-plugin": minor +--- + +Add Vite 8 to the supported peer dependency range + +The package now lists Vite 8 in its peer dependency range, so installs with Vite 8 no longer show a peer dependency warning. diff --git a/.changeset/kind-socks-beam.md b/.changeset/kind-socks-beam.md deleted file mode 100644 index a714be447f36..000000000000 --- a/.changeset/kind-socks-beam.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -"wrangler": minor ---- - -Support disabling persistence in `unstable_startWorker()` and `unstable_dev()` - -You can now disable persistence entirely by setting `persist: false` in the `dev` options: - -```typescript -const worker = await unstable_dev("./src/worker.ts", { - persist: false, -}); -``` - -Or when using `unstable_startWorker()`: - -```typescript -const worker = await unstable_startWorker({ - entrypoint: "./src/worker.ts", - dev: { - persist: false, - }, -}); -``` - -This is useful for testing scenarios where you want to ensure a clean state on each run without any persisted data from previous runs. diff --git a/.changeset/many-fishes-raise.md b/.changeset/many-fishes-raise.md deleted file mode 100644 index 499a88363fd9..000000000000 --- a/.changeset/many-fishes-raise.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -"@cloudflare/vitest-pool-workers": minor ---- - -Support Vitest 4 in `@cloudflare/vitest-pool-workers`. - -This a breaking change to the `@cloudflare/vitest-pool-workers` integration in order to support Vitest v4. Along with supporting Vitest v4 (and dropping support for Vitest v2 and v3), we've made a number of changes that may require changes to your tests. Our aim has been to improve stability & the foundations of `@cloudflare/vitest-pool-workers` as we move towards a v1 release of the package. - -We've made a codemod to make the migration easier, which will make the required changes to your config file: - -```sh -npx jscodeshift -t node_modules/@cloudflare/vitest-pool-workers/dist/codemods/vitest-v3-to-v4.mjs vitest.config.ts -``` - -Or, without installing the package first: - -```sh -npx jscodeshift -t https://unpkg.com/@cloudflare/vitest-pool-workers/dist/codemods/vitest-v3-to-v4.mjs --parser=ts vitest.config.ts -``` - -- **Config API:** `defineWorkersProject` and `defineWorkersConfig` from `@cloudflare/vitest-pool-workers/config` have been replaced with a `cloudflareTest()` Vite plugin exported from `@cloudflare/vitest-pool-workers`. The `test.poolOptions.workers` options are now passed directly to `cloudflareTest()`: - - Before: - - ```ts - import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config"; - - export default defineWorkersProject({ - test: { - poolOptions: { - workers: { - wrangler: { configPath: "./wrangler.jsonc" }, - }, - }, - }, - }); - ``` - - After: - - ```ts - import { cloudflareTest } from "@cloudflare/vitest-pool-workers"; - import { defineConfig } from "vitest/config"; - - export default defineConfig({ - plugins: [ - cloudflareTest({ - wrangler: { configPath: "./wrangler.jsonc" }, - }), - ], - }); - ``` - -- **`isolatedStorage` & `singleWorker`:** These have been removed in favour of a simpler isolation model that more closely matches Vitest. Storage isolation is now on a per test file basis, and you can make your test files share the same storage by using the Vitest flags `--max-workers=1 --no-isolate` -- **`import { env, SELF } from "cloudflare:test"`:** These have been removed in favour of `import { env, exports } from "cloudflare:workers"`. `exports.default.fetch()` has the same behaviour as `SELF.fetch()`, except that it doesn't expose Assets. To test your assets, write an integration test using [`startDevWorker()`](https://developers.cloudflare.com/workers/testing/unstable_startworker/) -- **`import { fetchMock } from "cloudflare:test"`:** This has been removed. Instead, [mock `globalThis.fetch`](https://github.com/cloudflare/workers-sdk/blob/main/fixtures/vitest-pool-workers-examples/request-mocking/test/imperative.test.ts) or use ecosystem libraries like [MSW (recommended)](https://mswjs.io/). -- **Vitest peer dependency:** `@cloudflare/vitest-pool-workers` now requires `vitest@^4.1.0`. diff --git a/.changeset/migrate-workers-playground.md b/.changeset/migrate-workers-playground.md deleted file mode 100644 index 54adfc165d49..000000000000 --- a/.changeset/migrate-workers-playground.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -"@cloudflare/workers-playground": patch -"@cloudflare/playground-preview-worker": patch ---- - -Migrate workers-playground from Cloudflare Pages to Cloudflare Workers - -Replace the Cloudflare Pages deployment with a Workers + static assets deployment. - -In production (`wrangler.jsonc`), this is an assets-only Worker with no code entry point — the `playground-preview-worker` handles all routing and proxying in front of it. - -For local development, a separate config (`wrangler.dev.jsonc`) adds a Worker entry point (`src/worker.ts`) that replicates the proxying behavior of the production `playground-preview-worker`. It proxies `/playground/api/*` requests to the testing `playground-preview-worker`, and for the `/playground` route it fetches an auth cookie from the testing endpoint, transforms it for local use (stripping `SameSite`/`Secure` directives and replacing the testing origin with `localhost`), and injects it into the response so the preview iframe can authenticate. - -The `playground-preview-worker` referer allowlist is updated to also accept requests from `*.workers-playground.workers.dev` (in addition to the existing `*.workers-playground.pages.dev`). diff --git a/.changeset/neat-glasses-open.md b/.changeset/neat-glasses-open.md new file mode 100644 index 000000000000..1811dd037e8d --- /dev/null +++ b/.changeset/neat-glasses-open.md @@ -0,0 +1,7 @@ +--- +"@cloudflare/local-explorer-ui": patch +--- + +Fixed table selection dropdown incorrect z-index. + +Previously, the dropdown you used to select a table in the data studio had an incorrect or missing z-index, meanint it conflicted with the table row header & was partially cut off when you had too many tables. This change ensures that the dropdown is always "on top" and visible. diff --git a/.changeset/olive-heads-arrive.md b/.changeset/olive-heads-arrive.md new file mode 100644 index 000000000000..ea40c96fcb98 --- /dev/null +++ b/.changeset/olive-heads-arrive.md @@ -0,0 +1,7 @@ +--- +"wrangler": patch +--- + +Fix unclear error when assets upload session returns a `null` response + +When deploying assets, if the Cloudflare API returns a `null` response object, Wrangler now provides a clear error message asking users to retry instead of failing with a confusing error. diff --git a/.changeset/quiet-foxes-grow.md b/.changeset/quiet-foxes-grow.md deleted file mode 100644 index 86a5dca81cfe..000000000000 --- a/.changeset/quiet-foxes-grow.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -"wrangler": patch ---- - -fix: `vectorize` commands now output valid json - -This fixes: - -- `wrangler vectorize create` -- `wrangler vectorize info` -- `wrangler vectorize insert` -- `wrangler vectorize upsert` -- `wrangler vectorize list` -- `wrangler vectorize list-vectors` -- `wrangler vectorize list-metadata-index` - -Also, `wrangler vectorize create --json` now also includes the `created_at`, `modified_on` and `description` fields. diff --git a/.changeset/real-plants-slide.md b/.changeset/real-plants-slide.md deleted file mode 100644 index 2af079ad576f..000000000000 --- a/.changeset/real-plants-slide.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"wrangler": patch ---- - -Fix autoconfig for Astro v6 projects to skip wrangler config generation - -Astro 6+ generates its own wrangler configuration on build, so autoconfig now detects the Astro version and skips creating a `wrangler.jsonc` file for projects using Astro 6 or later. This prevents conflicts between the autoconfig-generated config and Astro's built-in config generation. diff --git a/.changeset/ripe-pants-start.md b/.changeset/ripe-pants-start.md deleted file mode 100644 index 427eb5cb1e05..000000000000 --- a/.changeset/ripe-pants-start.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"create-cloudflare": patch ---- - -Generate `app/env.d.ts` and `server/env.d.ts` for Nuxt applications - -Previously, only a top-level `env.d.ts` was created, which meant server files didn't receive Cloudflare types. Now the CLI generates separate `app/env.d.ts` and `server/env.d.ts` files, both importing from a shared `_cloudflare/env.d.ts` to avoid duplication. - -This ensures Cloudflare types are available in both app and server directories. diff --git a/.changeset/rude-steaks-kick.md b/.changeset/rude-steaks-kick.md deleted file mode 100644 index 7f504b1b7e80..000000000000 --- a/.changeset/rude-steaks-kick.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"miniflare": patch ---- - -Fix local explorer route matching to be more precise - -Previously, the route matching used `startsWith("/cdn-cgi/explorer")` which would incorrectly match paths like `/cdn-cgi/explorerfoo` or `/cdn-cgi/explorereeeeee`, causing unexpected behavior. The route matching has been improved to only match: - -- `/cdn-cgi/explorer` (exact match) -- `/cdn-cgi/explorer/` and any sub-paths (e.g., `/cdn-cgi/explorer/api/*`) - -Paths that merely start with `/cdn-cgi/explorer` but aren't actually the explorer (like `/cdn-cgi/explorerfoo`) will now correctly fall through to the user worker. diff --git a/.changeset/secrets-file-versions-upload.md b/.changeset/secrets-file-versions-upload.md new file mode 100644 index 000000000000..33ddb0b4f1bd --- /dev/null +++ b/.changeset/secrets-file-versions-upload.md @@ -0,0 +1,16 @@ +--- +"wrangler": minor +--- + +feat: add `--secrets-file` parameter to `wrangler deploy` and `wrangler versions upload` + +You can now upload secrets alongside your Worker code in a single operation using the `--secrets-file` parameter on both `wrangler deploy` and `wrangler versions upload`. The file format matches what's used by `wrangler versions secret bulk`, supporting both JSON and .env formats. + +Example usage: + +```bash +wrangler deploy --secrets-file .env.production +wrangler versions upload --secrets-file secrets.json +``` + +Secrets not included in the file will be inherited from the previous version, matching the behavior of `wrangler versions secret bulk`. diff --git a/.changeset/ten-dancers-know.md b/.changeset/ten-dancers-know.md deleted file mode 100644 index 11d03a1faa4c..000000000000 --- a/.changeset/ten-dancers-know.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"miniflare": patch ---- - -Add support for worker connect handler in miniflare diff --git a/.changeset/ten-maps-judge.md b/.changeset/ten-maps-judge.md new file mode 100644 index 000000000000..eba3793ec73b --- /dev/null +++ b/.changeset/ten-maps-judge.md @@ -0,0 +1,7 @@ +--- +"create-cloudflare": patch +--- + +Fix React app creation flow skipping Cloudflare setup + +Creating a React app with create-cloudflare no longer allows the Cloudflare setup step to be skipped by accepting create-vite's `Install and start now` prompt. diff --git a/.changeset/thin-poems-dream.md b/.changeset/thin-poems-dream.md deleted file mode 100644 index eed356804992..000000000000 --- a/.changeset/thin-poems-dream.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@cloudflare/containers-shared": minor ---- - -Update the `proxy-everything` image used for containers local dev - -The egress interceptor image now supports HTTPS and ingress over HTTP CONNECT in workerd. diff --git a/.changeset/two-ants-yawn.md b/.changeset/two-ants-yawn.md deleted file mode 100644 index 75296424868a..000000000000 --- a/.changeset/two-ants-yawn.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@cloudflare/vite-plugin": patch ---- - -Fix Sandbox SDK preview URL WebSocket routing - -When using Sandbox SDK preview URLs, WebSocket requests using the `vite-hmr` protocol could be dropped before they reached the worker, causing HMR to fail. The plugin now forwards Sandbox WebSocket traffic and preserves the original request origin/host so worker proxy logic receives the correct URL. diff --git a/.changeset/vpc-hostname-validation.md b/.changeset/vpc-hostname-validation.md deleted file mode 100644 index 017f6997553f..000000000000 --- a/.changeset/vpc-hostname-validation.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"wrangler": patch ---- - -Add client-side validation for VPC service host flags - -The `--hostname`, `--ipv4`, and `--ipv6` flags on `wrangler vpc service create` and `wrangler vpc service update` now validate input before sending requests to the API. Previously, invalid values were accepted by the CLI and only rejected by the API with opaque error messages. Now users get clear, actionable error messages for common mistakes like passing a URL instead of a hostname, using an IP address in the `--hostname` flag, or providing malformed IP addresses. diff --git a/.changeset/warm-containers-instances.md b/.changeset/warm-containers-instances.md new file mode 100644 index 000000000000..67bdbef079ae --- /dev/null +++ b/.changeset/warm-containers-instances.md @@ -0,0 +1,8 @@ +--- +"wrangler": minor +"@cloudflare/containers-shared": minor +--- + +Add `wrangler containers instances ` command to list container instances + +Lists all container instances for a given application, matching the Dash instances view. Displays instance ID, state, location, version, and creation time. Supports pagination for applications with many instances. Also adds paginated request support to the containers-shared API client. diff --git a/.github/actions/check-remote-tests/action.yml b/.github/actions/check-remote-tests/action.yml new file mode 100644 index 000000000000..02e932e23574 --- /dev/null +++ b/.github/actions/check-remote-tests/action.yml @@ -0,0 +1,33 @@ +name: "Check Remote Tests" +description: > + Determines whether E2E tests should be given Cloudflare API credentials. + Returns true for merge-queue runs, Version Packages PRs, or when the + "run-remote-tests" label is present on a pull request. +outputs: + run-remote: + description: "'true' when remote tests should run, 'false' otherwise" + value: ${{ steps.decide.outputs.run_remote }} +runs: + using: "composite" + steps: + - name: Decide whether to run remote tests + id: decide + shell: bash + env: + GH_TOKEN: ${{ github.token }} + EVENT_NAME: ${{ github.event_name }} + HEAD_REF: ${{ github.head_ref }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} + run: | + if [ "$EVENT_NAME" = "merge_group" ] || \ + [ "$HEAD_REF" = "changeset-release/main" ]; then + echo "run_remote=true" >> "$GITHUB_OUTPUT" + elif [ "$EVENT_NAME" = "pull_request" ]; then + HAS_LABEL=$(gh pr view "$PR_NUMBER" \ + --repo "$REPO" --json labels \ + --jq '[.labels[].name] | any(. == "run-remote-tests")') + echo "run_remote=${HAS_LABEL}" >> "$GITHUB_OUTPUT" + else + echo "run_remote=false" >> "$GITHUB_OUTPUT" + fi diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 4a4e7ac80d69..8e03e253aeaf 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -25,6 +25,7 @@ See below for a summary of this repo's Actions - PRs in the merge queue. - Actions - Runs the E2E tests for Wrangler. + - Cloudflare API credentials are only passed on Version Packages PRs (`changeset-release/main`), in the merge queue, or when the `run-remote-tests` label is applied. Other PRs run the E2E suite without remote tests. ### Vite Plugin E2E tests (e2e-vite.yml) @@ -33,6 +34,7 @@ See below for a summary of this repo's Actions - PRs in the merge queue. - Actions - Runs the E2E tests for the Vite plugin. + - Cloudflare API credentials are only passed on Version Packages PRs (`changeset-release/main`), in the merge queue, or when the `run-remote-tests` label is applied. Other PRs run the E2E suite without remote tests. ## Deploy Pages Previews (deploy-pages-preview.yml) @@ -102,3 +104,11 @@ See below for a summary of this repo's Actions - Updates to PRs. - Actions - Runs the E2E tests for C3. + - Cloudflare API credentials are only passed on Version Packages PRs (`changeset-release/main`), in the merge queue, or when the `run-remote-tests` label is applied. Other PRs run the E2E suite without remote tests. + +### Rerun Remote Tests (rerun-remote-tests.yml) + +- Triggers + - The `run-remote-tests` label is added to a PR. +- Actions + - Re-runs the Wrangler, Vite, and C3 E2E workflows for the PR so they pick up the label and pass API credentials to the test steps. diff --git a/.github/workflows/c3-e2e.yml b/.github/workflows/c3-e2e.yml index 7c228674c5ff..c30eaf465556 100644 --- a/.github/workflows/c3-e2e.yml +++ b/.github/workflows/c3-e2e.yml @@ -5,6 +5,7 @@ on: permissions: contents: read + pull-requests: read jobs: e2e: @@ -45,6 +46,10 @@ jobs: everything_but_markdown: - '!**/*.md' + - name: Check if remote tests should run + id: check-remote + uses: ./.github/actions/check-remote-tests + - name: Install Dependencies if: steps.changes.outputs.everything_but_markdown == 'true' uses: ./.github/actions/install-dependencies @@ -77,8 +82,8 @@ jobs: run: pnpm run test:e2e:c3 env: NODE_VERSION: ${{ env.NODE_VERSION }} - CLOUDFLARE_API_TOKEN: ${{ secrets.TEST_CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ACCOUNT_ID: ${{ secrets.TEST_CLOUDFLARE_ACCOUNT_ID }} + CLOUDFLARE_API_TOKEN: ${{ steps.check-remote.outputs.run-remote == 'true' && secrets.TEST_CLOUDFLARE_API_TOKEN || '' }} + CLOUDFLARE_ACCOUNT_ID: ${{ steps.check-remote.outputs.run-remote == 'true' && secrets.TEST_CLOUDFLARE_ACCOUNT_ID || '' }} E2E_EXPERIMENTAL: ${{ matrix.experimental }} E2E_TEST_PM: ${{ matrix.pm.name }} E2E_TEST_PM_VERSION: ${{ matrix.pm.version }} diff --git a/.github/workflows/e2e-vite.yml b/.github/workflows/e2e-vite.yml index 99aaa5ec0f41..a247505dda1d 100644 --- a/.github/workflows/e2e-vite.yml +++ b/.github/workflows/e2e-vite.yml @@ -6,6 +6,7 @@ on: permissions: contents: read + pull-requests: read jobs: e2e-vite-plugin-test: @@ -38,6 +39,10 @@ jobs: everything_but_markdown: - '!**/*.md' + - name: Check if remote tests should run + id: check-remote + uses: ./.github/actions/check-remote-tests + - name: Install Dependencies if: steps.changes.outputs.everything_but_markdown == 'true' uses: ./.github/actions/install-dependencies @@ -60,8 +65,8 @@ jobs: env: NODE_DEBUG: "vite-plugin:test" # The remote-binding tests need to connect to Cloudflare - CLOUDFLARE_API_TOKEN: ${{ secrets.TEST_CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ACCOUNT_ID: ${{ secrets.TEST_CLOUDFLARE_ACCOUNT_ID }} + CLOUDFLARE_API_TOKEN: ${{ steps.check-remote.outputs.run-remote == 'true' && secrets.TEST_CLOUDFLARE_API_TOKEN || '' }} + CLOUDFLARE_ACCOUNT_ID: ${{ steps.check-remote.outputs.run-remote == 'true' && secrets.TEST_CLOUDFLARE_ACCOUNT_ID || '' }} NODE_OPTIONS: "--max_old_space_size=8192" CI_OS: ${{ matrix.os }} diff --git a/.github/workflows/e2e-wrangler.yml b/.github/workflows/e2e-wrangler.yml index 33112e79061a..2d20a868293d 100644 --- a/.github/workflows/e2e-wrangler.yml +++ b/.github/workflows/e2e-wrangler.yml @@ -6,6 +6,7 @@ on: permissions: contents: read + pull-requests: read jobs: e2e-wrangler-test: @@ -39,6 +40,10 @@ jobs: everything_but_markdown: - '!**/*.md' + - name: Check if remote tests should run + id: check-remote + uses: ./.github/actions/check-remote-tests + - name: Install Dependencies if: steps.changes.outputs.everything_but_markdown == 'true' uses: ./.github/actions/install-dependencies @@ -58,8 +63,8 @@ jobs: if: steps.changes.outputs.everything_but_markdown == 'true' run: pnpm run test:e2e:wrangler env: - CLOUDFLARE_API_TOKEN: ${{ secrets.TEST_CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ACCOUNT_ID: ${{ secrets.TEST_CLOUDFLARE_ACCOUNT_ID }} + CLOUDFLARE_API_TOKEN: ${{ steps.check-remote.outputs.run-remote == 'true' && secrets.TEST_CLOUDFLARE_API_TOKEN || '' }} + CLOUDFLARE_ACCOUNT_ID: ${{ steps.check-remote.outputs.run-remote == 'true' && secrets.TEST_CLOUDFLARE_ACCOUNT_ID || '' }} HYPERDRIVE_DATABASE_URL: ${{ secrets.TEST_HYPERDRIVE_DATABASE_URL}} HYPERDRIVE_MYSQL_DATABASE_URL: ${{ secrets.TEST_HYPERDRIVE_MYSQL_DATABASE_URL}} NODE_OPTIONS: "--max_old_space_size=8192" @@ -71,8 +76,8 @@ jobs: if: steps.changes.outputs.everything_but_markdown == 'true' run: pnpm run test:e2e -F @fixture/get-platform-proxy-remote-bindings env: - TEST_CLOUDFLARE_API_TOKEN: ${{ secrets.TEST_CLOUDFLARE_API_TOKEN }} - TEST_CLOUDFLARE_ACCOUNT_ID: ${{ secrets.TEST_CLOUDFLARE_ACCOUNT_ID }} + TEST_CLOUDFLARE_API_TOKEN: ${{ steps.check-remote.outputs.run-remote == 'true' && secrets.TEST_CLOUDFLARE_API_TOKEN || '' }} + TEST_CLOUDFLARE_ACCOUNT_ID: ${{ steps.check-remote.outputs.run-remote == 'true' && secrets.TEST_CLOUDFLARE_ACCOUNT_ID || '' }} NODE_OPTIONS: "--max_old_space_size=8192" CI_OS: ${{ matrix.os }} diff --git a/.github/workflows/rerun-remote-tests.yml b/.github/workflows/rerun-remote-tests.yml new file mode 100644 index 000000000000..402df32485df --- /dev/null +++ b/.github/workflows/rerun-remote-tests.yml @@ -0,0 +1,71 @@ +name: "Rerun Remote Tests" + +# When the "run-remote-tests" label is added to or removed from a PR, re-run +# the E2E workflows so they pick up the label change and pass (or withhold) the +# Cloudflare API token to the test steps. This avoids adding "labeled" as a +# trigger type to every E2E workflow (which would cause wasteful re-runs on +# unrelated label changes). +on: + pull_request: + types: [labeled, unlabeled] + +permissions: {} + +jobs: + rerun-e2e: + name: "Rerun E2E Tests" + if: github.event.label.name == 'run-remote-tests' + runs-on: ubuntu-latest + permissions: + actions: write + steps: + - name: "Re-run E2E workflows" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + REPO: ${{ github.repository }} + run: | + for workflow in e2e-wrangler.yml e2e-vite.yml c3-e2e.yml; do + run=$(gh api "repos/${REPO}/actions/workflows/${workflow}/runs?head_sha=${HEAD_SHA}&per_page=1" \ + --jq '.workflow_runs[0] | "\(.id) \(.status)"' || true) + + run_id=$(echo "$run" | awk '{print $1}') + status=$(echo "$run" | awk '{print $2}') + + if [ -z "$run_id" ] || [ "$run_id" = "null" ]; then + echo "No run found for ${workflow} at SHA ${HEAD_SHA}" + continue + fi + + # Each E2E workflow has a "check-remote" step that calls + # `gh pr view ... --json labels` at runtime, so a run that + # hasn't started yet will see the newly-added label on its + # own — no need to cancel and re-run it. + if [ "$status" != "completed" ] && [ "$status" != "in_progress" ]; then + echo "${workflow} run ${run_id} is ${status} — not yet started, skipping." + continue + fi + + # If the run is actively executing, the label check has + # already evaluated to false. Cancel it first — the rerun + # endpoint only works on completed runs. + if [ "$status" = "in_progress" ]; then + echo "Cancelling in-progress ${workflow} run ${run_id}..." + gh api "repos/${REPO}/actions/runs/${run_id}/cancel" --method POST || true + + # Cancellation is async; poll until the run reaches "completed". + for i in $(seq 1 30); do + current=$(gh api "repos/${REPO}/actions/runs/${run_id}" --jq '.status') + if [ "$current" = "completed" ]; then + echo "Run ${run_id} is now completed (cancelled)." + break + fi + echo " Waiting for cancellation to finish (${i}/30, status: ${current})..." + sleep 2 + done + fi + + gh api "repos/${REPO}/actions/runs/${run_id}/rerun" --method POST \ + && echo "Re-triggered ${workflow} (run ${run_id})" \ + || echo "Failed to re-run ${workflow} (run ${run_id})" + done diff --git a/.github/workflows/vite-plugin-playgrounds.yml b/.github/workflows/vite-plugin-playgrounds.yml index bd973038d9bf..756448ca4881 100644 --- a/.github/workflows/vite-plugin-playgrounds.yml +++ b/.github/workflows/vite-plugin-playgrounds.yml @@ -28,7 +28,7 @@ jobs: - os: ubuntu-latest vite: "vite-6" - os: ubuntu-latest - vite: vite-8-beta + vite: vite-8 runs-on: ${{ matrix.os }} steps: - name: Checkout Repo @@ -55,22 +55,23 @@ jobs: - name: Build plugin with initial Vite version if: steps.changes.outputs.everything_but_markdown == 'true' run: | - pnpm turbo build --filter @cloudflare/vite-plugin + pnpm dotenv -- pnpm turbo build --filter @cloudflare/vite-plugin - name: Downgrade to Vite 6 if: steps.changes.outputs.everything_but_markdown == 'true' && matrix.vite == 'vite-6' run: | pnpm update -r --no-save vite@6.4.1 - - name: Upgrade to Vite 8 beta - if: steps.changes.outputs.everything_but_markdown == 'true' && matrix.vite == 'vite-8-beta' + - name: Upgrade to Vite 8 + if: steps.changes.outputs.everything_but_markdown == 'true' && matrix.vite == 'vite-8' run: | - pnpm update -r --no-save vite@beta + pnpm update -r --no-save vite@^8.0.0 - name: Run dev playground tests if: steps.changes.outputs.everything_but_markdown == 'true' # We use `--only` to prevent TurboRepo from rebuilding dependencies # This ensures the Vite plugin is not rebuilt using a different Vite version run: | - pnpm turbo test:ci:serve --filter @vite-plugin-cloudflare/playground --only + pnpm dotenv -- pnpm turbo test:ci:serve --filter @vite-plugin-cloudflare/playground --only env: + VITE_VERSION: ${{ matrix.vite }} NODE_OPTIONS: "--max_old_space_size=8192" WRANGLER_LOG_PATH: ${{ runner.temp }}/wrangler-debug-logs/ TEST_REPORT_PATH: ${{ runner.temp }}/test-report/index.html @@ -81,8 +82,9 @@ jobs: # We use `--only` to prevent TurboRepo from rebuilding dependencies # This ensures the Vite plugin is not rebuilt using a different Vite version run: | - pnpm turbo test:ci:build --filter @vite-plugin-cloudflare/playground --only + pnpm dotenv -- pnpm turbo test:ci:build --filter @vite-plugin-cloudflare/playground --only env: + VITE_VERSION: ${{ matrix.vite }} NODE_OPTIONS: "--max_old_space_size=8192" WRANGLER_LOG_PATH: ${{ runner.temp }}/wrangler-debug-logs/ TEST_REPORT_PATH: ${{ runner.temp }}/test-report/index.html diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index afd7ce468d2d..b8f696338ed1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -345,6 +345,20 @@ If you need to test the interaction of Wrangler with a real Cloudflare account, A summary of this repositories actions can be found [in the `.github/workflows` folder](.github/workflows/README.md) +## Remote E2E Tests in CI + +E2E tests that hit the Cloudflare backend (deploying Workers, testing bindings, etc.) are inherently slow and can be flaky due to network and service dependencies. To keep PR feedback loops fast and reliable, **CI does not pass Cloudflare API credentials to E2E test jobs by default**. The E2E test suites still run on every PR, but tests that require remote access are automatically skipped when the API token is absent. + +Remote E2E tests run automatically in these cases: + +- **Version Packages PRs** (branch `changeset-release/main`) — acts as a pre-release safety net, catching remote-test failures before packages are published. +- **Merge queue** — final check before code lands on `main`. + +If you need remote E2E tests on your PR (e.g. you're changing deployment logic or binding behavior), apply the **`run-remote-tests`** label. This triggers a re-run of the E2E workflows with API credentials enabled. + +> [!NOTE] +> The `run-remote-tests` label has no effect on PRs from forks, because GitHub does not expose repository secrets to fork PRs. + ## Running E2E tests locally A large number of Wrangler, C3 & Vite's E2E tests don't require any authentication, and can be run with no Cloudflare account credentials. These can be run as follows, optionally providing [`CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN` environment variables.](#creating-an-api-token): diff --git a/fixtures/worker-with-resources/tests/index.test.ts b/fixtures/worker-with-resources/tests/index.test.ts index d26c6e6fb391..baf21ad44b2e 100644 --- a/fixtures/worker-with-resources/tests/index.test.ts +++ b/fixtures/worker-with-resources/tests/index.test.ts @@ -141,4 +141,63 @@ describe("local explorer", () => { expect(text).toBe("Hello World!"); }); }); + + // These tests verify that the local explorer API remains accessible when + // various host/routing options are configured. The security check for the + // explorer API must happen before header rewriting to work correctly. + describe.each([ + { name: "--route", flag: "--route=my-custom-site.com/*" }, + { + name: "--local-upstream", + flag: "--local-upstream=my-upstream.example.com", + }, + { name: "--host", flag: "--host=my-host.example.com" }, + ])("with $name configured", ({ flag }) => { + let ip: string; + let port: number; + let stop: (() => Promise) | undefined; + + beforeAll(async () => { + ({ ip, port, stop } = await runWranglerDev( + resolve(__dirname, ".."), + ["--port=0", "--inspector-port=0", flag], + { X_LOCAL_EXPLORER: "true" } + )); + }); + + afterAll(async () => { + await stop?.(); + }); + + it("local explorer API is still accessible via localhost", async ({ + expect, + }) => { + const response = await fetch( + `http://${ip}:${port}${LOCAL_EXPLORER_API_PATH}/storage/kv/namespaces` + ); + expect(response.status).toBe(200); + expect(response.headers.get("Content-Type")).toBe("application/json"); + const json = (await response.json()) as { success: boolean }; + expect(json.success).toBe(true); + }); + + it(`serves explorer UI`, async ({ expect }) => { + const response = await fetch( + `http://${ip}:${port}${LOCAL_EXPLORER_BASE_PATH}` + ); + expect(response.status).toBe(200); + expect(response.headers.get("Content-Type")).toBe( + "text/html; charset=utf-8" + ); + const text = await response.text(); + expect(text).toContain(""); + expect(text).toContain("Cloudflare Local Explorer"); + }); + + it("worker still responds to normal requests", async ({ expect }) => { + const response = await fetch(`http://${ip}:${port}/`); + const text = await response.text(); + expect(text).toBe("Hello World!"); + }); + }); }); diff --git a/packages/containers-shared/CHANGELOG.md b/packages/containers-shared/CHANGELOG.md index 2b6861b2ed20..0f6c8542ec18 100644 --- a/packages/containers-shared/CHANGELOG.md +++ b/packages/containers-shared/CHANGELOG.md @@ -1,5 +1,13 @@ # @cloudflare/containers-shared +## 0.11.0 + +### Minor Changes + +- [#12857](https://github.com/cloudflare/workers-sdk/pull/12857) [`3f09bb2`](https://github.com/cloudflare/workers-sdk/commit/3f09bb2e22672c4a11dc5fcdaf9e6954d3e1973b) Thanks [@gabivlj](https://github.com/gabivlj)! - Update the `proxy-everything` image used for containers local dev + + The egress interceptor image now supports HTTPS and ingress over HTTP CONNECT in workerd. + ## 0.10.0 ### Minor Changes diff --git a/packages/containers-shared/package.json b/packages/containers-shared/package.json index cd55dba2fcec..3877a85aa808 100644 --- a/packages/containers-shared/package.json +++ b/packages/containers-shared/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflare/containers-shared", - "version": "0.10.0", + "version": "0.11.0", "private": true, "description": "Package that contains shared container functionality for Cloudflare Workers SDK.", "homepage": "https://github.com/cloudflare/workers-sdk/tree/main/packages/containers-shared#readme", diff --git a/packages/containers-shared/src/client/core/PaginatedResult.ts b/packages/containers-shared/src/client/core/PaginatedResult.ts new file mode 100644 index 000000000000..d6b4c0ff6703 --- /dev/null +++ b/packages/containers-shared/src/client/core/PaginatedResult.ts @@ -0,0 +1,14 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type ResultInfo = { + page_token?: string; + per_page?: number; + next_page_token?: string; +}; + +export type PaginatedResult = { + data: T; + resultInfo?: ResultInfo; +}; diff --git a/packages/containers-shared/src/client/core/request.ts b/packages/containers-shared/src/client/core/request.ts index 09f4ab778ac4..da84b95ef365 100644 --- a/packages/containers-shared/src/client/core/request.ts +++ b/packages/containers-shared/src/client/core/request.ts @@ -7,6 +7,7 @@ import { type OpenAPIConfig } from "./OpenAPI"; import type { ApiRequestOptions } from "./ApiRequestOptions"; import type { ApiResult } from "./ApiResult"; import type { OnCancel } from "./CancelablePromise"; +import type { PaginatedResult, ResultInfo } from "./PaginatedResult"; type FetchResponseInfo = { code: number; @@ -18,6 +19,7 @@ type FetchResult = { result?: ResponseType; errors?: FetchResponseInfo[]; messages?: FetchResponseInfo[]; + result_info?: ResultInfo; }; const isDefined = ( @@ -335,6 +337,74 @@ const catchErrorCodes = ( } }; +type ExecuteRequestResult = { + url: string; + response: Response; + responseBody: any; + responseHeader: string | undefined; +}; + +/** + * Shared HTTP execution: builds URL, headers, body, sends the request, + * and returns the raw response components for further processing. + * Returns null if the request was cancelled before sending. + */ +const executeRequest = async ( + config: OpenAPIConfig, + options: ApiRequestOptions, + onCancel: OnCancel +): Promise => { + const url = getUrl(config, options); + const formData = getFormData(options); + const body = getRequestBody(options); + const headers = await getHeaders(config, options); + debugLogRequest(config, url, headers, formData ?? body ?? {}); + + if (onCancel.isCancelled) { + return null; + } + + const response = await sendRequest( + config, + options, + url, + body, + formData, + headers, + onCancel + ); + const responseBody = await getResponseBody(response); + const responseHeader = getResponseHeader(response, options.responseHeader); + + return { url, response, responseBody, responseHeader }; +}; + +/** + * Build an ApiResult from the raw response, handling V4 schema parsing. + */ +const buildApiResult = ( + config: OpenAPIConfig, + options: ApiRequestOptions, + req: ExecuteRequestResult +): ApiResult => { + if (isResponseSchemaV4(config, options)) { + return parseResponseSchemaV4( + req.url, + req.response, + req.responseHeader, + req.responseBody + ); + } + + return { + url: req.url, + ok: req.response.ok, + status: req.response.status, + statusText: req.response.statusText, + body: req.responseHeader ?? req.responseBody, + }; +}; + /** * Request method * @param config The OpenAPI configuration object @@ -348,50 +418,58 @@ export const request = ( ): CancelablePromise => { return new CancelablePromise(async (resolve, reject, onCancel) => { try { - const url = getUrl(config, options); - const formData = getFormData(options); - const body = getRequestBody(options); - const headers = await getHeaders(config, options); - debugLogRequest(config, url, headers, formData ?? body ?? {}); - - if (!onCancel.isCancelled) { - const response = await sendRequest( - config, - options, - url, - body, - formData, - headers, - onCancel - ); - const responseBody = await getResponseBody(response); - const responseHeader = getResponseHeader( - response, - options.responseHeader - ); + const req = await executeRequest(config, options, onCancel); + if (!req) { + return; + } - let result: ApiResult; + const result = buildApiResult(config, options, req); + debugLogResponse(config, result); + catchErrorCodes(options, result); + resolve(result.body); + } catch (error) { + reject(error); + } + }); +}; - if (isResponseSchemaV4(config, options)) { - result = parseResponseSchemaV4( - url, - response, - responseHeader, - responseBody - ); - } else { - result = { - url, - ok: response.ok, - status: response.status, - statusText: response.statusText, - body: responseHeader ?? responseBody, - }; - } - debugLogResponse(config, result); - catchErrorCodes(options, result); - resolve(result.body); +/** + * Request method that preserves pagination info from V4 responses + * @param config The OpenAPI configuration object + * @param options The request options from the service + * @returns CancelablePromise> + * @throws ApiError + */ +export const requestPaginated = ( + config: OpenAPIConfig, + options: ApiRequestOptions +): CancelablePromise> => { + return new CancelablePromise(async (resolve, reject, onCancel) => { + try { + const req = await executeRequest(config, options, onCancel); + if (!req) { + return; + } + + const result = buildApiResult(config, options, req); + debugLogResponse(config, result); + catchErrorCodes(options, result); + + // Extract result_info from the raw V4 response body for pagination + let resultInfo: ResultInfo | undefined; + if (isResponseSchemaV4(config, options)) { + const fetchResult = ( + typeof req.responseBody === "object" + ? req.responseBody + : JSON.parse(req.responseBody) + ) as FetchResult; + resultInfo = fetchResult.result_info; } + + resolve({ + data: result.body as T, + resultInfo, + }); } catch (error) { reject(error); } diff --git a/packages/containers-shared/src/client/index.ts b/packages/containers-shared/src/client/index.ts index f08905fbe2f2..698c3539483a 100644 --- a/packages/containers-shared/src/client/index.ts +++ b/packages/containers-shared/src/client/index.ts @@ -5,6 +5,8 @@ export { ApiError } from "./core/ApiError"; export { CancelablePromise, CancelError } from "./core/CancelablePromise"; export { OpenAPI } from "./core/OpenAPI"; export type { OpenAPIConfig } from "./core/OpenAPI"; +export type { PaginatedResult, ResultInfo } from "./core/PaginatedResult"; +export { requestPaginated } from "./core/request"; export type { AccountDefaults } from "./models/AccountDefaults"; export type { AccountID } from "./models/AccountID"; @@ -55,6 +57,9 @@ export type { CreateImageRegistryRequestBody } from "./models/CreateImageRegistr export type { CreateSSHPublicKeyError } from "./models/CreateSSHPublicKeyError"; export type { CreateSSHPublicKeyRequestBody } from "./models/CreateSSHPublicKeyRequestBody"; export type { CustomerImageRegistry } from "./models/CustomerImageRegistry"; +export type { DashApplicationDurableObjectInstance } from "./models/DashApplicationDurableObjectInstance"; +export type { DashApplicationInstance } from "./models/DashApplicationInstance"; +export type { DashApplicationInstances } from "./models/DashApplicationInstances"; export type { DeleteDeploymentError } from "./models/DeleteDeploymentError"; export type { DeleteImageRegistryResponse } from "./models/DeleteImageRegistryResponse"; export type { DeploymentAlreadyExists } from "./models/DeploymentAlreadyExists"; diff --git a/packages/containers-shared/src/client/models/DashApplicationDurableObjectInstance.ts b/packages/containers-shared/src/client/models/DashApplicationDurableObjectInstance.ts new file mode 100644 index 000000000000..0e994d22159a --- /dev/null +++ b/packages/containers-shared/src/client/models/DashApplicationDurableObjectInstance.ts @@ -0,0 +1,11 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type DashApplicationDurableObjectInstance = { + id: string; + deployment_id?: string; + placement_id?: string; + assigned_at: string; + name?: string; +}; diff --git a/packages/containers-shared/src/client/models/DashApplicationInstance.ts b/packages/containers-shared/src/client/models/DashApplicationInstance.ts new file mode 100644 index 000000000000..ff1779ac291d --- /dev/null +++ b/packages/containers-shared/src/client/models/DashApplicationInstance.ts @@ -0,0 +1,18 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { DeploymentType } from "./DeploymentType"; +import type { Placement } from "./Placement"; + +export type DashApplicationInstance = { + id: string; + created_at: string; + current_placement?: Placement; + type?: DeploymentType; + location: string; + region?: string; + app_version: number; + name?: string; + image?: string; +}; diff --git a/packages/containers-shared/src/client/models/DashApplicationInstances.ts b/packages/containers-shared/src/client/models/DashApplicationInstances.ts new file mode 100644 index 000000000000..2a7f7175b28c --- /dev/null +++ b/packages/containers-shared/src/client/models/DashApplicationInstances.ts @@ -0,0 +1,11 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { DashApplicationDurableObjectInstance } from "./DashApplicationDurableObjectInstance"; +import type { DashApplicationInstance } from "./DashApplicationInstance"; + +export type DashApplicationInstances = { + instances: DashApplicationInstance[]; + durable_objects?: DashApplicationDurableObjectInstance[]; +}; diff --git a/packages/containers-shared/src/client/models/PlacementStatus.ts b/packages/containers-shared/src/client/models/PlacementStatus.ts index 1c1b4b42c389..ccc3d21fc95f 100644 --- a/packages/containers-shared/src/client/models/PlacementStatus.ts +++ b/packages/containers-shared/src/client/models/PlacementStatus.ts @@ -14,6 +14,11 @@ export type PlacementStatus = { * Whether the deployment is ready based on the configured readiness checks. If no readiness checks are configured for this deployment, this field has the same value as "healthy". */ ready?: boolean; + /** + * The container runtime status. Preferred over `health` when deriving + * the displayed state in the Dash. + */ + container_status?: string; /** * The status of the placement in relationship to a durable object. * This only applies when the backing application is configured diff --git a/packages/containers-shared/src/client/services/ApplicationsService.ts b/packages/containers-shared/src/client/services/ApplicationsService.ts index 7ba8e83e5215..eb90aeb06169 100644 --- a/packages/containers-shared/src/client/services/ApplicationsService.ts +++ b/packages/containers-shared/src/client/services/ApplicationsService.ts @@ -2,8 +2,9 @@ /* tslint:disable */ /* eslint-disable */ import { OpenAPI } from "../core/OpenAPI"; -import { request as __request } from "../core/request"; +import { request as __request, requestPaginated } from "../core/request"; import type { CancelablePromise } from "../core/CancelablePromise"; +import type { PaginatedResult } from "../core/PaginatedResult"; import type { Application } from "../models/Application"; import type { ApplicationID } from "../models/ApplicationID"; import type { ApplicationJob } from "../models/ApplicationJob"; @@ -13,6 +14,7 @@ import type { ApplicationStatus } from "../models/ApplicationStatus"; import type { CreateApplicationJobRequest } from "../models/CreateApplicationJobRequest"; import type { CreateApplicationRequest } from "../models/CreateApplicationRequest"; import type { CreateApplicationRolloutRequest } from "../models/CreateApplicationRolloutRequest"; +import type { DashApplicationInstances } from "../models/DashApplicationInstances"; import type { DeploymentID } from "../models/DeploymentID"; import type { DeploymentV2 } from "../models/DeploymentV2"; import type { EmptyResponse } from "../models/EmptyResponse"; @@ -528,4 +530,36 @@ export class ApplicationsService { }, }); } + + /** + * List container instances for a given application + * Returns instances and optional durable object instances with pagination support + * @param applicationId + * @param perPage Number of results per page + * @param pageToken Token for fetching the next page + * @returns PaginatedResult Paginated list of instances + * @throws ApiError + */ + public static listDashApplicationInstances( + applicationId: ApplicationID, + perPage?: number, + pageToken?: string + ): CancelablePromise> { + return requestPaginated(OpenAPI, { + method: "GET", + url: "/dash/applications/{application_id}/instances", + path: { + application_id: applicationId, + }, + query: { + per_page: perPage, + page_token: pageToken, + }, + errors: { + 401: `Unauthorized`, + 404: `Application not found`, + 500: `Internal error`, + }, + }); + } } diff --git a/packages/create-cloudflare/CHANGELOG.md b/packages/create-cloudflare/CHANGELOG.md index c5e4f002a746..ff8a212f9768 100644 --- a/packages/create-cloudflare/CHANGELOG.md +++ b/packages/create-cloudflare/CHANGELOG.md @@ -1,5 +1,35 @@ # create-cloudflare +## 2.64.7 + +### Patch Changes + +- [#12805](https://github.com/cloudflare/workers-sdk/pull/12805) [`9e78285`](https://github.com/cloudflare/workers-sdk/commit/9e78285c84b11dfbc6e2d031a2f59dfcf11c5df1) Thanks [@dependabot](https://github.com/apps/dependabot)! - Update dependencies of "create-cloudflare" + + The following dependency versions have been updated: + + | Dependency | From | To | + | --------------- | ------ | ------ | + | @angular/create | 21.2.0 | 21.2.1 | + +- [#12806](https://github.com/cloudflare/workers-sdk/pull/12806) [`56986b9`](https://github.com/cloudflare/workers-sdk/commit/56986b95eea536a27f27c3177e37d675c5c5850b) 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.591 | 0.0.592 | + +- [#12512](https://github.com/cloudflare/workers-sdk/pull/12512) [`01f252d`](https://github.com/cloudflare/workers-sdk/commit/01f252db83daff52ede0c582443548833f02caf1) Thanks [@thebeyondr](https://github.com/thebeyondr)! - Fix C3 success summary dashboard link to point to Workers service production view + + The "Dash:" URL now includes `/production` so it opens the correct Workers & Pages service view in the Cloudflare dashboard. + +- [#12820](https://github.com/cloudflare/workers-sdk/pull/12820) [`556bce0`](https://github.com/cloudflare/workers-sdk/commit/556bce0acd78e42b84b25247d73699513c2cf1aa) Thanks [@dario-piotrowicz](https://github.com/dario-piotrowicz)! - Generate `app/env.d.ts` and `server/env.d.ts` for Nuxt applications + + Previously, only a top-level `env.d.ts` was created, which meant server files didn't receive Cloudflare types. Now the CLI generates separate `app/env.d.ts` and `server/env.d.ts` files, both importing from a shared `_cloudflare/env.d.ts` to avoid duplication. + + This ensures Cloudflare types are available in both app and server directories. + ## 2.64.6 ### Patch Changes diff --git a/packages/create-cloudflare/e2e/tests/frameworks/test-config.ts b/packages/create-cloudflare/e2e/tests/frameworks/test-config.ts index 4e420bb18a24..186ebb2eba32 100644 --- a/packages/create-cloudflare/e2e/tests/frameworks/test-config.ts +++ b/packages/create-cloudflare/e2e/tests/frameworks/test-config.ts @@ -66,19 +66,23 @@ function getFrameworkTestConfig(pm: string): NamedFrameworkTestConfig[] { { name: "astro:workers", argv: ["--platform", "workers"], - quarantine: true, testCommitMessage: true, unsupportedOSs: ["win32"], verifyDeploy: { route: "/", expectedText: "Hello, Astronaut!", }, + verifyDev: { + route: "/", + expectedText: "Hello, Astronaut!", + devArgs: ["--host=127.0.0.1"], + }, verifyPreview: { - previewArgs: ["--inspector-port=0"], + previewArgs: ["--host=127.0.0.1"], route: "/test", expectedText: "C3_TEST", }, - nodeCompat: true, + nodeCompat: false, flags: ["--skip-houston", "--template", "blog", "--typescript", "strict"], }, { diff --git a/packages/create-cloudflare/package.json b/packages/create-cloudflare/package.json index 67a84999942e..23c0904e5f58 100644 --- a/packages/create-cloudflare/package.json +++ b/packages/create-cloudflare/package.json @@ -1,6 +1,6 @@ { "name": "create-cloudflare", - "version": "2.64.6", + "version": "2.64.7", "description": "A CLI for creating and deploying new applications to Cloudflare.", "keywords": [ "cloudflare", diff --git a/packages/create-cloudflare/templates/astro/workers/c3.ts b/packages/create-cloudflare/templates/astro/workers/c3.ts index 2ac9750d80dd..3128ba710e57 100644 --- a/packages/create-cloudflare/templates/astro/workers/c3.ts +++ b/packages/create-cloudflare/templates/astro/workers/c3.ts @@ -1,11 +1,9 @@ -import { logRaw, updateStatus } from "@cloudflare/cli"; -import { blue, brandColor, dim } from "@cloudflare/cli/colors"; +import { logRaw } from "@cloudflare/cli"; +import { brandColor, dim } from "@cloudflare/cli/colors"; import { runFrameworkGenerator } from "frameworks/index"; -import { transformFile } from "helpers/codemod"; import { runCommand } from "helpers/command"; import { usesTypescript } from "helpers/files"; import { detectPackageManager } from "helpers/packageManagers"; -import * as recast from "recast"; import type { TemplateConfig } from "../../../src/templates"; import type { C3Context, PackageJson } from "types"; @@ -33,42 +31,6 @@ const configure = async () => { `via \`${npx} astro add cloudflare\``, )}`, }); - - // Update Astro config to enable platformProxy and imageService - const filePath = "astro.config.mjs"; - - updateStatus(`Updating configuration in ${blue(filePath)}`); - - transformFile(filePath, { - visitCallExpression: function (n) { - const callee = n.node.callee as recast.types.namedTypes.Identifier; - if (callee.name !== "cloudflare") { - return this.traverse(n); - } - - const b = recast.types.builders; - n.node.arguments = [ - b.objectExpression([ - // platformProxy: { - // enabled: true, - // }, - b.objectProperty( - b.identifier("platformProxy"), - b.objectExpression([ - b.objectProperty(b.identifier("enabled"), b.booleanLiteral(true)), - ]), - ), - // imageService: "cloudflare", - b.objectProperty( - b.identifier("imageService"), - b.stringLiteral("cloudflare"), - ), - ]), - ]; - - return false; - }, - }); }; const config: TemplateConfig = { @@ -103,7 +65,7 @@ const config: TemplateConfig = { transformPackageJson: async (pkgJson: PackageJson, ctx: C3Context) => ({ scripts: { deploy: `astro build && wrangler deploy`, - preview: `astro build && wrangler dev`, + preview: `astro build && astro preview`, ...(usesTypescript(ctx) && { "cf-typegen": `wrangler types` }), }, }), diff --git a/packages/create-cloudflare/templates/astro/workers/templates/js/wrangler.jsonc b/packages/create-cloudflare/templates/astro/workers/templates/js/wrangler.jsonc deleted file mode 100644 index dc0adf3e4b7f..000000000000 --- a/packages/create-cloudflare/templates/astro/workers/templates/js/wrangler.jsonc +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "", - "main": "./dist/_worker.js/index.js", - "compatibility_date": "", - "compatibility_flags": [ - "nodejs_compat", - "global_fetch_strictly_public" - ], - "assets": { - "binding": "ASSETS", - "directory": "./dist" - }, - "observability": { - "enabled": true - } -} diff --git a/packages/create-cloudflare/templates/astro/workers/templates/ts/wrangler.jsonc b/packages/create-cloudflare/templates/astro/workers/templates/ts/wrangler.jsonc deleted file mode 100644 index 3915689a19b6..000000000000 --- a/packages/create-cloudflare/templates/astro/workers/templates/ts/wrangler.jsonc +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "", - "main": "./dist/_worker.js/index.js", - "compatibility_date": "", - "compatibility_flags": [ - "nodejs_compat", - "global_fetch_strictly_public" - ], - "assets": { - "binding": "ASSETS", - "directory": "./dist" - }, - "observability": { - "enabled": true - }, -} diff --git a/packages/create-cloudflare/templates/react/pages/c3.ts b/packages/create-cloudflare/templates/react/pages/c3.ts index 1efb4ed8bee5..16d4f39491ca 100644 --- a/packages/create-cloudflare/templates/react/pages/c3.ts +++ b/packages/create-cloudflare/templates/react/pages/c3.ts @@ -10,7 +10,12 @@ const { npm } = detectPackageManager(); const generate = async (ctx: C3Context) => { const variant = await getVariant(ctx); - await runFrameworkGenerator(ctx, [ctx.project.name, "--template", variant]); + await runFrameworkGenerator(ctx, [ + ctx.project.name, + "--template", + variant, + "--no-immediate", + ]); logRaw(""); }; diff --git a/packages/create-cloudflare/templates/react/workers/c3.ts b/packages/create-cloudflare/templates/react/workers/c3.ts index fda371089ff2..2b60b50096e4 100644 --- a/packages/create-cloudflare/templates/react/workers/c3.ts +++ b/packages/create-cloudflare/templates/react/workers/c3.ts @@ -24,6 +24,7 @@ const generate = async (ctx: C3Context) => { ctx.project.name, "--template", variant.value, + "--no-immediate", ]); logRaw(""); diff --git a/packages/local-explorer-ui/CHANGELOG.md b/packages/local-explorer-ui/CHANGELOG.md index 9aacf4499203..ce9d6ee09b40 100644 --- a/packages/local-explorer-ui/CHANGELOG.md +++ b/packages/local-explorer-ui/CHANGELOG.md @@ -1,5 +1,11 @@ # @cloudflare/local-explorer-ui +## 0.8.1 + +### Patch Changes + +- [#12864](https://github.com/cloudflare/workers-sdk/pull/12864) [`ecc7f79`](https://github.com/cloudflare/workers-sdk/commit/ecc7f792f950fc786ff40fa140bd8907bd26ff31) Thanks [@NuroDev](https://github.com/NuroDev)! - Fix local explorer's sidebar header link to point to the correct `/cdn-cgi/explorer/` path rather than `/`. + ## 0.8.0 ### Minor Changes diff --git a/packages/local-explorer-ui/package.json b/packages/local-explorer-ui/package.json index 3dbb098284d9..cefd27cad219 100644 --- a/packages/local-explorer-ui/package.json +++ b/packages/local-explorer-ui/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflare/local-explorer-ui", - "version": "0.8.0", + "version": "0.8.1", "private": true, "type": "module", "scripts": { diff --git a/packages/local-explorer-ui/src/assets/icons/cloudflare-logo.svg b/packages/local-explorer-ui/src/assets/icons/cloudflare-logo.svg deleted file mode 100644 index 0b12cb4fd606..000000000000 --- a/packages/local-explorer-ui/src/assets/icons/cloudflare-logo.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/local-explorer-ui/src/assets/icons/d1.svg b/packages/local-explorer-ui/src/assets/icons/d1.svg new file mode 100644 index 000000000000..e2340868d450 --- /dev/null +++ b/packages/local-explorer-ui/src/assets/icons/d1.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/local-explorer-ui/src/assets/icons/durable-objects.svg b/packages/local-explorer-ui/src/assets/icons/durable-objects.svg new file mode 100644 index 000000000000..f43a1c394192 --- /dev/null +++ b/packages/local-explorer-ui/src/assets/icons/durable-objects.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/local-explorer-ui/src/assets/icons/kv.svg b/packages/local-explorer-ui/src/assets/icons/kv.svg index 6327cb2e6b9b..563665d38d97 100644 --- a/packages/local-explorer-ui/src/assets/icons/kv.svg +++ b/packages/local-explorer-ui/src/assets/icons/kv.svg @@ -1,4 +1,4 @@ - + diff --git a/packages/local-explorer-ui/src/components/AddKVForm.tsx b/packages/local-explorer-ui/src/components/AddKVForm.tsx index d001158d0349..ea09deae1ab5 100644 --- a/packages/local-explorer-ui/src/components/AddKVForm.tsx +++ b/packages/local-explorer-ui/src/components/AddKVForm.tsx @@ -1,5 +1,4 @@ -import { Button } from "@base-ui/react/button"; -import { cn } from "@cloudflare/kumo"; +import { Button, cn } from "@cloudflare/kumo"; import { useEffect, useState } from "react"; import { validateKey } from "../utils/kv-validation"; @@ -64,7 +63,7 @@ export function AddKVForm({ onAdd, clearSignal = 0 }: AddKVFormProps) {
- - - {title} - + + }> + {title} + - {items.map((item, index) => ( - - - {item} - - ))} + {items.map((item, index) => ( + + + {item} + + ))} + {children}
diff --git a/packages/local-explorer-ui/src/components/CopyButton.tsx b/packages/local-explorer-ui/src/components/CopyButton.tsx index c0e4a4c1f67d..be4865e4cbab 100644 --- a/packages/local-explorer-ui/src/components/CopyButton.tsx +++ b/packages/local-explorer-ui/src/components/CopyButton.tsx @@ -1,5 +1,4 @@ -import { Button } from "@base-ui/react/button"; -import { cn } from "@cloudflare/kumo"; +import { Button, cn } from "@cloudflare/kumo"; import { CheckIcon, CopyIcon } from "@phosphor-icons/react"; import { useState } from "react"; @@ -19,13 +18,15 @@ export function CopyButton({ text }: CopyButtonProps) { return ( diff --git a/packages/local-explorer-ui/src/components/KVTable.tsx b/packages/local-explorer-ui/src/components/KVTable.tsx index 1d530a106aba..3f80da53cc00 100644 --- a/packages/local-explorer-ui/src/components/KVTable.tsx +++ b/packages/local-explorer-ui/src/components/KVTable.tsx @@ -1,6 +1,4 @@ -import { Button } from "@base-ui/react/button"; -import { Menu } from "@base-ui/react/menu"; -import { cn } from "@cloudflare/kumo"; +import { Button, cn, DropdownMenu, Table } from "@cloudflare/kumo"; import { DotsThreeIcon, PencilIcon, TrashIcon } from "@phosphor-icons/react"; import { useState } from "react"; import { validateKey } from "../utils/kv-validation"; @@ -37,35 +35,34 @@ interface ActionMenuProps { function ActionMenu({ onEdit, onDelete }: ActionMenuProps) { return ( - - - - - - - - - - Edit - - - - - Delete - - - - - + + + + + } + /> + + + + Edit + + + + + Delete + + + ); } @@ -142,144 +139,124 @@ export function KVTable({ entries, onSave, onDelete }: KVTableProps) { const isKeyInvalid = editData ? !!validateKey(editData.key) : false; return ( - - - - - - - - - - {entries.map((entry, index) => { - const isEditing = editData?.originalKey === entry.key.name; - const isLast = index === entries.length - 1; - return ( - - -
- Key - - Value -
- {isEditing && editData ? ( -
- - handleKeyChange(e.target.value)} - onKeyDown={handleKeyDown} - disabled={saving} - autoFocus - /> - {editData.keyError && ( - - {editData.keyError} - - )} -
- ) : ( -
- - {entry.key.name} - - -
- )} -
- {isEditing && editData ? ( -
- -