wrangler 0.0.0-ecc8b172 → 0.0.0-ecded1af1

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 (250) hide show
  1. package/README.md +17 -19
  2. package/bin/wrangler.js +24 -93
  3. package/config-schema.json +3115 -0
  4. package/package.json +197 -190
  5. package/templates/__tests__/pages-dev-util.test.ts +128 -0
  6. package/templates/__tests__/tsconfig-sanity.ts +12 -0
  7. package/templates/__tests__/tsconfig.json +8 -0
  8. package/templates/checked-fetch.js +19 -6
  9. package/templates/facade.d.ts +19 -0
  10. package/templates/gitignore +2 -3
  11. package/templates/init-tests/test-jest-new-worker.js +3 -5
  12. package/templates/init-tests/test-vitest-new-worker.js +4 -6
  13. package/templates/init-tests/test-vitest-new-worker.ts +25 -0
  14. package/templates/middleware/common.ts +9 -4
  15. package/templates/middleware/loader-modules.ts +94 -44
  16. package/templates/middleware/loader-sw.ts +48 -32
  17. package/templates/middleware/middleware-ensure-req-body-drained.ts +18 -0
  18. package/templates/middleware/middleware-miniflare3-json-error.ts +32 -0
  19. package/templates/middleware/middleware-mock-analytics-engine.d.ts +3 -0
  20. package/templates/middleware/middleware-mock-analytics-engine.ts +30 -0
  21. package/templates/middleware/middleware-scheduled.ts +15 -1
  22. package/templates/middleware/middleware-serve-static-assets.d.ts +6 -0
  23. package/templates/middleware/middleware-serve-static-assets.ts +56 -0
  24. package/templates/modules-watch-stub.js +4 -0
  25. package/templates/new-worker-scheduled.ts +1 -1
  26. package/templates/new-worker.ts +4 -1
  27. package/templates/pages-dev-pipeline.ts +10 -13
  28. package/templates/pages-dev-util.ts +55 -0
  29. package/templates/pages-shim.ts +1 -1
  30. package/templates/pages-template-plugin.ts +37 -14
  31. package/templates/pages-template-worker.ts +33 -7
  32. package/templates/startDevWorker/InspectorProxyWorker.ts +664 -0
  33. package/templates/startDevWorker/ProxyWorker.ts +336 -0
  34. package/templates/tsconfig-sanity.ts +11 -0
  35. package/templates/tsconfig.init.json +22 -0
  36. package/templates/tsconfig.json +5 -102
  37. package/wrangler-dist/InspectorProxyWorker.js +464 -0
  38. package/wrangler-dist/InspectorProxyWorker.js.map +6 -0
  39. package/wrangler-dist/ProxyWorker.js +241 -0
  40. package/wrangler-dist/ProxyWorker.js.map +6 -0
  41. package/wrangler-dist/cli.d.ts +26095 -471
  42. package/wrangler-dist/cli.js +178062 -122008
  43. package/wrangler-dist/wasm-sync.wasm +0 -0
  44. package/Cloudflare_CA.pem +0 -18
  45. package/import_meta_url.js +0 -3
  46. package/miniflare-config-stubs/.env.empty +0 -0
  47. package/miniflare-config-stubs/package.empty.json +0 -1
  48. package/miniflare-config-stubs/wrangler.empty.toml +0 -0
  49. package/miniflare-dist/index.mjs +0 -6310
  50. package/src/__tests__/api-dev.test.ts +0 -209
  51. package/src/__tests__/api-devregistry.test.js +0 -56
  52. package/src/__tests__/api.test.ts +0 -80
  53. package/src/__tests__/config-cache-without-cache-dir.test.ts +0 -38
  54. package/src/__tests__/config-cache.test.ts +0 -42
  55. package/src/__tests__/configuration.test.ts +0 -3864
  56. package/src/__tests__/d1.test.ts +0 -70
  57. package/src/__tests__/delete.test.ts +0 -116
  58. package/src/__tests__/dev.test.tsx +0 -1357
  59. package/src/__tests__/dialogs.test.tsx +0 -40
  60. package/src/__tests__/generate.test.ts +0 -235
  61. package/src/__tests__/get-host-from-url.test.ts +0 -16
  62. package/src/__tests__/guess-worker-format.test.ts +0 -85
  63. package/src/__tests__/helpers/clipboardy-mock.js +0 -4
  64. package/src/__tests__/helpers/cmd-shim.d.ts +0 -11
  65. package/src/__tests__/helpers/mock-account-id.ts +0 -48
  66. package/src/__tests__/helpers/mock-bin.ts +0 -36
  67. package/src/__tests__/helpers/mock-cfetch.ts +0 -333
  68. package/src/__tests__/helpers/mock-console.ts +0 -106
  69. package/src/__tests__/helpers/mock-dialogs.ts +0 -153
  70. package/src/__tests__/helpers/mock-get-zone-from-host.ts +0 -8
  71. package/src/__tests__/helpers/mock-http-server.ts +0 -46
  72. package/src/__tests__/helpers/mock-istty.ts +0 -74
  73. package/src/__tests__/helpers/mock-known-routes.ts +0 -7
  74. package/src/__tests__/helpers/mock-kv.ts +0 -33
  75. package/src/__tests__/helpers/mock-oauth-flow.ts +0 -252
  76. package/src/__tests__/helpers/mock-process.ts +0 -39
  77. package/src/__tests__/helpers/mock-stdin.ts +0 -108
  78. package/src/__tests__/helpers/mock-web-socket.ts +0 -29
  79. package/src/__tests__/helpers/msw/handlers/namespaces.ts +0 -104
  80. package/src/__tests__/helpers/msw/handlers/oauth.ts +0 -36
  81. package/src/__tests__/helpers/msw/handlers/r2.ts +0 -80
  82. package/src/__tests__/helpers/msw/handlers/script.ts +0 -75
  83. package/src/__tests__/helpers/msw/handlers/user.ts +0 -63
  84. package/src/__tests__/helpers/msw/index.ts +0 -15
  85. package/src/__tests__/helpers/run-in-tmp.ts +0 -38
  86. package/src/__tests__/helpers/run-wrangler.ts +0 -16
  87. package/src/__tests__/helpers/worker-scripts/child-wrangler.toml +0 -1
  88. package/src/__tests__/helpers/worker-scripts/hello-world-worker.js +0 -5
  89. package/src/__tests__/helpers/worker-scripts/hello-world-wrangler.toml +0 -1
  90. package/src/__tests__/helpers/worker-scripts/parent-worker.js +0 -8
  91. package/src/__tests__/helpers/worker-scripts/parent-wrangler.toml +0 -5
  92. package/src/__tests__/helpers/write-worker-source.ts +0 -31
  93. package/src/__tests__/helpers/write-wrangler-toml.ts +0 -17
  94. package/src/__tests__/https-options.test.ts +0 -163
  95. package/src/__tests__/index.test.ts +0 -251
  96. package/src/__tests__/init.test.ts +0 -2816
  97. package/src/__tests__/jest.setup.ts +0 -191
  98. package/src/__tests__/kv.test.ts +0 -1675
  99. package/src/__tests__/logger.test.ts +0 -141
  100. package/src/__tests__/metrics.test.ts +0 -450
  101. package/src/__tests__/middleware.scheduled.test.ts +0 -135
  102. package/src/__tests__/middleware.test.ts +0 -726
  103. package/src/__tests__/package-manager.test.ts +0 -353
  104. package/src/__tests__/pages.test.ts +0 -2111
  105. package/src/__tests__/parse.test.ts +0 -330
  106. package/src/__tests__/paths.test.ts +0 -39
  107. package/src/__tests__/publish.test.ts +0 -7119
  108. package/src/__tests__/pubsub.test.ts +0 -367
  109. package/src/__tests__/r2.test.ts +0 -371
  110. package/src/__tests__/route.test.ts +0 -45
  111. package/src/__tests__/secret.test.ts +0 -644
  112. package/src/__tests__/tail.test.ts +0 -800
  113. package/src/__tests__/test-old-node-version.js +0 -31
  114. package/src/__tests__/user.test.ts +0 -155
  115. package/src/__tests__/utils-collectKeyValues.test.ts +0 -47
  116. package/src/__tests__/validate-dev-props.test.ts +0 -56
  117. package/src/__tests__/version.test.ts +0 -35
  118. package/src/__tests__/whoami.test.tsx +0 -278
  119. package/src/__tests__/worker-namespace.test.ts +0 -321
  120. package/src/abort.d.ts +0 -3
  121. package/src/api/dev.ts +0 -208
  122. package/src/api/index.ts +0 -2
  123. package/src/bundle-reporter.tsx +0 -29
  124. package/src/bundle.ts +0 -756
  125. package/src/cfetch/index.ts +0 -160
  126. package/src/cfetch/internal.ts +0 -263
  127. package/src/cli.ts +0 -29
  128. package/src/config/README.md +0 -107
  129. package/src/config/config.ts +0 -275
  130. package/src/config/diagnostics.ts +0 -80
  131. package/src/config/environment.ts +0 -505
  132. package/src/config/index.ts +0 -293
  133. package/src/config/validation-helpers.ts +0 -597
  134. package/src/config/validation.ts +0 -1930
  135. package/src/config-cache.ts +0 -85
  136. package/src/create-worker-preview.ts +0 -275
  137. package/src/create-worker-upload-form.ts +0 -301
  138. package/src/d1/backups.tsx +0 -212
  139. package/src/d1/create.tsx +0 -54
  140. package/src/d1/delete.tsx +0 -56
  141. package/src/d1/execute.tsx +0 -294
  142. package/src/d1/formatTimeAgo.ts +0 -14
  143. package/src/d1/index.ts +0 -75
  144. package/src/d1/list.tsx +0 -48
  145. package/src/d1/options.ts +0 -12
  146. package/src/d1/types.tsx +0 -14
  147. package/src/d1/utils.ts +0 -39
  148. package/src/delete.ts +0 -86
  149. package/src/deprecated/index.ts +0 -453
  150. package/src/dev/dev-vars.ts +0 -36
  151. package/src/dev/dev.tsx +0 -575
  152. package/src/dev/get-local-persistence-path.tsx +0 -31
  153. package/src/dev/local.tsx +0 -686
  154. package/src/dev/remote.tsx +0 -601
  155. package/src/dev/start-server.ts +0 -506
  156. package/src/dev/use-esbuild.ts +0 -197
  157. package/src/dev/validate-dev-props.ts +0 -31
  158. package/src/dev-registry.tsx +0 -201
  159. package/src/dev.tsx +0 -865
  160. package/src/dialogs.tsx +0 -163
  161. package/src/dispatch-namespace.ts +0 -206
  162. package/src/durable.ts +0 -102
  163. package/src/entry.ts +0 -329
  164. package/src/environment-variables.ts +0 -50
  165. package/src/errors.ts +0 -11
  166. package/src/git-client.ts +0 -124
  167. package/src/global-wrangler-config-path.ts +0 -26
  168. package/src/https-options.ts +0 -127
  169. package/src/index.tsx +0 -582
  170. package/src/init.ts +0 -970
  171. package/src/inspect.ts +0 -747
  172. package/src/intl-polyfill.d.ts +0 -139
  173. package/src/is-ci.ts +0 -14
  174. package/src/is-interactive.ts +0 -12
  175. package/src/kv/helpers.ts +0 -430
  176. package/src/kv/index.ts +0 -645
  177. package/src/logger.ts +0 -80
  178. package/src/metrics/index.ts +0 -5
  179. package/src/metrics/metrics-config.ts +0 -239
  180. package/src/metrics/metrics-dispatcher.ts +0 -96
  181. package/src/metrics/metrics-usage-headers.ts +0 -24
  182. package/src/metrics/send-event.ts +0 -94
  183. package/src/miniflare-cli/README.md +0 -30
  184. package/src/miniflare-cli/assets.ts +0 -215
  185. package/src/miniflare-cli/index.ts +0 -208
  186. package/src/miniflare-cli/request-context.ts +0 -40
  187. package/src/module-collection.ts +0 -267
  188. package/src/open-in-browser.ts +0 -17
  189. package/src/package-manager.ts +0 -219
  190. package/src/pages/build.tsx +0 -238
  191. package/src/pages/constants.ts +0 -16
  192. package/src/pages/deployments.tsx +0 -106
  193. package/src/pages/dev.tsx +0 -689
  194. package/src/pages/errors.ts +0 -66
  195. package/src/pages/functions/buildPlugin.ts +0 -110
  196. package/src/pages/functions/buildWorker.ts +0 -156
  197. package/src/pages/functions/filepath-routing.test.ts +0 -234
  198. package/src/pages/functions/filepath-routing.ts +0 -189
  199. package/src/pages/functions/identifiers.ts +0 -78
  200. package/src/pages/functions/routes-consolidation.test.ts +0 -250
  201. package/src/pages/functions/routes-consolidation.ts +0 -73
  202. package/src/pages/functions/routes-transformation.test.ts +0 -282
  203. package/src/pages/functions/routes-transformation.ts +0 -115
  204. package/src/pages/functions/routes-validation.test.ts +0 -403
  205. package/src/pages/functions/routes-validation.ts +0 -202
  206. package/src/pages/functions/routes.ts +0 -151
  207. package/src/pages/functions.tsx +0 -82
  208. package/src/pages/hash.tsx +0 -13
  209. package/src/pages/index.tsx +0 -94
  210. package/src/pages/projects.tsx +0 -169
  211. package/src/pages/publish.tsx +0 -436
  212. package/src/pages/types.ts +0 -49
  213. package/src/pages/upload.tsx +0 -398
  214. package/src/pages/utils.ts +0 -12
  215. package/src/parse.ts +0 -286
  216. package/src/paths.ts +0 -71
  217. package/src/proxy.ts +0 -657
  218. package/src/publish/index.ts +0 -267
  219. package/src/publish/publish.ts +0 -913
  220. package/src/pubsub/index.ts +0 -286
  221. package/src/pubsub/pubsub-commands.tsx +0 -623
  222. package/src/r2/helpers.ts +0 -131
  223. package/src/r2/index.ts +0 -277
  224. package/src/routes.ts +0 -140
  225. package/src/secret/index.ts +0 -403
  226. package/src/selfsigned.d.ts +0 -29
  227. package/src/sites.tsx +0 -372
  228. package/src/tail/createTail.ts +0 -315
  229. package/src/tail/filters.ts +0 -277
  230. package/src/tail/index.ts +0 -217
  231. package/src/tail/printing.ts +0 -107
  232. package/src/update-check.ts +0 -19
  233. package/src/user/choose-account.tsx +0 -69
  234. package/src/user/env-vars.ts +0 -46
  235. package/src/user/generate-auth-url.ts +0 -33
  236. package/src/user/generate-random-state.ts +0 -16
  237. package/src/user/index.ts +0 -3
  238. package/src/user/user.tsx +0 -1192
  239. package/src/utils/collectKeyValues.ts +0 -14
  240. package/src/whoami.tsx +0 -169
  241. package/src/worker.ts +0 -234
  242. package/src/yargs-types.ts +0 -7
  243. package/src/zones.ts +0 -165
  244. package/templates/d1-beta-facade.js +0 -174
  245. package/templates/experimental-local-cache-stubs.js +0 -27
  246. package/templates/first-party-worker-module-facade.ts +0 -18
  247. package/templates/format-dev-errors.ts +0 -32
  248. package/templates/serve-static-assets.ts +0 -63
  249. package/templates/service-bindings-module-facade.js +0 -54
  250. package/templates/service-bindings-sw-facade.js +0 -42
