wrangler 0.0.0-e57758f8 → 0.0.0-e5ae13ade

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.
Files changed (34) hide show
  1. package/README.md +29 -37
  2. package/bin/wrangler.js +3 -37
  3. package/config-schema.json +1782 -2290
  4. package/package.json +62 -98
  5. package/templates/checked-fetch.js +1 -1
  6. package/templates/facade.d.ts +2 -2
  7. package/templates/gitignore +3 -0
  8. package/templates/init-tests/test-vitest-new-worker.ts +2 -2
  9. package/templates/middleware/loader-modules.ts +14 -13
  10. package/templates/middleware/middleware-patch-console-prefix.d.ts +3 -0
  11. package/templates/middleware/middleware-patch-console-prefix.ts +21 -0
  12. package/templates/middleware/middleware-scheduled.ts +15 -1
  13. package/templates/mixedMode/proxyServerWorker/index.ts +40 -0
  14. package/templates/mixedMode/proxyServerWorker/wrangler.jsonc +4 -0
  15. package/templates/pages-dev-pipeline.ts +3 -2
  16. package/templates/pages-template-plugin.ts +2 -2
  17. package/templates/pages-template-worker.ts +1 -1
  18. package/templates/startDevWorker/InspectorProxyWorker.ts +19 -1
  19. package/templates/startDevWorker/ProxyWorker.ts +30 -25
  20. package/templates/strip-cf-connecting-ip-header.js +13 -0
  21. package/templates/tsconfig.init.json +4 -87
  22. package/templates/tsconfig.json +8 -1
  23. package/templates/tsconfig.tsbuildinfo +1 -0
  24. package/wrangler-dist/InspectorProxyWorker.js +15 -8
  25. package/wrangler-dist/InspectorProxyWorker.js.map +1 -1
  26. package/wrangler-dist/ProxyWorker.js +28 -30
  27. package/wrangler-dist/ProxyWorker.js.map +1 -1
  28. package/wrangler-dist/cli.d.ts +3783 -911
  29. package/wrangler-dist/cli.js +168505 -199177
  30. package/wrangler-dist/cli.js.map +7 -0
  31. package/Cloudflare_CA.pem +0 -18
  32. package/templates/middleware/middleware-serve-static-assets.d.ts +0 -6
  33. package/templates/middleware/middleware-serve-static-assets.ts +0 -56
  34. package/wrangler-dist/wasm-sync.wasm +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wrangler",
3
- "version": "0.0.0-e57758f8",
3
+ "version": "0.0.0-e5ae13ade",
4
4
  "description": "Command-line interface for all things Cloudflare Workers",
5
5
  "keywords": [
6
6
  "wrangler",
@@ -47,152 +47,116 @@
47
47
  "wrangler-dist",
48
48
  "templates",
49
49
  "kv-asset-handler.js",
50
- "Cloudflare_CA.pem",
51
50
  "config-schema.json"
52
51
  ],
