From 8527675e1cf83519a211c8b4cc43161ac29757f1 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 11 Sep 2024 11:41:22 +0100 Subject: [PATCH 1/8] feat: experimental workers assets can be ignored by adding a .assetsignore file (#6640) * feat: experimental workers assets can be ignored by adding a .cfassetsignore file * fixup! feat: experimental workers assets can be ignored by adding a .cfassetsignore file --- .changeset/nasty-hats-rhyme.md | 10 +++++ .vscode/settings.json | 2 + .../wrangler/src/__tests__/deploy.test.ts | 41 +++++++++++++++++++ packages/wrangler/src/experimental-assets.ts | 36 ++++++++++++++++ packages/wrangler/src/sites.ts | 14 +------ packages/wrangler/src/utils/filesystem.ts | 16 ++++++++ 6 files changed, 106 insertions(+), 13 deletions(-) create mode 100644 .changeset/nasty-hats-rhyme.md diff --git a/.changeset/nasty-hats-rhyme.md b/.changeset/nasty-hats-rhyme.md new file mode 100644 index 000000000000..a6d754c6d60f --- /dev/null +++ b/.changeset/nasty-hats-rhyme.md @@ -0,0 +1,10 @@ +--- +"wrangler": minor +--- + +feat: experimental workers assets can be ignored by adding a .assetsignore file + +This file can be added to the root of the assets directory that is to be uploaded alongside the Worker +when using `experimental_assets`. + +The file follows the `.gitignore` syntax, and any matching paths will not be included in the upload. diff --git a/.vscode/settings.json b/.vscode/settings.json index 09b6e6507cd5..af3e13c4c8a8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,7 @@ "editor.defaultFormatter": "esbenp.prettier-vscode", "cSpell.words": [ "Abortable", + "assetsignore", "cfetch", "chatgpt", "clipboardy", @@ -11,6 +12,7 @@ "esbuild", "eslintcache", "execa", + "filestat", "haikunate", "haikunator", "httplogs", diff --git a/packages/wrangler/src/__tests__/deploy.test.ts b/packages/wrangler/src/__tests__/deploy.test.ts index 54b68b901b2b..bd94d079db57 100644 --- a/packages/wrangler/src/__tests__/deploy.test.ts +++ b/packages/wrangler/src/__tests__/deploy.test.ts @@ -4381,6 +4381,47 @@ addEventListener('fetch', event => {});` }); }); + it("should ignore assets that match patterns in an .assetsignore file in the root of the assets directory", async () => { + const assets = [ + { filePath: ".assetsignore", content: "*.bak\nsub-dir" }, + { filePath: "file-1.txt", content: "Content of file-1" }, + { filePath: "file-2.bak", content: "Content of file-2" }, + { filePath: "file-3.txt", content: "Content of file-3" }, + { filePath: "sub-dir/file-4.bak", content: "Content of file-4" }, + { filePath: "sub-dir/file-5.txt", content: "Content of file-5" }, + ]; + writeAssets(assets, "some/path/assets"); + writeWranglerToml( + { + experimental_assets: { directory: "assets" }, + }, + "some/path/wrangler.toml" + ); + const bodies: AssetManifest[] = []; + await mockAUSRequest(bodies); + mockSubDomainRequest(); + mockUploadWorkerRequest({ + expectedExperimentalAssets: true, + expectedType: "none", + }); + await runWrangler("deploy --config some/path/wrangler.toml"); + expect(bodies.length).toBe(1); + expect(bodies[0]).toMatchInlineSnapshot(` + Object { + "manifest": Object { + "/file-1.txt": Object { + "hash": "0de3dd5df907418e9730fd2bd747bd5e", + "size": 17, + }, + "/file-3.txt": Object { + "hash": "ff5016e92f039aa743a4ff7abb3180fa", + "size": 17, + }, + }, + } + `); + }); + it("should resolve assets directory relative to cwd if using cli", async () => { const assets = [{ filePath: "file-1.txt", content: "Content of file-1" }]; writeAssets(assets, "some/path/assets"); diff --git a/packages/wrangler/src/experimental-assets.ts b/packages/wrangler/src/experimental-assets.ts index 211a2f6adc06..47f587adb099 100644 --- a/packages/wrangler/src/experimental-assets.ts +++ b/packages/wrangler/src/experimental-assets.ts @@ -14,6 +14,7 @@ import { logger, LOGGER_LEVELS } from "./logger"; import { hashFile } from "./pages/hash"; import { isJwtExpired } from "./pages/upload"; import { APIError } from "./parse"; +import { createPatternMatcher } from "./utils/filesystem"; import type { Config } from "./config"; import type { ExperimentalAssets } from "./config/environment"; @@ -220,10 +221,20 @@ export const buildAssetsManifest = async (dir: string) => { const files = await readdir(dir, { recursive: true }); const manifest: AssetManifest = {}; let counter = 0; + + const ignoreFn = await createAssetIgnoreFunction(dir); + await Promise.all( files.map(async (file) => { const filepath = path.join(dir, file); const relativeFilepath = path.relative(dir, filepath); + + if (ignoreFn?.(relativeFilepath)) { + logger.debug("Ignoring asset:", relativeFilepath); + // This file should not be included in the manifest. + return; + } + const filestat = await stat(filepath); if (filestat.isSymbolicLink() || filestat.isDirectory()) { @@ -361,3 +372,28 @@ const decodeFilepath = (filePath: string) => { .map((segment) => decodeURIComponent(segment)) .join(path.sep); }; + +/** + * Create a function for filtering out ignored assets. + * + * The generated function takes an asset path, relative to the asset directory, + * and returns true if the asset should not be ignored. + */ +async function createAssetIgnoreFunction(dir: string) { + const CF_ASSETS_IGNORE_FILENAME = ".assetsignore"; + + const cfAssetIgnorePath = path.resolve(dir, CF_ASSETS_IGNORE_FILENAME); + + if (!existsSync(cfAssetIgnorePath)) { + return null; + } + + const ignorePatterns = ( + await readFile(cfAssetIgnorePath, { encoding: "utf8" }) + ).split("\n"); + + // Always ignore the `.assetsignore` file. + ignorePatterns.push(CF_ASSETS_IGNORE_FILENAME); + + return createPatternMatcher(ignorePatterns, true); +} diff --git a/packages/wrangler/src/sites.ts b/packages/wrangler/src/sites.ts index 649f9fb278bd..1f189c621191 100644 --- a/packages/wrangler/src/sites.ts +++ b/packages/wrangler/src/sites.ts @@ -2,7 +2,6 @@ import assert from "node:assert"; import { readdir, readFile, stat } from "node:fs/promises"; import * as path from "node:path"; import chalk from "chalk"; -import ignore from "ignore"; import xxhash from "xxhash-wasm"; import { UserError } from "./errors"; import { @@ -17,6 +16,7 @@ import { putKVKeyValue, } from "./kv/helpers"; import { logger, LOGGER_LEVELS } from "./logger"; +import { createPatternMatcher } from "./utils/filesystem"; import type { Config } from "./config"; import type { KeyValue } from "./kv/helpers"; import type { XXHashAPI } from "xxhash-wasm"; @@ -391,18 +391,6 @@ export async function syncLegacyAssets( return { manifest, namespace }; } -function createPatternMatcher( - patterns: string[], - exclude: boolean -): (filePath: string) => boolean { - if (patterns.length === 0) { - return (_filePath) => !exclude; - } else { - const ignorer = ignore().add(patterns); - return (filePath) => ignorer.test(filePath).ignored; - } -} - /** * validate that the passed-in file is below 25 MiB * **PRIOR** to base64 encoding. 25 MiB is a KV limit diff --git a/packages/wrangler/src/utils/filesystem.ts b/packages/wrangler/src/utils/filesystem.ts index 390eb7d24169..c0866d0a709f 100644 --- a/packages/wrangler/src/utils/filesystem.ts +++ b/packages/wrangler/src/utils/filesystem.ts @@ -1,6 +1,7 @@ import { mkdirSync } from "fs"; import { mkdir } from "fs/promises"; import path from "path"; +import ignore from "ignore"; export async function ensureDirectoryExists(filepath: string) { const dirpath = path.dirname(filepath); @@ -13,3 +14,18 @@ export function ensureDirectoryExistsSync(filepath: string) { mkdirSync(dirpath, { recursive: true }); } + +/** + * Generate a function that can match relative filepaths against a list of gitignore formatted patterns. + */ +export function createPatternMatcher( + patterns: string[], + exclude: boolean +): (filePath: string) => boolean { + if (patterns.length === 0) { + return (_filePath) => !exclude; + } else { + const ignorer = ignore().add(patterns); + return (filePath) => ignorer.test(filePath).ignored; + } +} From 81c40f0a211e3b4bba6e24f76f7a2707566bbd76 Mon Sep 17 00:00:00 2001 From: Greg Brimble Date: Wed, 11 Sep 2024 10:03:14 -0400 Subject: [PATCH 2/8] chore(workers-shared): Configure GitHub Actions to deploy Asset Worker (#6542) Co-authored-by: Carmen Popoviciu --- .github/workflows/changesets.yml | 1 + packages/workers-shared/asset-worker/README.md | 2 +- packages/workers-shared/asset-worker/wrangler.toml | 2 ++ packages/workers-shared/package.json | 2 ++ 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/changesets.yml b/.github/workflows/changesets.yml index c4bcc0c05f63..edaa365b863f 100644 --- a/.github/workflows/changesets.yml +++ b/.github/workflows/changesets.yml @@ -64,6 +64,7 @@ jobs: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} WORKERS_NEW_CLOUDFLARE_ACCOUNT_ID: ${{ secrets.WORKERS_NEW_CLOUDFLARE_ACCOUNT_ID }} WORKERS_NEW_CLOUDFLARE_API_TOKEN: ${{ secrets.WORKERS_NEW_CLOUDFLARE_API_TOKEN }} + WORKERS_DEPLOY_AND_CONFIG_CLOUDFLARE_API_TOKEN: ${{ secrets.WORKERS_DEPLOY_AND_CONFIG_CLOUDFLARE_API_TOKEN }} - name: Create C3 Diffs PR if: contains(steps.changesets.outputs.publishedPackages, '"create-cloudflare"') diff --git a/packages/workers-shared/asset-worker/README.md b/packages/workers-shared/asset-worker/README.md index eee3c83195aa..555e74dd7291 100644 --- a/packages/workers-shared/asset-worker/README.md +++ b/packages/workers-shared/asset-worker/README.md @@ -1,3 +1,3 @@ # `asset-worker` -The Asset Worker is a [Cloudflare Worker](https://developers.cloudflare.com/workers/) that is responsible of serving assets for Workers deployed on the edge, that contain static assets as well. +The Asset Worker is a [Cloudflare Worker](https://developers.cloudflare.com/workers/) that is responsible of serving static assets. It is deployed in production and is also used in local `wrangler dev`. diff --git a/packages/workers-shared/asset-worker/wrangler.toml b/packages/workers-shared/asset-worker/wrangler.toml index 52c101d14c04..97a2c3faf404 100644 --- a/packages/workers-shared/asset-worker/wrangler.toml +++ b/packages/workers-shared/asset-worker/wrangler.toml @@ -8,5 +8,7 @@ # (see packages/wrangler/src/dev/miniflare.ts -> buildMiniflareOptions()) ## name = "asset-worker" +account_id = "0f1b8aa119a907021f659042f95ea9ba" +workers_dev = false main = "src/index.ts" compatibility_date = "2024-07-31" diff --git a/packages/workers-shared/package.json b/packages/workers-shared/package.json index 9850528a427b..a128ee2f65c4 100644 --- a/packages/workers-shared/package.json +++ b/packages/workers-shared/package.json @@ -31,6 +31,8 @@ "check:type": "pnpm run check:type:tests && tsc", "check:type:tests": "tsc -p ./asset-worker/tests/tsconfig.json", "clean": "rimraf dist", + "deploy": "pnpm run deploy:asset-worker", + "deploy:asset-worker": "CLOUDFLARE_API_TOKEN=$WORKERS_NEW_CLOUDFLARE_API_TOKEN wrangler versions upload --experimental-versions -c asset-worker/wrangler.toml", "dev": "pnpm run clean && concurrently -n bundle:asset-worker,bundle:router-worker -c blue,magenta \"pnpm run bundle:asset-worker --watch\" \"pnpm run bundle:router-worker --watch\"", "test": "vitest", "test:ci": "pnpm run test run" From 3f5b9343a46dedcb80c8e216eb3ca9d7f687f6cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:39:10 +0100 Subject: [PATCH 3/8] chore(deps): bump the workerd-and-workers-types group across 1 directory with 2 updates (#6673) * chore(deps): bump the workerd-and-workers-types group across 1 directory with 2 updates Bumps the workerd-and-workers-types group with 2 updates in the / directory: [@cloudflare/workers-types](https://github.com/cloudflare/workerd) and [workerd](https://github.com/cloudflare/workerd). Updates `@cloudflare/workers-types` from 4.20240821.1 to 4.20240909.0 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) Updates `workerd` from 1.20240821.1 to 1.20240909.0 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits/v1.20240909.0) --- updated-dependencies: - dependency-name: "@cloudflare/workers-types" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: workerd-and-workers-types - dependency-name: workerd dependency-type: direct:production update-type: version-update:semver-minor dependency-group: workerd-and-workers-types ... Signed-off-by: dependabot[bot] * chore: update dependencies of "miniflare" package The following dependency versions have been updated: | Dependency | From | To | | ------------------------- | ------------- | ------------- | | workerd | 1.20240821.1 | 1.20240909.0 | | @cloudflare/workers-types | ^4.20240821.1 | ^4.20240909.0 | * Inject `node:url` polyfill into the Vitest integration * Create hot-moons-walk.md --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Wrangler automated PR updater Co-authored-by: Samuel Macleod --- .changeset/dependabot-update-6673.md | 12 ++ .changeset/hot-moons-walk.md | 5 + fixtures/additional-modules/package.json | 2 +- .../external-durable-objects-app/package.json | 2 +- .../package.json | 2 +- fixtures/get-platform-proxy/package.json | 2 +- fixtures/local-mode-tests/package.json | 2 +- fixtures/node-app-pages/package.json | 2 +- fixtures/nodejs-als-app/package.json | 2 +- fixtures/nodejs-hybrid-app/package.json | 2 +- .../pages-dev-proxy-with-script/package.json | 2 +- fixtures/pages-functions-app/package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- fixtures/pages-simple-assets/package.json | 2 +- .../vitest-pool-workers-examples/package.json | 2 +- fixtures/worker-ts/package.json | 2 +- .../workers-with-assets-only/package.json | 2 +- fixtures/workers-with-assets/package.json | 2 +- package.json | 2 +- packages/create-cloudflare/package.json | 2 +- .../package.json | 2 +- packages/format-errors/package.json | 2 +- packages/kv-asset-handler/package.json | 2 +- packages/miniflare/package.json | 4 +- packages/pages-shared/package.json | 2 +- .../playground-preview-worker/package.json | 2 +- packages/prerelease-registry/package.json | 2 +- packages/quick-edit-extension/package.json | 2 +- packages/vitest-pool-workers/package.json | 2 +- .../vitest-pool-workers/src/pool/index.ts | 9 + packages/workers-shared/package.json | 2 +- packages/workers.new/package.json | 2 +- packages/wrangler/package.json | 6 +- pnpm-lock.yaml | 186 +++++++++--------- 35 files changed, 153 insertions(+), 127 deletions(-) create mode 100644 .changeset/dependabot-update-6673.md create mode 100644 .changeset/hot-moons-walk.md diff --git a/.changeset/dependabot-update-6673.md b/.changeset/dependabot-update-6673.md new file mode 100644 index 000000000000..7e1ff6de2dc6 --- /dev/null +++ b/.changeset/dependabot-update-6673.md @@ -0,0 +1,12 @@ +--- +"miniflare": patch +--- + +chore: update dependencies of "miniflare" package + +The following dependency versions have been updated: + +| Dependency | From | To | +| ------------------------- | ------------- | ------------- | +| workerd | 1.20240821.1 | 1.20240909.0 | +| @cloudflare/workers-types | ^4.20240821.1 | ^4.20240909.0 | diff --git a/.changeset/hot-moons-walk.md b/.changeset/hot-moons-walk.md new file mode 100644 index 000000000000..d58911469331 --- /dev/null +++ b/.changeset/hot-moons-walk.md @@ -0,0 +1,5 @@ +--- +"@cloudflare/vitest-pool-workers": patch +--- + +fix: The `workerd` provided `node:url` module doesn't support everything Vitest needs. As a short-term fix, inject the `node:url` polyfill into the worker bundle. diff --git a/fixtures/additional-modules/package.json b/fixtures/additional-modules/package.json index b2fa77c80074..b2201fdb49c7 100644 --- a/fixtures/additional-modules/package.json +++ b/fixtures/additional-modules/package.json @@ -12,7 +12,7 @@ }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "undici": "^5.28.4", "wrangler": "workspace:*" }, diff --git a/fixtures/external-durable-objects-app/package.json b/fixtures/external-durable-objects-app/package.json index 7f192bb54706..11567e9d847c 100644 --- a/fixtures/external-durable-objects-app/package.json +++ b/fixtures/external-durable-objects-app/package.json @@ -11,7 +11,7 @@ }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "undici": "^5.28.4", "wrangler": "workspace:*" }, diff --git a/fixtures/external-service-bindings-app/package.json b/fixtures/external-service-bindings-app/package.json index e53a401a7187..96a5b0bfc403 100644 --- a/fixtures/external-service-bindings-app/package.json +++ b/fixtures/external-service-bindings-app/package.json @@ -16,7 +16,7 @@ }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "concurrently": "^8.2.2", "undici": "^5.28.4", "wrangler": "workspace:*" diff --git a/fixtures/get-platform-proxy/package.json b/fixtures/get-platform-proxy/package.json index 8378bb549f39..31b34360c72f 100644 --- a/fixtures/get-platform-proxy/package.json +++ b/fixtures/get-platform-proxy/package.json @@ -9,7 +9,7 @@ }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "undici": "^5.28.4", "wrangler": "workspace:*" }, diff --git a/fixtures/local-mode-tests/package.json b/fixtures/local-mode-tests/package.json index 20ba85438d53..2e5c659fd0ca 100644 --- a/fixtures/local-mode-tests/package.json +++ b/fixtures/local-mode-tests/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "@types/node": "20.8.3", "buffer": "^6.0.3", "wrangler": "workspace:*" diff --git a/fixtures/node-app-pages/package.json b/fixtures/node-app-pages/package.json index 73be1fd72298..ce53365359e5 100644 --- a/fixtures/node-app-pages/package.json +++ b/fixtures/node-app-pages/package.json @@ -15,7 +15,7 @@ }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "undici": "^5.28.4", "wrangler": "workspace:*" }, diff --git a/fixtures/nodejs-als-app/package.json b/fixtures/nodejs-als-app/package.json index e8190212056b..3932fa74e2af 100644 --- a/fixtures/nodejs-als-app/package.json +++ b/fixtures/nodejs-als-app/package.json @@ -9,7 +9,7 @@ }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "undici": "^5.28.4", "wrangler": "workspace:*" } diff --git a/fixtures/nodejs-hybrid-app/package.json b/fixtures/nodejs-hybrid-app/package.json index 785308b2a6d7..d141a9cd904e 100644 --- a/fixtures/nodejs-hybrid-app/package.json +++ b/fixtures/nodejs-hybrid-app/package.json @@ -9,7 +9,7 @@ }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "@types/pg": "^8.11.2", "pg": "8.11.3", "pg-cloudflare": "^1.1.1", diff --git a/fixtures/pages-dev-proxy-with-script/package.json b/fixtures/pages-dev-proxy-with-script/package.json index f64c6c2a41c4..fe3c96e2b803 100644 --- a/fixtures/pages-dev-proxy-with-script/package.json +++ b/fixtures/pages-dev-proxy-with-script/package.json @@ -10,7 +10,7 @@ }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "undici": "^5.28.4", "wrangler": "workspace:*" }, diff --git a/fixtures/pages-functions-app/package.json b/fixtures/pages-functions-app/package.json index c86e468790b5..18be8ad9568b 100644 --- a/fixtures/pages-functions-app/package.json +++ b/fixtures/pages-functions-app/package.json @@ -15,7 +15,7 @@ }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "pages-plugin-example": "workspace:*", "undici": "^5.28.4", "wrangler": "workspace:*" diff --git a/fixtures/pages-functions-with-routes-app/package.json b/fixtures/pages-functions-with-routes-app/package.json index f6c8ec5e5810..2a1375c4d66e 100644 --- a/fixtures/pages-functions-with-routes-app/package.json +++ b/fixtures/pages-functions-with-routes-app/package.json @@ -11,7 +11,7 @@ }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "undici": "^5.28.4", "wrangler": "workspace:*" }, diff --git a/fixtures/pages-plugin-mounted-on-root-app/package.json b/fixtures/pages-plugin-mounted-on-root-app/package.json index c3b87338ac31..a0d8ee7acc88 100644 --- a/fixtures/pages-plugin-mounted-on-root-app/package.json +++ b/fixtures/pages-plugin-mounted-on-root-app/package.json @@ -15,7 +15,7 @@ }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "pages-plugin-example": "workspace:*", "undici": "^5.28.4", "wrangler": "workspace:*" diff --git a/fixtures/pages-simple-assets/package.json b/fixtures/pages-simple-assets/package.json index b7da99f2b7b4..25d65bc7633e 100644 --- a/fixtures/pages-simple-assets/package.json +++ b/fixtures/pages-simple-assets/package.json @@ -12,7 +12,7 @@ }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "undici": "^5.28.4", "wrangler": "workspace:*" }, diff --git a/fixtures/vitest-pool-workers-examples/package.json b/fixtures/vitest-pool-workers-examples/package.json index 4010efeec8eb..9f7c6791f032 100644 --- a/fixtures/vitest-pool-workers-examples/package.json +++ b/fixtures/vitest-pool-workers-examples/package.json @@ -9,7 +9,7 @@ }, "devDependencies": { "@cloudflare/vitest-pool-workers": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "@types/node": "20.8.3", "ext-dep": "file:./internal-module-resolution/vendor/ext-dep", "jose": "^5.2.2", diff --git a/fixtures/worker-ts/package.json b/fixtures/worker-ts/package.json index 3d422cf5f89a..2c42c0e3cd26 100644 --- a/fixtures/worker-ts/package.json +++ b/fixtures/worker-ts/package.json @@ -6,7 +6,7 @@ "start": "wrangler dev --x-dev-env" }, "devDependencies": { - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "wrangler": "workspace:*" }, "volta": { diff --git a/fixtures/workers-with-assets-only/package.json b/fixtures/workers-with-assets-only/package.json index aa7b12de79fa..562ffdc70927 100644 --- a/fixtures/workers-with-assets-only/package.json +++ b/fixtures/workers-with-assets-only/package.json @@ -9,7 +9,7 @@ }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "undici": "^5.28.4", "wrangler": "workspace:*" }, diff --git a/fixtures/workers-with-assets/package.json b/fixtures/workers-with-assets/package.json index 32620a77748f..8b28acf3d224 100644 --- a/fixtures/workers-with-assets/package.json +++ b/fixtures/workers-with-assets/package.json @@ -10,7 +10,7 @@ }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "undici": "^5.28.4", "wrangler": "workspace:*" }, diff --git a/package.json b/package.json index faef534318ac..f24967fa5474 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "@changesets/changelog-github": "^0.5.0", "@changesets/cli": "^2.27.1", "@changesets/parse": "^0.4.0", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "@ianvs/prettier-plugin-sort-imports": "4.2.1", "@manypkg/cli": "^0.21.4", "@turbo/gen": "^1.10.13", diff --git a/packages/create-cloudflare/package.json b/packages/create-cloudflare/package.json index dfca55041716..a61d2bdfec23 100644 --- a/packages/create-cloudflare/package.json +++ b/packages/create-cloudflare/package.json @@ -48,7 +48,7 @@ "@cloudflare/cli": "workspace:*", "@cloudflare/eslint-config-worker": "workspace:*", "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "@iarna/toml": "^3.0.0", "@types/command-exists": "^1.2.0", "@types/cross-spawn": "^6.0.2", diff --git a/packages/edge-preview-authenticated-proxy/package.json b/packages/edge-preview-authenticated-proxy/package.json index e4dc4c3a094c..4ea44624abe5 100644 --- a/packages/edge-preview-authenticated-proxy/package.json +++ b/packages/edge-preview-authenticated-proxy/package.json @@ -12,7 +12,7 @@ }, "devDependencies": { "@cloudflare/eslint-config-worker": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "@types/cookie": "^0.6.0", "cookie": "^0.6.0", "promjs": "^0.4.2", diff --git a/packages/format-errors/package.json b/packages/format-errors/package.json index d59a9e8704d7..8abeffad1f19 100644 --- a/packages/format-errors/package.json +++ b/packages/format-errors/package.json @@ -11,7 +11,7 @@ }, "devDependencies": { "@cloudflare/eslint-config-worker": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "mustache": "^4.2.0", "promjs": "^0.4.2", "toucan-js": "^3.3.1", diff --git a/packages/kv-asset-handler/package.json b/packages/kv-asset-handler/package.json index 34c783c4f605..ece0cf8d0af0 100644 --- a/packages/kv-asset-handler/package.json +++ b/packages/kv-asset-handler/package.json @@ -42,7 +42,7 @@ }, "devDependencies": { "@ava/typescript": "^4.1.0", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "@types/mime": "^3.0.4", "@types/node": "20.8.3", "@types/service-worker-mock": "^2.0.1", diff --git a/packages/miniflare/package.json b/packages/miniflare/package.json index 57d67e8fd380..5aec07861b29 100644 --- a/packages/miniflare/package.json +++ b/packages/miniflare/package.json @@ -50,7 +50,7 @@ "glob-to-regexp": "^0.4.1", "stoppable": "^1.1.0", "undici": "^5.28.4", - "workerd": "1.20240821.1", + "workerd": "1.20240909.0", "ws": "^8.17.1", "youch": "^3.2.2", "zod": "^3.22.3" @@ -59,7 +59,7 @@ "@ava/typescript": "^4.1.0", "@cloudflare/kv-asset-handler": "workspace:*", "@cloudflare/workers-shared": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "@microsoft/api-extractor": "^7.47.0", "@types/debug": "^4.1.7", "@types/estree": "^1.0.0", diff --git a/packages/pages-shared/package.json b/packages/pages-shared/package.json index 30825ef74365..580cab9e1d9a 100644 --- a/packages/pages-shared/package.json +++ b/packages/pages-shared/package.json @@ -23,7 +23,7 @@ }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "@miniflare/cache": "^2.14.2", "@miniflare/core": "^2.14.2", "@miniflare/html-rewriter": "^2.14.2", diff --git a/packages/playground-preview-worker/package.json b/packages/playground-preview-worker/package.json index 4675684861ca..07ae5d22223b 100644 --- a/packages/playground-preview-worker/package.json +++ b/packages/playground-preview-worker/package.json @@ -19,7 +19,7 @@ }, "devDependencies": { "@cloudflare/eslint-config-worker": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "@types/cookie": "^0.6.0", "cookie": "^0.6.0", "itty-router": "^4.0.13", diff --git a/packages/prerelease-registry/package.json b/packages/prerelease-registry/package.json index 8d8581adfad8..9f69deb600ad 100644 --- a/packages/prerelease-registry/package.json +++ b/packages/prerelease-registry/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@cloudflare/eslint-config-worker": "workspace:*", "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "wrangler": "workspace:*" }, "volta": { diff --git a/packages/quick-edit-extension/package.json b/packages/quick-edit-extension/package.json index 95f651ee7932..b60de12c9193 100644 --- a/packages/quick-edit-extension/package.json +++ b/packages/quick-edit-extension/package.json @@ -43,7 +43,7 @@ ], "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:^", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "esbuild": "0.17.19", "esbuild-register": "^3.5.0" }, diff --git a/packages/vitest-pool-workers/package.json b/packages/vitest-pool-workers/package.json index e85c0d12d806..d1a54cd97b82 100644 --- a/packages/vitest-pool-workers/package.json +++ b/packages/vitest-pool-workers/package.json @@ -64,7 +64,7 @@ "devDependencies": { "@cloudflare/eslint-config-worker": "workspace:*", "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "@types/node": "20.8.3", "@types/semver": "^7.5.1", "@vitest/runner": "1.3.x - 1.5.x", diff --git a/packages/vitest-pool-workers/src/pool/index.ts b/packages/vitest-pool-workers/src/pool/index.ts index 7fde04ac0b52..291b2fb4cd49 100644 --- a/packages/vitest-pool-workers/src/pool/index.ts +++ b/packages/vitest-pool-workers/src/pool/index.ts @@ -92,6 +92,8 @@ const __dirname = path.dirname(__filename); const DIST_PATH = path.resolve(__dirname, ".."); const POOL_WORKER_PATH = path.join(DIST_PATH, "worker", "index.mjs"); +const NODE_URL_PATH = path.join(DIST_PATH, "worker", "lib", "node", "url.mjs"); + const symbolizerWarning = "warning: Not symbolizing stack traces because $LLVM_SYMBOLIZER is not set."; const ignoreMessages = [ @@ -486,6 +488,13 @@ function buildProjectWorkerOptions( path: path.join(modulesRoot, DEFINES_MODULE_PATH), contents: defines, }, + // The workerd provided `node:url` module doesn't support everything Vitest needs. + // As a short-term fix, inject a `node:url` polyfill into the worker bundle + { + type: "ESModule", + path: path.join(modulesRoot, "node:url"), + contents: fs.readFileSync(NODE_URL_PATH), + }, ]; // Build array of workers contributed by the workspace diff --git a/packages/workers-shared/package.json b/packages/workers-shared/package.json index a128ee2f65c4..36dadcf611c2 100644 --- a/packages/workers-shared/package.json +++ b/packages/workers-shared/package.json @@ -40,7 +40,7 @@ "devDependencies": { "@cloudflare/eslint-config-worker": "workspace:*", "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "concurrently": "^8.2.2", "esbuild": "0.17.19", "rimraf": "^6.0.1", diff --git a/packages/workers.new/package.json b/packages/workers.new/package.json index 1238bda3c7e8..b2c0aa3e5c70 100644 --- a/packages/workers.new/package.json +++ b/packages/workers.new/package.json @@ -11,7 +11,7 @@ }, "devDependencies": { "@cloudflare/vitest-pool-workers": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "@types/node": "20.8.3", "miniflare": "workspace:*", "typescript": "^5.5.2", diff --git a/packages/wrangler/package.json b/packages/wrangler/package.json index 3d1983f0e9de..90af8da111a7 100644 --- a/packages/wrangler/package.json +++ b/packages/wrangler/package.json @@ -85,7 +85,7 @@ "selfsigned": "^2.0.1", "source-map": "^0.6.1", "unenv": "npm:unenv-nightly@2.0.0-1724863496.70db6f1", - "workerd": "1.20240821.1", + "workerd": "1.20240909.0", "xxhash-wasm": "^1.0.1" }, "devDependencies": { @@ -94,7 +94,7 @@ "@cloudflare/pages-shared": "workspace:^", "@cloudflare/types": "^6.18.4", "@cloudflare/workers-tsconfig": "workspace:*", - "@cloudflare/workers-types": "^4.20240821.1", + "@cloudflare/workers-types": "^4.20240909.0", "@cspotcode/source-map-support": "0.8.1", "@iarna/toml": "^3.0.0", "@microsoft/api-extractor": "^7.47.0", @@ -177,7 +177,7 @@ "yoga-layout": "file:../../vendor/yoga-layout-2.0.0-beta.1.tgz" }, "peerDependencies": { - "@cloudflare/workers-types": "^4.20240821.1" + "@cloudflare/workers-types": "^4.20240909.0" }, "peerDependenciesMeta": { "@cloudflare/workers-types": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f26b33d649c5..11f25d99b978 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,8 +42,8 @@ importers: specifier: ^0.4.0 version: 0.4.0 '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 '@ianvs/prettier-plugin-sort-imports': specifier: 4.2.1 version: 4.2.1(@vue/compiler-sfc@3.3.4)(prettier@3.2.5) @@ -105,8 +105,8 @@ importers: specifier: workspace:* version: link:../../packages/workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 undici: specifier: ^5.28.4 version: 5.28.4 @@ -165,8 +165,8 @@ importers: specifier: workspace:* version: link:../../packages/workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 undici: specifier: ^5.28.4 version: 5.28.4 @@ -180,8 +180,8 @@ importers: specifier: workspace:* version: link:../../packages/workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 concurrently: specifier: ^8.2.2 version: 8.2.2 @@ -198,8 +198,8 @@ importers: specifier: workspace:* version: link:../../packages/workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 undici: specifier: ^5.28.4 version: 5.28.4 @@ -251,8 +251,8 @@ importers: specifier: workspace:* version: link:../../packages/workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 '@types/node': specifier: 20.8.3 version: 20.8.3 @@ -282,8 +282,8 @@ importers: specifier: workspace:* version: link:../../packages/workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 undici: specifier: ^5.28.4 version: 5.28.4 @@ -297,8 +297,8 @@ importers: specifier: workspace:* version: link:../../packages/workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 undici: specifier: ^5.28.4 version: 5.28.4 @@ -312,8 +312,8 @@ importers: specifier: workspace:* version: link:../../packages/workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 '@types/pg': specifier: ^8.11.2 version: 8.11.6 @@ -348,8 +348,8 @@ importers: specifier: workspace:* version: link:../../packages/workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 undici: specifier: ^5.28.4 version: 5.28.4 @@ -367,8 +367,8 @@ importers: specifier: workspace:* version: link:../../packages/workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 pages-plugin-example: specifier: workspace:* version: link:../pages-plugin-example @@ -409,8 +409,8 @@ importers: specifier: workspace:* version: link:../../packages/workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 undici: specifier: ^5.28.4 version: 5.28.4 @@ -449,8 +449,8 @@ importers: specifier: workspace:* version: link:../../packages/workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 pages-plugin-example: specifier: workspace:* version: link:../pages-plugin-example @@ -482,8 +482,8 @@ importers: specifier: workspace:* version: link:../../packages/workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 undici: specifier: ^5.28.4 version: 5.28.4 @@ -639,8 +639,8 @@ importers: specifier: workspace:* version: link:../../packages/vitest-pool-workers '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 '@types/node': specifier: 20.8.3 version: 20.8.3 @@ -690,8 +690,8 @@ importers: fixtures/worker-ts: devDependencies: '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 wrangler: specifier: workspace:* version: link:../../packages/wrangler @@ -704,8 +704,8 @@ importers: specifier: workspace:* version: link:../../packages/workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 undici: specifier: ^5.28.4 version: 5.28.4 @@ -719,8 +719,8 @@ importers: specifier: workspace:* version: link:../../packages/workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 undici: specifier: ^5.28.4 version: 5.28.4 @@ -782,8 +782,8 @@ importers: specifier: workspace:* version: link:../workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 '@iarna/toml': specifier: ^3.0.0 version: 3.0.0 @@ -908,8 +908,8 @@ importers: specifier: workspace:* version: link:../eslint-config-worker '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 '@types/cookie': specifier: ^0.6.0 version: 0.6.0 @@ -963,8 +963,8 @@ importers: specifier: workspace:* version: link:../eslint-config-worker '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 mustache: specifier: ^4.2.0 version: 4.2.0 @@ -994,8 +994,8 @@ importers: specifier: ^4.1.0 version: 4.1.0 '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 '@types/mime': specifier: ^3.0.4 version: 3.0.4 @@ -1039,8 +1039,8 @@ importers: specifier: ^5.28.4 version: 5.28.4 workerd: - specifier: 1.20240821.1 - version: 1.20240821.1 + specifier: 1.20240909.0 + version: 1.20240909.0 ws: specifier: ^8.17.1 version: 8.17.1 @@ -1061,8 +1061,8 @@ importers: specifier: workspace:* version: link:../workers-shared '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 '@microsoft/api-extractor': specifier: ^7.47.0 version: 7.47.0(@types/node@20.8.3) @@ -1170,8 +1170,8 @@ importers: specifier: workspace:* version: link:../workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 '@miniflare/cache': specifier: ^2.14.2 version: 2.14.2 @@ -1204,8 +1204,8 @@ importers: specifier: workspace:* version: link:../eslint-config-worker '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 '@types/cookie': specifier: ^0.6.0 version: 0.6.0 @@ -1241,8 +1241,8 @@ importers: specifier: workspace:* version: link:../workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 wrangler: specifier: workspace:* version: link:../wrangler @@ -1272,8 +1272,8 @@ importers: specifier: workspace:^ version: link:../workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 esbuild: specifier: 0.17.19 version: 0.17.19 @@ -1336,8 +1336,8 @@ importers: specifier: workspace:* version: link:../workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 '@types/node': specifier: 20.8.3 version: 20.8.3 @@ -1551,8 +1551,8 @@ importers: specifier: workspace:* version: link:../workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 concurrently: specifier: ^8.2.2 version: 8.2.2 @@ -1577,8 +1577,8 @@ importers: specifier: workspace:* version: link:../vitest-pool-workers '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 '@types/node': specifier: 20.8.3 version: 20.8.3 @@ -1646,8 +1646,8 @@ importers: specifier: npm:unenv-nightly@2.0.0-1724863496.70db6f1 version: unenv-nightly@2.0.0-1724863496.70db6f1 workerd: - specifier: 1.20240821.1 - version: 1.20240821.1 + specifier: 1.20240909.0 + version: 1.20240909.0 xxhash-wasm: specifier: ^1.0.1 version: 1.0.1 @@ -1672,8 +1672,8 @@ importers: specifier: workspace:* version: link:../workers-tsconfig '@cloudflare/workers-types': - specifier: ^4.20240821.1 - version: 4.20240821.1 + specifier: ^4.20240909.0 + version: 4.20240909.0 '@cspotcode/source-map-support': specifier: 0.8.1 version: 0.8.1 @@ -2482,38 +2482,38 @@ packages: '@cloudflare/util-markdown@1.2.15': resolution: {integrity: sha512-H8q/Msk+9Fga6iqqmff7i4mi+kraBCQWFbMEaKIRq3+HBNN5gkpizk05DSG6iIHVxCG1M3WR1FkN9CQ0ZtK4Cw==} - '@cloudflare/workerd-darwin-64@1.20240821.1': - resolution: {integrity: sha512-CDBpfZKrSy4YrIdqS84z67r3Tzal2pOhjCsIb63IuCnvVes59/ft1qhczBzk9EffeOE2iTCrA4YBT7Sbn7USew==} + '@cloudflare/workerd-darwin-64@1.20240909.0': + resolution: {integrity: sha512-nJ8jm/6PR8DPzVb4QifNAfSdrFZXNblwIdOhLTU5FpSvFFocmzFX5WgzQagvtmcC9/ZAQyxuf7WynDNyBcoe0Q==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20240821.1': - resolution: {integrity: sha512-Q+9RedvNbPcEt/dKni1oN94OxbvuNAeJkgHmrLFTGF8zu21wzOhVkQeRNxcYxrMa9mfStc457NAg13OVCj2kHQ==} + '@cloudflare/workerd-darwin-arm64@1.20240909.0': + resolution: {integrity: sha512-gJqKa811oSsoxy9xuoQn7bS0Hr1sY+o3EUORTcEnulG6Kz9NQ6nd8QNdp2Hrk2jmmSqwrNkn+a6PZkWzk6Q0Gw==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20240821.1': - resolution: {integrity: sha512-j6z3KsPtawrscoLuP985LbqFrmsJL6q1mvSXOXTqXGODAHIzGBipHARdOjms3UQqovzvqB2lQaQsZtLBwCZxtA==} + '@cloudflare/workerd-linux-64@1.20240909.0': + resolution: {integrity: sha512-sJrmtccfMg73sZljiBpe4R+lhF58TqzqhF2pQG8HRjyxkzkM1sjpZqfEFaIkNUDqd3/Ibji49fklhPCGXljKSg==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20240821.1': - resolution: {integrity: sha512-I9bHgZOxJQW0CV5gTdilyxzTG7ILzbTirehQWgfPx9X77E/7eIbR9sboOMgyeC69W4he0SKtpx0sYZuTJu4ERw==} + '@cloudflare/workerd-linux-arm64@1.20240909.0': + resolution: {integrity: sha512-dTbSdceyRXPOSER+18AwYRbPQG0e/Dwl2trmfMMCETkfJhNLv1fU3FFMJPjfILijKnhTZHSnHCx0+xwHdon2fg==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20240821.1': - resolution: {integrity: sha512-keC97QPArs6LWbPejQM7/Y8Jy8QqyaZow4/ZdsGo+QjlOLiZRDpAenfZx3CBUoWwEeFwQTl2FLO+8hV1SWFFYw==} + '@cloudflare/workerd-windows-64@1.20240909.0': + resolution: {integrity: sha512-/d4BT0kcWFa7Qc0K4K9+cwVQ1qyPNKiO42JZUijlDlco+TYTPkLO3qGEohmwbfMq+BieK7JTMSgjO81ZHpA0HQ==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@4.20240821.1': - resolution: {integrity: sha512-icAkbnAqgVl6ef9lgLTom8na+kj2RBw2ViPAQ586hbdj0xZcnrjK7P46Eu08OU9D/lNDgN2sKU/sxhe2iK/gIg==} + '@cloudflare/workers-types@4.20240909.0': + resolution: {integrity: sha512-4knwtX6efxIsIxawdmPyynU9+S8A78wntU8eUIEldStWP4gNgxGkeWcfCMXulTx8oxr3DU4aevHyld9HGV8VKQ==} '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} @@ -8773,8 +8773,8 @@ packages: wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - workerd@1.20240821.1: - resolution: {integrity: sha512-y4phjCnEG96u8ZkgkkHB+gSw0i6uMNo23rBmixylWpjxDklB+LWD8dztasvsu7xGaZbLoTxQESdEw956F7VJDA==} + workerd@1.20240909.0: + resolution: {integrity: sha512-NwuYh/Fgr/MK0H+Ht687sHl/f8tumwT5CWzYR0MZMHri8m3CIYu2IaY4tBFWoKE/tOU1Z5XjEXECa9zXY4+lwg==} engines: {node: '>=16'} hasBin: true @@ -9777,22 +9777,22 @@ snapshots: lodash.memoize: 4.1.2 marked: 0.3.19 - '@cloudflare/workerd-darwin-64@1.20240821.1': + '@cloudflare/workerd-darwin-64@1.20240909.0': optional: true - '@cloudflare/workerd-darwin-arm64@1.20240821.1': + '@cloudflare/workerd-darwin-arm64@1.20240909.0': optional: true - '@cloudflare/workerd-linux-64@1.20240821.1': + '@cloudflare/workerd-linux-64@1.20240909.0': optional: true - '@cloudflare/workerd-linux-arm64@1.20240821.1': + '@cloudflare/workerd-linux-arm64@1.20240909.0': optional: true - '@cloudflare/workerd-windows-64@1.20240821.1': + '@cloudflare/workerd-windows-64@1.20240909.0': optional: true - '@cloudflare/workers-types@4.20240821.1': {} + '@cloudflare/workers-types@4.20240909.0': {} '@colors/colors@1.5.0': optional: true @@ -11235,7 +11235,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.5.4) '@typescript-eslint/utils': 6.10.0(eslint@8.49.0)(typescript@5.5.4) - debug: 4.3.6(supports-color@9.2.2) + debug: 4.3.5 eslint: 8.49.0 ts-api-utils: 1.0.3(typescript@5.5.4) optionalDependencies: @@ -16838,13 +16838,13 @@ snapshots: wordwrap@1.0.0: {} - workerd@1.20240821.1: + workerd@1.20240909.0: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20240821.1 - '@cloudflare/workerd-darwin-arm64': 1.20240821.1 - '@cloudflare/workerd-linux-64': 1.20240821.1 - '@cloudflare/workerd-linux-arm64': 1.20240821.1 - '@cloudflare/workerd-windows-64': 1.20240821.1 + '@cloudflare/workerd-darwin-64': 1.20240909.0 + '@cloudflare/workerd-darwin-arm64': 1.20240909.0 + '@cloudflare/workerd-linux-64': 1.20240909.0 + '@cloudflare/workerd-linux-arm64': 1.20240909.0 + '@cloudflare/workerd-windows-64': 1.20240909.0 wrap-ansi@6.2.0: dependencies: From 6523db2695d70ad64da7cfe6f4731ac82181ac51 Mon Sep 17 00:00:00 2001 From: emily-shen <69125074+emily-shen@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:39:03 +0100 Subject: [PATCH 4/8] fix(wrangler): Validate `routes` for Workers with assets (#6621) We want wrangler to error if users are trying to deploy a Worker with assets, and routes with a path component. All Workers with assets must have either: - custom domain routes - pattern routes which have no path component (except for the wildcard splat) "some.domain.com/\*" --- .changeset/lazy-poems-mate.md | 12 ++ .../wrangler/src/__tests__/deploy.test.ts | 111 ++++++++++++++++-- packages/wrangler/src/__tests__/dev.test.tsx | 84 +++++++++++++ .../api/startDevWorker/ConfigController.ts | 2 + packages/wrangler/src/deploy/deploy.ts | 67 ++++++++--- packages/wrangler/src/dev.tsx | 15 ++- packages/wrangler/src/triggers/deploy.ts | 14 +-- packages/wrangler/src/triggers/index.ts | 6 + 8 files changed, 272 insertions(+), 39 deletions(-) create mode 100644 .changeset/lazy-poems-mate.md diff --git a/.changeset/lazy-poems-mate.md b/.changeset/lazy-poems-mate.md new file mode 100644 index 000000000000..62810edff7b5 --- /dev/null +++ b/.changeset/lazy-poems-mate.md @@ -0,0 +1,12 @@ +--- +"wrangler": patch +--- + +fix: Validate `routes` in `wrangler dev` and `wrangler deploy` for Workers with assets + +We want wrangler to error if users are trying to deploy a Worker with assets, and routes with a path component. + +All Workers with assets must have either: + +- custom domain routes +- pattern routes which have no path component (except for the wildcard splat) "some.domain.com/\*" diff --git a/packages/wrangler/src/__tests__/deploy.test.ts b/packages/wrangler/src/__tests__/deploy.test.ts index bd94d079db57..0c835981a0c6 100644 --- a/packages/wrangler/src/__tests__/deploy.test.ts +++ b/packages/wrangler/src/__tests__/deploy.test.ts @@ -1455,11 +1455,12 @@ Update them to point to this script instead?`, routes: [{ pattern: "*.example.com", custom_domain: true }], }); writeWorkerSource(); - await expect( - runWrangler("deploy ./index") - ).rejects.toThrowErrorMatchingInlineSnapshot( - `[Error: Cannot use "*.example.com" as a Custom Domain; wildcard operators (*) are not allowed]` - ); + await expect(runWrangler("deploy ./index")).rejects + .toThrowErrorMatchingInlineSnapshot(` + [Error: Invalid Routes: + *.example.com: + Wildcard operators (*) are not allowed in Custom Domains] + `); writeWranglerToml({ routes: [ @@ -1469,11 +1470,12 @@ Update them to point to this script instead?`, writeWorkerSource(); mockServiceScriptData({}); - await expect( - runWrangler("deploy ./index") - ).rejects.toThrowErrorMatchingInlineSnapshot( - `[Error: Cannot use "api.example.com/at/a/path" as a Custom Domain; paths are not allowed]` - ); + await expect(runWrangler("deploy ./index")).rejects + .toThrowErrorMatchingInlineSnapshot(` + [Error: Invalid Routes: + api.example.com/at/a/path: + Paths are not allowed in Custom Domains] + `); }); it("should not continue with publishing an override if user does not confirm", async () => { @@ -1516,6 +1518,95 @@ Update them to point to this script instead?`, }); }); + it("should error on routes with paths if experimental assets are present", async () => { + writeWranglerToml({ + routes: [ + "simple.co.uk/path", + "simple.co.uk/path/*", + "simple.co.uk/", + "simple.co.uk/*", + "simple.co.uk", + { pattern: "route.co.uk/path", zone_id: "asdfadsf" }, + { pattern: "route.co.uk/path/*", zone_id: "asdfadsf" }, + { pattern: "route.co.uk/*", zone_id: "asdfadsf" }, + { pattern: "route.co.uk/", zone_id: "asdfadsf" }, + { pattern: "route.co.uk", zone_id: "asdfadsf" }, + { pattern: "custom.co.uk/path", custom_domain: true }, + { pattern: "custom.co.uk/*", custom_domain: true }, + { pattern: "custom.co.uk", custom_domain: true }, + ], + }); + writeWorkerSource(); + writeAssets([{ filePath: "asset.txt", content: "Content of file-1" }]); + + await expect(runWrangler(`deploy --experimental-assets="assets"`)).rejects + .toThrowErrorMatchingInlineSnapshot(` + [Error: Invalid Routes: + simple.co.uk/path: + Workers which have static assets cannot be routed on a URL which has a path component. Update the route to replace /path with /* + + simple.co.uk/path/*: + Workers which have static assets cannot be routed on a URL which has a path component. Update the route to replace /path/* with /* + + simple.co.uk/: + Workers which have static assets must end with a wildcard path. Update the route to end with /* + + simple.co.uk: + Workers which have static assets must end with a wildcard path. Update the route to end with /* + + route.co.uk/path: + Workers which have static assets cannot be routed on a URL which has a path component. Update the route to replace /path with /* + + route.co.uk/path/*: + Workers which have static assets cannot be routed on a URL which has a path component. Update the route to replace /path/* with /* + + route.co.uk/: + Workers which have static assets must end with a wildcard path. Update the route to end with /* + + route.co.uk: + Workers which have static assets must end with a wildcard path. Update the route to end with /* + + custom.co.uk/path: + Paths are not allowed in Custom Domains + + custom.co.uk/*: + Wildcard operators (*) are not allowed in Custom Domains + Paths are not allowed in Custom Domains] + `); + }); + + it("shouldn't error on routes with paths if there are no experimental assets", async () => { + writeWranglerToml({ + routes: [ + "simple.co.uk/path", + "simple.co.uk/path/*", + "simple.co.uk/", + "simple.co.uk/*", + "simple.co.uk", + { pattern: "route.co.uk/path", zone_id: "asdfadsf" }, + { pattern: "route.co.uk/path/*", zone_id: "asdfadsf" }, + { pattern: "route.co.uk/*", zone_id: "asdfadsf" }, + { pattern: "route.co.uk/", zone_id: "asdfadsf" }, + { pattern: "route.co.uk", zone_id: "asdfadsf" }, + { pattern: "custom.co.uk/path", custom_domain: true }, + { pattern: "custom.co.uk/*", custom_domain: true }, + { pattern: "custom.co.uk", custom_domain: true }, + ], + }); + writeWorkerSource(); + + await expect(runWrangler(`deploy ./index`)).rejects + .toThrowErrorMatchingInlineSnapshot(` + [Error: Invalid Routes: + custom.co.uk/path: + Paths are not allowed in Custom Domains + + custom.co.uk/*: + Wildcard operators (*) are not allowed in Custom Domains + Paths are not allowed in Custom Domains] + `); + }); + it.todo("should error if it's a workers.dev route"); }); diff --git a/packages/wrangler/src/__tests__/dev.test.tsx b/packages/wrangler/src/__tests__/dev.test.tsx index ff5893335ed9..bad3b6b0d710 100644 --- a/packages/wrangler/src/__tests__/dev.test.tsx +++ b/packages/wrangler/src/__tests__/dev.test.tsx @@ -297,6 +297,90 @@ describe("wrangler dev", () => { }) ); }); + it("should error if custom domains with paths are passed in but allow paths on normal routes", async () => { + fs.writeFileSync("index.js", `export default {};`); + writeWranglerToml({ + main: "index.js", + routes: [ + "simple.co.uk/path", + "simple.co.uk/*", + "simple.co.uk", + { pattern: "route.co.uk/path", zone_id: "asdfadsf" }, + { pattern: "route.co.uk/*", zone_id: "asdfadsf" }, + { pattern: "route.co.uk", zone_id: "asdfadsf" }, + { pattern: "custom.co.uk/path", custom_domain: true }, + { pattern: "custom.co.uk/*", custom_domain: true }, + { pattern: "custom.co.uk", custom_domain: true }, + ], + }); + await expect(runWrangler(`dev`)).rejects + .toThrowErrorMatchingInlineSnapshot(` + [Error: Invalid Routes: + custom.co.uk/path: + Paths are not allowed in Custom Domains + + custom.co.uk/*: + Wildcard operators (*) are not allowed in Custom Domains + Paths are not allowed in Custom Domains] + `); + }); + it("should error on routes with paths if experimental assets are present", async () => { + writeWranglerToml({ + routes: [ + "simple.co.uk/path", + "simple.co.uk/path/*", + "simple.co.uk/", + "simple.co.uk/*", + "simple.co.uk", + { pattern: "route.co.uk/path", zone_id: "asdfadsf" }, + { pattern: "route.co.uk/path/*", zone_id: "asdfadsf" }, + { pattern: "route.co.uk/*", zone_id: "asdfadsf" }, + { pattern: "route.co.uk/", zone_id: "asdfadsf" }, + { pattern: "route.co.uk", zone_id: "asdfadsf" }, + { pattern: "custom.co.uk/path", custom_domain: true }, + { pattern: "custom.co.uk/*", custom_domain: true }, + { pattern: "custom.co.uk", custom_domain: true }, + ], + experimental_assets: { + directory: "assets", + }, + }); + fs.openSync("assets", "w"); + await expect(runWrangler(`dev`)).rejects + .toThrowErrorMatchingInlineSnapshot(` + [Error: Invalid Routes: + simple.co.uk/path: + Workers which have static assets cannot be routed on a URL which has a path component. Update the route to replace /path with /* + + simple.co.uk/path/*: + Workers which have static assets cannot be routed on a URL which has a path component. Update the route to replace /path/* with /* + + simple.co.uk/: + Workers which have static assets must end with a wildcard path. Update the route to end with /* + + simple.co.uk: + Workers which have static assets must end with a wildcard path. Update the route to end with /* + + route.co.uk/path: + Workers which have static assets cannot be routed on a URL which has a path component. Update the route to replace /path with /* + + route.co.uk/path/*: + Workers which have static assets cannot be routed on a URL which has a path component. Update the route to replace /path/* with /* + + route.co.uk/: + Workers which have static assets must end with a wildcard path. Update the route to end with /* + + route.co.uk: + Workers which have static assets must end with a wildcard path. Update the route to end with /* + + custom.co.uk/path: + Paths are not allowed in Custom Domains + + custom.co.uk/*: + Wildcard operators (*) are not allowed in Custom Domains + Paths are not allowed in Custom Domains] + `); + }); }); describe("host", () => { diff --git a/packages/wrangler/src/api/startDevWorker/ConfigController.ts b/packages/wrangler/src/api/startDevWorker/ConfigController.ts index d748af36deb1..1e9649feb9c7 100644 --- a/packages/wrangler/src/api/startDevWorker/ConfigController.ts +++ b/packages/wrangler/src/api/startDevWorker/ConfigController.ts @@ -62,6 +62,7 @@ async function resolveDevConfig( routes: input.triggers?.filter( (t): t is Extract => t.type === "route" ), + experimentalAssets: input.experimental?.assets?.directory, }, config ); @@ -163,6 +164,7 @@ async function resolveTriggers( routes: input.triggers?.filter( (t): t is Extract => t.type === "route" ), + experimentalAssets: input.experimental?.assets?.directory, }, config ); diff --git a/packages/wrangler/src/deploy/deploy.ts b/packages/wrangler/src/deploy/deploy.ts index 165c4f965c24..1a423482aea3 100644 --- a/packages/wrangler/src/deploy/deploy.ts +++ b/packages/wrangler/src/deploy/deploy.ts @@ -171,6 +171,58 @@ function errIsStartupErr(err: unknown): err is ParseError & { code: 10021 } { return false; } +export const validateRoutes = ( + routes: Route[], + hasExperimentalAssets: boolean +) => { + const invalidRoutes: Record = {}; + for (const route of routes) { + if (typeof route !== "string" && route.custom_domain) { + if (route.pattern.includes("*")) { + invalidRoutes[route.pattern] ??= []; + invalidRoutes[route.pattern].push( + `Wildcard operators (*) are not allowed in Custom Domains` + ); + } + if (route.pattern.includes("/")) { + invalidRoutes[route.pattern] ??= []; + invalidRoutes[route.pattern].push( + `Paths are not allowed in Custom Domains` + ); + } + } else if (hasExperimentalAssets) { + const pattern = typeof route === "string" ? route : route.pattern; + const components = pattern.split("/"); + + if ( + // = ["route.com"] bare domains are invalid as it would only match exactly that + components.length === 1 || + // = ["route.com",""] as above + (components.length === 2 && components[1] === "") + ) { + invalidRoutes[pattern] ??= []; + invalidRoutes[pattern].push( + `Workers which have static assets must end with a wildcard path. Update the route to end with /*` + ); + // ie it doesn't match exactly "route.com/*" = [route.com, *] + } else if (!(components.length === 2 && components[1] === "*")) { + invalidRoutes[pattern] ??= []; + invalidRoutes[pattern].push( + `Workers which have static assets cannot be routed on a URL which has a path component. Update the route to replace /${components.slice(1).join("/")} with /*` + ); + } + } + } + if (Object.keys(invalidRoutes).length > 0) { + throw new UserError( + `Invalid Routes:\n` + + Object.entries(invalidRoutes) + .map(([route, errors]) => `${route}:\n` + errors.join("\n")) + .join(`\n\n`) + ); + } +}; + export function renderRoute(route: Route): string { let result = ""; if (typeof route === "string") { @@ -383,20 +435,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m const routes = props.routes ?? config.routes ?? (config.route ? [config.route] : []) ?? []; - for (const route of routes) { - if (typeof route !== "string" && route.custom_domain) { - if (route.pattern.includes("*")) { - throw new UserError( - `Cannot use "${route.pattern}" as a Custom Domain; wildcard operators (*) are not allowed` - ); - } - if (route.pattern.includes("/")) { - throw new UserError( - `Cannot use "${route.pattern}" as a Custom Domain; paths are not allowed` - ); - } - } - } + validateRoutes(routes, Boolean(props.experimentalAssetsOptions)); const jsxFactory = props.jsxFactory || config.jsx_factory; const jsxFragment = props.jsxFragment || config.jsx_fragment; diff --git a/packages/wrangler/src/dev.tsx b/packages/wrangler/src/dev.tsx index 1f87ccf410f6..d545f6e7bc42 100644 --- a/packages/wrangler/src/dev.tsx +++ b/packages/wrangler/src/dev.tsx @@ -11,6 +11,7 @@ import { extractBindingsOfType, } from "./api/startDevWorker/utils"; import { findWranglerToml, printBindings, readConfig } from "./config"; +import { validateRoutes } from "./deploy/deploy"; import { getEntry } from "./deployment-bundle/entry"; import { getNodeCompatMode } from "./deployment-bundle/node-compat"; import { getBoundRegisteredWorkers } from "./dev-registry"; @@ -1214,12 +1215,13 @@ export function maskVars( export async function getHostAndRoutes( args: - | Pick + | Pick | { host?: string; routes?: Extract[]; + experimentalAssets?: string; }, - config: Pick & { + config: Pick & { dev: Pick; } ) { @@ -1241,7 +1243,12 @@ export async function getHostAndRoutes( return r.pattern; } }); - + if (routes) { + validateRoutes( + routes, + Boolean(args.experimentalAssets || config.experimental_assets) + ); + } return { host, routes }; } @@ -1304,9 +1311,7 @@ export async function validateDevServerSettings( config, "dev" ); - const { host, routes } = await getHostAndRoutes(args, config); - // TODO: Remove this hack // This function throws if the zone ID can't be found given the provided host and routes // However, it's called as part of initialising a preview session, which is nested deep within diff --git a/packages/wrangler/src/triggers/deploy.ts b/packages/wrangler/src/triggers/deploy.ts index e331f4a681e7..8d6f55c33d24 100644 --- a/packages/wrangler/src/triggers/deploy.ts +++ b/packages/wrangler/src/triggers/deploy.ts @@ -8,6 +8,7 @@ import { sleep, updateQueueConsumers, updateQueueProducers, + validateRoutes, } from "../deploy/deploy"; import { UserError } from "../errors"; import { logger } from "../logger"; @@ -17,6 +18,7 @@ import { getZoneForRoute } from "../zones"; import type { Config } from "../config"; import type { Route } from "../config/environment"; import type { RouteObject } from "../deploy/deploy"; +import type { ExperimentalAssetsOptions } from "../experimental-assets"; type Props = { config: Config; @@ -28,6 +30,7 @@ type Props = { legacyEnv: boolean | undefined; dryRun: boolean | undefined; experimentalVersions: boolean | undefined; + experimentalAssetsOptions: ExperimentalAssetsOptions | undefined; }; export default async function triggersDeploy( @@ -40,18 +43,9 @@ export default async function triggersDeploy( props.routes ?? config.routes ?? (config.route ? [config.route] : []) ?? []; const routesOnly: Array = []; const customDomainsOnly: Array = []; + validateRoutes(routes, Boolean(props.experimentalAssetsOptions)); for (const route of routes) { if (typeof route !== "string" && route.custom_domain) { - if (route.pattern.includes("*")) { - throw new UserError( - `Cannot use "${route.pattern}" as a Custom Domain; wildcard operators (*) are not allowed` - ); - } - if (route.pattern.includes("/")) { - throw new UserError( - `Cannot use "${route.pattern}" as a Custom Domain; paths are not allowed` - ); - } customDomainsOnly.push(route); } else { routesOnly.push(route); diff --git a/packages/wrangler/src/triggers/index.ts b/packages/wrangler/src/triggers/index.ts index 3b6e6af59ba5..7480c90c9c9b 100644 --- a/packages/wrangler/src/triggers/index.ts +++ b/packages/wrangler/src/triggers/index.ts @@ -1,4 +1,5 @@ import { readConfig } from "../config"; +import { processExperimentalAssetsArg } from "../experimental-assets"; import { getScriptName, isLegacyEnv, printWranglerBanner } from "../index"; import * as metrics from "../metrics"; import { requireAuth } from "../user"; @@ -57,6 +58,10 @@ export async function triggersDeployHandler( await printWranglerBanner(); const config = readConfig(undefined, args); + const experimentalAssetsOptions = processExperimentalAssetsArg( + { experimentalAssets: undefined }, + config + ); await metrics.sendMetricsEvent( "deploy worker triggers", {}, @@ -77,5 +82,6 @@ export async function triggersDeployHandler( legacyEnv: isLegacyEnv(config), dryRun: args.dryRun, experimentalVersions: args.experimentalJsonConfig, + experimentalAssetsOptions, }); } From 8dcd45665c0c420653f57cc7218269e05b2f9a25 Mon Sep 17 00:00:00 2001 From: Max Peterson <64494795+maxwellpeterson@users.noreply.github.com> Date: Wed, 11 Sep 2024 10:38:58 -0700 Subject: [PATCH 5/8] feat(wrangler): Add support for placement hints (#6625) - Add hint field to smart placement configuration Co-authored-by: Max Peterson --- .changeset/three-nails-bake.md | 7 ++++++ .../src/__tests__/configuration.test.ts | 25 +++++++++++++++++++ .../pages/create-worker-bundle-contents.ts | 4 ++- packages/wrangler/src/config/environment.ts | 2 +- packages/wrangler/src/config/validation.ts | 12 +++++++++ packages/wrangler/src/deploy/deploy.ts | 4 ++- .../wrangler/src/deployment-bundle/worker.ts | 1 + packages/wrangler/src/versions/upload.ts | 4 ++- 8 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 .changeset/three-nails-bake.md diff --git a/.changeset/three-nails-bake.md b/.changeset/three-nails-bake.md new file mode 100644 index 000000000000..0829c6fa8cdb --- /dev/null +++ b/.changeset/three-nails-bake.md @@ -0,0 +1,7 @@ +--- +"wrangler": minor +--- + +feature: Add support for placement hints + +Adds the `hint` field to smart placement configuration. When set, placement hints will be used to decide where smart-placement-enabled Workers are run. diff --git a/packages/wrangler/src/__tests__/configuration.test.ts b/packages/wrangler/src/__tests__/configuration.test.ts index 4762c4acac71..eabf60d81110 100644 --- a/packages/wrangler/src/__tests__/configuration.test.ts +++ b/packages/wrangler/src/__tests__/configuration.test.ts @@ -3485,6 +3485,31 @@ describe("normalizeAndValidateConfig()", () => { }); }); + describe("[placement]", () => { + it(`should error if placement hint is set with placement mode "off"`, () => { + const { diagnostics } = normalizeAndValidateConfig( + { placement: { mode: "off", hint: "wnam" } }, + undefined, + { env: undefined } + ); + + expect(diagnostics.renderErrors()).toMatchInlineSnapshot(` + "Processing wrangler configuration: + - \\"placement.hint\\" cannot be set if \\"placement.mode\\" is not \\"smart\\"" + `); + }); + + it(`should not error if placement hint is set with placement mode "smart"`, () => { + const { diagnostics } = normalizeAndValidateConfig( + { placement: { mode: "smart", hint: "wnam" } }, + undefined, + { env: undefined } + ); + + expect(diagnostics.hasErrors()).toBe(false); + }); + }); + describe("(deprecated)", () => { it("should remove and warn about deprecated properties", () => { const rawConfig: RawConfig = { diff --git a/packages/wrangler/src/api/pages/create-worker-bundle-contents.ts b/packages/wrangler/src/api/pages/create-worker-bundle-contents.ts index b35e1ee482fe..6354ceea24ab 100644 --- a/packages/wrangler/src/api/pages/create-worker-bundle-contents.ts +++ b/packages/wrangler/src/api/pages/create-worker-bundle-contents.ts @@ -73,7 +73,9 @@ function createWorkerBundleFormData( // The upload API only accepts an empty string or no specified placement for the "off" mode. const placement: CfPlacement | undefined = - config?.placement?.mode === "smart" ? { mode: "smart" } : undefined; + config?.placement?.mode === "smart" + ? { mode: "smart", hint: config.placement.hint } + : undefined; const worker: CfWorkerInit = { name: mainModule.name, diff --git a/packages/wrangler/src/config/environment.ts b/packages/wrangler/src/config/environment.ts index 262f21ae1b9f..f04a9d3b37a1 100644 --- a/packages/wrangler/src/config/environment.ts +++ b/packages/wrangler/src/config/environment.ts @@ -337,7 +337,7 @@ interface EnvironmentInheritable { * * @inheritable */ - placement: { mode: "off" | "smart" } | undefined; + placement: { mode: "off" | "smart"; hint?: string } | undefined; /** * Specify the directory of static assets to deploy/serve diff --git a/packages/wrangler/src/config/validation.ts b/packages/wrangler/src/config/validation.ts index 1917e80c8380..bb12664b51e2 100644 --- a/packages/wrangler/src/config/validation.ts +++ b/packages/wrangler/src/config/validation.ts @@ -945,6 +945,18 @@ function normalizeAndValidatePlacement( "string", ["off", "smart"] ); + validateOptionalProperty( + diagnostics, + "placement", + "hint", + rawEnv.placement.hint, + "string" + ); + if (rawEnv.placement.hint && rawEnv.placement.mode !== "smart") { + diagnostics.errors.push( + `"placement.hint" cannot be set if "placement.mode" is not "smart"` + ); + } } return inheritable( diff --git a/packages/wrangler/src/deploy/deploy.ts b/packages/wrangler/src/deploy/deploy.ts index 1a423482aea3..d3785f249e87 100644 --- a/packages/wrangler/src/deploy/deploy.ts +++ b/packages/wrangler/src/deploy/deploy.ts @@ -714,7 +714,9 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m // The upload API only accepts an empty string or no specified placement for the "off" mode. const placement: CfPlacement | undefined = - config.placement?.mode === "smart" ? { mode: "smart" } : undefined; + config.placement?.mode === "smart" + ? { mode: "smart", hint: config.placement.hint } + : undefined; const entryPointName = path.basename(resolvedEntryPointPath); const main: CfModule = { diff --git a/packages/wrangler/src/deployment-bundle/worker.ts b/packages/wrangler/src/deployment-bundle/worker.ts index 37e6478f6579..13b6426608df 100644 --- a/packages/wrangler/src/deployment-bundle/worker.ts +++ b/packages/wrangler/src/deployment-bundle/worker.ts @@ -267,6 +267,7 @@ export interface CfDurableObjectMigrations { export interface CfPlacement { mode: "smart"; + hint?: string; } export interface CfTailConsumer { diff --git a/packages/wrangler/src/versions/upload.ts b/packages/wrangler/src/versions/upload.ts index 9493713a4b9b..3b6f11acdf0e 100644 --- a/packages/wrangler/src/versions/upload.ts +++ b/packages/wrangler/src/versions/upload.ts @@ -401,7 +401,9 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m // The upload API only accepts an empty string or no specified placement for the "off" mode. const placement: CfPlacement | undefined = - config.placement?.mode === "smart" ? { mode: "smart" } : undefined; + config.placement?.mode === "smart" + ? { mode: "smart", hint: config.placement.hint } + : undefined; const entryPointName = path.basename(resolvedEntryPointPath); const main = { From 648cfdd32d8c1b60e037c3d453fcb1691fbf4b45 Mon Sep 17 00:00:00 2001 From: bthwaites Date: Wed, 11 Sep 2024 14:31:29 -0400 Subject: [PATCH 6/8] Bradley/r2 event notification get (#6652) * Update R2 Get Event Notification response, display, and actions * Add open-beta marker to R2 event notification wrangler commands * changeset --- .changeset/nice-beds-raise.md | 5 + packages/wrangler/src/__tests__/r2.test.ts | 78 ++++++++++++--- .../wrangler/src/__tests__/r2/helpers.test.ts | 74 +++++++------- packages/wrangler/src/index.ts | 2 +- packages/wrangler/src/queues/client.ts | 3 +- packages/wrangler/src/r2/helpers.ts | 99 +++++++++++++------ packages/wrangler/src/r2/index.ts | 15 ++- packages/wrangler/src/r2/notification.ts | 10 +- 8 files changed, 192 insertions(+), 94 deletions(-) create mode 100644 .changeset/nice-beds-raise.md diff --git a/.changeset/nice-beds-raise.md b/.changeset/nice-beds-raise.md new file mode 100644 index 000000000000..a07d532115df --- /dev/null +++ b/.changeset/nice-beds-raise.md @@ -0,0 +1,5 @@ +--- +"wrangler": minor +--- + +feat: Update R2 Get Event Notification response, display, and actions diff --git a/packages/wrangler/src/__tests__/r2.test.ts b/packages/wrangler/src/__tests__/r2.test.ts index e071dde661de..50ba8d383c38 100644 --- a/packages/wrangler/src/__tests__/r2.test.ts +++ b/packages/wrangler/src/__tests__/r2.test.ts @@ -96,7 +96,7 @@ describe("r2", () => { wrangler r2 bucket list List R2 buckets wrangler r2 bucket delete Delete an R2 bucket wrangler r2 bucket sippy Manage Sippy incremental migration on an R2 bucket - wrangler r2 bucket notification Manage event notifications for an R2 bucket + wrangler r2 bucket notification Manage event notifications for an R2 bucket [open beta] GLOBAL FLAGS -j, --experimental-json-config Experimental: support wrangler.json [boolean] @@ -130,7 +130,7 @@ describe("r2", () => { wrangler r2 bucket list List R2 buckets wrangler r2 bucket delete Delete an R2 bucket wrangler r2 bucket sippy Manage Sippy incremental migration on an R2 bucket - wrangler r2 bucket notification Manage event notifications for an R2 bucket + wrangler r2 bucket notification Manage event notifications for an R2 bucket [open beta] GLOBAL FLAGS -j, --experimental-json-config Experimental: support wrangler.json [boolean] @@ -772,6 +772,61 @@ describe("r2", () => { describe("notification", () => { describe("get", () => { it("follows happy path as expected", async () => { + const bucketName = "my-bucket"; + const queueId = "471537e8-6e5a-4163-a4d4-9478087c32c3"; + const queueName = "my-queue"; + msw.use( + http.get( + "*/accounts/:accountId/event_notifications/r2/:bucketName/configuration", + async ({ request, params }) => { + const { accountId, bucketName: bucketParam } = params; + expect(accountId).toEqual("some-account-id"); + expect(bucketName).toEqual(bucketParam); + expect(request.headers.get("authorization")).toEqual( + "Bearer some-api-token" + ); + const getResponse = { + bucketName, + queues: [ + { + queueId: queueId, + queueName, + rules: [ + { + ruleId: "8cdcce8a-89b3-474f-a087-3eb4fcacfa37", + createdAt: "2024-09-05T01:02:03.000Z", + prefix: "", + suffix: "", + actions: [ + "PutObject", + "CompleteMultipartUpload", + "CopyObject", + ], + }, + ], + }, + ], + }; + return HttpResponse.json(createFetchResult(getResponse)); + }, + { once: true } + ) + ); + await expect( + await runWrangler(`r2 bucket notification get ${bucketName}`) + ).toBe(undefined); + expect(std.out).toMatchInlineSnapshot(` + "Fetching notification configuration for bucket my-bucket... + rule_id: 8cdcce8a-89b3-474f-a087-3eb4fcacfa37 + created_at: 2024-09-05T01:02:03.000Z + queue_name: my-queue + prefix: + suffix: + event_type: PutObject,CompleteMultipartUpload,CopyObject" + `); + }); + + it("is backwards compatible with old API version", async () => { const bucketName = "my-bucket"; const queueId = "471537e8-6e5a-4163-a4d4-9478087c32c3"; const queueName = "my-queue"; @@ -828,11 +883,12 @@ describe("r2", () => { ).toBe(undefined); expect(std.out).toMatchInlineSnapshot(` "Fetching notification configuration for bucket my-bucket... - ┌────────────┬────────┬────────┬───────────────┐ - │ queue_name │ prefix │ suffix │ event_type │ - ├────────────┼────────┼────────┼───────────────┤ - │ my-queue │ │ │ object-create │ - └────────────┴────────┴────────┴───────────────┘" + rule_id: + created_at: + queue_name: my-queue + prefix: + suffix: + event_type: PutObject,CompleteMultipartUpload,CopyObject" `); }); @@ -846,7 +902,7 @@ describe("r2", () => { " wrangler r2 bucket notification get - Get event notification configuration for a bucket + Get event notification configuration for a bucket [open beta] POSITIONALS bucket The name of the bucket for which notifications will be emitted [string] [required] @@ -937,7 +993,7 @@ describe("r2", () => { ) ).resolves.toBe(undefined); expect(std.out).toMatchInlineSnapshot(` - "Creating event notification rule for object creation and deletion (PutObject,CompleteMultipartUpload,CopyObject,DeleteObject) + "Creating event notification rule for object creation and deletion (PutObject,CompleteMultipartUpload,CopyObject,DeleteObject,LifecycleDeletion) Configuration created successfully!" `); }); @@ -952,7 +1008,7 @@ describe("r2", () => { " wrangler r2 bucket notification create - Create new event notification configuration for an R2 bucket + Create new event notification configuration for an R2 bucket [open beta] POSITIONALS bucket The name of the bucket for which notifications will be emitted [string] [required] @@ -1044,7 +1100,7 @@ describe("r2", () => { " wrangler r2 bucket notification delete - Delete event notification configuration for an R2 bucket and queue + Delete event notification configuration for an R2 bucket and queue [open beta] POSITIONALS bucket The name of the bucket for which notifications will be emitted [string] [required] diff --git a/packages/wrangler/src/__tests__/r2/helpers.test.ts b/packages/wrangler/src/__tests__/r2/helpers.test.ts index 9e77c600d16e..0055af69b4ac 100644 --- a/packages/wrangler/src/__tests__/r2/helpers.test.ts +++ b/packages/wrangler/src/__tests__/r2/helpers.test.ts @@ -1,12 +1,10 @@ -import crypto from "crypto"; -import { vi } from "vitest"; import { logger } from "../../logger"; import { eventNotificationHeaders, tableFromNotificationGetResponse, } from "../../r2/helpers"; +import formatLabelledValues from "../../utils/render-labelled-values"; import { mockConsoleMethods } from "../helpers/mock-console"; -import type { Config } from "../../config"; import type { GetNotificationConfigResponse } from "../../r2/helpers"; import type { ApiCredentials } from "../../user"; @@ -16,63 +14,67 @@ describe("event notifications", () => { test("tableFromNotificationsGetResponse", async () => { const bucketName = "my-bucket"; const config = { account_id: "my-account" }; - const queueMap: Record = { - "471537e8-6e5a-4163-a4d4-9478087c32c3": "my-queue-1", - "be6b6a37-ae49-4eea-9032-5e8d3ad1d29b": "my-queue-2", - }; const response: GetNotificationConfigResponse = { - [bucketName]: { - "9d738cb7-be18-433a-957f-a9b88793de2c": { - queue: "471537e8-6e5a-4163-a4d4-9478087c32c3", + bucketName, + queues: [ + { + queueId: "471537e8-6e5a-4163-a4d4-9478087c32c3", + queueName: "my-queue-1", rules: [ { + ruleId: "68746106-12f8-4bba-a57b-adaf37fe11ca", prefix: "/p1", suffix: "s1/", - actions: [ - "PutObject", - "CompleteMultipartUpload", - "CopyObject", - "DeleteObject", - ], + actions: ["PutObject", "CopyObject", "DeleteObject"], }, { + ruleId: "5aa280bb-39d7-48ed-8c21-405fcd078192", actions: ["DeleteObject"], }, ], }, - [crypto.randomUUID()]: { - queue: "be6b6a37-ae49-4eea-9032-5e8d3ad1d29b", + { + queueId: "be6b6a37-ae49-4eea-9032-5e8d3ad1d29b", + queueName: "my-queue-2", rules: [ { + ruleId: "c4725929-3799-477a-a8d9-2d300f957e51", + createdAt: "2024-09-05T01:02:03.000Z", prefix: "//1", suffix: "2//", - actions: ["DeleteObject"], + actions: ["LifecycleDeletion"], }, ], }, - }, + ], }; const tableOutput = await tableFromNotificationGetResponse( config, - response[bucketName], - vi - .fn() - .mockImplementation((_: Pick, queue: string) => ({ - queue_name: queueMap[queue], - })) + response ); - logger.table(tableOutput); + logger.log(tableOutput.map((x) => formatLabelledValues(x)).join("\n\n")); await expect(std.out).toMatchInlineSnapshot(` - "┌────────────┬────────┬────────┬─────────────────────────────┐ - │ queue_name │ prefix │ suffix │ event_type │ - ├────────────┼────────┼────────┼─────────────────────────────┤ - │ my-queue-1 │ /p1 │ s1/ │ object-create,object-delete │ - ├────────────┼────────┼────────┼─────────────────────────────┤ - │ my-queue-1 │ │ │ object-delete │ - ├────────────┼────────┼────────┼─────────────────────────────┤ - │ my-queue-2 │ //1 │ 2// │ object-delete │ - └────────────┴────────┴────────┴─────────────────────────────┘" + "rule_id: 68746106-12f8-4bba-a57b-adaf37fe11ca + created_at: + queue_name: my-queue-1 + prefix: /p1 + suffix: s1/ + event_type: PutObject,CopyObject,DeleteObject + + rule_id: 5aa280bb-39d7-48ed-8c21-405fcd078192 + created_at: + queue_name: my-queue-1 + prefix: + suffix: + event_type: DeleteObject + + rule_id: c4725929-3799-477a-a8d9-2d300f957e51 + created_at: 2024-09-05T01:02:03.000Z + queue_name: my-queue-2 + prefix: //1 + suffix: 2// + event_type: LifecycleDeletion" `); }); test("auth email eventNotificationHeaders", () => { diff --git a/packages/wrangler/src/index.ts b/packages/wrangler/src/index.ts index 1baf8b2eda72..e472504fe937 100644 --- a/packages/wrangler/src/index.ts +++ b/packages/wrangler/src/index.ts @@ -81,7 +81,7 @@ import type { Arguments } from "yargs"; const resetColor = "\x1b[0m"; const fgGreenColor = "\x1b[32m"; -const betaCmdColor = "#BD5B08"; +export const betaCmdColor = "#BD5B08"; export const DEFAULT_LOCAL_PORT = 8787; export const DEFAULT_INSPECTOR_PORT = 9229; diff --git a/packages/wrangler/src/queues/client.ts b/packages/wrangler/src/queues/client.ts index d4a624719e94..d437f13a4597 100644 --- a/packages/wrangler/src/queues/client.ts +++ b/packages/wrangler/src/queues/client.ts @@ -204,10 +204,9 @@ async function ensureQueuesExist(config: Config, queueNames: string[]) { } export async function getQueueById( - config: Pick, + accountId: string, queueId: string ): Promise { - const accountId = await requireAuth(config); return fetchResult(queuesUrl(accountId, queueId), {}); } diff --git a/packages/wrangler/src/r2/helpers.ts b/packages/wrangler/src/r2/helpers.ts index e3ae9134b884..4515512514ff 100644 --- a/packages/wrangler/src/r2/helpers.ts +++ b/packages/wrangler/src/r2/helpers.ts @@ -5,9 +5,8 @@ import { getLocalPersistencePath } from "../dev/get-local-persistence-path"; import { buildPersistOptions } from "../dev/miniflare"; import { UserError } from "../errors"; import { logger } from "../logger"; -import { getQueue } from "../queues/client"; +import { getQueue, getQueueById } from "../queues/client"; import type { Config } from "../config"; -import type { getQueueById } from "../queues/client"; import type { ApiCredentials } from "../user"; import type { R2Bucket } from "@cloudflare/workers-types/experimental"; import type { ReplaceWorkersTypes } from "miniflare"; @@ -362,7 +361,9 @@ export const R2EventableOperations = [ "PutObject", "DeleteObject", "CompleteMultipartUpload", + "AbortMultipartUpload", "CopyObject", + "LifecycleDeletion", ] as const; export type R2EventableOperation = (typeof R2EventableOperations)[number]; @@ -371,21 +372,30 @@ export const actionsForEventCategories: Record< R2EventableOperation[] > = { "object-create": ["PutObject", "CompleteMultipartUpload", "CopyObject"], - "object-delete": ["DeleteObject"], + "object-delete": ["DeleteObject", "LifecycleDeletion"], }; export type R2EventType = keyof typeof actionsForEventCategories; -export const eventCategoryByAction: Record = - { - PutObject: "object-create", - CompleteMultipartUpload: "object-create", - CopyObject: "object-create", - DeleteObject: "object-delete", - }; type NotificationRule = { prefix?: string; suffix?: string; actions: R2EventableOperation[]; }; +type GetNotificationRule = { + ruleId: string; + createdAt?: string; + prefix?: string; + suffix?: string; + actions: R2EventableOperation[]; +}; +export type GetQueueDetail = { + queueId: string; + queueName: string; + rules: GetNotificationRule[]; +}; +export type GetNotificationConfigResponse = { + bucketName: string; + queues: GetQueueDetail[]; +}; export type DetailID = string; type QueueID = string; type BucketName = string; @@ -393,7 +403,8 @@ export type NotificationDetail = Record< DetailID, // This is the detail ID that identifies this config { queue: QueueID; rules: NotificationRule[] } >; -export type GetNotificationConfigResponse = Record< +// Event Notifications API Backwards Compatibility +export type GetNotificationConfigResponseOld = Record< BucketName, NotificationDetail >; @@ -422,14 +433,9 @@ export function eventNotificationHeaders( return headers; } -// Reformat the per-bucket get-notification response into a format -// suitable for `logger.table()` export async function tableFromNotificationGetResponse( config: Pick, - response: GetNotificationConfigResponse[BucketName], - // We're injecting this parameter because it makes testing easier, - // relative to mocking. - queueIdentifier: typeof getQueueById + response: GetNotificationConfigResponse ): Promise< { queue_name: string; @@ -438,23 +444,22 @@ export async function tableFromNotificationGetResponse( event_type: string; }[] > { - const reducer = async ([_, { queue, rules }]: [ - DetailID, - NotificationDetail[DetailID], - ]) => { - const queueResp = await queueIdentifier(config, queue); + const reducer = async (entry: GetQueueDetail) => { const rows = []; - for (const { prefix = "", suffix = "", actions } of rules) { + for (const { + prefix = "", + suffix = "", + actions, + ruleId, + createdAt = "", + } of entry.rules) { rows.push({ - queue_name: queueResp.queue_name, + rule_id: ruleId, + created_at: createdAt, + queue_name: entry.queueName, prefix, suffix, - event_type: Array.from( - actions.reduce((acc, action) => { - acc.add(eventCategoryByAction[action]); - return acc; - }, new Set()) - ).join(","), + event_type: actions.join(","), }); } return rows; @@ -466,7 +471,7 @@ export async function tableFromNotificationGetResponse( suffix: string; event_type: string; }[] = []; - for (const entry of Object.entries(response)) { + for (const entry of response.queues) { const result = await reducer(entry); tableOutput = tableOutput.concat(...result); } @@ -480,10 +485,40 @@ export async function getEventNotificationConfig( ): Promise { const headers = eventNotificationHeaders(apiCredentials); logger.log(`Fetching notification configuration for bucket ${bucketName}...`); - return await fetchResult( + const res = await fetchResult( `/accounts/${accountId}/event_notifications/r2/${bucketName}/configuration`, { method: "GET", headers } ); + if ("bucketName" in res && "queues" in res) { + return res; + } + // API response doesn't match new format. Trying the old format. + // Convert the old style payload to the new + // We can assume that the old payload has a single bucket entry + const oldResult = res as GetNotificationConfigResponseOld; + const [oldBucketName, oldDetail] = Object.entries(oldResult)[0]; + const newResult: GetNotificationConfigResponse = { + bucketName: oldBucketName, + queues: await Promise.all( + Object.values(oldDetail).map(async (oldQueue) => { + const newQueue: GetQueueDetail = { + queueId: oldQueue.queue, + queueName: (await getQueueById(accountId, oldQueue.queue)).queue_name, + rules: oldQueue.rules.map((oldRule) => { + const newRule: GetNotificationRule = { + ruleId: "", + prefix: oldRule.prefix, + suffix: oldRule.suffix, + actions: oldRule.actions, + }; + return newRule; + }), + }; + return newQueue; + }) + ), + }; + return newResult; } /** Construct & transmit notification configuration to EWC. diff --git a/packages/wrangler/src/r2/index.ts b/packages/wrangler/src/r2/index.ts index fbd9add4c10c..7dea960c6d8f 100644 --- a/packages/wrangler/src/r2/index.ts +++ b/packages/wrangler/src/r2/index.ts @@ -3,10 +3,15 @@ import * as fs from "node:fs"; import * as path from "node:path"; import * as stream from "node:stream"; import { ReadableStream } from "node:stream/web"; +import chalk from "chalk"; import prettyBytes from "pretty-bytes"; import { readConfig } from "../config"; import { FatalError, UserError } from "../errors"; -import { CommandLineArgsError, printWranglerBanner } from "../index"; +import { + betaCmdColor, + CommandLineArgsError, + printWranglerBanner, +} from "../index"; import { logger } from "../logger"; import * as metrics from "../metrics"; import { requireAuth } from "../user"; @@ -643,24 +648,24 @@ export function r2(r2Yargs: CommonYargsArgv, subHelp: SubHelp) { r2BucketYargs.command( "notification", - "Manage event notifications for an R2 bucket", + `Manage event notifications for an R2 bucket ${chalk.hex(betaCmdColor)("[open beta]")}`, (r2EvNotifyYargs) => { return r2EvNotifyYargs .command( "get ", - "Get event notification configuration for a bucket", + `Get event notification configuration for a bucket ${chalk.hex(betaCmdColor)("[open beta]")}`, Notification.GetOptions, Notification.GetHandler ) .command( "create ", - "Create new event notification configuration for an R2 bucket", + `Create new event notification configuration for an R2 bucket ${chalk.hex(betaCmdColor)("[open beta]")}`, Notification.CreateOptions, Notification.CreateHandler ) .command( "delete ", - "Delete event notification configuration for an R2 bucket and queue", + `Delete event notification configuration for an R2 bucket and queue ${chalk.hex(betaCmdColor)("[open beta]")}`, Notification.DeleteOptions, Notification.DeleteHandler ); diff --git a/packages/wrangler/src/r2/notification.ts b/packages/wrangler/src/r2/notification.ts index dc6237b171fc..8b618fe6396c 100644 --- a/packages/wrangler/src/r2/notification.ts +++ b/packages/wrangler/src/r2/notification.ts @@ -1,8 +1,8 @@ import { readConfig } from "../config"; import { logger } from "../logger"; -import { getQueueById } from "../queues/client"; import { printWranglerBanner } from "../update-check"; import { requireApiToken, requireAuth } from "../user"; +import formatLabelledValues from "../utils/render-labelled-values"; import { actionsForEventCategories, deleteEventNotificationConfig, @@ -36,12 +36,8 @@ export async function GetHandler( accountId, `${args.bucket}` ); - const tableOutput = await tableFromNotificationGetResponse( - config, - resp[args.bucket], - getQueueById - ); - logger.table(tableOutput); + const tableOutput = await tableFromNotificationGetResponse(config, resp); + logger.log(tableOutput.map((x) => formatLabelledValues(x)).join("\n\n")); } export function CreateOptions(yargs: CommonYargsArgv) { From 7579bd8e53a30a5ae8e3dc606b46e575948c9ff1 Mon Sep 17 00:00:00 2001 From: Daniel Walsh Date: Wed, 11 Sep 2024 23:26:07 +0100 Subject: [PATCH 7/8] Fix Pages duplicating hash in redirects (#6680) * Fix Pages duplicating hash in redirects * Fix issue where an incoming query string got lost in a hash redirect --- .changeset/eighty-birds-kiss.md | 5 + .../__tests__/asset-server/handler.test.ts | 162 ++++++++++++++---- packages/pages-shared/asset-server/handler.ts | 7 +- 3 files changed, 137 insertions(+), 37 deletions(-) create mode 100644 .changeset/eighty-birds-kiss.md diff --git a/.changeset/eighty-birds-kiss.md b/.changeset/eighty-birds-kiss.md new file mode 100644 index 000000000000..7c40971b3ced --- /dev/null +++ b/.changeset/eighty-birds-kiss.md @@ -0,0 +1,5 @@ +--- +"@cloudflare/pages-shared": patch +--- + +fix: fix Pages redirects going to a hash location to be duped. This means if you have a rule like `/foo/bar /foo#bar` it will no longer result in `/foo#bar#bar` but the correct `/foo#bar`. diff --git a/packages/pages-shared/__tests__/asset-server/handler.test.ts b/packages/pages-shared/__tests__/asset-server/handler.test.ts index 76d9273a7923..1bf81649f306 100644 --- a/packages/pages-shared/__tests__/asset-server/handler.test.ts +++ b/packages/pages-shared/__tests__/asset-server/handler.test.ts @@ -390,40 +390,6 @@ describe("asset-server handler", () => { }); } - // test("Returns a redirect without duplicating the hash component", async () => { - // const { response, spies } = await getTestResponse({ - // request: "https://foo.com/bar", - // metadata: createMetadataObjectWithRedirects([ - // { from: "/bar", to: "https://foobar.com/##heading-7", status: 301 }, - // ]), - // }); - - // expect(spies.fetchAsset).toBe(0); - // expect(spies.findAssetEntryForPath).toBe(0); - // expect(spies.getAssetKey).toBe(0); - // expect(spies.negotiateContent).toBe(0); - // expect(response.status).toBe(301); - // expect(response.headers.get("Location")).toBe( - // "https://foobar.com/##heading-7" - // ); - // }); - - test("it should redirect uri-encoded paths", async () => { - const { response, spies } = await getTestResponse({ - request: "https://foo.com/some%20page", - metadata: createMetadataObjectWithRedirects([ - { from: "/some%20page", to: "/home", status: 301 }, - ]), - }); - - expect(spies.fetchAsset).toBe(0); - expect(spies.findAssetEntryForPath).toBe(0); - expect(spies.getAssetKey).toBe(0); - expect(spies.negotiateContent).toBe(0); - expect(response.status).toBe(301); - expect(response.headers.get("Location")).toBe("/home"); - }); - // test("getResponseFromMatch - same origin paths specified as root-relative", () => { // const res = getResponseFromMatch( // { @@ -920,6 +886,134 @@ describe("asset-server handler", () => { ); }); }); + + describe("redirects", () => { + test("it should redirect uri-encoded paths", async () => { + const { response, spies } = await getTestResponse({ + request: "https://foo.com/some%20page", + metadata: createMetadataObjectWithRedirects([ + { from: "/some%20page", to: "/home", status: 301 }, + ]), + }); + + expect(spies.fetchAsset).toBe(0); + expect(spies.findAssetEntryForPath).toBe(0); + expect(spies.getAssetKey).toBe(0); + expect(spies.negotiateContent).toBe(0); + expect(response.status).toBe(301); + expect(response.headers.get("Location")).toBe("/home"); + }); + + test("redirects to a query string same-origin", async () => { + const { response } = await getTestResponse({ + request: "https://foo.com/bar", + metadata: createMetadataObjectWithRedirects([ + { from: "/bar", to: "/?test=abc", status: 301 }, + ]), + }); + + expect(response.status).toBe(301); + expect(response.headers.get("Location")).toBe("/?test=abc"); + }); + + test("redirects to a query string cross-origin", async () => { + const { response } = await getTestResponse({ + request: "https://foo.com/bar", + metadata: createMetadataObjectWithRedirects([ + { from: "/bar", to: "https://foobar.com/?test=abc", status: 301 }, + ]), + }); + + expect(response.status).toBe(301); + expect(response.headers.get("Location")).toBe( + "https://foobar.com/?test=abc" + ); + }); + + test("redirects to hash component same-origin", async () => { + const { response } = await getTestResponse({ + request: "https://foo.com/bar", + metadata: createMetadataObjectWithRedirects([ + { from: "/bar", to: "https://foo.com/##heading-7", status: 301 }, + ]), + }); + + expect(response.status).toBe(301); + expect(response.headers.get("Location")).toBe("/##heading-7"); + }); + + test("redirects to hash component cross-origin", async () => { + const { response } = await getTestResponse({ + request: "https://foo.com/bar", + metadata: createMetadataObjectWithRedirects([ + { from: "/bar", to: "https://foobar.com/##heading-7", status: 301 }, + ]), + }); + + expect(response.status).toBe(301); + expect(response.headers.get("Location")).toBe( + "https://foobar.com/##heading-7" + ); + }); + + test("redirects to a query string and hash same-origin", async () => { + const { response } = await getTestResponse({ + request: "https://foo.com/bar", + metadata: createMetadataObjectWithRedirects([ + { from: "/bar", to: "/?test=abc#def", status: 301 }, + ]), + }); + + expect(response.status).toBe(301); + expect(response.headers.get("Location")).toBe("/?test=abc#def"); + }); + + test("redirects to a query string and hash cross-origin", async () => { + const { response } = await getTestResponse({ + request: "https://foo.com/bar", + metadata: createMetadataObjectWithRedirects([ + { from: "/bar", to: "https://foobar.com/?test=abc#def", status: 301 }, + ]), + }); + + expect(response.status).toBe(301); + expect(response.headers.get("Location")).toBe( + "https://foobar.com/?test=abc#def" + ); + }); + + // Query strings must be before the hash to be considered query strings + // https://www.rfc-editor.org/rfc/rfc3986#section-4.1 + // Behaviour in Chrome is that the .hash is "#def?test=abc" and .search is "" + test("redirects to a query string and hash against rfc", async () => { + const { response } = await getTestResponse({ + request: "https://foo.com/bar", + metadata: createMetadataObjectWithRedirects([ + { from: "/bar", to: "https://foobar.com/#def?test=abc", status: 301 }, + ]), + }); + + expect(response.status).toBe(301); + expect(response.headers.get("Location")).toBe( + "https://foobar.com/#def?test=abc" + ); + }); + + // Query string needs to be _before_ the hash + test("redirects to a hash with an incoming query cross-origin", async () => { + const { response } = await getTestResponse({ + request: "https://foo.com/bar?test=abc", + metadata: createMetadataObjectWithRedirects([ + { from: "/bar", to: "https://foobar.com/#heading", status: 301 }, + ]), + }); + + expect(response.status).toBe(301); + expect(response.headers.get("Location")).toBe( + "https://foobar.com/?test=abc#heading" + ); + }); + }); }); interface HandlerSpies { diff --git a/packages/pages-shared/asset-server/handler.ts b/packages/pages-shared/asset-server/handler.ts index 4cbca336c2b7..603abaa48207 100644 --- a/packages/pages-shared/asset-server/handler.ts +++ b/packages/pages-shared/asset-server/handler.ts @@ -227,9 +227,10 @@ export async function generateHandler< ? `${destination.pathname}${destination.search || search}${ destination.hash }` - : `${destination.href}${destination.search ? "" : search}${ - destination.hash - }`; + : `${destination.href.slice(0, destination.href.length - (destination.search.length + destination.hash.length))}${ + destination.search ? destination.search : search + }${destination.hash}`; + switch (status) { case 301: return new MovedPermanentlyResponse(location, undefined, { From 831f89217627554f4fc984dd8d51bf2a4409ec31 Mon Sep 17 00:00:00 2001 From: Andy Jessop Date: Thu, 12 Sep 2024 10:05:36 +0200 Subject: [PATCH 8/8] Add pipeline binding to wrangler.toml (#6674) * Add [[pipelines]] binding in wrangler. * chore: fix lint and formatting errors * chore: fix test * chore: remove only * chore: update wording * chore: fix tests --------- Co-authored-by: Oli Yu Co-authored-by: Andy Jessop --- .changeset/angry-keys-dance.md | 12 ++ .prettierignore | 2 + .../src/__tests__/configuration.test.ts | 109 ++++++++++++++++++ .../wrangler/src/__tests__/deploy.test.ts | 37 ++++++ .../src/__tests__/type-generation.test.ts | 1 + .../pages/create-worker-bundle-contents.ts | 1 + .../wrangler/src/api/startDevWorker/types.ts | 2 + .../wrangler/src/api/startDevWorker/utils.ts | 10 ++ packages/wrangler/src/config/config.ts | 1 + packages/wrangler/src/config/environment.ts | 17 +++ packages/wrangler/src/config/index.ts | 15 ++- packages/wrangler/src/config/validation.ts | 45 ++++++++ packages/wrangler/src/deploy/deploy.ts | 2 + .../create-worker-upload-form.ts | 9 ++ .../wrangler/src/deployment-bundle/worker.ts | 6 + packages/wrangler/src/dev.tsx | 2 + packages/wrangler/src/secret/index.ts | 1 + packages/wrangler/src/versions/upload.ts | 1 + 18 files changed, 271 insertions(+), 2 deletions(-) create mode 100644 .changeset/angry-keys-dance.md diff --git a/.changeset/angry-keys-dance.md b/.changeset/angry-keys-dance.md new file mode 100644 index 000000000000..78f466b4f9e5 --- /dev/null +++ b/.changeset/angry-keys-dance.md @@ -0,0 +1,12 @@ +--- +"wrangler": minor +--- + +Added new [[pipelines]] bindings. This creates a new binding that allows sending events to +the specified pipeline. + +Example: + +[[pipelines]] +binding = "MY_PIPELINE" +pipeline = "my-pipeline" diff --git a/.prettierignore b/.prettierignore index b489d5f785ec..f9587c3f48de 100644 --- a/.prettierignore +++ b/.prettierignore @@ -24,6 +24,8 @@ packages/create-cloudflare/templates/**/*.* # but still exclude the worker-configuration.d.ts file, since it's generated !packages/create-cloudflare/templates/hello-world/**/*.* packages/create-cloudflare/templates/hello-world/**/worker-configuration.d.ts +# dist-functions are generated in the fixtures/vitest-pool-workers-examples/pages-functions-unit-integration-self folder +dist-functions vscode.d.ts vscode.*.d.ts diff --git a/packages/wrangler/src/__tests__/configuration.test.ts b/packages/wrangler/src/__tests__/configuration.test.ts index eabf60d81110..c55200e843b3 100644 --- a/packages/wrangler/src/__tests__/configuration.test.ts +++ b/packages/wrangler/src/__tests__/configuration.test.ts @@ -130,6 +130,7 @@ describe("normalizeAndValidateConfig()", () => { upload_source_maps: undefined, placement: undefined, tail_consumers: undefined, + pipelines: [], }); expect(diagnostics.hasErrors()).toBe(false); expect(diagnostics.hasWarnings()).toBe(false); @@ -3181,6 +3182,114 @@ describe("normalizeAndValidateConfig()", () => { }); }); + describe("[pipelines]", () => { + it("should error if pipelines is an object", () => { + const { diagnostics } = normalizeAndValidateConfig( + // @ts-expect-error purposely using an invalid value + { pipelines: {} }, + undefined, + { env: undefined } + ); + + expect(diagnostics.hasWarnings()).toBe(false); + expect(diagnostics.renderErrors()).toMatchInlineSnapshot(` + "Processing wrangler configuration: + - The field \\"pipelines\\" should be an array but got {}." + `); + }); + + it("should error if pipelines is a string", () => { + const { diagnostics } = normalizeAndValidateConfig( + // @ts-expect-error purposely using an invalid value + { pipelines: "BAD" }, + undefined, + { env: undefined } + ); + + expect(diagnostics.hasWarnings()).toBe(false); + expect(diagnostics.renderErrors()).toMatchInlineSnapshot(` + "Processing wrangler configuration: + - The field \\"pipelines\\" should be an array but got \\"BAD\\"." + `); + }); + + it("should error if pipelines is a number", () => { + const { diagnostics } = normalizeAndValidateConfig( + // @ts-expect-error purposely using an invalid value + { pipelines: 999 }, + undefined, + { env: undefined } + ); + + expect(diagnostics.hasWarnings()).toBe(false); + expect(diagnostics.renderErrors()).toMatchInlineSnapshot(` + "Processing wrangler configuration: + - The field \\"pipelines\\" should be an array but got 999." + `); + }); + + it("should error if pipelines is null", () => { + const { diagnostics } = normalizeAndValidateConfig( + // @ts-expect-error purposely using an invalid value + { pipelines: null }, + undefined, + { env: undefined } + ); + + expect(diagnostics.hasWarnings()).toBe(false); + expect(diagnostics.renderErrors()).toMatchInlineSnapshot(` + "Processing wrangler configuration: + - The field \\"pipelines\\" should be an array but got null." + `); + }); + + it("should accept valid bindings", () => { + const { diagnostics } = normalizeAndValidateConfig( + { + pipelines: [ + { + binding: "VALID", + pipeline: "343cd4f1d58c42fbb5bd082592fd7143", + }, + ], + } as unknown as RawConfig, + undefined, + { env: undefined } + ); + + expect(diagnostics.hasErrors()).toBe(false); + }); + + it("should error if pipelines.bindings are not valid", () => { + const { diagnostics } = normalizeAndValidateConfig( + { + pipelines: [ + {}, + { + binding: "VALID", + pipeline: "343cd4f1d58c42fbb5bd082592fd7143", + }, + { binding: 2000, project: 2111 }, + ], + } as unknown as RawConfig, + undefined, + { env: undefined } + ); + expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(` + "Processing wrangler configuration: + - Unexpected fields found in pipelines[2] field: \\"project\\"" + `); + expect(diagnostics.hasWarnings()).toBe(true); + expect(diagnostics.renderErrors()).toMatchInlineSnapshot(` + "Processing wrangler configuration: + - \\"pipelines[0]\\" bindings must have a string \\"binding\\" field but got {}. + - \\"pipelines[0]\\" bindings must have a string \\"pipeline\\" field but got {}. + - \\"pipelines[2]\\" bindings must have a string \\"binding\\" field but got {\\"binding\\":2000,\\"project\\":2111}. + - \\"pipelines[2]\\" bindings must have a string \\"pipeline\\" field but got {\\"binding\\":2000,\\"project\\":2111}." + `); + }); + }); + describe("[unsafe.bindings]", () => { it("should error if unsafe is an array", () => { const { diagnostics } = normalizeAndValidateConfig( diff --git a/packages/wrangler/src/__tests__/deploy.test.ts b/packages/wrangler/src/__tests__/deploy.test.ts index 0c835981a0c6..82b8b03a5e89 100644 --- a/packages/wrangler/src/__tests__/deploy.test.ts +++ b/packages/wrangler/src/__tests__/deploy.test.ts @@ -10615,6 +10615,43 @@ export default{ }); }); + describe("pipelines", () => { + it("should upload pipelines bindings", async () => { + writeWranglerToml({ + pipelines: [ + { + binding: "MY_PIPELINE", + pipeline: "0123456789ABCDEF0123456789ABCDEF", + }, + ], + }); + await fs.promises.writeFile("index.js", `export default {};`); + mockSubDomainRequest(); + mockUploadWorkerRequest({ + expectedBindings: [ + { + type: "pipelines", + name: "MY_PIPELINE", + id: "0123456789ABCDEF0123456789ABCDEF", + }, + ], + }); + + await runWrangler("deploy index.js"); + expect(std.out).toMatchInlineSnapshot(` + "Total Upload: xx KiB / gzip: xx KiB + Worker Startup Time: 100 ms + Your worker has access to the following bindings: + - Pipelines: + - MY_PIPELINE: 0123456789ABCDEF0123456789ABCDEF + Uploaded test-name (TIMINGS) + Deployed test-name triggers (TIMINGS) + https://test-name.test-sub-domain.workers.dev + Current Version ID: Galaxy-Class" + `); + }); + }); + describe("--keep-vars", () => { it("should send keepVars when keep-vars is passed in", async () => { vi.stubEnv("CLOUDFLARE_API_TOKEN", "hunter2"); diff --git a/packages/wrangler/src/__tests__/type-generation.test.ts b/packages/wrangler/src/__tests__/type-generation.test.ts index df1f416f997c..88fc1cdbfd00 100644 --- a/packages/wrangler/src/__tests__/type-generation.test.ts +++ b/packages/wrangler/src/__tests__/type-generation.test.ts @@ -217,6 +217,7 @@ const bindingsConfigMock: Omit< }, { type: "CompiledWasm", globs: ["**/*.wasm"], fallthrough: true }, ], + pipelines: [], }; describe("generateTypes()", () => { diff --git a/packages/wrangler/src/api/pages/create-worker-bundle-contents.ts b/packages/wrangler/src/api/pages/create-worker-bundle-contents.ts index 6354ceea24ab..824788e2aa64 100644 --- a/packages/wrangler/src/api/pages/create-worker-bundle-contents.ts +++ b/packages/wrangler/src/api/pages/create-worker-bundle-contents.ts @@ -66,6 +66,7 @@ function createWorkerBundleFormData( text_blobs: undefined, data_blobs: undefined, dispatch_namespaces: undefined, + pipelines: undefined, logfwdr: undefined, unsafe: undefined, experimental_assets: undefined, diff --git a/packages/wrangler/src/api/startDevWorker/types.ts b/packages/wrangler/src/api/startDevWorker/types.ts index 14bce75d059e..f5c7a3178415 100644 --- a/packages/wrangler/src/api/startDevWorker/types.ts +++ b/packages/wrangler/src/api/startDevWorker/types.ts @@ -16,6 +16,7 @@ import type { CfLogfwdrBinding, CfModule, CfMTlsCertificate, + CfPipeline, CfQueue, CfR2Bucket, CfScriptFormat, @@ -261,6 +262,7 @@ export type Binding = | ({ type: "analytics_engine" } & Omit) | ({ type: "dispatch_namespace" } & Omit) | ({ type: "mtls_certificate" } & Omit) + | ({ type: "pipeline" } & Omit) | ({ type: "logfwdr" } & Omit) | { type: `unsafe_${string}` } | { type: "assets" }; diff --git a/packages/wrangler/src/api/startDevWorker/utils.ts b/packages/wrangler/src/api/startDevWorker/utils.ts index b237ede5814f..383bf71b4f85 100644 --- a/packages/wrangler/src/api/startDevWorker/utils.ts +++ b/packages/wrangler/src/api/startDevWorker/utils.ts @@ -244,6 +244,12 @@ export function convertCfWorkerInitBindingstoBindings( output[info["binding"]] = { type: "assets" }; break; } + case "pipelines": { + for (const { binding, ...x } of info) { + output[binding] = { type: "pipeline", ...x }; + } + break; + } default: { assertNever(type); } @@ -282,6 +288,7 @@ export async function convertBindingsToCfWorkerInitBindings( logfwdr: undefined, unsafe: undefined, experimental_assets: undefined, + pipelines: undefined, }; const fetchers: Record = {}; @@ -354,6 +361,9 @@ export async function convertBindingsToCfWorkerInitBindings( } else if (binding.type === "mtls_certificate") { bindings.mtls_certificates ??= []; bindings.mtls_certificates.push({ ...binding, binding: name }); + } else if (binding.type === "pipeline") { + bindings.pipelines ??= []; + bindings.pipelines.push({ ...binding, binding: name }); } else if (binding.type === "logfwdr") { bindings.logfwdr ??= { bindings: [] }; bindings.logfwdr.bindings.push({ ...binding, name: name }); diff --git a/packages/wrangler/src/config/config.ts b/packages/wrangler/src/config/config.ts index 640743c0de33..80dc47c02c73 100644 --- a/packages/wrangler/src/config/config.ts +++ b/packages/wrangler/src/config/config.ts @@ -402,4 +402,5 @@ export const defaultWranglerConfig: Config = { }, mtls_certificates: [], tail_consumers: undefined, + pipelines: [], }; diff --git a/packages/wrangler/src/config/environment.ts b/packages/wrangler/src/config/environment.ts index f04a9d3b37a1..73d36d468437 100644 --- a/packages/wrangler/src/config/environment.ts +++ b/packages/wrangler/src/config/environment.ts @@ -754,6 +754,23 @@ export interface EnvironmentNonInheritable { /** Details about the outbound Worker which will handle outbound requests from your namespace */ outbound?: DispatchNamespaceOutbound; }[]; + + /** + * Specifies list of Pipelines bound to this Worker environment + * + * NOTE: This field is not automatically inherited from the top level environment, + * and so must be specified in every named environment. + * + * @default `[]` + * @nonInheritable + */ + pipelines: { + /** The binding name used to refer to the bound service. */ + binding: string; + + /** Name of the Pipeline to bind */ + pipeline: string; + }[]; } /** diff --git a/packages/wrangler/src/config/index.ts b/packages/wrangler/src/config/index.ts index 6d1c400d5141..6110ffa6e644 100644 --- a/packages/wrangler/src/config/index.ts +++ b/packages/wrangler/src/config/index.ts @@ -15,15 +15,15 @@ import type { NormalizeAndValidateConfigArgs } from "./validation"; export type { Config, - RawConfig, ConfigFields, DevConfig, + RawConfig, RawDevConfig, } from "./config"; export type { + ConfigModuleRuleType, Environment, RawEnvironment, - ConfigModuleRuleType, } from "./environment"; type ReadConfigCommandArgs = NormalizeAndValidateConfigArgs & { @@ -232,6 +232,7 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) { wasm_modules, dispatch_namespaces, mtls_certificates, + pipelines, } = bindings; if (data_blobs !== undefined && Object.keys(data_blobs).length > 0) { @@ -443,6 +444,16 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) { }); } + if (pipelines?.length) { + output.push({ + type: "Pipelines", + entries: pipelines.map(({ binding, pipeline }) => ({ + key: binding, + value: pipeline, + })), + }); + } + if (version_metadata !== undefined) { output.push({ type: "Worker Version Metadata", diff --git a/packages/wrangler/src/config/validation.ts b/packages/wrangler/src/config/validation.ts index bb12664b51e2..ccb385d49c1b 100644 --- a/packages/wrangler/src/config/validation.ts +++ b/packages/wrangler/src/config/validation.ts @@ -1456,6 +1456,16 @@ function normalizeAndValidateEnvironment( validateAIBinding(envName), undefined ), + pipelines: notInheritable( + diagnostics, + topLevelEnv, + rawConfig, + rawEnv, + envName, + "pipelines", + validateBindingArray(envName, validatePipelineBinding), + [] + ), version_metadata: notInheritable( diagnostics, topLevelEnv, @@ -2213,6 +2223,7 @@ const validateUnsafeBinding: ValidatorFn = (diagnostics, field, value) => { "service", "logfwdr", "mtls_certificate", + "pipeline", ]; if (safeBindings.includes(value.type)) { @@ -3115,6 +3126,40 @@ const validateConsumer: ValidatorFn = (diagnostics, field, value, _config) => { return isValid; }; +const validatePipelineBinding: ValidatorFn = (diagnostics, field, value) => { + if (typeof value !== "object" || value === null) { + diagnostics.errors.push( + `"pipeline" bindings should be objects, but got ${JSON.stringify(value)}` + ); + return false; + } + let isValid = true; + // Pipeline bindings must have a binding and a pipeline. + if (!isRequiredProperty(value, "binding", "string")) { + diagnostics.errors.push( + `"${field}" bindings must have a string "binding" field but got ${JSON.stringify( + value + )}.` + ); + isValid = false; + } + if (!isRequiredProperty(value, "pipeline", "string")) { + diagnostics.errors.push( + `"${field}" bindings must have a string "pipeline" field but got ${JSON.stringify( + value + )}.` + ); + isValid = false; + } + + validateAdditionalProperties(diagnostics, field, Object.keys(value), [ + "binding", + "pipeline", + ]); + + return isValid; +}; + function normalizeAndValidateLimits( diagnostics: Diagnostics, topLevelEnv: Environment | undefined, diff --git a/packages/wrangler/src/deploy/deploy.ts b/packages/wrangler/src/deploy/deploy.ts index d3785f249e87..dd1013bc8ef8 100644 --- a/packages/wrangler/src/deploy/deploy.ts +++ b/packages/wrangler/src/deploy/deploy.ts @@ -692,6 +692,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m analytics_engine_datasets: config.analytics_engine_datasets, dispatch_namespaces: config.dispatch_namespaces, mtls_certificates: config.mtls_certificates, + pipelines: config.pipelines, logfwdr: config.logfwdr, experimental_assets: config.experimental_assets?.binding ? { binding: config.experimental_assets.binding } @@ -875,6 +876,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m logger.log("Worker Startup Time:", result.startup_time_ms, "ms"); } bindingsPrinted = true; + printBindings({ ...withoutStaticAssets, vars: maskedVars }); versionId = parseNonHyphenedUuid(result.deployment_id); diff --git a/packages/wrangler/src/deployment-bundle/create-worker-upload-form.ts b/packages/wrangler/src/deployment-bundle/create-worker-upload-form.ts index 08b4886cd458..c3a48dc42f37 100644 --- a/packages/wrangler/src/deployment-bundle/create-worker-upload-form.ts +++ b/packages/wrangler/src/deployment-bundle/create-worker-upload-form.ts @@ -110,6 +110,7 @@ export type WorkerMetadataBinding = }; } | { type: "mtls_certificate"; name: string; certificate_id: string } + | { type: "pipelines"; name: string; id: string } | { type: "logfwdr"; name: string; @@ -316,6 +317,14 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData { }); }); + bindings.pipelines?.forEach(({ binding, pipeline }) => { + metadataBindings.push({ + name: binding, + type: "pipelines", + id: pipeline, + }); + }); + bindings.logfwdr?.bindings.forEach(({ name, destination }) => { metadataBindings.push({ name: name, diff --git a/packages/wrangler/src/deployment-bundle/worker.ts b/packages/wrangler/src/deployment-bundle/worker.ts index 13b6426608df..fc8679847c7b 100644 --- a/packages/wrangler/src/deployment-bundle/worker.ts +++ b/packages/wrangler/src/deployment-bundle/worker.ts @@ -226,6 +226,11 @@ export interface CfExperimentalAssetBinding { binding: string; } +export interface CfPipeline { + binding: string; + pipeline: string; +} + export interface CfUnsafeBinding { name: string; type: string; @@ -327,6 +332,7 @@ export interface CfWorkerInit { dispatch_namespaces: CfDispatchNamespace[] | undefined; mtls_certificates: CfMTlsCertificate[] | undefined; logfwdr: CfLogfwdr | undefined; + pipelines: CfPipeline[] | undefined; unsafe: CfUnsafe | undefined; experimental_assets: CfExperimentalAssetBinding | undefined; }; diff --git a/packages/wrangler/src/dev.tsx b/packages/wrangler/src/dev.tsx index d545f6e7bc42..8bb0b0a2ce9c 100644 --- a/packages/wrangler/src/dev.tsx +++ b/packages/wrangler/src/dev.tsx @@ -726,6 +726,7 @@ export async function startDev(args: StartDevOptions) { analytics_engine_datasets: undefined, dispatch_namespaces: undefined, mtls_certificates: undefined, + pipelines: undefined, logfwdr: undefined, unsafe: undefined, experimental_assets: undefined, @@ -1600,6 +1601,7 @@ export function getBindings( capnp: configParam.unsafe.capnp, }, mtls_certificates: configParam.mtls_certificates, + pipelines: configParam.pipelines, send_email: configParam.send_email, experimental_assets: configParam.experimental_assets?.binding ? { binding: configParam.experimental_assets?.binding } diff --git a/packages/wrangler/src/secret/index.ts b/packages/wrangler/src/secret/index.ts index 0a2c62f620c6..23579f036093 100644 --- a/packages/wrangler/src/secret/index.ts +++ b/packages/wrangler/src/secret/index.ts @@ -92,6 +92,7 @@ async function createDraftWorker({ data_blobs: {}, dispatch_namespaces: [], mtls_certificates: [], + pipelines: [], logfwdr: { bindings: [] }, experimental_assets: undefined, unsafe: { diff --git a/packages/wrangler/src/versions/upload.ts b/packages/wrangler/src/versions/upload.ts index 3b6f11acdf0e..cf7bb5e455d3 100644 --- a/packages/wrangler/src/versions/upload.ts +++ b/packages/wrangler/src/versions/upload.ts @@ -388,6 +388,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m analytics_engine_datasets: config.analytics_engine_datasets, dispatch_namespaces: config.dispatch_namespaces, mtls_certificates: config.mtls_certificates, + pipelines: config.pipelines, logfwdr: config.logfwdr, experimental_assets: config.experimental_assets?.binding ? { binding: config.experimental_assets?.binding }