@cloudflare/vite-plugin 2.0.0-beta.sha-b747ec8ea → 2.0.0-beta.sha-be282d7ee

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  //#region package.json
2
2
  var package_default = {
3
3
  name: "@cloudflare/vite-plugin",
4
- version: "2.0.0-beta.sha-b747ec8ea",
4
+ version: "2.0.0-beta.sha-be282d7ee",
5
5
  description: "Cloudflare plugin for Vite",
6
6
  keywords: [
7
7
  "cloudflare",
@@ -91,4 +91,4 @@ var package_default = {
91
91
 
92
92
  //#endregion
93
93
  export { package_default as default };
94
- //# sourceMappingURL=package-BJ7U5J4t.mjs.map
94
+ //# sourceMappingURL=package-Ci21HGBv.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"package-BJ7U5J4t.mjs","names":[],"sources":["../package.json"],"sourcesContent":["{\n\t\"name\": \"@cloudflare/vite-plugin\",\n\t\"version\": \"2.0.0-beta.sha-b747ec8ea\",\n\t\"description\": \"Cloudflare plugin for Vite\",\n\t\"keywords\": [\n\t\t\"cloudflare\",\n\t\t\"cloudflare-workers\",\n\t\t\"vite\",\n\t\t\"vite-plugin\",\n\t\t\"workers\"\n\t],\n\t\"homepage\": \"https://github.com/cloudflare/workers-sdk/tree/main/packages/vite-plugin-cloudflare#readme\",\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/cloudflare/workers-sdk/issues\"\n\t},\n\t\"license\": \"MIT\",\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"https://github.com/cloudflare/workers-sdk.git\",\n\t\t\"directory\": \"packages/vite-plugin-cloudflare\"\n\t},\n\t\"bin\": {\n\t\t\"cf-vite\": \"./bin/cf-vite\"\n\t},\n\t\"files\": [\n\t\t\"bin\",\n\t\t\"dist\"\n\t],\n\t\"type\": \"module\",\n\t\"sideEffects\": false,\n\t\"main\": \"./dist/index.mjs\",\n\t\"types\": \"./dist/index.d.mts\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./dist/index.d.mts\",\n\t\t\t\"import\": \"./dist/index.mjs\"\n\t\t},\n\t\t\"./experimental-config\": {\n\t\t\t\"types\": \"./dist/experimental-config.d.mts\",\n\t\t\t\"import\": \"./dist/experimental-config.mjs\"\n\t\t}\n\t},\n\t\"publishConfig\": {\n\t\t\"access\": \"public\"\n\t},\n\t\"scripts\": {\n\t\t\"build\": \"tsdown\",\n\t\t\"check:type\": \"tsc --build\",\n\t\t\"dev\": \"tsdown --watch\",\n\t\t\"test\": \"vitest run\",\n\t\t\"test:ci\": \"pnpm test\",\n\t\t\"test:e2e\": \"vitest run -c e2e/vitest.config.ts\",\n\t\t\"test:watch\": \"vitest\"\n\t},\n\t\"dependencies\": {\n\t\t\"@cloudflare/config\": \"0.17.0\",\n\t\t\"@cloudflare/unenv-preset\": \"2.16.2\",\n\t\t\"miniflare\": \"5.20260921.0-alpha\",\n\t\t\"unenv\": \"2.0.0-rc.24\",\n\t\t\"workerd\": \"1.20260918.1\",\n\t\t\"ws\": \"catalog:default\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@cloudflare/build-output-utils\": \"workspace:*\",\n\t\t\"@cloudflare/cli-shared-helpers\": \"workspace:*\",\n\t\t\"@cloudflare/containers-shared\": \"workspace:*\",\n\t\t\"@cloudflare/mock-npm-registry\": \"workspace:*\",\n\t\t\"@cloudflare/remote-bindings\": \"workspace:*\",\n\t\t\"@cloudflare/runtime-types\": \"workspace:*\",\n\t\t\"@cloudflare/workers-auth\": \"workspace:*\",\n\t\t\"@cloudflare/workers-shared\": \"workspace:*\",\n\t\t\"@cloudflare/workers-tsconfig\": \"workspace:*\",\n\t\t\"@cloudflare/workers-types\": \"catalog:default\",\n\t\t\"@cloudflare/workers-utils\": \"workspace:*\",\n\t\t\"@remix-run/node-fetch-server\": \"^0.8.0\",\n\t\t\"@types/node\": \"catalog:default\",\n\t\t\"@types/semver\": \"^7.5.1\",\n\t\t\"@types/ws\": \"^8.5.13\",\n\t\t\"am-i-vibing\": \"^0.5.0\",\n\t\t\"defu\": \"^6.1.4\",\n\t\t\"get-port\": \"^7.1.0\",\n\t\t\"magic-string\": \"^0.30.12\",\n\t\t\"mlly\": \"^1.7.4\",\n\t\t\"open\": \"catalog:default\",\n\t\t\"picocolors\": \"^1.1.1\",\n\t\t\"qr\": \"^0.6.0\",\n\t\t\"semver\": \"^7.7.1\",\n\t\t\"tinyglobby\": \"catalog:default\",\n\t\t\"tree-kill\": \"catalog:default\",\n\t\t\"tsdown\": \"0.16.3\",\n\t\t\"typescript\": \"catalog:default\",\n\t\t\"vite\": \"catalog:default\",\n\t\t\"vite-legacy\": \"npm:vite@7.1.12\",\n\t\t\"vitest\": \"catalog:default\"\n\t},\n\t\"peerDependencies\": {\n\t\t\"vite\": \"^7.0.0 || ^8.0.0\"\n\t},\n\t\"workers-sdk\": {\n\t\t\"prerelease\": true\n\t}\n}\n"],"mappings":";sBAAA;OACS;UACG;cACI;WACH;EACX;EACA;EACA;EACA;EACA;EACA;WACW;OACJ,EACP,OAAO,oDACP;UACU;aACG;EACb,QAAQ;EACR,OAAO;EACP,aAAa;EACb;MACM,EACN,WAAW,iBACX;QACQ,CACR,OACA,OACA;OACO;cACO;OACP;QACC;UACE;EACV,KAAK;GACJ,SAAS;GACT,UAAU;GACV;EACD,yBAAyB;GACxB,SAAS;GACT,UAAU;GACV;EACD;gBACgB,EAChB,UAAU,UACV;UACU;EACV,SAAS;EACT,cAAc;EACd,OAAO;EACP,QAAQ;EACR,WAAW;EACX,YAAY;EACZ,cAAc;EACd;eACe;EACf,sBAAsB;EACtB,4BAA4B;EAC5B,aAAa;EACb,SAAS;EACT,WAAW;EACX,MAAM;EACN;kBACkB;EAClB,kCAAkC;EAClC,kCAAkC;EAClC,iCAAiC;EACjC,iCAAiC;EACjC,+BAA+B;EAC/B,6BAA6B;EAC7B,4BAA4B;EAC5B,8BAA8B;EAC9B,gCAAgC;EAChC,6BAA6B;EAC7B,6BAA6B;EAC7B,gCAAgC;EAChC,eAAe;EACf,iBAAiB;EACjB,aAAa;EACb,eAAe;EACf,QAAQ;EACR,YAAY;EACZ,gBAAgB;EAChB,QAAQ;EACR,QAAQ;EACR,cAAc;EACd,MAAM;EACN,UAAU;EACV,cAAc;EACd,aAAa;EACb,UAAU;EACV,cAAc;EACd,QAAQ;EACR,eAAe;EACf,UAAU;EACV;mBACmB,EACnB,QAAQ,oBACR;CACD,eAAe,EACd,cAAc,MACd;CACD"}
1
+ {"version":3,"file":"package-Ci21HGBv.mjs","names":[],"sources":["../package.json"],"sourcesContent":["{\n\t\"name\": \"@cloudflare/vite-plugin\",\n\t\"version\": \"2.0.0-beta.sha-be282d7ee\",\n\t\"description\": \"Cloudflare plugin for Vite\",\n\t\"keywords\": [\n\t\t\"cloudflare\",\n\t\t\"cloudflare-workers\",\n\t\t\"vite\",\n\t\t\"vite-plugin\",\n\t\t\"workers\"\n\t],\n\t\"homepage\": \"https://github.com/cloudflare/workers-sdk/tree/main/packages/vite-plugin-cloudflare#readme\",\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/cloudflare/workers-sdk/issues\"\n\t},\n\t\"license\": \"MIT\",\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"https://github.com/cloudflare/workers-sdk.git\",\n\t\t\"directory\": \"packages/vite-plugin-cloudflare\"\n\t},\n\t\"bin\": {\n\t\t\"cf-vite\": \"./bin/cf-vite\"\n\t},\n\t\"files\": [\n\t\t\"bin\",\n\t\t\"dist\"\n\t],\n\t\"type\": \"module\",\n\t\"sideEffects\": false,\n\t\"main\": \"./dist/index.mjs\",\n\t\"types\": \"./dist/index.d.mts\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./dist/index.d.mts\",\n\t\t\t\"import\": \"./dist/index.mjs\"\n\t\t},\n\t\t\"./experimental-config\": {\n\t\t\t\"types\": \"./dist/experimental-config.d.mts\",\n\t\t\t\"import\": \"./dist/experimental-config.mjs\"\n\t\t}\n\t},\n\t\"publishConfig\": {\n\t\t\"access\": \"public\"\n\t},\n\t\"scripts\": {\n\t\t\"build\": \"tsdown\",\n\t\t\"check:type\": \"tsc --build\",\n\t\t\"dev\": \"tsdown --watch\",\n\t\t\"test\": \"vitest run\",\n\t\t\"test:ci\": \"pnpm test\",\n\t\t\"test:e2e\": \"vitest run -c e2e/vitest.config.ts\",\n\t\t\"test:watch\": \"vitest\"\n\t},\n\t\"dependencies\": {\n\t\t\"@cloudflare/config\": \"0.17.0\",\n\t\t\"@cloudflare/unenv-preset\": \"2.16.2\",\n\t\t\"miniflare\": \"5.20260921.0-alpha\",\n\t\t\"unenv\": \"2.0.0-rc.24\",\n\t\t\"workerd\": \"1.20260918.1\",\n\t\t\"ws\": \"catalog:default\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@cloudflare/build-output-utils\": \"workspace:*\",\n\t\t\"@cloudflare/cli-shared-helpers\": \"workspace:*\",\n\t\t\"@cloudflare/containers-shared\": \"workspace:*\",\n\t\t\"@cloudflare/mock-npm-registry\": \"workspace:*\",\n\t\t\"@cloudflare/remote-bindings\": \"workspace:*\",\n\t\t\"@cloudflare/runtime-types\": \"workspace:*\",\n\t\t\"@cloudflare/workers-auth\": \"workspace:*\",\n\t\t\"@cloudflare/workers-shared\": \"workspace:*\",\n\t\t\"@cloudflare/workers-tsconfig\": \"workspace:*\",\n\t\t\"@cloudflare/workers-types\": \"catalog:default\",\n\t\t\"@cloudflare/workers-utils\": \"workspace:*\",\n\t\t\"@remix-run/node-fetch-server\": \"^0.8.0\",\n\t\t\"@types/node\": \"catalog:default\",\n\t\t\"@types/semver\": \"^7.5.1\",\n\t\t\"@types/ws\": \"^8.5.13\",\n\t\t\"am-i-vibing\": \"^0.5.0\",\n\t\t\"defu\": \"^6.1.4\",\n\t\t\"get-port\": \"^7.1.0\",\n\t\t\"magic-string\": \"^0.30.12\",\n\t\t\"mlly\": \"^1.7.4\",\n\t\t\"open\": \"catalog:default\",\n\t\t\"picocolors\": \"^1.1.1\",\n\t\t\"qr\": \"^0.6.0\",\n\t\t\"semver\": \"^7.7.1\",\n\t\t\"tinyglobby\": \"catalog:default\",\n\t\t\"tree-kill\": \"catalog:default\",\n\t\t\"tsdown\": \"0.16.3\",\n\t\t\"typescript\": \"catalog:default\",\n\t\t\"vite\": \"catalog:default\",\n\t\t\"vite-legacy\": \"npm:vite@7.1.12\",\n\t\t\"vitest\": \"catalog:default\"\n\t},\n\t\"peerDependencies\": {\n\t\t\"vite\": \"^7.0.0 || ^8.0.0\"\n\t},\n\t\"workers-sdk\": {\n\t\t\"prerelease\": true\n\t}\n}\n"],"mappings":";sBAAA;OACS;UACG;cACI;WACH;EACX;EACA;EACA;EACA;EACA;EACA;WACW;OACJ,EACP,OAAO,oDACP;UACU;aACG;EACb,QAAQ;EACR,OAAO;EACP,aAAa;EACb;MACM,EACN,WAAW,iBACX;QACQ,CACR,OACA,OACA;OACO;cACO;OACP;QACC;UACE;EACV,KAAK;GACJ,SAAS;GACT,UAAU;GACV;EACD,yBAAyB;GACxB,SAAS;GACT,UAAU;GACV;EACD;gBACgB,EAChB,UAAU,UACV;UACU;EACV,SAAS;EACT,cAAc;EACd,OAAO;EACP,QAAQ;EACR,WAAW;EACX,YAAY;EACZ,cAAc;EACd;eACe;EACf,sBAAsB;EACtB,4BAA4B;EAC5B,aAAa;EACb,SAAS;EACT,WAAW;EACX,MAAM;EACN;kBACkB;EAClB,kCAAkC;EAClC,kCAAkC;EAClC,iCAAiC;EACjC,iCAAiC;EACjC,+BAA+B;EAC/B,6BAA6B;EAC7B,4BAA4B;EAC5B,8BAA8B;EAC9B,gCAAgC;EAChC,6BAA6B;EAC7B,6BAA6B;EAC7B,gCAAgC;EAChC,eAAe;EACf,iBAAiB;EACjB,aAAa;EACb,eAAe;EACf,QAAQ;EACR,YAAY;EACZ,gBAAgB;EAChB,QAAQ;EACR,QAAQ;EACR,cAAc;EACd,MAAM;EACN,UAAU;EACV,cAAc;EACd,aAAa;EACb,UAAU;EACV,cAAc;EACd,QAAQ;EACR,eAAe;EACf,UAAU;EACV;mBACmB,EACnB,QAAQ,oBACR;CACD,eAAe,EACd,cAAc,MACd;CACD"}
@@ -4816,6 +4816,66 @@ const Uint8ToHexString = (array) => {
4816
4816
  return [...array].map((b) => b.toString(16).padStart(2, "0")).join("");
4817
4817
  };
4818
4818
 
4819
+ //#endregion
4820
+ //#region ../workers-shared/utils/base-path.ts
4821
+ const CONTROL_CHARACTER = /[\u0000-\u001f\u007f]/;
4822
+ /**
4823
+ * Validate and normalize a raw `base_path` value into its canonical stored form.
4824
+ *
4825
+ * Relative and absolute pathname inputs are accepted and interpreted as a fixed
4826
+ * root-relative request prefix. Empty and dot segments are resolved before a
4827
+ * leading and trailing slash are applied. URL-shaped values, query or fragment
4828
+ * delimiters, backslashes, control characters, and percent-encoded sequences
4829
+ * are rejected. Unicode pathname characters are allowed.
4830
+ */
4831
+ function normalizeBasePath(value) {
4832
+ if (value === "/") return {
4833
+ valid: true,
4834
+ value: "/"
4835
+ };
4836
+ if (typeof value !== "string") return {
4837
+ valid: false,
4838
+ error: `Expected a string but got ${typeof value}.`
4839
+ };
4840
+ if (value.length === 0) return {
4841
+ valid: false,
4842
+ error: `The value must not be empty.`
4843
+ };
4844
+ if (value.startsWith("//") || /^[a-zA-Z][a-zA-Z\d+.-]*:/.test(value)) return {
4845
+ valid: false,
4846
+ error: `The value must be a pathname, not a URL.`
4847
+ };
4848
+ if (value.includes("\\")) return {
4849
+ valid: false,
4850
+ error: `The value must not contain backslashes.`
4851
+ };
4852
+ if (CONTROL_CHARACTER.test(value)) return {
4853
+ valid: false,
4854
+ error: `The value must not contain control characters.`
4855
+ };
4856
+ if (value.includes("?") || value.includes("#")) return {
4857
+ valid: false,
4858
+ error: `The value must not contain query strings or fragments.`
4859
+ };
4860
+ if (value.includes("%")) return {
4861
+ valid: false,
4862
+ error: `The value must not contain percent-encoded sequences.`
4863
+ };
4864
+ const normalizedSegments = [];
4865
+ for (const segment of value.split("/")) {
4866
+ if (segment === "" || segment === ".") continue;
4867
+ if (segment === "..") {
4868
+ normalizedSegments.pop();
4869
+ continue;
4870
+ }
4871
+ normalizedSegments.push(segment);
4872
+ }
4873
+ return {
4874
+ valid: true,
4875
+ value: normalizedSegments.length === 0 ? "/" : `/${normalizedSegments.join("/")}/`
4876
+ };
4877
+ }
4878
+
4819
4879
  //#endregion
4820
4880
  //#region ../workers-shared/asset-worker/src/compatibility-flags.ts
4821
4881
  const SEC_FETCH_MODE_NAVIGATE_HEADER_PREFERS_ASSET_SERVING = {
@@ -4841,11 +4901,14 @@ const flagIsEnabled = (configuration, compatibilityFlag) => {
4841
4901
  //#region ../workers-shared/asset-worker/src/configuration.ts
4842
4902
  const normalizeConfiguration = (configuration) => {
4843
4903
  const compatibilityOptions = resolveCompatibilityOptions(configuration);
4904
+ const basePath = normalizeBasePath(configuration?.base_path ?? "/");
4905
+ if (!basePath.valid) throw new Error(`Invalid assets base_path: ${basePath.error}`);
4844
4906
  return {
4845
4907
  compatibility_date: compatibilityOptions.compatibilityDate,
4846
4908
  compatibility_flags: compatibilityOptions.compatibilityFlags,
4847
4909
  html_handling: configuration?.html_handling ?? "auto-trailing-slash",
4848
4910
  not_found_handling: configuration?.not_found_handling ?? "none",
4911
+ base_path: basePath.value,
4849
4912
  redirects: configuration?.redirects ?? {
4850
4913
  version: 1,
4851
4914
  staticRules: {},
@@ -5018,6 +5081,62 @@ var PermanentRedirectResponse = class PermanentRedirectResponse extends Response
5018
5081
  }
5019
5082
  };
5020
5083
 
5084
+ //#endregion
5085
+ //#region ../workers-shared/asset-worker/src/utils/base-path.ts
5086
+ /**
5087
+ * Asset-worker-local helpers for converting between public request pathnames
5088
+ * and asset-directory-relative pathnames using the configured `base_path`.
5089
+ *
5090
+ * `base` is always the canonical stored form: either `"/"` or an absolute
5091
+ * pathname with a trailing slash such as `"/subpath/"`.
5092
+ */
5093
+ /**
5094
+ * Segment-aware check that a decoded public path is at, or nested under, the
5095
+ * base. `/subpath` matches `/subpath` and `/subpath/...`, but not
5096
+ * `/subpath-other`. The root base `"/"` matches everything.
5097
+ */
5098
+ function isWithinBase(publicDecodedPath, base) {
5099
+ if (base === "/") return true;
5100
+ return publicDecodedPath === base.slice(0, -1) || publicDecodedPath.startsWith(base);
5101
+ }
5102
+ /**
5103
+ * Strip the configured base from a decoded public pathname, returning the
5104
+ * asset-directory-relative path.
5105
+ *
5106
+ * - When `base` is `"/"` this is the identity mapping (`inPrefix: true`).
5107
+ * - The base root itself maps to `assetPath: "/"`.
5108
+ * - A leading `/` is always preserved on the returned asset path.
5109
+ * - When the path is off-prefix, `inPrefix` is `false` and the caller returns
5110
+ * `NoIntentResponse`.
5111
+ */
5112
+ function stripBasePath(publicDecodedPath, base) {
5113
+ if (base === "/") return {
5114
+ inPrefix: true,
5115
+ assetPath: publicDecodedPath
5116
+ };
5117
+ if (!isWithinBase(publicDecodedPath, base)) return {
5118
+ inPrefix: false,
5119
+ assetPath: publicDecodedPath
5120
+ };
5121
+ const stripped = publicDecodedPath.slice(base.length - 1);
5122
+ return {
5123
+ inPrefix: true,
5124
+ assetPath: stripped === "" ? "/" : stripped
5125
+ };
5126
+ }
5127
+ /**
5128
+ * Re-append the configured base to an asset-directory-relative location,
5129
+ * producing a public path. Only ever call this with an explicitly
5130
+ * asset-relative location (e.g. an asset-worker-generated redirect); never with
5131
+ * a value that might already be public. The empty string represents the base
5132
+ * root without its canonical trailing slash.
5133
+ */
5134
+ function addBasePath(assetRelativePath, base) {
5135
+ if (base === "/") return assetRelativePath;
5136
+ if (assetRelativePath === "/") return base;
5137
+ return base.slice(0, -1) + assetRelativePath;
5138
+ }
5139
+
5021
5140
  //#endregion
5022
5141
  //#region ../workers-shared/asset-worker/src/constants.ts
5023
5142
  const CACHE_CONTROL_BROWSER = "public, max-age=0, must-revalidate";
@@ -5138,30 +5257,21 @@ function attachCustomHeaders(request, response, configuration, env) {
5138
5257
  //#region ../workers-shared/asset-worker/src/handler.ts
5139
5258
  const REDIRECTS_VERSION = 1;
5140
5259
  const HEADERS_VERSION = 2;
5141
- const getResponseOrAssetIntent = async (request, env, configuration, exists, analytics) => {
5260
+ const getResponseOrAssetIntent = async (request, env, configuration, exists, originalDecodedPathname, analytics) => {
5142
5261
  const url = new URL(request.url);
5143
5262
  const { search } = url;
5263
+ const base = configuration.base_path;
5144
5264
  const redirectResult = handleRedirects(env, request, configuration, url.host, url.pathname, search);
5145
5265
  if (redirectResult instanceof Response) {
5146
5266
  analytics?.setData({ servedBy: "redirect" });
5147
5267
  return redirectResult;
5148
5268
  }
5149
5269
  const { proxied, pathname } = redirectResult;
5150
- const decodedPathname = decodePath(pathname);
5151
- const intent = await getIntent(decodedPathname, request, configuration, exists);
5152
- if (!intent) {
5153
- const response = proxied ? new NotFoundResponse() : new NoIntentResponse();
5154
- analytics?.setData({ servedBy: "none" });
5155
- return env.JAEGER.enterSpan("no_intent", (span) => {
5156
- span.setTags({
5157
- decodedPathname,
5158
- configuration: JSON.stringify(configuration),
5159
- proxied,
5160
- status: response.status
5161
- });
5162
- return response;
5163
- });
5164
- }
5270
+ const effectiveDecodedPathname = proxied ? decodePath(pathname) : originalDecodedPathname;
5271
+ const { inPrefix, assetPath } = stripBasePath(effectiveDecodedPathname, base);
5272
+ if (!inPrefix) return noIntent(env, analytics, configuration, effectiveDecodedPathname, proxied);
5273
+ const intent = (proxied ? null : await getBaseRootIntent(effectiveDecodedPathname, base, request, configuration, exists)) ?? await getIntent(assetPath, request, configuration, exists);
5274
+ if (!intent) return noIntent(env, analytics, configuration, effectiveDecodedPathname, proxied);
5165
5275
  const method = request.method.toUpperCase();
5166
5276
  if (!["GET", "HEAD"].includes(method)) {
5167
5277
  analytics?.setData({ servedBy: "method-not-allowed" });
@@ -5173,13 +5283,13 @@ const getResponseOrAssetIntent = async (request, env, configuration, exists, ana
5173
5283
  return new MethodNotAllowedResponse();
5174
5284
  });
5175
5285
  }
5176
- const encodedDestination = encodePath(intent.redirect ?? decodedPathname);
5286
+ const encodedDestination = encodePath(intent.redirect !== null ? addBasePath(intent.redirect, base) : effectiveDecodedPathname);
5177
5287
  /**
5178
5288
  * The canonical path we serve an asset at is the decoded and re-encoded version.
5179
5289
  * Thus we need to redirect if that is different from the decoded version.
5180
5290
  * We combine this with other redirects (e.g. for html_handling) to avoid multiple redirects.
5181
5291
  */
5182
- if (encodedDestination !== pathname && intent.asset || intent.redirect) {
5292
+ if (encodedDestination !== pathname && intent.asset || intent.redirect !== null) {
5183
5293
  analytics?.setData({ servedBy: "redirect" });
5184
5294
  return env.JAEGER.enterSpan("redirect", (span) => {
5185
5295
  span.setTags({
@@ -5205,6 +5315,24 @@ const getResponseOrAssetIntent = async (request, env, configuration, exists, ana
5205
5315
  resolver: intent.resolver
5206
5316
  };
5207
5317
  };
5318
+ /**
5319
+ * Return the protocol-level response for a request the Asset Worker does not
5320
+ * intend to serve, while recording the public decoded pathname in telemetry.
5321
+ * Proxied rewrites are claimed as 404s unless the caller explicitly opts out.
5322
+ */
5323
+ function noIntent(env, analytics, configuration, decodedPathname, proxied, claimProxiedRequest = true) {
5324
+ const response = proxied && claimProxiedRequest ? new NotFoundResponse() : new NoIntentResponse();
5325
+ analytics?.setData({ servedBy: "none" });
5326
+ return env.JAEGER.enterSpan("no_intent", (span) => {
5327
+ span.setTags({
5328
+ decodedPathname,
5329
+ configuration: JSON.stringify(configuration),
5330
+ proxied,
5331
+ status: response.status
5332
+ });
5333
+ return response;
5334
+ });
5335
+ }
5208
5336
  const resolveAssetIntentToResponse = async (assetIntent, request, env, configuration, getByETag, analytics) => {
5209
5337
  const { pathname } = new URL(request.url);
5210
5338
  const method = request.method.toUpperCase();
@@ -5245,16 +5373,32 @@ const resolveAssetIntentToResponse = async (assetIntent, request, env, configura
5245
5373
  }
5246
5374
  });
5247
5375
  };
5376
+ /**
5377
+ * Decode the request pathname exactly once and map it from the public URL
5378
+ * namespace into the asset-directory namespace. The returned decodedPathname
5379
+ * always remains public and is suitable for request-facing telemetry.
5380
+ */
5381
+ function getRequestBasePath(request, base) {
5382
+ const decodedPathname = decodePath(new URL(request.url).pathname);
5383
+ return {
5384
+ decodedPathname,
5385
+ ...stripBasePath(decodedPathname, base)
5386
+ };
5387
+ }
5248
5388
  const canFetch = async (request, env, configuration, exists) => {
5389
+ const requestPath = getRequestBasePath(request, configuration.base_path);
5390
+ if (!requestPath.inPrefix) return false;
5249
5391
  if (!(configuration.has_static_routing || flagIsEnabled(configuration, SEC_FETCH_MODE_NAVIGATE_HEADER_PREFERS_ASSET_SERVING) && request.headers.get("Sec-Fetch-Mode") === "navigate")) configuration = {
5250
5392
  ...configuration,
5251
5393
  not_found_handling: "none"
5252
5394
  };
5253
- if (await getResponseOrAssetIntent(request, env, configuration, exists) instanceof NoIntentResponse) return false;
5395
+ if (await getResponseOrAssetIntent(request, env, configuration, exists, requestPath.decodedPathname) instanceof NoIntentResponse) return false;
5254
5396
  return true;
5255
5397
  };
5256
5398
  const handleRequest = async (request, env, configuration, exists, getByETag, analytics) => {
5257
- const responseOrAssetIntent = await getResponseOrAssetIntent(request, env, configuration, exists, analytics);
5399
+ const requestPath = getRequestBasePath(request, configuration.base_path);
5400
+ if (!requestPath.inPrefix) return noIntent(env, analytics, configuration, requestPath.decodedPathname, false);
5401
+ const responseOrAssetIntent = await getResponseOrAssetIntent(request, env, configuration, exists, requestPath.decodedPathname, analytics);
5258
5402
  return attachCustomHeaders(request, responseOrAssetIntent instanceof Response ? responseOrAssetIntent : await resolveAssetIntentToResponse(responseOrAssetIntent, request, env, configuration, getByETag, analytics), configuration, env);
5259
5403
  };
5260
5404
  function servedByForResolver(resolver, configuration) {
@@ -5276,6 +5420,31 @@ const getIntent = async (pathname, request, configuration, exists, skipRedirects
5276
5420
  case "none": return htmlHandlingNone(pathname, request, configuration, exists);
5277
5421
  }
5278
5422
  };
5423
+ /**
5424
+ * Resolve the generated redirect, if any, for the public base root. The
5425
+ * returned redirect is asset-relative and must be prefixed exactly once by the
5426
+ * top-level handler. Authored redirects and proxy rewrites are handled before
5427
+ * this helper is called.
5428
+ */
5429
+ async function getBaseRootIntent(publicDecodedPath, base, request, configuration, exists) {
5430
+ let pathname;
5431
+ switch (configuration.html_handling) {
5432
+ case "force-trailing-slash":
5433
+ case "auto-trailing-slash":
5434
+ pathname = base !== "/" && publicDecodedPath === base.slice(0, -1) ? "/" : null;
5435
+ break;
5436
+ case "drop-trailing-slash":
5437
+ pathname = base !== "/" && publicDecodedPath === base ? "" : null;
5438
+ break;
5439
+ case "none": return null;
5440
+ }
5441
+ if (pathname === null || !await exists("/index.html", request)) return null;
5442
+ return {
5443
+ asset: null,
5444
+ redirect: pathname,
5445
+ resolver: "html-handling"
5446
+ };
5447
+ }
5279
5448
  const htmlHandlingAutoTrailingSlash = async (pathname, request, configuration, exists, skipRedirects) => {
5280
5449
  let redirectResult = null;
5281
5450
  let eTagResult = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudflare/vite-plugin",
3
- "version": "2.0.0-beta.sha-b747ec8ea",
3
+ "version": "2.0.0-beta.sha-be282d7ee",
4
4
  "description": "Cloudflare plugin for Vite",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -73,16 +73,16 @@
73
73
  "vite": "8.1.5",
74
74
  "vite-legacy": "npm:vite@7.1.12",
75
75
  "vitest": "4.1.0",
76
- "@cloudflare/cli-shared-helpers": "0.1.35",
77
- "@cloudflare/containers-shared": "0.19.0",
78
76
  "@cloudflare/build-output-utils": "0.7.1",
79
- "@cloudflare/remote-bindings": "0.0.30",
77
+ "@cloudflare/cli-shared-helpers": "0.1.36",
78
+ "@cloudflare/containers-shared": "0.19.1",
80
79
  "@cloudflare/mock-npm-registry": "0.0.0",
81
- "@cloudflare/runtime-types": "0.0.31",
82
- "@cloudflare/workers-auth": "0.8.0",
83
- "@cloudflare/workers-utils": "0.41.0",
80
+ "@cloudflare/remote-bindings": "0.0.32",
81
+ "@cloudflare/workers-auth": "0.9.0",
84
82
  "@cloudflare/workers-shared": "0.19.9",
85
- "@cloudflare/workers-tsconfig": "0.0.0"
83
+ "@cloudflare/workers-tsconfig": "0.0.0",
84
+ "@cloudflare/runtime-types": "0.1.0",
85
+ "@cloudflare/workers-utils": "0.41.1"
86
86
  },
87
87
  "peerDependencies": {
88
88
  "vite": "^7.0.0 || ^8.0.0"