53
- "jest": {
54
- "moduleNameMapper": {
55
- "clipboardy": "<rootDir>/src/__tests__/helpers/clipboardy-mock.js",
56
- "miniflare/cli": "<rootDir>/../../node_modules/miniflare/dist/src/cli.js"
57
- },
58
- "restoreMocks": true,
59
- "setupFilesAfterEnv": [
60
- "<rootDir>/src/__tests__/jest.setup.ts"
61
- ],
62
- "testRegex": "src/__tests__/.*\\.(test|spec)\\.[jt]sx?$",
63
- "testTimeout": 50000,
64
- "transform": {
65
- "^.+\\.c?(t|j)sx?$": [
66
- "esbuild-jest",
67
- {
68
- "sourcemap": true
69
- }
70
- ]
71
- },
72
- "transformIgnorePatterns": [
73
- "node_modules/.pnpm/(?!find-up|locate-path|p-locate|p-limit|p-timeout|p-queue|yocto-queue|path-exists|execa|strip-final-newline|npm-run-path|path-key|onetime|mimic-fn|human-signals|is-stream|get-port|supports-color|pretty-bytes|strip-ansi|ansi-regex)"
74
- ],
75
- "snapshotFormat": {
76
- "escapeString": true,
77
- "printBasicPrototype": true
78
- }
79
- },
80
52
  "dependencies": {
81
- "@esbuild-plugins/node-globals-polyfill": "^0.2.3",
82
- "@esbuild-plugins/node-modules-polyfill": "^0.2.2",
83
- "blake3-wasm": "^2.1.5",
84
- "chokidar": "^3.5.3",
85
- "esbuild": "0.17.19",
86
- "nanoid": "^3.3.3",
87
- "path-to-regexp": "^6.2.0",
88
- "resolve": "^1.22.8",
89
- "resolve.exports": "^2.0.2",
90
- "selfsigned": "^2.0.1",
91
- "source-map": "0.6.1",
92
- "xxhash-wasm": "^1.0.1",
93
- "@cloudflare/kv-asset-handler": "0.3.2",
94
- "miniflare": "3.20240419.0"
53
+ "@cloudflare/unenv-preset": "2.3.2",
54
+ "blake3-wasm": "2.1.5",
55
+ "esbuild": "0.25.4",
56
+ "path-to-regexp": "6.3.0",
57
+ "unenv": "2.0.0-rc.17",
58
+ "workerd": "1.20250508.0",
59
+ "@cloudflare/kv-asset-handler": "0.4.0",
60
+ "miniflare": "0.0.0-e5ae13ade"
95
61
  },
