wrangler 0.0.0-e2f9bb2 → 0.0.0-e322475

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 (103) hide show
  1. package/Cloudflare_CA.pem +18 -0
  2. package/README.md +3 -1
  3. package/bin/wrangler.js +36 -4
  4. package/kv-asset-handler.js +1 -0
  5. package/package.json +15 -7
  6. package/pages/functions/buildPlugin.ts +13 -0
  7. package/pages/functions/buildWorker.ts +14 -1
  8. package/pages/functions/template-plugin.ts +3 -2
  9. package/src/__tests__/configuration.test.ts +652 -229
  10. package/src/__tests__/dev.test.tsx +372 -79
  11. package/src/__tests__/helpers/mock-cfetch.ts +2 -2
  12. package/src/__tests__/helpers/mock-console.ts +11 -1
  13. package/src/__tests__/helpers/mock-dialogs.ts +75 -11
  14. package/src/__tests__/helpers/mock-process.ts +24 -0
  15. package/src/__tests__/index.test.ts +81 -72
  16. package/src/__tests__/init.test.ts +1360 -410
  17. package/src/__tests__/jest.setup.ts +38 -0
  18. package/src/__tests__/kv.test.ts +218 -152
  19. package/src/__tests__/logger.test.ts +26 -0
  20. package/src/__tests__/package-manager.test.ts +154 -7
  21. package/src/__tests__/pages.test.ts +478 -53
  22. package/src/__tests__/parse.test.ts +8 -0
  23. package/src/__tests__/publish.test.ts +1614 -333
  24. package/src/__tests__/r2.test.ts +55 -36
  25. package/src/__tests__/secret.test.ts +39 -4
  26. package/src/__tests__/user.test.ts +1 -0
  27. package/src/__tests__/whoami.test.tsx +34 -0
  28. package/src/abort.d.ts +3 -0
  29. package/src/bundle-reporter.tsx +29 -0
  30. package/src/bundle.ts +42 -7
  31. package/src/cfetch/index.ts +21 -4
  32. package/src/cfetch/internal.ts +25 -20
  33. package/src/config/config.ts +11 -2
  34. package/src/config/environment.ts +41 -15
  35. package/src/config/index.ts +162 -0
  36. package/src/config/validation-helpers.ts +10 -1
  37. package/src/config/validation.ts +234 -78
  38. package/src/create-worker-preview.ts +32 -22
  39. package/src/create-worker-upload-form.ts +22 -8
  40. package/src/dev/dev.tsx +48 -67
  41. package/src/dev/local.tsx +16 -7
  42. package/src/dev/remote.tsx +57 -21
  43. package/src/dev/use-esbuild.ts +1 -7
  44. package/src/dialogs.tsx +48 -0
  45. package/src/durable.ts +102 -0
  46. package/src/entry.ts +71 -10
  47. package/src/generate-auth-url.ts +33 -0
  48. package/src/generate-random-state.ts +16 -0
  49. package/src/git-client.ts +42 -0
  50. package/src/index.tsx +619 -338
  51. package/src/inspect.ts +39 -0
  52. package/src/kv.ts +116 -27
  53. package/src/logger.ts +8 -1
  54. package/src/open-in-browser.ts +3 -12
  55. package/src/package-manager.ts +50 -3
  56. package/src/pages.tsx +434 -207
  57. package/src/parse.ts +44 -5
  58. package/src/proxy.ts +66 -44
  59. package/src/publish.ts +407 -137
  60. package/src/sites.tsx +103 -28
  61. package/src/user.tsx +35 -26
  62. package/src/whoami.tsx +3 -2
  63. package/src/worker.ts +10 -1
  64. package/src/zones.ts +73 -0
  65. package/templates/new-worker-scheduled.js +17 -0
  66. package/templates/new-worker-scheduled.ts +32 -0
  67. package/templates/new-worker.ts +16 -1
  68. package/templates/no-op-worker.js +10 -0
  69. package/templates/static-asset-facade.js +2 -6
  70. package/wrangler-dist/cli.js +46122 -32396
  71. package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
  72. package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
  73. package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
  74. package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
  75. package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
  76. package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
  77. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
  78. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
  79. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
  80. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
  81. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
  82. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
  83. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
  84. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
  85. package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
  86. package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
  87. package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
  88. package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
  89. package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
  90. package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
  91. package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
  92. package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
  93. package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
  94. package/vendor/wrangler-mime/CHANGELOG.md +0 -289
  95. package/vendor/wrangler-mime/LICENSE +0 -21
  96. package/vendor/wrangler-mime/Mime.js +0 -97
  97. package/vendor/wrangler-mime/README.md +0 -187
  98. package/vendor/wrangler-mime/cli.js +0 -46
  99. package/vendor/wrangler-mime/index.js +0 -4
  100. package/vendor/wrangler-mime/lite.js +0 -4
  101. package/vendor/wrangler-mime/package.json +0 -52
  102. package/vendor/wrangler-mime/types/other.js +0 -1
  103. package/vendor/wrangler-mime/types/standard.js +0 -1
