wrangler 0.0.0-e3cab74 → 0.0.0-e452a35

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 (102) hide show
  1. package/Cloudflare_CA.pem +18 -0
  2. package/README.md +27 -14
  3. package/bin/wrangler.js +49 -5
  4. package/miniflare-dist/index.mjs +4886 -0
  5. package/package.json +42 -28
  6. package/pages/functions/buildPlugin.ts +105 -0
  7. package/pages/functions/buildWorker.ts +93 -4
  8. package/pages/functions/filepath-routing.test.ts +136 -25
  9. package/pages/functions/filepath-routing.ts +14 -3
  10. package/pages/functions/routes.ts +6 -1
  11. package/pages/functions/template-plugin.ts +147 -0
  12. package/pages/functions/template-worker.ts +21 -8
  13. package/src/__tests__/config-cache.test.ts +36 -0
  14. package/src/__tests__/configuration.test.ts +1514 -367
  15. package/src/__tests__/dev.test.tsx +870 -84
  16. package/src/__tests__/guess-worker-format.test.ts +37 -18
  17. package/src/__tests__/helpers/mock-account-id.ts +22 -4
  18. package/src/__tests__/helpers/mock-cfetch.ts +8 -4
  19. package/src/__tests__/helpers/mock-console.ts +35 -10
  20. package/src/__tests__/helpers/mock-dialogs.ts +26 -1
  21. package/src/__tests__/helpers/mock-http-server.ts +46 -0
  22. package/src/__tests__/helpers/mock-istty.ts +27 -0
  23. package/src/__tests__/helpers/mock-kv.ts +9 -16
  24. package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
  25. package/src/__tests__/helpers/mock-stdin.ts +103 -0
  26. package/src/__tests__/helpers/run-in-tmp.ts +10 -0
  27. package/src/__tests__/helpers/write-worker-source.ts +31 -0
  28. package/src/__tests__/https-options.test.ts +130 -0
  29. package/src/__tests__/index.test.ts +137 -747
  30. package/src/__tests__/init.test.ts +1557 -0
  31. package/src/__tests__/jest.setup.ts +28 -8
  32. package/src/__tests__/kv.test.ts +598 -258
  33. package/src/__tests__/logger.test.ts +115 -0
  34. package/src/__tests__/pages.test.ts +657 -0
  35. package/src/__tests__/parse.test.ts +8 -0
  36. package/src/__tests__/publish.test.ts +4003 -941
  37. package/src/__tests__/r2.test.ts +69 -34
  38. package/src/__tests__/route.test.ts +45 -0
  39. package/src/__tests__/secret.test.ts +228 -73
  40. package/src/__tests__/tail.test.ts +146 -20
  41. package/src/__tests__/user.test.ts +131 -0
  42. package/src/__tests__/whoami.test.tsx +26 -7
  43. package/src/abort.d.ts +3 -0
  44. package/src/bundle.ts +74 -7
  45. package/src/cfetch/index.ts +21 -4
  46. package/src/cfetch/internal.ts +20 -14
  47. package/src/cli.ts +5 -11
  48. package/src/config/README.md +7 -7
  49. package/src/config/config.ts +69 -100
  50. package/src/config/environment.ts +119 -4
  51. package/src/config/index.ts +177 -4
  52. package/src/config/validation-helpers.ts +228 -23
  53. package/src/config/validation.ts +687 -174
  54. package/src/config-cache.ts +45 -0
  55. package/src/{api/preview.ts → create-worker-preview.ts} +74 -36
  56. package/src/{api/form_data.ts → create-worker-upload-form.ts} +55 -18
  57. package/src/dev/dev-vars.ts +36 -0
  58. package/src/dev/dev.tsx +422 -0
  59. package/src/dev/local.tsx +318 -0
  60. package/src/dev/remote.tsx +227 -0
  61. package/src/dev/use-esbuild.ts +118 -0
  62. package/src/dialogs.tsx +16 -6
  63. package/src/durable.ts +102 -0
  64. package/src/entry.ts +279 -14
  65. package/src/environment-variables.ts +3 -1
  66. package/src/errors.ts +11 -0
  67. package/src/https-options.ts +123 -0
  68. package/src/index.tsx +1268 -806
  69. package/src/inspect.ts +64 -11
  70. package/src/intl-polyfill.d.ts +139 -0
  71. package/src/{kv.tsx → kv.ts} +195 -62
  72. package/src/logger.ts +66 -0
  73. package/src/miniflare-cli/README.md +30 -0
  74. package/src/miniflare-cli/enum-keys.ts +17 -0
  75. package/src/miniflare-cli/index.ts +41 -0
  76. package/src/miniflare-cli/request-context.ts +40 -0
  77. package/src/module-collection.ts +94 -6
  78. package/src/open-in-browser.ts +9 -3
  79. package/src/package-manager.ts +9 -8
  80. package/src/pages.tsx +1057 -75
  81. package/src/parse.ts +24 -5
  82. package/src/proxy.ts +143 -54
  83. package/src/publish.ts +444 -229
  84. package/src/selfsigned.d.ts +29 -0
  85. package/src/sites.tsx +98 -44
  86. package/src/tail/index.ts +2 -2
  87. package/src/tail/printing.ts +13 -8
  88. package/src/update-check.ts +19 -0
  89. package/src/user.tsx +263 -109
  90. package/src/whoami.tsx +2 -1
  91. package/src/{api/worker.ts → worker.ts} +24 -30
  92. package/templates/gitignore +171 -0
  93. package/templates/new-worker.ts +16 -1
  94. package/templates/static-asset-facade.js +1 -1
  95. package/wrangler-dist/cli.js +55345 -69246
  96. package/src/__tests__/helpers/mock-user.ts +0 -27
  97. package/src/__tests__/logout.test.ts +0 -50
  98. package/src/__tests__/sentry.test.ts +0 -146
  99. package/src/dev.tsx +0 -961
  100. package/src/guess-worker-format.ts +0 -68
  101. package/src/reporting.ts +0 -115
  102. package/wrangler-dist/cli.js.map +0 -7