package/src/proxy.ts DELETED
@@ -1,657 +0,0 @@
1
- import { createServer as createHttpServer } from "node:http";
2
- import { connect } from "node:http2";
3
- import { createServer as createHttpsServer } from "node:https";
4
- import https from "node:https";
5
- import { networkInterfaces } from "node:os";
6
- import { createHttpTerminator } from "http-terminator";
7
- import { useEffect, useRef, useState } from "react";
8
- import serveStatic from "serve-static";
9
- import { getHttpsOptions } from "./https-options";
10
- import { logger } from "./logger";
11
- import type { CfPreviewToken } from "./create-worker-preview";
12
- import type { HttpTerminator } from "http-terminator";
13
- import type {
14
- IncomingHttpHeaders,
15
- RequestListener,
16
- IncomingMessage,
17
- ServerResponse,
18
- Server as HttpServer,
19
- } from "node:http";
20
- import type { ClientHttp2Session, ServerHttp2Stream } from "node:http2";
21
- import type { Server as HttpsServer } from "node:https";
22
- import type { Duplex, Writable } from "node:stream";
23
-
24
- /**
25
- * `usePreviewServer` is a React hook that creates a local development
26
- * server that can be used to develop a Worker.
27
- *
28
- * When we run `wrangler dev`, we start by uploading the compiled worker
29
- * to the preview service, which responds with a preview token.
30
- * (see `useWorker`/`createWorker` for details.)
31
- * We can then use that token to connect to the preview server for a
32
- * great local development experience. Further, as we change the worker,
33
- * we can update the preview token transparently without having to restart
34
- * the development server.
35
- */
36
-
37
- /** Rewrite request headers to add the preview token. */
38
- function addCfPreviewTokenHeader(
39
- headers: IncomingHttpHeaders,
40
- previewTokenValue: string
41
- ) {
42
- headers["cf-workers-preview-token"] = previewTokenValue;
43
- }
44
-
45
- /**
46
- * Rewrite references in request headers
47
- * from the preview host to the local host.
48
- */
49
- function rewriteRemoteHostToLocalHostInHeaders(
50
- headers: IncomingHttpHeaders,
51
- remoteHost: string,
52
- localPort: number,
53
- localProtocol: "https" | "http"
54
- ) {
55
- for (const [name, value] of Object.entries(headers)) {
56
- // Rewrite the remote host to the local host.
57
- if (typeof value === "string" && value.includes(remoteHost)) {
58
- headers[name] = value
59
- .replaceAll(
60
- `https://${remoteHost}`,
61
- `${localProtocol}://localhost:${localPort}`
62
- )
63
- .replaceAll(remoteHost, `localhost:${localPort}`);
64
- }
65
- }
66
- }
67
-
68
- function writeHead(
69
- socket: Writable,
70
- res: Pick<
71
- IncomingMessage,
72
- "httpVersion" | "statusCode" | "statusMessage" | "headers"
73
- >
74
- ) {
75
- socket.write(
76
- `HTTP/${res.httpVersion} ${res.statusCode} ${res.statusMessage}\r\n`
77
- );
78
- for (const [key, values] of Object.entries(res.headers)) {
79
- if (Array.isArray(values)) {
80
- for (const value of values) socket.write(`${key}: ${value}\r\n`);
81
- } else {
82
- socket.write(`${key}: ${values}\r\n`);
83
- }
84
- }
85
- socket.write("\r\n");
86
- }
87
-
88
- type PreviewProxy = {
89
- server: HttpServer | HttpsServer;
90
- terminator: HttpTerminator;
91
- };
92
-
93
- export async function startPreviewServer({
94
- previewToken,
95
- assetDirectory,
96
- localProtocol,
97
- localPort: port,
98
- ip,
99
- onReady,
100
- }: {
101
- previewToken: CfPreviewToken;
102
- assetDirectory: string | undefined;
103
- localProtocol: "https" | "http";
104
- localPort: number;
105
- ip: string;
106
- onReady: ((readyIp: string, readyPort: number) => void) | undefined;
107
- }) {
108
- try {
109
- const abortController = new AbortController();
110
-
111
- const server = await createProxyServer(localProtocol);
112
- const proxy = {
113
- server,
114
- terminator: createHttpTerminator({
115
- server,
116
- gracefulTerminationTimeout: 0,
117
- }),
118
- };
119
-
120
- // We have a token. Let's proxy requests to the preview end point.
121
- const streamBufferRef = { current: [] };
122
- const requestResponseBufferRef = { current: [] };
123
- const cleanupListeners = configureProxyServer({
124
- proxy,
125
- previewToken,
126
- streamBufferRef,
127
- requestResponseBufferRef,
128
- retryServerSetup: () => {}, // no-op outside of React
129
- assetDirectory,
130
- localProtocol,
131
- port,
132
- });
133
-
134
- await waitForPortToBeAvailable(port, {
135
- retryPeriod: 200,
136
- timeout: 2000,
137
- abortSignal: abortController.signal,
138
- });
139
-
140
- proxy.server.on("listening", () => {
141
- const address = proxy.server.address();
142
- const usedPort =
143
- address && typeof address === "object" ? address.port : port;
144
- logger.log(`⬣ Listening at ${localProtocol}://${ip}:${usedPort}`);
145
- const accessibleHosts = ip !== "0.0.0.0" ? [ip] : getAccessibleHosts();
146
- for (const accessibleHost of accessibleHosts) {
147
- logger.log(`- ${localProtocol}://${accessibleHost}:${usedPort}`);
148
- }
149
- onReady?.(ip, usedPort);
150
- });
151
-
152
- proxy.server.listen(port, ip);
153
- return {
154
- stop: () => {
155
- abortController.abort();
156
- cleanupListeners?.forEach((cleanup) => cleanup());
157
- },
158
- };
159
- } catch (err) {
160
- if ((err as { code: string }).code !== "ABORT_ERR") {
161
- logger.error(`Failed to start server: ${err}`);
162
- }
163
- logger.error("Failed to create proxy server:", err);
164
- }
165
- }
166
-
167
- export function usePreviewServer({
168
- previewToken,
169
- assetDirectory,
170
- localProtocol,
171
- localPort: port,
172
- ip,
173
- }: {
174
- previewToken: CfPreviewToken | undefined;
175
- assetDirectory: string | undefined;
176
- localProtocol: "https" | "http";
177
- localPort: number;
178
- ip: string;
179
- }) {
180
- /** Creates an HTTP/1 proxy that sends requests over HTTP/2. */
181
- const [proxy, setProxy] = useState<PreviewProxy>();
182
-
183
- /**
184
- * Create the instance of the local proxy server that will pass on
185
- * requests to the preview worker.
186
- */
187
- useEffect(() => {
188
- if (proxy === undefined) {
189
- createProxyServer(localProtocol)
190
- .then((server) => {
191
- setProxy({
192
- server,
193
- terminator: createHttpTerminator({
194
- server,
195
- gracefulTerminationTimeout: 0,
196
- }),
197
- });
198
- })
199
- .catch(async (err) => {
200
- logger.error("Failed to create proxy server:", err);
201
- });
202
- }
203
- }, [proxy, localProtocol]);
204
-
205
- /**
206
- * When we're not connected / getting a fresh token on changes,
207
- * we'd like to buffer streams/requests until we're connected.
208
- * Once connected, we can flush the buffered streams/requests.
209
- * streamBufferRef is used to buffer http/2 streams, while
210
- * requestResponseBufferRef is used to buffer http/1 requests.
211
- */
212
- const streamBufferRef = useRef<
213
- { stream: ServerHttp2Stream; headers: IncomingHttpHeaders }[]
214
- >([]);
215
- const requestResponseBufferRef = useRef<
216
- { request: IncomingMessage; response: ServerResponse }[]
217
- >([]);
218
-
219
- /**
220
- * The session doesn't last forever, and will eventually drop
221
- * (usually within 5-15 minutes). When that happens, we simply
222
- * restart the effect, effectively restarting the server. We use
223
- * a state sigil as an effect dependency to do so.
224
- */
225
- const [retryServerSetupSigil, setRetryServerSetupSigil] = useState<number>(0);
226
- function retryServerSetup() {
227
- setRetryServerSetupSigil((x) => x + 1);
228
- }
229
-
230
- useEffect(() => {
231
- const cleanupListeners = configureProxyServer({
232
- proxy,
233
- previewToken,
234
- streamBufferRef,
235
- requestResponseBufferRef,
236
- retryServerSetup,
237
- assetDirectory,
238
- localProtocol,
239
- port,
240
- });
241
- return () => {
242
- cleanupListeners?.forEach((cleanup) => cleanup());
243
- };
244
- }, [
245
- previewToken,
246
- assetDirectory,
247
- port,
248
- localProtocol,
249
- proxy,
250
- // We use a state value as a sigil to trigger reconnecting the server.
251
- // It's not used inside the effect, so react-hooks/exhaustive-deps
252
- // doesn't complain if it's not included in the dependency array.
253
- // But its presence is critical, so Do NOT remove it from the dependency list.
254
- retryServerSetupSigil,
255
- ]);
256
-
257
- // Start/stop the server whenever the
258
- // containing component is mounted/unmounted.
259
- useEffect(() => {
260
- const abortController = new AbortController();
261
- if (proxy === undefined) {
262
- return;
263
- }
264
-
265
- waitForPortToBeAvailable(port, {
266
- retryPeriod: 200,
267
- timeout: 2000,
268
- abortSignal: abortController.signal,
269
- })
270
- .then(() => {
271
- proxy.server.on("listening", () => {
272
- const address = proxy.server.address();
273
- const usedPort =
274
- address && typeof address === "object" ? address.port : port;
275
- logger.log(`⬣ Listening at ${localProtocol}://${ip}:${usedPort}`);
276
- const accessibleHosts =
277
- ip !== "0.0.0.0" ? [ip] : getAccessibleHosts();
278
- for (const accessibleHost of accessibleHosts) {
279
- logger.log(`- ${localProtocol}://${accessibleHost}:${usedPort}`);
280
- }
281
- });
282
- proxy.server.listen(port, ip);
283
- })
284
- .catch((err) => {
285
- if ((err as { code: string }).code !== "ABORT_ERR") {
286
- logger.error(`Failed to start server: ${err}`);
287
- }
288
- });
289
-
290
- return () => {
291
- abortController.abort();
292
- // Running `proxy.server.close()` does not close open connections, preventing the process from exiting.
293
- // So we use this `terminator` to close all the connections and force the server to shutdown.
294
- proxy.terminator
295
- .terminate()
296
- .catch(() => logger.error("Failed to terminate the proxy server."));
297
- };
298
- }, [port, ip, proxy, localProtocol]);
299
- }
300
-
301
- function configureProxyServer({
302
- proxy,
303
- previewToken,
304
- streamBufferRef,
305
- requestResponseBufferRef,
306
- retryServerSetup,
307
- port,
308
- localProtocol,
309
- assetDirectory,
310
- }: {
311
- proxy: PreviewProxy | undefined;
312
- previewToken: CfPreviewToken | undefined;
313
- // normally the type of streamBufferRef should be
314
- // React.MutableRefObject<T>, but we don't want to require react
315
- streamBufferRef: {
316
- current: { stream: ServerHttp2Stream; headers: IncomingHttpHeaders }[];
317
- };
318
- // normally the type of requestResponseBufferRef should be
319
- // React.MutableRefObject<T>, but we don't want to require react
320
- requestResponseBufferRef: {
321
- current: { request: IncomingMessage; response: ServerResponse }[];
322
- };
323
- retryServerSetup: () => void;
324
- port: number;
325
- localProtocol: "https" | "http";
326
- assetDirectory: string | undefined;
327
- }) {
328
- if (proxy === undefined) {
329
- return;
330
- }
331
-
332
- // If we don't have a token, that means either we're just starting up,
333
- // or we're refreshing the token.
334
- if (!previewToken) {
335
- const cleanupListeners: (() => void)[] = [];
336
- const bufferStream = (
337
- stream: ServerHttp2Stream,
338
- headers: IncomingHttpHeaders
339
- ) => {
340
- // store the stream in a buffer so we can replay it later
341
- streamBufferRef.current.push({ stream, headers });
342
- };
343
- proxy.server.on("stream", bufferStream);
344
- cleanupListeners.push(() => proxy.server.off("stream", bufferStream));
345
-
346
- const bufferRequestResponse = (
347
- request: IncomingMessage,
348
- response: ServerResponse
349
- ) => {
350
- // store the request and response in a buffer so we can replay it later
351
- requestResponseBufferRef.current.push({ request, response });
352
- };
353
-
354
- proxy.server.on("request", bufferRequestResponse);
355
- cleanupListeners.push(() =>
356
- proxy.server.off("request", bufferRequestResponse)
357
- );
358
- return cleanupListeners;
359
- }
360
-
361
- // We have a token. Let's proxy requests to the preview end point.
362
- const cleanupListeners: (() => void)[] = [];
363
-
364
- // create a ClientHttp2Session
365
- logger.debug("PREVIEW URL:", `https://${previewToken.host}`);
366
- const remote = connect(`https://${previewToken.host}`);
367
- cleanupListeners.push(() => remote.destroy());
368
-
369
- // As mentioned above, the session may die at any point,
370
- // so we need to restart the effect.
371
- remote.on("close", retryServerSetup);
372
- cleanupListeners.push(() => remote.off("close", retryServerSetup));
373
-
374
- /** HTTP/2 -> HTTP/2 */
375
- const handleStream = createStreamHandler(
376
- previewToken,
377
- remote,
378
- port,
379
- localProtocol
380
- );
381
- proxy.server.on("stream", handleStream);
382
- cleanupListeners.push(() => proxy.server.off("stream", handleStream));
383
-
384
- // flush and replay buffered streams
385
- streamBufferRef.current.forEach(
386
- (buffer: { stream: ServerHttp2Stream; headers: IncomingHttpHeaders }) =>
387
- handleStream(buffer.stream, buffer.headers)
388
- );
389
- streamBufferRef.current = [];
390
-
391
- /** HTTP/1 -> HTTP/2 */
392
- const handleRequest: RequestListener = (
393
- message: IncomingMessage,
394
- response: ServerResponse
395
- ) => {
396
- const { httpVersionMajor, headers, method, url } = message;
397
- if (httpVersionMajor >= 2) {
398
- return; // Already handled by the "stream" event.
399
- }
400
- addCfPreviewTokenHeader(headers, previewToken.value);
401
- headers[":method"] = method;
402
- headers[":path"] = url;
403
- headers[":authority"] = previewToken.host;
404
- headers[":scheme"] = "https";
405
- for (const name of Object.keys(headers)) {
406
- if (HTTP1_HEADERS.has(name.toLowerCase())) {
407
- delete headers[name];
408
- }
409
- }
410
- const request = message.pipe(remote.request(headers));
411
- logger.debug(
412
- "WORKER REQUEST",
413
- new Date().toLocaleTimeString(),
414
- method,
415
- url
416
- );
417
- logger.debug("HEADERS", JSON.stringify(headers, null, 2));
418
- logger.debug("PREVIEW TOKEN", previewToken);
419
-
420
- request.on("response", (responseHeaders) => {
421
- const status = responseHeaders[":status"] ?? 500;
422
-
423
- // log all requests to terminal
424
- logger.log(new Date().toLocaleTimeString(), method, url, status);
425
-
426
- rewriteRemoteHostToLocalHostInHeaders(
427
- responseHeaders,
428
- previewToken.host,
429
- port,
430
- localProtocol
431
- );
432
- for (const name of Object.keys(responseHeaders)) {
433
- if (name.startsWith(":")) {
434
- delete responseHeaders[name];
435
- }
436
- }
437
- response.writeHead(status, responseHeaders);
438
- request.pipe(response, { end: true });
439
- });
440
- };
441
-
442
- // If an asset path is defined, check the file system
443
- // for a file first and serve if it exists.
444
- const actualHandleRequest = assetDirectory
445
- ? createHandleAssetsRequest(assetDirectory, handleRequest)
446
- : handleRequest;
447
-
448
- proxy.server.on("request", actualHandleRequest);
449
- cleanupListeners.push(() => proxy.server.off("request", actualHandleRequest));
450
-
451
- // flush and replay buffered requests
452
- requestResponseBufferRef.current.forEach(
453
- ({
454
- request,
455
- response,
456
- }: {
457
- request: IncomingMessage;
458
- response: ServerResponse;
459
- }) => actualHandleRequest(request, response)
460
- );
461
- requestResponseBufferRef.current = [];
462
-
463
- /** HTTP/1 -> WebSocket (over HTTP/1) */
464
- const handleUpgrade = (
465
- originalMessage: IncomingMessage,
466
- originalSocket: Duplex,
467
- originalHead: Buffer
468
- ) => {
469
- const { headers, method, url } = originalMessage;
470
- addCfPreviewTokenHeader(headers, previewToken.value);
471
- headers["host"] = previewToken.host;
472
-
473
- if (originalHead?.byteLength) originalSocket.unshift(originalHead);
474
-
475
- const runtimeRequest = https.request(
476
- {
477
- hostname: previewToken.host,
478
- path: url,
479
- method,
480
- headers,
481
- },
482
- (runtimeResponse) => {
483
- if (!(runtimeResponse as { upgrade?: boolean }).upgrade) {
484
- writeHead(originalSocket, runtimeResponse);
485
- runtimeResponse.pipe(originalSocket);
486
- }
487
- }
488
- );
489
-
490
- runtimeRequest.on(
491
- "upgrade",
492
- (runtimeResponse, runtimeSocket, runtimeHead) => {
493
- if (runtimeHead?.byteLength) runtimeSocket.unshift(runtimeHead);
494
- writeHead(originalSocket, {
495
- httpVersion: "1.1",
496
- statusCode: 101,
497
- statusMessage: "Switching Protocols",
498
- headers: runtimeResponse.headers,
499
- });
500
- runtimeSocket.pipe(originalSocket).pipe(runtimeSocket);
501
- }
502
- );
503
- originalMessage.pipe(runtimeRequest);
504
- };
505
-
506
- proxy.server.on("upgrade", handleUpgrade);
507
- cleanupListeners.push(() => proxy.server.off("upgrade", handleUpgrade));
508
- return cleanupListeners;
509
- }
510
-
511
- function createHandleAssetsRequest(
512
- assetDirectory: string,
513
- handleRequest: RequestListener
514
- ) {
515
- const handleAsset = serveStatic(assetDirectory, {
516
- cacheControl: false,
517
- });
518
- return (request: IncomingMessage, response: ServerResponse) => {
519
- handleAsset(request, response, () => {
520
- handleRequest(request, response);
521
- });
522
- };
523
- }
524
-
525
- /** A Set of headers we want to remove from HTTP/1 requests. */
526
- const HTTP1_HEADERS = new Set([
527
- "host",
528
- "connection",
529
- "upgrade",
530
- "keep-alive",
531
- "proxy-connection",
532
- "transfer-encoding",
533
- "http2-settings",
534
- ]);
535
-
536
- async function createProxyServer(
537
- localProtocol: "https" | "http"
538
- ): Promise<HttpServer | HttpsServer> {
539
- const server: HttpServer | HttpsServer =
540
- localProtocol === "https"
541
- ? createHttpsServer(await getHttpsOptions())
542
- : createHttpServer();
543
-
544
- return server
545
- .on("upgrade", (req) => {
546
- // log all websocket connections
547
- logger.log(
548
- new Date().toLocaleTimeString(),
549
- req.method,
550
- req.url,
551
- 101,
552
- "(WebSocket)"
553
- );
554
- })
555
- .on("error", (err) => {
556
- // log all connection errors
557
- logger.error(new Date().toLocaleTimeString(), err);
558
- });
559
- }
560
-
561
- function createStreamHandler(
562
- previewToken: CfPreviewToken,
563
- remote: ClientHttp2Session,
564
- localPort: number,
565
- localProtocol: "https" | "http"
566
- ) {
567
- return function handleStream(
568
- stream: ServerHttp2Stream,
569
- headers: IncomingHttpHeaders
570
- ) {
571
- addCfPreviewTokenHeader(headers, previewToken.value);
572
- headers[":authority"] = previewToken.host;
573
- const request = stream.pipe(remote.request(headers));
574
- request.on("response", (responseHeaders: IncomingHttpHeaders) => {
575
- rewriteRemoteHostToLocalHostInHeaders(
576
- responseHeaders,
577
- previewToken.host,
578
- localPort,
579
- localProtocol
580
- );
581
- stream.respond(responseHeaders);
582
- request.pipe(stream, { end: true });
583
- });
584
- };
585
- }
586
-
587
- /**
588
- * A helper function that waits for a port to be available.
589
- */
590
- export async function waitForPortToBeAvailable(
591
- port: number,
592
- options: { retryPeriod: number; timeout: number; abortSignal: AbortSignal }
593
- ): Promise<void> {
594
- return new Promise((resolve, reject) => {
595
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
596
- options.abortSignal.addEventListener("abort", () => {
597
- const abortError = new Error("waitForPortToBeAvailable() aborted");
598
- (abortError as Error & { code: string }).code = "ABORT_ERR";
599
- doReject(abortError);
600
- });
601
-
602
- const timeout = setTimeout(() => {
603
- doReject(new Error(`Timed out waiting for port ${port}`));
604
- }, options.timeout);
605
-
606
- const interval = setInterval(checkPort, options.retryPeriod);
607
- checkPort();
608
-
609
- function doResolve() {
610
- clearTimeout(timeout);
611
- clearInterval(interval);
612
- resolve();
613
- }
614
-
615
- function doReject(err: unknown) {
616
- clearInterval(interval);
617
- clearTimeout(timeout);
618
- reject(err);
619
- }
620
-
621
- function checkPort() {
622
- // Testing whether a port is 'available' involves simply
623
- // trying to make a server listen on that port, and retrying
624
- // until it succeeds.
625
- const server = createHttpServer();
626
- const terminator = createHttpTerminator({
627
- server,
628
- gracefulTerminationTimeout: 0, // default 1000
629
- });
630
-
631
- server.on("error", (err) => {
632
- // @ts-expect-error non standard property on Error
633
- if (err.code !== "EADDRINUSE") {
634
- doReject(err);
635
- }
636
- });
637
- server.listen(port, () =>
638
- terminator
639
- .terminate()
640
- .then(doResolve, () =>
641
- logger.error("Failed to terminate the port checker.")
642
- )
643
- );
644
- }
645
- });
646
- }
647
-
648
- function getAccessibleHosts(): string[] {
649
- const hosts: string[] = [];
650
- Object.values(networkInterfaces()).forEach((net) => {
651
- net?.forEach(({ family, address }) => {
652
- // @ts-expect-error the `family` property is numeric as of Node.js 18.0.0
653
- if (family === "IPv4" || family === 4) hosts.push(address);
654
- });
655
- });
656
- return hosts;
657
- }