96
62
  "devDependencies": {
97
- "@cloudflare/eslint-config-worker": "*",
98
- "@cloudflare/types": "^6.18.4",
99
- "@cloudflare/workers-types": "^4.20240419.0",
63
+ "@aws-sdk/client-s3": "^3.721.0",
64
+ "@cloudflare/types": "6.18.4",
65
+ "@cloudflare/workers-types": "^4.20250508.0",
100
66
  "@cspotcode/source-map-support": "0.8.1",
101
67
  "@iarna/toml": "^3.0.0",
102
- "@microsoft/api-extractor": "^7.28.3",
68
+ "@microsoft/api-extractor": "^7.47.0",
103
69
  "@sentry/node": "^7.86.0",
104
70
  "@sentry/types": "^7.86.0",
105
71
  "@sentry/utils": "^7.86.0",
106
- "@types/body-parser": "^1.19.2",
107
72
  "@types/command-exists": "^1.2.0",
108
- "@types/express": "^4.17.13",
109
- "@types/glob-to-regexp": "0.4.1",
73
+ "@types/glob-to-regexp": "^0.4.1",
110
74
  "@types/is-ci": "^3.0.0",
111
75
  "@types/javascript-time-ago": "^2.0.3",
112
- "@types/jest": "^29.5.5",
113
- "@types/mime": "^2.0.3",
76
+ "@types/mime": "^3.0.4",
114
77
  "@types/minimatch": "^5.1.2",
78
+ "@types/node": "^20.17.32",
79
+ "@types/node-forge": "^1.3.11",
115
80
  "@types/prompts": "^2.0.14",
116
- "@types/react": "^17.0.37",
117
81
  "@types/resolve": "^1.20.6",
118
- "@types/serve-static": "^1.13.10",
119
82
  "@types/shell-quote": "^1.7.2",
120
83
  "@types/signal-exit": "^3.0.1",
121
84
  "@types/supports-color": "^8.1.1",
122
- "@types/ws": "^8.5.3",
123
- "@types/yargs": "^17.0.10",
85
+ "@types/ws": "^8.5.7",
86
+ "@types/yargs": "^17.0.22",
87
+ "@vitest/ui": "~3.1.1",
124
88
  "@webcontainer/env": "^1.1.0",
125
- "body-parser": "^1.20.0",
126
- "chalk": "^2.4.2",
89
+ "chalk": "^5.2.0",
90
+ "chokidar": "^4.0.1",
127
91
  "cli-table3": "^0.6.3",
128
- "clipboardy": "^3.0.0",
129
92
  "cmd-shim": "^4.1.0",
130
93
  "command-exists": "^1.2.9",
131
- "concurrently": "^7.2.2",
132
- "devtools-protocol": "^0.0.955664",
133
- "dotenv": "^16.0.0",
134
- "es-module-lexer": "^1.3.0",
135
- "esbuild-jest": "0.5.0",
94
+ "concurrently": "^8.2.2",
95
+ "date-fns": "^4.1.0",
96
+ "devtools-protocol": "^0.0.1182435",
97
+ "dotenv": "^16.3.1",
136
98
  "execa": "^6.1.0",
137
- "express": "^4.18.1",
138
99
  "find-up": "^6.3.0",
139
- "get-port": "^6.1.2",
140
- "glob-to-regexp": "0.4.1",
141
- "http-terminator": "^3.2.0",
100
+ "get-port": "^7.0.0",
101
+ "glob-to-regexp": "^0.4.1",
142
102
  "https-proxy-agent": "7.0.2",
143
- "ignore": "^5.2.0",
144
- "ink": "^3.2.0",
145
- "ink-select-input": "^4.2.1",
146
- "ink-spinner": "^4.0.3",
147
- "ink-table": "^3.0.0",
148
103
  "is-ci": "^3.0.1",
104
+ "itty-time": "^1.0.6",
149
105
  "javascript-time-ago": "^2.5.4",
150
- "jest": "^29.7.0",
151
- "jest-fetch-mock": "^3.0.3",
152
- "jest-websocket-mock": "^2.5.0",
106
+ "md5-file": "5.0.0",
153
107
  "mime": "^3.0.0",
154
108
  "minimatch": "^5.1.0",
155
- "msw": "^0.49.1",
109
+ "mock-socket": "^9.3.1",
110
+ "msw": "2.4.3",
111
+ "node-forge": "^1.3.1",
156
112
  "open": "^8.4.0",
157
113
  "p-queue": "^7.2.0",
158
114
  "patch-console": "^1.0.0",
159
115
  "pretty-bytes": "^6.0.0",
160
116
  "prompts": "^2.4.2",
161
- "react": "^17.0.2",
162
- "react-error-boundary": "^3.1.4",
117
+ "resolve": "^1.22.8",
118
+ "rimraf": "^6.0.1",
119
+ "selfsigned": "^2.0.1",
163
120
  "semiver": "^1.1.0",
164
- "serve-static": "^1.15.0",
165
121
  "shell-quote": "^1.8.1",
166
- "shellac": "^0.8.0",
167
122
  "signal-exit": "^3.0.7",
168
- "strip-ansi": "^7.0.1",
123
+ "source-map": "^0.6.1",
124
+ "strip-ansi": "^7.1.0",
169
125
  "supports-color": "^9.2.2",
170
126
  "timeago.js": "^4.0.2",
171
127
  "ts-dedent": "^2.2.0",
172
128
  "ts-json-schema-generator": "^1.5.0",
173
- "undici": "5.28.3",
129
+ "typescript": "^5.7.2",
130
+ "undici": "^5.28.5",
174
131
  "update-check": "^1.5.4",
175
- "ws": "^8.5.0",
132
+ "vitest": "~3.1.1",
133
+ "vitest-websocket-mock": "^0.4.0",
134
+ "ws": "8.18.0",
176
135
  "xdg-app-paths": "^8.3.0",
136
+ "xxhash-wasm": "^1.0.1",
177
137
  "yargs": "^17.7.2",
178
- "yoga-layout": "file:../../vendor/yoga-layout-2.0.0-beta.1.tgz",
179
138
  "@cloudflare/cli": "1.1.1",
180
- "@cloudflare/pages-shared": "^0.11.33",
181
- "@cloudflare/workers-tsconfig": "0.0.0"
182
- },
183
- "optionalDependencies": {
184
- "fsevents": "~2.3.2"
139
+ "@cloudflare/eslint-config-worker": "1.1.0",
140
+ "@cloudflare/workers-shared": "0.0.0-e5ae13ade",
141
+ "@cloudflare/workers-tsconfig": "0.0.0",
142
+ "@cloudflare/pages-shared": "^0.13.40"
185
143
  },