package/src/parse.ts CHANGED
@@ -2,6 +2,7 @@ import * as fs from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import TOML from "@iarna/toml";
4
4
  import { formatMessagesSync } from "esbuild";
5
+ import { logger } from "./logger";
5
6
 
6
7
  export type Message = {
7
8
  text: string;
@@ -38,7 +39,7 @@ export function formatMessage(
38
39
  const lines = formatMessagesSync([input], {
39
40
  color,
40
41
  kind: kind,
41
- terminalWidth: process.stderr.columns,
42
+ terminalWidth: logger.columns,
42
43
  });
43
44
  return lines.join("\n");
44
45
  }
@@ -62,8 +63,6 @@ export class ParseError extends Error implements Message {
62
63
  }
63
64
  }
64
65
 
65
- /* eslint-disable @typescript-eslint/no-explicit-any */
66
-
67
66
  const TOML_ERROR_NAME = "TomlError";
68
67
  const TOML_ERROR_SUFFIX = " at row ";
69
68
 
@@ -77,7 +76,9 @@ type TomlError = Error & {
77
76
  */
78
77
  export function parseTOML(input: string, file?: string): TOML.JsonMap | never {
79
78
  try {
80
- return TOML.parse(input);
79
+ // Normalize CRLF to LF to avoid hitting https://github.com/iarna/iarna-toml/issues/33.
80
+ const normalizedInput = input.replace(/\r\n/g, "\n");
81
+ return TOML.parse(normalizedInput);
81
82
  } catch (err) {
82
83
  const { name, message, line, col } = err as TomlError;
83
84
  if (name !== TOML_ERROR_NAME) {
@@ -98,10 +99,29 @@ export function parseTOML(input: string, file?: string): TOML.JsonMap | never {
98
99
 
99
100
  const JSON_ERROR_SUFFIX = " in JSON at position ";
100
101
 
102
+ /**
103
+ * A minimal type describing a package.json file.
104
+ */
105
+ export type PackageJSON = {
106
+ devDependencies?: Record<string, unknown>;
107
+ dependencies?: Record<string, unknown>;
108
+ scripts?: Record<string, unknown>;
109
+ };
110
+
111
+ /**
112
+ * A typed version of `parseJSON()`.
113
+ */
114
+ export function parsePackageJSON<T extends PackageJSON = PackageJSON>(
115
+ input: string,
116
+ file?: string
117
+ ): T {
118
+ return parseJSON<T>(input, file);
119
+ }
120
+
101
121
  /**
102
122
  * A wrapper around `JSON.parse` that throws a `ParseError`.
103
123
  */
104
- export function parseJSON(input: string, file?: string): any {
124
+ export function parseJSON<T>(input: string, file?: string): T {
105
125
  try {
106
126
  return JSON.parse(input);
107
127
  } catch (err) {
@@ -119,6 +139,25 @@ export function parseJSON(input: string, file?: string): any {
119
139
  }
120
140
  }
121
141
 
142
+ /**
143
+ * Reads a file into a node Buffer.
144
+ */
145
+ export function readFileSyncToBuffer(file: string): Buffer {
146
+ try {
147
+ return fs.readFileSync(file);
148
+ } catch (err) {
149
+ const { message } = err as Error;
150
+ throw new ParseError({
151
+ text: `Could not read file: ${file}`,
152
+ notes: [
153
+ {
154
+ text: message.replace(file, resolve(file)),
155
+ },
156
+ ],
157
+ });
158
+ }
159
+ }
160
+
122
161
  /**
123
162
  * Reads a file and parses it based on its type.
124
163
  */
package/src/proxy.ts CHANGED
@@ -2,11 +2,13 @@ import { createServer as createHttpServer } from "node:http";
2
2
  import { connect } from "node:http2";
3
3
  import { createServer as createHttpsServer } from "node:https";
4
4
  import WebSocket from "faye-websocket";
5
+ import { createHttpTerminator } from "http-terminator";
5
6
  import { useEffect, useRef, useState } from "react";
6
7
  import serveStatic from "serve-static";
7
8
  import { getHttpsOptions } from "./https-options";
8
9
  import { logger } from "./logger";
9
10
  import type { CfPreviewToken } from "./create-worker-preview";
11
+ import type { HttpTerminator } from "http-terminator";
10
12
  import type {
11
13
  IncomingHttpHeaders,
12
14
  RequestListener,
@@ -67,35 +69,48 @@ function rewriteRemoteHostToLocalHostInHeaders(
67
69
  }
68
70
  }
69
71
 
72
+ type PreviewProxy = {
73
+ server: HttpServer | HttpsServer;
74
+ terminator: HttpTerminator;
75
+ };
76
+
70
77
  export function usePreviewServer({
71
78
  previewToken,
72
- publicRoot,
79
+ assetDirectory,
73
80
  localProtocol,
74
81
  localPort: port,
75
82
  ip,
76
83
  }: {
77
84
  previewToken: CfPreviewToken | undefined;
78
- publicRoot: string | undefined;
85
+ assetDirectory: string | undefined;
79
86
  localProtocol: "https" | "http";
80
87
  localPort: number;
81
88
  ip: string;
82
89
  }) {
83
90
  /** Creates an HTTP/1 proxy that sends requests over HTTP/2. */
84
- const [proxyServer, setProxyServer] = useState<HttpServer | HttpsServer>();
91
+ const [proxy, setProxy] = useState<PreviewProxy>();
85
92
 
86
93
  /**
87
94
  * Create the instance of the local proxy server that will pass on
88
95
  * requests to the preview worker.
89
96
  */
90
97
  useEffect(() => {
91
- if (proxyServer === undefined) {
98
+ if (proxy === undefined) {
92
99
  createProxyServer(localProtocol)
93
- .then((proxy) => setProxyServer(proxy))
100
+ .then((server) => {
101
+ setProxy({
102
+ server,
103
+ terminator: createHttpTerminator({
104
+ server,
105
+ gracefulTerminationTimeout: 0,
106
+ }),
107
+ });
108
+ })
94
109
  .catch(async (err) => {
95
110
  logger.error("Failed to create proxy server:", err);
96
111
  });
97
112
  }
98
- }, [proxyServer, localProtocol]);
113
+ }, [proxy, localProtocol]);
99
114
 
100
115
  /**
101
116
  * When we're not connected / getting a fresh token on changes,
@@ -123,7 +138,7 @@ export function usePreviewServer({
123
138
  }
124
139
 
125
140
  useEffect(() => {
126
- if (proxyServer === undefined) {
141
+ if (proxy === undefined) {
127
142
  return;
128
143
  }
129
144
 
@@ -138,8 +153,8 @@ export function usePreviewServer({
138
153
  // store the stream in a buffer so we can replay it later
139
154
  streamBufferRef.current.push({ stream, headers });
140
155
  };
141
- proxyServer.on("stream", bufferStream);
142
- cleanupListeners.push(() => proxyServer.off("stream", bufferStream));
156
+ proxy.server.on("stream", bufferStream);
157
+ cleanupListeners.push(() => proxy.server.off("stream", bufferStream));
143
158
 
144
159
  const bufferRequestResponse = (
145
160
  request: IncomingMessage,
@@ -149,9 +164,9 @@ export function usePreviewServer({
149
164
  requestResponseBufferRef.current.push({ request, response });
150
165
  };
151
166
 
152
- proxyServer.on("request", bufferRequestResponse);
167
+ proxy.server.on("request", bufferRequestResponse);
153
168
  cleanupListeners.push(() =>
154
- proxyServer.off("request", bufferRequestResponse)
169
+ proxy.server.off("request", bufferRequestResponse)
155
170
  );
156
171
  return () => {
157
172
  cleanupListeners.forEach((cleanup) => cleanup());
@@ -161,8 +176,6 @@ export function usePreviewServer({
161
176
  // We have a token. Let's proxy requests to the preview end point.
162
177
  const cleanupListeners: (() => void)[] = [];
163
178
 
164
- const assetPath = typeof publicRoot === "string" ? publicRoot : null;
165
-
166
179
  // create a ClientHttp2Session
167
180
  const remote = connect(`https://${previewToken.host}`);
168
181
  cleanupListeners.push(() => remote.destroy());
@@ -179,8 +192,8 @@ export function usePreviewServer({
179
192
  port,
180
193
  localProtocol
181
194
  );
182
- proxyServer.on("stream", handleStream);
183
- cleanupListeners.push(() => proxyServer.off("stream", handleStream));
195
+ proxy.server.on("stream", handleStream);
196
+ cleanupListeners.push(() => proxy.server.off("stream", handleStream));
184
197
 
185
198
  // flush and replay buffered streams
186
199
  streamBufferRef.current.forEach((buffer) =>
@@ -210,6 +223,10 @@ export function usePreviewServer({
210
223
  const request = message.pipe(remote.request(headers));
211
224
  request.on("response", (responseHeaders) => {
212
225
  const status = responseHeaders[":status"] ?? 500;
226
+
227
+ // log all requests to terminal
228
+ logger.log(new Date().toLocaleTimeString(), method, url, status);
229
+
213
230
  rewriteRemoteHostToLocalHostInHeaders(
214
231
  responseHeaders,
215
232
  previewToken.host,
@@ -228,13 +245,13 @@ export function usePreviewServer({
228
245
 
229
246
  // If an asset path is defined, check the file system
230
247
  // for a file first and serve if it exists.
231
- const actualHandleRequest = assetPath
232
- ? createHandleAssetsRequest(assetPath, handleRequest)
248
+ const actualHandleRequest = assetDirectory
249
+ ? createHandleAssetsRequest(assetDirectory, handleRequest)
233
250
  : handleRequest;
234
251
 
235
- proxyServer.on("request", actualHandleRequest);
252
+ proxy.server.on("request", actualHandleRequest);
236
253
  cleanupListeners.push(() =>
237
- proxyServer.off("request", actualHandleRequest)
254
+ proxy.server.off("request", actualHandleRequest)
238
255
  );
239
256
 
240
257
  // flush and replay buffered requests
@@ -266,18 +283,18 @@ export function usePreviewServer({
266
283
  remoteWebsocketClient.close();
267
284
  });
268
285
  };
269
- proxyServer.on("upgrade", handleUpgrade);
270
- cleanupListeners.push(() => proxyServer.off("upgrade", handleUpgrade));
286
+ proxy.server.on("upgrade", handleUpgrade);
287
+ cleanupListeners.push(() => proxy.server.off("upgrade", handleUpgrade));
271
288
 
272
289
  return () => {
273
290
  cleanupListeners.forEach((cleanup) => cleanup());
274
291
  };
275
292
  }, [
276
293
  previewToken,
277
- publicRoot,
294
+ assetDirectory,
278
295
  port,
279
296
  localProtocol,
280
- proxyServer,
297
+ proxy,
281
298
  // We use a state value as a sigil to trigger reconnecting the server.
282
299
  // It's not used inside the effect, so react-hooks/exhaustive-deps
283
300
  // doesn't complain if it's not included in the dependency array.
@@ -289,7 +306,7 @@ export function usePreviewServer({
289
306
  // containing component is mounted/unmounted.
290
307
  useEffect(() => {
291
308
  const abortController = new AbortController();
292
- if (proxyServer === undefined) {
309
+ if (proxy === undefined) {
293
310
  return;
294
311
  }
295
312
 
@@ -299,8 +316,10 @@ export function usePreviewServer({
299
316
  abortSignal: abortController.signal,
300
317
  })
301
318
  .then(() => {
302
- proxyServer.listen(port, ip);
303
- logger.log(`⬣ Listening at ${localProtocol}://${ip}:${port}`);
319
+ proxy.server.on("listening", () => {
320
+ logger.log(`⬣ Listening at ${localProtocol}://${ip}:${port}`);
321
+ });
322
+ proxy.server.listen(port, ip);
304
323
  })
305
324
  .catch((err) => {
306
325
  if ((err as { code: string }).code !== "ABORT_ERR") {
@@ -309,17 +328,21 @@ export function usePreviewServer({
309
328
  });
310
329
 
311
330
  return () => {
312
- proxyServer.close();
313
331
  abortController.abort();
332
+ // Running `proxy.server.close()` does not close open connections, preventing the process from exiting.
333
+ // So we use this `terminator` to close all the connections and force the server to shutdown.
334
+ proxy.terminator
335
+ .terminate()
336
+ .catch(() => logger.error("Failed to terminate the proxy server."));
314
337
  };
315
- }, [port, ip, proxyServer, localProtocol]);
338
+ }, [port, ip, proxy, localProtocol]);
316
339
  }
317
340
 
318
341
  function createHandleAssetsRequest(
319
- assetPath: string,
342
+ assetDirectory: string,
320
343
  handleRequest: RequestListener
321
344
  ) {
322
- const handleAsset = serveStatic(assetPath, {
345
+ const handleAsset = serveStatic(assetDirectory, {
323
346
  cacheControl: false,
324
347
  });
325
348
  return (request: IncomingMessage, response: ServerResponse) => {
@@ -349,15 +372,6 @@ async function createProxyServer(
349
372
  : createHttpServer();
350
373
 
351
374
  return server
352
- .on("request", function (req, res) {
353
- // log all requests
354
- logger.log(
355
- new Date().toLocaleTimeString(),
356
- req.method,
357
- req.url,
358
- res.statusCode
359
- );
360
- })
361
375
  .on("upgrade", (req) => {
362
376
  // log all websocket connections
363
377
  logger.log(
@@ -409,7 +423,7 @@ export async function waitForPortToBeAvailable(
409
423
  ): Promise<void> {
410
424
  return new Promise((resolve, reject) => {
411
425
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
412
- (options.abortSignal as any).addEventListener("abort", () => {
426
+ options.abortSignal.addEventListener("abort", () => {
413
427
  const abortError = new Error("waitForPortToBeAvailable() aborted");
414
428
  (abortError as Error & { code: string }).code = "ABORT_ERR";
415
429
  doReject(abortError);
@@ -439,16 +453,24 @@ export async function waitForPortToBeAvailable(
439
453
  // trying to make a server listen on that port, and retrying
440
454
  // until it succeeds.
441
455
  const server = createHttpServer();
456
+ const terminator = createHttpTerminator({
457
+ server,
458
+ gracefulTerminationTimeout: 0, // default 1000
459
+ });
460
+
442
461
  server.on("error", (err) => {
443
462
  // @ts-expect-error non standard property on Error
444
463
  if (err.code !== "EADDRINUSE") {
445
464
  doReject(err);
446
465
  }
447
466
  });
448
- server.listen(port, () => {
449
- server.close();
450
- doResolve();
451
- });
467
+ server.listen(port, () =>
468
+ terminator
469
+ .terminate()
470
+ .then(doResolve, () =>
471
+ logger.error("Failed to terminate the port checker.")
472
+ )
473
+ );
452
474
  }
453
475
  });
454
476
  }