package/src/parse.ts CHANGED
@@ -62,8 +62,6 @@ export class ParseError extends Error implements Message {
62
62
  }
63
63
  }
64
64
 
65
- /* eslint-disable @typescript-eslint/no-explicit-any */
66
-
67
65
  const TOML_ERROR_NAME = "TomlError";
68
66
  const TOML_ERROR_SUFFIX = " at row ";
69
67
 
@@ -75,9 +73,11 @@ type TomlError = Error & {
75
73
  /**
76
74
  * A wrapper around `TOML.parse` that throws a `ParseError`.
77
75
  */
78
- export function parseTOML(input: string, file?: string): object {
76
+ export function parseTOML(input: string, file?: string): TOML.JsonMap | never {
79
77
  try {
80
- return TOML.parse(input);
78
+ // Normalize CRLF to LF to avoid hitting https://github.com/iarna/iarna-toml/issues/33.
79
+ const normalizedInput = input.replace(/\r\n/g, "\n");
80
+ return TOML.parse(normalizedInput);
81
81
  } catch (err) {
82
82
  const { name, message, line, col } = err as TomlError;
83
83
  if (name !== TOML_ERROR_NAME) {
@@ -98,10 +98,29 @@ export function parseTOML(input: string, file?: string): object {
98
98
 
99
99
  const JSON_ERROR_SUFFIX = " in JSON at position ";
100
100
 
101
+ /**
102
+ * A minimal type describing a package.json file.
103
+ */
104
+ export type PackageJSON = {
105
+ devDependencies?: Record<string, unknown>;
106
+ dependencies?: Record<string, unknown>;
107
+ scripts?: Record<string, unknown>;
108
+ };
109
+
110
+ /**
111
+ * A typed version of `parseJSON()`.
112
+ */
113
+ export function parsePackageJSON<T extends PackageJSON = PackageJSON>(
114
+ input: string,
115
+ file?: string
116
+ ): T {
117
+ return parseJSON<T>(input, file);
118
+ }
119
+
101
120
  /**
102
121
  * A wrapper around `JSON.parse` that throws a `ParseError`.
103
122
  */
104
- export function parseJSON(input: string, file?: string): any {
123
+ export function parseJSON<T>(input: string, file?: string): T {
105
124
  try {
106
125
  return JSON.parse(input);
107
126
  } catch (err) {
package/src/proxy.ts CHANGED
@@ -1,17 +1,23 @@
1
- import { createServer } from "node:http";
1
+ import { createServer as createHttpServer } from "node:http";
2
2
  import { connect } from "node:http2";
3
+ import { createServer as createHttpsServer } from "node:https";
3
4
  import WebSocket from "faye-websocket";
5
+ import { createHttpTerminator } from "http-terminator";
4
6
  import { useEffect, useRef, useState } from "react";
5
7
  import serveStatic from "serve-static";
6
- import type { CfPreviewToken } from "./api/preview";
8
+ import { getHttpsOptions } from "./https-options";
9
+ import { logger } from "./logger";
10
+ import type { CfPreviewToken } from "./create-worker-preview";
11
+ import type { HttpTerminator } from "http-terminator";
7
12
  import type {
8
13
  IncomingHttpHeaders,
9
14
  RequestListener,
10
15
  IncomingMessage,
11
16
  ServerResponse,
12
- Server,
17
+ Server as HttpServer,
13
18
  } from "node:http";
14
19
  import type { ClientHttp2Session, ServerHttp2Stream } from "node:http2";
20
+ import type { Server as HttpsServer } from "node:https";
15
21
  import type ws from "ws";
16
22
 
17
23
  interface IWebsocket extends ws {
@@ -47,30 +53,61 @@ function addCfPreviewTokenHeader(
47
53
  function rewriteRemoteHostToLocalHostInHeaders(
48
54
  headers: IncomingHttpHeaders,
49
55
  remoteHost: string,
50
- localPort: number
56
+ localPort: number,
57
+ localProtocol: "https" | "http"
51
58
  ) {
52
59
  for (const [name, value] of Object.entries(headers)) {
53
60
  // Rewrite the remote host to the local host.
54
61
  if (typeof value === "string" && value.includes(remoteHost)) {
55
62
  headers[name] = value
56
- .replaceAll(`https://${remoteHost}`, `http://localhost:${localPort}`)
63
+ .replaceAll(
64
+ `https://${remoteHost}`,
65
+ `${localProtocol}://localhost:${localPort}`
66
+ )
57
67
  .replaceAll(remoteHost, `localhost:${localPort}`);
58
68
  }
59
69
  }
60
70
  }
61
71
 
72
+ type PreviewProxy = {
73
+ server: HttpServer | HttpsServer;
74
+ terminator: HttpTerminator;
75
+ };
76
+
62
77
  export function usePreviewServer({
63
78
  previewToken,
64
79
  publicRoot,
65
- port,
80
+ localProtocol,
81
+ localPort: port,
82
+ ip,
66
83
  }: {
67
84
  previewToken: CfPreviewToken | undefined;
68
- publicRoot: undefined | string;
69
- port: number;
85
+ publicRoot: string | undefined;
86
+ localProtocol: "https" | "http";
87
+ localPort: number;
88
+ ip: string;
70
89
  }) {
71
90
  /** Creates an HTTP/1 proxy that sends requests over HTTP/2. */
72
- const proxyServer = useRef<Server>();
73
- const proxy = (proxyServer.current ??= createProxyServer());
91
+ const [proxy, setProxy] = useState<PreviewProxy>();
92
+
93
+ /**
94
+ * Create the instance of the local proxy server that will pass on
95
+ * requests to the preview worker.
96
+ */
97
+ useEffect(() => {
98
+ if (proxy === undefined) {
99
+ createProxyServer(localProtocol)
100
+ .then((server) => {
101
+ setProxy({
102
+ server,
103
+ terminator: createHttpTerminator({ server }),
104
+ });
105
+ })
106
+ .catch(async (err) => {
107
+ logger.error("Failed to create proxy server:", err);
108
+ });
109
+ }
110
+ }, [proxy, localProtocol]);
74
111
 
75
112
  /**
76
113
  * When we're not connected / getting a fresh token on changes,
@@ -98,6 +135,10 @@ export function usePreviewServer({
98
135
  }
99
136
 
100
137
  useEffect(() => {
138
+ if (proxy === undefined) {
139
+ return;
140
+ }
141
+
101
142
  // If we don't have a token, that means either we're just starting up,
102
143
  // or we're refreshing the token.
103
144
  if (!previewToken) {
@@ -109,8 +150,8 @@ export function usePreviewServer({
109
150
  // store the stream in a buffer so we can replay it later
110
151
  streamBufferRef.current.push({ stream, headers });
111
152
  };
112
- proxy.on("stream", bufferStream);
113
- cleanupListeners.push(() => proxy.off("stream", bufferStream));
153
+ proxy.server.on("stream", bufferStream);
154
+ cleanupListeners.push(() => proxy.server.off("stream", bufferStream));
114
155
 
115
156
  const bufferRequestResponse = (
116
157
  request: IncomingMessage,
@@ -120,8 +161,10 @@ export function usePreviewServer({
120
161
  requestResponseBufferRef.current.push({ request, response });
121
162
  };
122
163
 
123
- proxy.on("request", bufferRequestResponse);
124
- cleanupListeners.push(() => proxy.off("request", bufferRequestResponse));
164
+ proxy.server.on("request", bufferRequestResponse);
165
+ cleanupListeners.push(() =>
166
+ proxy.server.off("request", bufferRequestResponse)
167
+ );
125
168
  return () => {
126
169
  cleanupListeners.forEach((cleanup) => cleanup());
127
170
  };
@@ -142,9 +185,14 @@ export function usePreviewServer({
142
185
  cleanupListeners.push(() => remote.off("close", retryServerSetup));
143
186
 
144
187
  /** HTTP/2 -> HTTP/2 */
145
- const handleStream = createStreamHandler(previewToken, remote, port);
146
- proxy.on("stream", handleStream);
147
- cleanupListeners.push(() => proxy.off("stream", handleStream));
188
+ const handleStream = createStreamHandler(
189
+ previewToken,
190
+ remote,
191
+ port,
192
+ localProtocol
193
+ );
194
+ proxy.server.on("stream", handleStream);
195
+ cleanupListeners.push(() => proxy.server.off("stream", handleStream));
148
196
 
149
197
  // flush and replay buffered streams
150
198
  streamBufferRef.current.forEach((buffer) =>
@@ -174,10 +222,15 @@ export function usePreviewServer({
174
222
  const request = message.pipe(remote.request(headers));
175
223
  request.on("response", (responseHeaders) => {
176
224
  const status = responseHeaders[":status"] ?? 500;
225
+
226
+ // log all requests to terminal
227
+ logger.log(new Date().toLocaleTimeString(), method, url, status);
228
+
177
229
  rewriteRemoteHostToLocalHostInHeaders(
178
230
  responseHeaders,
179
231
  previewToken.host,
180
- port
232
+ port,
233
+ localProtocol
181
234
  );
182
235
  for (const name of Object.keys(responseHeaders)) {
183
236
  if (name.startsWith(":")) {
@@ -195,8 +248,10 @@ export function usePreviewServer({
195
248
  ? createHandleAssetsRequest(assetPath, handleRequest)
196
249
  : handleRequest;
197
250
 
198
- proxy.on("request", actualHandleRequest);
199
- cleanupListeners.push(() => proxy.off("request", actualHandleRequest));
251
+ proxy.server.on("request", actualHandleRequest);
252
+ cleanupListeners.push(() =>
253
+ proxy.server.off("request", actualHandleRequest)
254
+ );
200
255
 
201
256
  // flush and replay buffered requests
202
257
  requestResponseBufferRef.current.forEach(({ request, response }) =>
@@ -227,8 +282,8 @@ export function usePreviewServer({
227
282
  remoteWebsocketClient.close();
228
283
  });
229
284
  };
230
- proxy.on("upgrade", handleUpgrade);
231
- cleanupListeners.push(() => proxy.off("upgrade", handleUpgrade));
285
+ proxy.server.on("upgrade", handleUpgrade);
286
+ cleanupListeners.push(() => proxy.server.off("upgrade", handleUpgrade));
232
287
 
233
288
  return () => {
234
289
  cleanupListeners.forEach((cleanup) => cleanup());
@@ -237,6 +292,7 @@ export function usePreviewServer({
237
292
  previewToken,
238
293
  publicRoot,
239
294
  port,
295
+ localProtocol,
240
296
  proxy,
241
297
  // We use a state value as a sigil to trigger reconnecting the server.
242
298
  // It's not used inside the effect, so react-hooks/exhaustive-deps
@@ -248,19 +304,35 @@ export function usePreviewServer({
248
304
  // Start/stop the server whenever the
249
305
  // containing component is mounted/unmounted.
250
306
  useEffect(() => {
251
- waitForPortToBeAvailable(port, { retryPeriod: 200, timeout: 2000 })
307
+ const abortController = new AbortController();
308
+ if (proxy === undefined) {
309
+ return;
310
+ }
311
+
312
+ waitForPortToBeAvailable(port, {
313
+ retryPeriod: 200,
314
+ timeout: 2000,
315
+ abortSignal: abortController.signal,
316
+ })
252
317
  .then(() => {
253
- proxy.listen(port);
254
- console.log(`⬣ Listening at http://localhost:${port}`);
318
+ proxy.server.on("listening", () => {
319
+ logger.log(`⬣ Listening at ${localProtocol}://${ip}:${port}`);
320
+ });
321
+ proxy.server.listen(port, ip);
255
322
  })
256
323
  .catch((err) => {
257
- console.error(`⬣ Failed to start server: ${err}`);
324
+ if ((err as { code: string }).code !== "ABORT_ERR") {
325
+ logger.error(`Failed to start server: ${err}`);
326
+ }
258
327
  });
259
328
 
260
329
  return () => {
261
- proxy.close();
330
+ abortController.abort();
331
+ // Running `proxy.server.close()` does not close open connections, preventing the process from exiting.
332
+ // So we use this `terminator` to close all the connections and force the server to shutdown.
333
+ proxy.terminator.terminate();
262
334
  };
263
- }, [port, proxy]);
335
+ }, [port, ip, proxy, localProtocol]);
264
336
  }
265
337
 
266
338
  function createHandleAssetsRequest(
@@ -288,20 +360,18 @@ const HTTP1_HEADERS = new Set([
288
360
  "http2-settings",
289
361
  ]);
290
362
 
291
- function createProxyServer() {
292
- return createServer()
293
- .on("request", function (req, res) {
294
- // log all requests
295
- console.log(
296
- new Date().toLocaleTimeString(),
297
- req.method,
298
- req.url,
299
- res.statusCode
300
- );
301
- })
363
+ async function createProxyServer(
364
+ localProtocol: "https" | "http"
365
+ ): Promise<HttpServer | HttpsServer> {
366
+ const server: HttpServer | HttpsServer =
367
+ localProtocol === "https"
368
+ ? createHttpsServer(await getHttpsOptions())
369
+ : createHttpServer();
370
+
371
+ return server
302
372
  .on("upgrade", (req) => {
303
373
  // log all websocket connections
304
- console.log(
374
+ logger.log(
305
375
  new Date().toLocaleTimeString(),
306
376
  req.method,
307
377
  req.url,
@@ -311,14 +381,15 @@ function createProxyServer() {
311
381
  })
312
382
  .on("error", (err) => {
313
383
  // log all connection errors
314
- console.error(new Date().toLocaleTimeString(), err);
384
+ logger.error(new Date().toLocaleTimeString(), err);
315
385
  });
316
386
  }
317
387
 
318
388
  function createStreamHandler(
319
389
  previewToken: CfPreviewToken,
320
390
  remote: ClientHttp2Session,
321
- port: number
391
+ localPort: number,
392
+ localProtocol: "https" | "http"
322
393
  ) {
323
394
  return function handleStream(
324
395
  stream: ServerHttp2Stream,
@@ -331,7 +402,8 @@ function createStreamHandler(
331
402
  rewriteRemoteHostToLocalHostInHeaders(
332
403
  responseHeaders,
333
404
  previewToken.host,
334
- port
405
+ localPort,
406
+ localProtocol
335
407
  );
336
408
  stream.respond(responseHeaders);
337
409
  request.pipe(stream, { end: true });
@@ -344,33 +416,50 @@ function createStreamHandler(
344
416
  */
345
417
  export async function waitForPortToBeAvailable(
346
418
  port: number,
347
- options: { retryPeriod: number; timeout: number }
419
+ options: { retryPeriod: number; timeout: number; abortSignal: AbortSignal }
348
420
  ): Promise<void> {
349
421
  return new Promise((resolve, reject) => {
422
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
423
+ options.abortSignal.addEventListener("abort", () => {
424
+ const abortError = new Error("waitForPortToBeAvailable() aborted");
425
+ (abortError as Error & { code: string }).code = "ABORT_ERR";
426
+ doReject(abortError);
427
+ });
428
+
350
429
  const timeout = setTimeout(() => {
351
- reject(new Error(`Timed out waiting for port ${port}`));
430
+ doReject(new Error(`Timed out waiting for port ${port}`));
352
431
  }, options.timeout);
353
432
 
433
+ const interval = setInterval(checkPort, options.retryPeriod);
434
+ checkPort();
435
+
436
+ function doResolve() {
437
+ clearTimeout(timeout);
438
+ clearInterval(interval);
439
+ resolve();
440
+ }
441
+
442
+ function doReject(err: unknown) {
443
+ clearInterval(interval);
444
+ clearTimeout(timeout);
445
+ reject(err);
446
+ }
447
+
354
448
  function checkPort() {
355
449
  // Testing whether a port is 'available' involves simply
356
450
  // trying to make a server listen on that port, and retrying
357
451
  // until it succeeds.
358
- const server = createServer();
452
+ const server = createHttpServer();
359
453
  server.on("error", (err) => {
360
454
  // @ts-expect-error non standard property on Error
361
- if (err.code === "EADDRINUSE") {
362
- setTimeout(checkPort, options.retryPeriod);
363
- } else {
364
- reject(err);
455
+ if (err.code !== "EADDRINUSE") {
456
+ doReject(err);
365
457
  }
366
458
  });
367
459
  server.listen(port, () => {
368
460
  server.close();
369
- clearTimeout(timeout);
370
- resolve();
461
+ doResolve();
371
462
  });
372
463
  }
373
-
374
- checkPort();
375
464
  });
376
465
  }