186
144
  "peerDependencies": {
187
- "@cloudflare/workers-types": "^4.20240419.0"
145
+ "@cloudflare/workers-types": "^4.20250508.0"
188
146
  },
189
147
  "peerDependenciesMeta": {
190
148
  "@cloudflare/workers-types": {
191
149
  "optional": true
192
150
  }
193
151
  },
152
+ "optionalDependencies": {
153
+ "fsevents": "~2.3.2"
154
+ },
194
155
  "engines": {
195
- "node": ">=16.17.0"
156
+ "node": ">=18.0.0"
157
+ },
158
+ "volta": {
159
+ "extends": "../../package.json"
196
160
  },
197
161
  "workers-sdk": {
198
162
  "prerelease": true
@@ -201,17 +165,17 @@
201
165
  "assert-git-version": "node -r esbuild-register scripts/assert-git-version.ts",
202
166
  "build": "pnpm run clean && pnpm run bundle && pnpm run emit-types && pnpm run generate-json-schema",
203
167
  "bundle": "node -r esbuild-register scripts/bundle.ts",
204
- "check:lint": "eslint .",
205
- "check:type": "tsc",
168
+ "check:lint": "eslint . --max-warnings=0",
169
+ "check:type": "tsc -p ./tsconfig.json && tsc -p ./templates/tsconfig.json",
206
170
  "clean": "rimraf wrangler-dist miniflare-dist emitted-types",
207
171
  "dev": "pnpm run clean && concurrently -c black,blue --kill-others-on-fail false \"pnpm run bundle --watch\" \"pnpm run check:type --watch --preserveWatchOutput\"",
208
172
  "emit-types": "tsc -p tsconfig.emit.json && node -r esbuild-register scripts/emit-types.ts",
209
- "generate-json-schema": "pnpm exec ts-json-schema-generator --no-type-check --path src/config/config.ts --type RawConfig --out config-schema.json",
173
+ "generate-json-schema": "node -r esbuild-register scripts/generate-json-schema.ts",
210
174
  "start": "pnpm run bundle && cross-env NODE_OPTIONS=--enable-source-maps ./bin/wrangler.js",
211
- "test": "pnpm run assert-git-version && jest",
212
- "test:ci": "pnpm run test",
175
+ "test": "dotenv -- pnpm run assert-git-version && dotenv -- vitest",
176
+ "test:ci": "pnpm run test run",
213
177
  "test:debug": "pnpm run test --silent=false --verbose=true",
214
- "test:e2e": "vitest -c ./e2e/vitest.config.ts",
178
+ "test:e2e": "dotenv -- vitest run -c ./e2e/vitest.config.mts",
215
179
  "test:watch": "pnpm run test --testTimeout=50000 --watch",
216
180
  "type:tests": "tsc -p ./src/__tests__/tsconfig.json && tsc -p ./e2e/tsconfig.json"
217
181
  }
@@ -9,7 +9,7 @@ function checkURL(request, init) {
9
9
  ? new Request(request, init)
10
10
  : request
11
11
  ).url
12
- );
12
+ );
13
13
  if (url.port && url.port !== "443" && url.protocol === "https:") {
14
14
  if (!urls.has(url.toString())) {
15
15
  urls.add(url.toString());
@@ -3,10 +3,10 @@ declare module "__ENTRY_POINT__" {
3
3
  import { WorkerEntrypoint } from "cloudflare:workers";
4
4
 
5
5
  export type WorkerEntrypointConstructor = typeof WorkerEntrypoint;
6
- export type WithMiddleware<T> = T & { middleware?: Middleware[] };
7
6
 
8
- const worker: WithMiddleware<ExportedHandler | WorkerEntrypointConstructor>;
7
+ const worker: ExportedHandler | WorkerEntrypointConstructor;
9
8
  export default worker;
9
+ export const __INTERNAL_WRANGLER_MIDDLEWARE__: Middleware[];
10
10
  }
11
11
 
12
12
  declare module "__KV_ASSET_HANDLER__" {
@@ -165,3 +165,6 @@ dist
165
165
  .yarn/build-state.yml
166
166
  .yarn/install-state.gz
167
167
  .pnp.\*
168
+
169
+ .wrangler
170
+ .DS_Store
@@ -1,9 +1,9 @@
1
1
  import { afterAll, beforeAll, describe, expect, it } from "vitest";
2
2
  import { unstable_dev } from "wrangler";
3
- import type { UnstableDevWorker } from "wrangler";
3
+ import type { Unstable_DevWorker } from "wrangler";
4
4
 
5
5
  describe("Worker", () => {
6
- let worker: UnstableDevWorker;
6
+ let worker: Unstable_DevWorker;
7
7
 
8
8
  beforeAll(async () => {
9
9
  worker = await unstable_dev("src/index.ts", {
@@ -3,12 +3,9 @@
3
3
  // export dynamically through wrangler, or we can potentially let users directly
4
4
  // add them as a sort of "plugin" system.
5
5
 
6
- import ENTRY from "__ENTRY_POINT__";
6
+ import ENTRY, { __INTERNAL_WRANGLER_MIDDLEWARE__ } from "__ENTRY_POINT__";
7
7
  import { __facade_invoke__, __facade_register__, Dispatcher } from "./common";
8
- import type {
9
- WithMiddleware,
10
- WorkerEntrypointConstructor,
11
- } from "__ENTRY_POINT__";
8
+ import type { WorkerEntrypointConstructor } from "__ENTRY_POINT__";
12
9
 
13
10
  // Preserve all the exports from the worker
14
11
  export * from "__ENTRY_POINT__";
@@ -33,15 +30,16 @@ class __Facade_ScheduledController__ implements ScheduledController {
33
30
  }
34
31
  }
35
32
 
36
- function wrapExportedHandler(
37
- worker: WithMiddleware<ExportedHandler>
38
- ): ExportedHandler {
33
+ function wrapExportedHandler(worker: ExportedHandler): ExportedHandler {
39
34
  // If we don't have any middleware defined, just return the handler as is
40
- if (worker.middleware === undefined || worker.middleware.length === 0) {
35
+ if (
36
+ __INTERNAL_WRANGLER_MIDDLEWARE__ === undefined ||
37
+ __INTERNAL_WRANGLER_MIDDLEWARE__.length === 0
38
+ ) {
41
39
  return worker;
42
40
  }
43
41
  // Otherwise, register all middleware once
44
- for (const middleware of worker.middleware) {
42
+ for (const middleware of __INTERNAL_WRANGLER_MIDDLEWARE__) {
45
43
  __facade_register__(middleware);
46
44
  }
47
45
 
@@ -75,14 +73,17 @@ function wrapExportedHandler(
75
73
  }
76
74
 
77
75
  function wrapWorkerEntrypoint(
78
- klass: WithMiddleware<WorkerEntrypointConstructor>
76
+ klass: WorkerEntrypointConstructor
79
77
  ): WorkerEntrypointConstructor {
80
78
  // If we don't have any middleware defined, just return the handler as is
81
- if (klass.middleware === undefined || klass.middleware.length === 0) {
79
+ if (
80
+ __INTERNAL_WRANGLER_MIDDLEWARE__ === undefined ||
81
+ __INTERNAL_WRANGLER_MIDDLEWARE__.length === 0
82
+ ) {
82
83
  return klass;
83
84
  }
84
85
  // Otherwise, register all middleware once
85
- for (const middleware of klass.middleware) {
86
+ for (const middleware of __INTERNAL_WRANGLER_MIDDLEWARE__) {
86
87
  __facade_register__(middleware);
87
88
  }
88
89
 
@@ -0,0 +1,3 @@
1
+ declare module "config:middleware/patch-console-prefix" {
2
+ export const prefix: string;
3
+ }
@@ -0,0 +1,21 @@
1
+ /// <reference path="middleware-patch-console-prefix.d.ts"/>
2
+
3
+ import { prefix } from "config:middleware/patch-console-prefix";
4
+ import type { Middleware } from "./common";
5
+
6
+ // @ts-expect-error globalThis.console _does_ exist
7
+ globalThis.console = new Proxy(globalThis.console, {
8
+ get(target, p, receiver) {
9
+ if (p === "log" || p === "debug" || p === "info") {
10
+ return (...args: unknown[]) =>
11
+ Reflect.get(target, p, receiver)(prefix, ...args);
12
+ }
13
+ return Reflect.get(target, p, receiver);
14
+ },
15
+ });
16
+
17
+ const passthrough: Middleware = (request, env, _ctx, middlewareCtx) => {
18
+ return middlewareCtx.next(request, env);
19
+ };
20
+
21
+ export default passthrough;
@@ -9,7 +9,21 @@ const scheduled: Middleware = async (request, env, _ctx, middlewareCtx) => {
9
9
 
10
10
  return new Response("Ran scheduled event");
11
11
  }
12
- return middlewareCtx.next(request, env);
12
+
13
+ const resp = await middlewareCtx.next(request, env);
14
+
15
+ // If you open the `/__scheduled` page in a browser, the browser will automatically make a request to `/favicon.ico`.
16
+ // For scheduled Workers _without_ a fetch handler, this will result in a 500 response that clutters the log with unhelpful error messages.
17
+ // To avoid this, inject a 404 response to favicon.ico loads on the `/__scheduled` page
18
+ if (
19
+ request.headers.get("referer")?.endsWith("/__scheduled") &&
20
+ url.pathname === "/favicon.ico" &&
21
+ resp.status === 500
22
+ ) {
23
+ return new Response(null, { status: 404 });
24
+ }
25
+
26
+ return resp;
13
27
  };
14
28
 
15
29
  export default scheduled;
@@ -0,0 +1,40 @@
1
+ export default {
2
+ async fetch(request, env) {
3
+ const targetBinding = request.headers.get("MF-Binding");
4
+
5
+ if (targetBinding) {
6
+ const originalHeaders = new Headers();
7
+ for (const [name, value] of request.headers) {
8
+ if (name.startsWith("mf-header-")) {
9
+ originalHeaders.set(name.slice("mf-header-".length), value);
10
+ } else if (name === "upgrade") {
11
+ // The `Upgrade` header needs to be special-cased to prevent:
12
+ // TypeError: Worker tried to return a WebSocket in a response to a request which did not contain the header "Upgrade: websocket"
13
+ originalHeaders.set(name, value);
14
+ }
15
+ }
16
+ let fetcher = env[targetBinding];
17
+
18
+ // Special case the Dispatch Namespace binding because it has a top-level synchronous .get() call
19
+ const dispatchNamespaceOptions = originalHeaders.get(
20
+ "MF-Dispatch-Namespace-Options"
21
+ );
22
+ if (dispatchNamespaceOptions) {
23
+ const { name, args, options } = JSON.parse(dispatchNamespaceOptions);
24
+ fetcher = (env[targetBinding] as DispatchNamespace).get(
25
+ name,
26
+ args,
27
+ options
28
+ );
29
+ }
30
+ return (fetcher as Fetcher).fetch(
31
+ request.headers.get("MF-URL")!,
32
+ new Request(request, {
33
+ redirect: "manual",
34
+ headers: originalHeaders,
35
+ })
36
+ );
37
+ }
38
+ return new Response("Provide a binding", { status: 400 });
39
+ },
40
+ } satisfies ExportedHandler<Record<string, Fetcher | DispatchNamespace>>;
@@ -0,0 +1,4 @@
1
+ {
2
+ "main": "./index.ts",
3
+ "compatibility_date": "2025-04-28",
4
+ }
@@ -20,10 +20,11 @@ export default <ExportedHandler<{ ASSETS: Fetcher }>>{
20
20
 
21
21
  for (const include of routes.include) {
22
22
  if (isRoutingRuleMatch(pathname, include)) {
23
- if (worker.fetch === undefined) {
23
+ const workerAsHandler = worker as ExportedHandler;
24
+ if (workerAsHandler.fetch === undefined) {
24
25
  throw new TypeError("Entry point missing `fetch` handler");
25
26
  }
26
- return worker.fetch(request, env, context);
27
+ return workerAsHandler.fetch(request, env, context);
27
28
  }
28
29
  }
29
30
 
@@ -41,14 +41,14 @@ type EventPluginContext<Env, P extends string, Data, PluginArgs> = {
41
41
  declare type PagesFunction<
42
42
  Env = unknown,
43
43
  P extends string = string,
44
- Data extends Record<string, unknown> = Record<string, unknown>
44
+ Data extends Record<string, unknown> = Record<string, unknown>,
45
45
  > = (context: EventContext<Env, P, Data>) => Response | Promise<Response>;
46
46
 
47
47
  declare type PagesPluginFunction<
48
48
  Env = unknown,
49
49
  P extends string = string,
50
50
  Data extends Record<string, unknown> = Record<string, unknown>,
51
- PluginArgs = unknown
51
+ PluginArgs = unknown,
52
52
  > = (
53
53
  context: EventPluginContext<Env, P, Data, PluginArgs>
54
54
  ) => Response | Promise<Response>;
@@ -29,7 +29,7 @@ type EventContext<Env, P extends string, Data> = {
29
29
  declare type PagesFunction<
30
30
  Env = unknown,
31
31
  P extends string = string,
32
- Data extends Record<string, unknown> = Record<string, unknown>
32
+ Data extends Record<string, unknown> = Record<string, unknown>,
33
33
  > = (context: EventContext<Env, P, Data>) => Response | Promise<Response>;
34
34
  /* end @cloudflare/workers-types */
35
35
 
@@ -60,7 +60,10 @@ function isDevToolsEvent<Method extends DevToolsEvents["method"]>(
60
60
  }
61
61
 
62
62
  export class InspectorProxyWorker implements DurableObject {
63
- constructor(_state: DurableObjectState, readonly env: Env) {}
63
+ constructor(
64
+ _state: DurableObjectState,
65
+ readonly env: Env
66
+ ) {}
64
67
 
65
68
  websockets: {
66
69
  proxyController?: WebSocket;
@@ -82,6 +85,9 @@ export class InspectorProxyWorker implements DurableObject {
82
85
  proxyData?: ProxyData;
83
86
  runtimeMessageBuffer: (DevToolsCommandResponses | DevToolsEvents)[] = [];
84
87
 
88
+ // Only allow a limited number of error-based reconnections, so as not to infinite loop
89
+ reconnectionsRemaining = 3;
90
+
85
91
  async fetch(req: Request) {
86
92
  if (
87
93
  req.headers.get("Authorization") === this.env.PROXY_CONTROLLER_AUTH_SECRET
@@ -314,6 +320,17 @@ export class InspectorProxyWorker implements DurableObject {
314
320
  `Failed to establish the WebSocket connection: expected server to reply with HTTP status code 101 (switching protocols), but received ${upgrade.status} instead.`
315
321
  );
316
322
 
323
+ // Sometimes the backend will fail to connect the runtime websocket with a 502 error. These are usually transient, so try and reconnect
324
+ if (upgrade.status === 502 && this.reconnectionsRemaining >= 0) {
325
+ await scheduler.wait((3 - this.reconnectionsRemaining) * 1000);
326
+ this.sendDebugLog(
327
+ "RECONNECTING RUNTIME WEBSOCKET after 502. Reconnections remaining:",
328
+ this.reconnectionsRemaining
329
+ );
330
+
331
+ return this.reconnectRuntimeWebSocket();
332
+ }
333
+
317
334
  this.websockets.runtimeDeferred.reject(error);
318
335
  this.sendProxyControllerRequest({
319
336
  type: "runtime-websocket-error",
@@ -374,6 +391,7 @@ export class InspectorProxyWorker implements DurableObject {
374
391
  }
375
392
  handleRuntimeWebSocketOpen(runtime: WebSocket) {
376
393
  this.sendDebugLog("RUNTIME WEBSOCKET OPENED");
394
+ this.reconnectionsRemaining = 3;
377
395
 
378
396
  this.sendRuntimeMessage(
379
397
  { method: "Runtime.enable", id: this.nextCounter() },
@@ -33,7 +33,10 @@ export default {
33
33
  } as ExportedHandler<Env, unknown, ProxyWorkerIncomingRequestBody>;
34
34
 
35
35
  export class ProxyWorker implements DurableObject {
36
- constructor(readonly state: DurableObjectState, readonly env: Env) {}
36
+ constructor(
37
+ readonly state: DurableObjectState,
38
+ readonly env: Env
39
+ ) {}
37
40
 
38
41
  proxyData?: ProxyData;
39
42
  requestQueue = new Map<Request, DeferredPromise<Response>>();
@@ -121,8 +124,10 @@ export class ProxyWorker implements DurableObject {
121
124
  Object.assign(userWorkerUrl, proxyData.userWorkerUrl);
122
125
 
123
126
  // set request.url in the UserWorker
124
- const innerUrl = new URL(request.url);
125
- Object.assign(innerUrl, proxyData.userWorkerInnerUrlOverrides);
127
+ const innerUrl = urlFromParts(
128
+ proxyData.userWorkerInnerUrlOverrides ?? {},
129
+ request.url
130
+ );
126
131
  headers.set("MF-Original-URL", innerUrl.href);
127
132
  headers.set("MF-Disable-Pretty-Error", "true"); // disables the UserWorker miniflare instance from rendering the pretty error -- instead the ProxyWorker miniflare instance will intercept the json error response and render the pretty error page
128
133
 
@@ -273,28 +278,7 @@ function insertLiveReloadScript(
273
278
  websocketUrl.protocol =
274
279
  websocketUrl.protocol === "http:" ? "ws:" : "wss:";
275
280
 
276
- end.append(
277
- `
278
- <script>
279
- (function() {
280
- var ws;
281
- function recover() {
282
- ws = null;
283
- setTimeout(initLiveReload, 100);
284
- }
285
- function initLiveReload() {
286
- if (ws) return;
287
- var origin = (location.protocol === "http:" ? "ws://" : "wss://") + location.host;
288
- ws = new WebSocket(origin + "/cdn-cgi/live-reload", "${LIVE_RELOAD_PROTOCOL}");
289
- ws.onclose = recover;
290
- ws.onerror = recover;
291
- ws.onmessage = location.reload.bind(location);
292
- }
293
- })();
294
- </script>
295
- `,
296
- { html: true }
297
- );
281
+ end.append(liveReloadScript, { html: true });
298
282
  }
299
283
  },
300
284
  });
@@ -302,6 +286,27 @@ function insertLiveReloadScript(
302
286
  return htmlRewriter.transform(response);
303
287
  }
304
288
 
289
+ const liveReloadScript = `
290
+ <script defer type="application/javascript">
291
+ (function() {
292
+ var ws;
293
+ function recover() {
294
+ ws = null;
295
+ setTimeout(initLiveReload, 100);
296
+ }
297
+ function initLiveReload() {
298
+ if (ws) return;
299
+ var origin = (location.protocol === "http:" ? "ws://" : "wss://") + location.host;
300
+ ws = new WebSocket(origin + "/cdn-cgi/live-reload", "${LIVE_RELOAD_PROTOCOL}");
301
+ ws.onclose = recover;
302
+ ws.onerror = recover;
303
+ ws.onmessage = location.reload.bind(location);
304
+ }
305
+ initLiveReload();
306
+ })();
307
+ </script>
308
+ `;
309
+
305
310
  /**
306
311
  * Rewrite references to URLs in request/response headers.
307
312
  *
@@ -0,0 +1,13 @@
1
+ function stripCfConnectingIPHeader(input, init) {
2
+ const request = new Request(input, init);
3
+ request.headers.delete("CF-Connecting-IP");
4
+ return request;
5
+ }
6
+
7
+ globalThis.fetch = new Proxy(globalThis.fetch, {
8
+ apply(target, thisArg, argArray) {
9
+ return Reflect.apply(target, thisArg, [
10
+ stripCfConnectingIPHeader.apply(null, argArray),
11
+ ]);
12
+ },
13
+ });