wrangler 2.0.16 → 2.0.19

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 (51) hide show
  1. package/bin/wrangler.js +42 -8
  2. package/miniflare-dist/index.mjs +1 -0
  3. package/package.json +65 -63
  4. package/src/__tests__/configuration.test.ts +11 -7
  5. package/src/__tests__/helpers/run-in-tmp.ts +22 -23
  6. package/src/__tests__/https-options.test.ts +51 -18
  7. package/src/__tests__/index.test.ts +13 -13
  8. package/src/__tests__/jest.setup.ts +13 -0
  9. package/src/__tests__/kv.test.ts +33 -2
  10. package/src/__tests__/metrics.test.ts +415 -0
  11. package/src/__tests__/pages.test.ts +15 -8
  12. package/src/__tests__/publish.test.ts +49 -23
  13. package/src/__tests__/secret.test.ts +84 -78
  14. package/src/__tests__/tail.test.ts +8 -0
  15. package/src/__tests__/test-old-node-version.js +31 -0
  16. package/src/__tests__/user.test.ts +7 -7
  17. package/src/__tests__/whoami.test.tsx +11 -1
  18. package/src/api/dev.ts +4 -1
  19. package/src/cli.ts +1 -1
  20. package/src/config/config.ts +8 -0
  21. package/src/config/validation.ts +9 -0
  22. package/src/config-cache.ts +2 -1
  23. package/src/dev/local.tsx +31 -25
  24. package/src/dev/remote.tsx +2 -2
  25. package/src/dev.tsx +6 -0
  26. package/src/entry.ts +1 -1
  27. package/src/{__tests__/helpers/faye-websocket.d.ts → faye-websocket.d.ts} +0 -0
  28. package/src/global-wrangler-config-path.ts +26 -0
  29. package/src/https-options.ts +8 -4
  30. package/src/index.tsx +120 -22
  31. package/src/kv.ts +23 -1
  32. package/src/metrics/index.ts +4 -0
  33. package/src/metrics/metrics-config.ts +222 -0
  34. package/src/metrics/metrics-dispatcher.ts +93 -0
  35. package/src/metrics/send-event.ts +80 -0
  36. package/src/miniflare-cli/index.ts +1 -0
  37. package/src/package-manager.ts +45 -0
  38. package/src/pages/build.tsx +2 -0
  39. package/src/pages/deployments.tsx +2 -0
  40. package/src/pages/dev.tsx +4 -0
  41. package/src/pages/projects.tsx +3 -0
  42. package/src/pages/publish.tsx +3 -0
  43. package/src/pages/upload.tsx +26 -14
  44. package/src/publish.ts +28 -15
  45. package/src/pubsub/pubsub-commands.tsx +43 -0
  46. package/src/user/user.tsx +29 -20
  47. package/src/worker-namespace.ts +16 -0
  48. package/templates/static-asset-facade.js +11 -5
  49. package/templates/tsconfig.json +2 -2
  50. package/wrangler-dist/cli.d.ts +298 -0
  51. package/wrangler-dist/cli.js +2589 -1659
@@ -73,9 +73,9 @@ describe("wrangler secret", () => {
73
73
  mockPutRequest({ name: `secret-name`, text: `hunter2` });
74
74
  await runWrangler("secret put secret-name --name script-name");
75
75
  expect(std.out).toMatchInlineSnapshot(`
76
- "🌀 Creating the secret for script script-name
77
- ✨ Success! Uploaded secret secret-name"
78
- `);
76
+ "🌀 Creating the secret for the Worker \\"script-name\\"
77
+ ✨ Success! Uploaded secret secret-name"
78
+ `);
79
79
  });
80
80
 
81
81
  it("should create a secret", async () => {
@@ -89,9 +89,9 @@ describe("wrangler secret", () => {
89
89
  await runWrangler("secret put the-key --name script-name");
90
90
 
91
91
  expect(std.out).toMatchInlineSnapshot(`
92
- "🌀 Creating the secret for script script-name
93
- ✨ Success! Uploaded secret the-key"
94
- `);
92
+ "🌀 Creating the secret for the Worker \\"script-name\\"
93
+ ✨ Success! Uploaded secret the-key"
94
+ `);
95
95
  expect(std.err).toMatchInlineSnapshot(`""`);
96
96
  });
97
97
 
@@ -112,9 +112,9 @@ describe("wrangler secret", () => {
112
112
  );
113
113
 
114
114
  expect(std.out).toMatchInlineSnapshot(`
115
- "🌀 Creating the secret for script script-name-some-env
116
- ✨ Success! Uploaded secret the-key"
117
- `);
115
+ "🌀 Creating the secret for the Worker \\"script-name-some-env\\"
116
+ ✨ Success! Uploaded secret the-key"
117
+ `);
118
118
  expect(std.err).toMatchInlineSnapshot(`""`);
119
119
  });
120
120
 
@@ -135,9 +135,9 @@ describe("wrangler secret", () => {
135
135
  );
136
136
 
137
137
  expect(std.out).toMatchInlineSnapshot(`
138
- "🌀 Creating the secret for script script-name (some-env)
139
- ✨ Success! Uploaded secret the-key"
140
- `);
138
+ "🌀 Creating the secret for the Worker \\"script-name\\" (some-env)
139
+ ✨ Success! Uploaded secret the-key"
140
+ `);
141
141
  expect(std.err).toMatchInlineSnapshot(`""`);
142
142
  });
143
143
 
@@ -149,15 +149,17 @@ describe("wrangler secret", () => {
149
149
  error = e as Error;
150
150
  }
151
151
  expect(std.out).toMatchInlineSnapshot(`
152
- "
153
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
154
- `);
152
+ "
153
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
154
+ `);
155
155
  expect(std.err).toMatchInlineSnapshot(`
156
- "X [ERROR] Missing script name
156
+ "X [ERROR] Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with \`--name <worker-name>\`
157
157
 
158
- "
159
- `);
160
- expect(error).toMatchInlineSnapshot(`[Error: Missing script name]`);
158
+ "
159
+ `);
160
+ expect(error).toMatchInlineSnapshot(
161
+ `[Error: Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with \`--name <worker-name>\`]`
162
+ );
161
163
  });
162
164
  });
163
165
 
@@ -176,9 +178,9 @@ describe("wrangler secret", () => {
176
178
  await runWrangler("secret put the-key --name script-name");
177
179
 
178
180
  expect(std.out).toMatchInlineSnapshot(`
179
- "🌀 Creating the secret for script script-name
180
- ✨ Success! Uploaded secret the-key"
181
- `);
181
+ "🌀 Creating the secret for the Worker \\"script-name\\"
182
+ ✨ Success! Uploaded secret the-key"
183
+ `);
182
184
  expect(std.warn).toMatchInlineSnapshot(`""`);
183
185
  expect(std.err).toMatchInlineSnapshot(`""`);
184
186
  });
@@ -190,9 +192,9 @@ describe("wrangler secret", () => {
190
192
  await runWrangler("secret put the-key --name script-name");
191
193
 
192
194
  expect(std.out).toMatchInlineSnapshot(`
193
- "🌀 Creating the secret for script script-name
194
- ✨ Success! Uploaded secret the-key"
195
- `);
195
+ "🌀 Creating the secret for the Worker \\"script-name\\"
196
+ ✨ Success! Uploaded secret the-key"
197
+ `);
196
198
  expect(std.warn).toMatchInlineSnapshot(`""`);
197
199
  expect(std.err).toMatchInlineSnapshot(`""`);
198
200
  });
@@ -205,9 +207,9 @@ describe("wrangler secret", () => {
205
207
  ).rejects.toThrowErrorMatchingInlineSnapshot(`"Error in stdin stream"`);
206
208
 
207
209
  expect(std.out).toMatchInlineSnapshot(`
208
- "
209
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
210
- `);
210
+ "
211
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
212
+ `);
211
213
  expect(std.warn).toMatchInlineSnapshot(`""`);
212
214
  });
213
215
 
@@ -244,9 +246,9 @@ describe("wrangler secret", () => {
244
246
  mockPutRequest({ name: "the-key", text: "the-secret" });
245
247
  await runWrangler("secret put the-key --name script-name");
246
248
  expect(std.out).toMatchInlineSnapshot(`
247
- "🌀 Creating the secret for script script-name
248
- ✨ Success! Uploaded secret the-key"
249
- `);
249
+ "🌀 Creating the secret for the Worker \\"script-name\\"
250
+ ✨ Success! Uploaded secret the-key"
251
+ `);
250
252
  expect(std.warn).toMatchInlineSnapshot(`""`);
251
253
  expect(std.err).toMatchInlineSnapshot(`""`);
252
254
  });
@@ -317,14 +319,14 @@ describe("wrangler secret", () => {
317
319
  it("should delete a secret", async () => {
318
320
  mockDeleteRequest({ scriptName: "script-name", secretName: "the-key" });
319
321
  mockConfirm({
320
- text: "Are you sure you want to permanently delete the variable the-key on the script script-name?",
322
+ text: "Are you sure you want to permanently delete the secret the-key on the Worker script-name?",
321
323
  result: true,
322
324
  });
323
325
  await runWrangler("secret delete the-key --name script-name");
324
326
  expect(std.out).toMatchInlineSnapshot(`
325
- "🌀 Deleting the secret the-key on script script-name
326
- ✨ Success! Deleted secret the-key"
327
- `);
327
+ "🌀 Deleting the secret the-key on the Worker script-name
328
+ ✨ Success! Deleted secret the-key"
329
+ `);
328
330
  expect(std.err).toMatchInlineSnapshot(`""`);
329
331
  });
330
332
 
@@ -335,16 +337,16 @@ describe("wrangler secret", () => {
335
337
  true
336
338
  );
337
339
  mockConfirm({
338
- text: "Are you sure you want to permanently delete the variable the-key on the script script-name-some-env?",
340
+ text: "Are you sure you want to permanently delete the secret the-key on the Worker script-name-some-env?",
339
341
  result: true,
340
342
  });
341
343
  await runWrangler(
342
344
  "secret delete the-key --name script-name --env some-env --legacy-env"
343
345
  );
344
346
  expect(std.out).toMatchInlineSnapshot(`
345
- "🌀 Deleting the secret the-key on script script-name-some-env
346
- ✨ Success! Deleted secret the-key"
347
- `);
347
+ "🌀 Deleting the secret the-key on the Worker script-name-some-env
348
+ ✨ Success! Deleted secret the-key"
349
+ `);
348
350
  expect(std.err).toMatchInlineSnapshot(`""`);
349
351
  });
350
352
 
@@ -354,16 +356,16 @@ describe("wrangler secret", () => {
354
356
  "some-env"
355
357
  );
356
358
  mockConfirm({
357
- text: "Are you sure you want to permanently delete the variable the-key on the script script-name (some-env)?",
359
+ text: "Are you sure you want to permanently delete the secret the-key on the Worker script-name (some-env)?",
358
360
  result: true,
359
361
  });
360
362
  await runWrangler(
361
363
  "secret delete the-key --name script-name --env some-env --legacy-env false"
362
364
  );
363
365
  expect(std.out).toMatchInlineSnapshot(`
364
- "🌀 Deleting the secret the-key on script script-name (some-env)
365
- ✨ Success! Deleted secret the-key"
366
- `);
366
+ "🌀 Deleting the secret the-key on the Worker script-name (some-env)
367
+ ✨ Success! Deleted secret the-key"
368
+ `);
367
369
  expect(std.err).toMatchInlineSnapshot(`""`);
368
370
  });
369
371
 
@@ -376,15 +378,17 @@ describe("wrangler secret", () => {
376
378
  error = e as Error;
377
379
  }
378
380
  expect(std.out).toMatchInlineSnapshot(`
379
- "
380
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
381
- `);
381
+ "
382
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
383
+ `);
382
384
  expect(std.err).toMatchInlineSnapshot(`
383
- "X [ERROR] Missing script name
385
+ "X [ERROR] Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with \`--name <worker-name>\`
384
386
 
385
- "
386
- `);
387
- expect(error).toMatchInlineSnapshot(`[Error: Missing script name]`);
387
+ "
388
+ `);
389
+ expect(error).toMatchInlineSnapshot(
390
+ `[Error: Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with \`--name <worker-name>\`]`
391
+ );
388
392
  });
389
393
  });
390
394
 
@@ -422,13 +426,13 @@ describe("wrangler secret", () => {
422
426
  mockListRequest({ scriptName: "script-name" });
423
427
  await runWrangler("secret list --name script-name");
424
428
  expect(std.out).toMatchInlineSnapshot(`
425
- "[
426
- {
427
- \\"name\\": \\"the-secret-name\\",
428
- \\"type\\": \\"secret_text\\"
429
- }
430
- ]"
431
- `);
429
+ "[
430
+ {
431
+ \\"name\\": \\"the-secret-name\\",
432
+ \\"type\\": \\"secret_text\\"
433
+ }
434
+ ]"
435
+ `);
432
436
  expect(std.err).toMatchInlineSnapshot(`""`);
433
437
  });
434
438
 
@@ -438,13 +442,13 @@ describe("wrangler secret", () => {
438
442
  "secret list --name script-name --env some-env --legacy-env"
439
443
  );
440
444
  expect(std.out).toMatchInlineSnapshot(`
441
- "[
442
- {
443
- \\"name\\": \\"the-secret-name\\",
444
- \\"type\\": \\"secret_text\\"
445
- }
446
- ]"
447
- `);
445
+ "[
446
+ {
447
+ \\"name\\": \\"the-secret-name\\",
448
+ \\"type\\": \\"secret_text\\"
449
+ }
450
+ ]"
451
+ `);
448
452
  expect(std.err).toMatchInlineSnapshot(`""`);
449
453
  });
450
454
 
@@ -454,13 +458,13 @@ describe("wrangler secret", () => {
454
458
  "secret list --name script-name --env some-env --legacy-env false"
455
459
  );
456
460
  expect(std.out).toMatchInlineSnapshot(`
457
- "[
458
- {
459
- \\"name\\": \\"the-secret-name\\",
460
- \\"type\\": \\"secret_text\\"
461
- }
462
- ]"
463
- `);
461
+ "[
462
+ {
463
+ \\"name\\": \\"the-secret-name\\",
464
+ \\"type\\": \\"secret_text\\"
465
+ }
466
+ ]"
467
+ `);
464
468
  expect(std.err).toMatchInlineSnapshot(`""`);
465
469
  });
466
470
 
@@ -472,15 +476,17 @@ describe("wrangler secret", () => {
472
476
  error = e as Error;
473
477
  }
474
478
  expect(std.out).toMatchInlineSnapshot(`
475
- "
476
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
477
- `);
479
+ "
480
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
481
+ `);
478
482
  expect(std.err).toMatchInlineSnapshot(`
479
- "X [ERROR] Missing script name
483
+ "X [ERROR] Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with \`--name <worker-name>\`
480
484
 
481
- "
482
- `);
483
- expect(error).toMatchInlineSnapshot(`[Error: Missing script name]`);
485
+ "
486
+ `);
487
+ expect(error).toMatchInlineSnapshot(
488
+ `[Error: Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with \`--name <worker-name>\`]`
489
+ );
484
490
  });
485
491
  });
486
492
  });
@@ -27,6 +27,14 @@ describe("tail", () => {
27
27
  * deletion, and connection.
28
28
  */
29
29
  describe("API interaction", () => {
30
+ it("should throw an error if name isn't provided", async () => {
31
+ await expect(
32
+ runWrangler("tail")
33
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
34
+ `"Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with \`wrangler tail <worker-name>\`"`
35
+ );
36
+ });
37
+
30
38
  it("creates and then delete tails", async () => {
31
39
  const api = mockWebsocketAPIs();
32
40
  expect(api.requests.creation.count).toStrictEqual(0);
@@ -0,0 +1,31 @@
1
+ // this test has to be run with a version of node.js older than 16.7 to pass
2
+
3
+ const { spawn } = require("child_process");
4
+ const path = require("path");
5
+
6
+ const assert = require("assert");
7
+
8
+ const wranglerProcess = spawn(
9
+ "node",
10
+ [path.join(__dirname, "../../bin/wrangler.js")],
11
+ { stdio: "pipe" }
12
+ );
13
+
14
+ const messageToMatch = "Wrangler requires at least node.js v16.7.0";
15
+
16
+ wranglerProcess.once("exit", (code) => {
17
+ try {
18
+ const errorMessage = wranglerProcess.stderr.read().toString();
19
+
20
+ assert(code === 1, "Expected exit code 1");
21
+ assert(
22
+ errorMessage.includes(messageToMatch),
23
+ `Expected error message to include "${messageToMatch}"`
24
+ );
25
+ } catch (err) {
26
+ console.error("Error:", err);
27
+ throw new Error(
28
+ "This test has to be run with a version of node.js under 16.7 to pass"
29
+ );
30
+ }
31
+ });
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs";
2
- import os from "node:os";
3
2
  import path from "node:path";
4
3
  import fetchMock from "jest-fetch-mock";
4
+ import { getGlobalWranglerConfigPath } from "../global-wrangler-config-path";
5
5
  import {
6
6
  loginOrRefreshIfRequired,
7
7
  readAuthConfigFile,
@@ -18,7 +18,7 @@ import type { Config } from "../config";
18
18
  import type { UserAuthConfig } from "../user";
19
19
 
20
20
  describe("User", () => {
21
- runInTempDir({ homedir: "./home" });
21
+ runInTempDir();
22
22
  const std = mockConsoleMethods();
23
23
  const {
24
24
  mockOAuthServerCallback,
@@ -86,7 +86,10 @@ describe("User", () => {
86
86
  expect(fetchMock).toHaveBeenCalledTimes(1);
87
87
 
88
88
  // Make sure that logout removed the config file containing the auth tokens.
89
- const config = path.join(os.homedir(), USER_AUTH_CONFIG_FILE);
89
+ const config = path.join(
90
+ getGlobalWranglerConfigPath(),
91
+ USER_AUTH_CONFIG_FILE
92
+ );
90
93
  expect(fs.existsSync(config)).toBeFalsy();
91
94
  });
92
95
  });
@@ -94,13 +97,10 @@ describe("User", () => {
94
97
  // TODO: Improve OAuth mocking to handle `/token` endpoints from different calls
95
98
  it("should handle errors for failed token refresh", async () => {
96
99
  setIsTTY(false);
97
- mockOAuthServerCallback();
98
100
  writeAuthConfigFile({
99
101
  oauth_token: "hunter2",
100
102
  refresh_token: "Order 66",
101
103
  });
102
- mockGrantAuthorization({ respondWith: "success" });
103
-
104
104
  mockExchangeRefreshTokenForAccessToken({
105
105
  respondWith: "badResponse",
106
106
  });
@@ -109,7 +109,7 @@ describe("User", () => {
109
109
  await expect(
110
110
  requireAuth({} as Config)
111
111
  ).rejects.toThrowErrorMatchingInlineSnapshot(
112
- `"Did not login, quitting..."`
112
+ `"In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/api/tokens/create/ for instructions on how to create an api token, and assign its value to CLOUDFLARE_API_TOKEN."`
113
113
  );
114
114
  });
115
115
 
@@ -16,7 +16,7 @@ import type { UserInfo } from "../whoami";
16
16
  describe("getUserInfo()", () => {
17
17
  const ENV_COPY = process.env;
18
18
 
19
- runInTempDir({ homedir: "./home" });
19
+ runInTempDir();
20
20
  const std = mockConsoleMethods();
21
21
  const { setIsTTY } = useMockIsTTY();
22
22
 
@@ -173,6 +173,16 @@ describe("getUserInfo()", () => {
173
173
 
174
174
  it("should display a warning message if the config file contains a legacy api_token field", async () => {
175
175
  writeAuthConfigFile({ api_token: "API_TOKEN" });
176
+ setMockResponse("/user", () => {
177
+ return { email: "user@example.com" };
178
+ });
179
+ setMockResponse("/accounts", () => {
180
+ return [
181
+ { name: "Account One", id: "account-1" },
182
+ { name: "Account Two", id: "account-2" },
183
+ { name: "Account Three", id: "account-3" },
184
+ ];
185
+ });
176
186
  await getUserInfo();
177
187
  expect(std.warn).toMatchInlineSnapshot(`
178
188
  "▲ [WARNING] It looks like you have used Wrangler 1's \`config\` command to login with an API token.
package/src/api/dev.ts CHANGED
@@ -43,7 +43,10 @@ export async function unstable_dev(script: string, options: DevOptions) {
43
43
  showInteractiveDevSession: false,
44
44
  });
45
45
  }).then((devServer) => {
46
- resolve({ stop: devServer.stop, fetch: devServer.fetch });
46
+ resolve({
47
+ stop: devServer.stop,
48
+ fetch: devServer.fetch,
49
+ });
47
50
  });
48
51
  });
49
52
  }
package/src/cli.ts CHANGED
@@ -24,4 +24,4 @@ if (typeof jest === "undefined" && require.main) {
24
24
  * It makes it possible to import wrangler from 'wrangler',
25
25
  * and call wrangler.unstable_dev().
26
26
  */
27
- module.exports = { unstable_dev };
27
+ export default { unstable_dev };
@@ -38,6 +38,14 @@ export interface ConfigFields<Dev extends RawDevConfig> {
38
38
  */
39
39
  legacy_env: boolean;
40
40
 
41
+ /**
42
+ * Whether Wrangler should send usage metrics to Cloudflare for this project.
43
+ *
44
+ * When defined this will override any user settings.
45
+ * Otherwise, Wrangler will use the user's preference.
46
+ */
47
+ send_metrics: boolean | undefined;
48
+
41
49
  /**
42
50
  * Options to configure the development server that your worker will use.
43
51
  */
@@ -94,6 +94,14 @@ export function normalizeAndValidateConfig(
94
94
  "boolean"
95
95
  );
96
96
 
97
+ validateOptionalProperty(
98
+ diagnostics,
99
+ "",
100
+ "send_metrics",
101
+ rawConfig.send_metrics,
102
+ "boolean"
103
+ );
104
+
97
105
  // TODO: set the default to false to turn on service environments as the default
98
106
  const isLegacyEnv =
99
107
  (args as { "legacy-env": boolean | undefined })["legacy-env"] ??
@@ -173,6 +181,7 @@ export function normalizeAndValidateConfig(
173
181
  const config: Config = {
174
182
  configPath,
175
183
  legacy_env: isLegacyEnv,
184
+ send_metrics: rawConfig.send_metrics,
176
185
  ...activeEnv,
177
186
  dev: normalizeAndValidateDev(diagnostics, rawConfig.dev ?? {}),
178
187
  migrations: normalizeAndValidateMigrations(
@@ -6,7 +6,7 @@ import { logger } from "./logger";
6
6
 
7
7
  let cacheMessageShown = false;
8
8
 
9
- let __cacheFolder: string | null;
9
+ let __cacheFolder: string | null | undefined;
10
10
  function getCacheFolder() {
11
11
  if (__cacheFolder || __cacheFolder === null) return __cacheFolder;
12
12
 
@@ -80,4 +80,5 @@ export function purgeConfigCaches() {
80
80
  if (cacheFolder) {
81
81
  rmSync(cacheFolder, { recursive: true, force: true });
82
82
  }
83
+ __cacheFolder = undefined;
83
84
  }
package/src/dev/local.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { spawn } from "node:child_process";
1
+ import { fork } from "node:child_process";
2
2
  import { realpathSync } from "node:fs";
3
3
  import { writeFile } from "node:fs/promises";
4
4
  import path from "node:path";
@@ -13,7 +13,7 @@ import type { AssetPaths } from "../sites";
13
13
  import type { CfWorkerInit, CfScriptFormat } from "../worker";
14
14
  import type { EsbuildBundle } from "./use-esbuild";
15
15
  import type { MiniflareOptions } from "miniflare";
16
-
16
+ import type { ChildProcess } from "node:child_process";
17
17
  interface LocalProps {
18
18
  name: string | undefined;
19
19
  bundle: EsbuildBundle | undefined;
@@ -67,7 +67,7 @@ function useLocalWorker({
67
67
  logLevel,
68
68
  }: LocalProps) {
69
69
  // TODO: pass vars via command line
70
- const local = useRef<ReturnType<typeof spawn>>();
70
+ const local = useRef<ChildProcess>();
71
71
  const removeSignalExitListener = useRef<() => void>();
72
72
  const [inspectorUrl, setInspectorUrl] = useState<string | undefined>();
73
73
  // if we're using local persistence for data, we should use the cwd
@@ -187,6 +187,7 @@ function useLocalWorker({
187
187
  compatibilityDate,
188
188
  compatibilityFlags,
189
189
  kvNamespaces: bindings.kv_namespaces?.map((kv) => kv.binding),
190
+ r2Buckets: bindings.r2_buckets?.map((r2) => r2.binding),
190
191
  durableObjects: Object.fromEntries(
191
192
  (bindings.durable_objects?.bindings ?? []).map<[string, string]>(
192
193
  (value) => [value.name, value.class_name]
@@ -194,9 +195,10 @@ function useLocalWorker({
194
195
  ),
195
196
  ...(localPersistencePath
196
197
  ? {
197
- kvPersist: path.join(localPersistencePath, "kv"),
198
- durableObjectsPersist: path.join(localPersistencePath, "do"),
199
198
  cachePersist: path.join(localPersistencePath, "cache"),
199
+ durableObjectsPersist: path.join(localPersistencePath, "do"),
200
+ kvPersist: path.join(localPersistencePath, "kv"),
201
+ r2Persist: path.join(localPersistencePath, "r2"),
200
202
  }
201
203
  : {
202
204
  // We mark these as true, so that they'll
@@ -204,9 +206,10 @@ function useLocalWorker({
204
206
  // This means they'll persist across a dev session,
205
207
  // even if we change source and reload,
206
208
  // and be deleted when the dev session ends
207
- durableObjectsPersist: true,
208
209
  cachePersist: true,
210
+ durableObjectsPersist: true,
209
211
  kvPersist: true,
212
+ r2Persist: true,
210
213
  }),
211
214
 
212
215
  sitePath: assetPaths?.assetDirectory
@@ -236,43 +239,45 @@ function useLocalWorker({
236
239
  __dirname,
237
240
  "../miniflare-dist/index.mjs"
238
241
  );
239
- const optionsArg = JSON.stringify(options, null);
242
+ const miniflareOptions = JSON.stringify(options, null);
240
243
 
241
244
  logger.log("⎔ Starting a local server...");
242
- const localServerOptions = [
245
+ const nodeOptions = [
243
246
  "--experimental-vm-modules", // ensures that Miniflare can run ESM Workers
244
247
  "--no-warnings", // hide annoying Node warnings
245
- miniflareCLIPath,
246
- optionsArg,
247
248
  // "--log=VERBOSE", // uncomment this to Miniflare to log "everything"!
248
249
  ];
249
250
  if (inspect) {
250
- localServerOptions.push("--inspect"); // start Miniflare listening for a debugger to attach
251
+ nodeOptions.push("--inspect"); // start Miniflare listening for a debugger to attach
251
252
  }
252
- // spawn isn't technically synchronous here
253
- local.current = spawn("node", localServerOptions, {
254
- cwd: path.dirname(scriptPath),
253
+ const child = (local.current = fork(
254
+ miniflareCLIPath,
255
+ [miniflareOptions],
256
+ {
257
+ cwd: path.dirname(scriptPath),
258
+ execArgv: nodeOptions,
259
+ }
260
+ ));
261
+ child.on("message", (message) => {
262
+ if (message === "ready") {
263
+ onReady?.();
264
+ }
255
265
  });
256
- //TODO: instead of being lucky with spawn's timing, have miniflare-cli notify wrangler that it's ready in packages/wrangler/src/miniflare-cli/index.ts, after the mf.startScheduler promise resolves
257
- if (onReady) {
258
- await new Promise((resolve) => setTimeout(resolve, 500));
259
- onReady();
260
- }
261
266
 
262
- local.current.on("close", (code) => {
267
+ child.on("close", (code) => {
263
268
  if (code) {
264
269
  logger.log(`Miniflare process exited with code ${code}`);
265
270
  }
266
271
  });
267
272
 
268
- local.current.stdout?.on("data", (data: Buffer) => {
273
+ child.stdout?.on("data", (data: Buffer) => {
269
274
  process.stdout.write(data);
270
275
  });
271
276
 
272
277
  // parse the node inspector url (which may be received in chunks) from stderr
273
278
  let stderrData = "";
274
279
  let inspectorUrlFound = false;
275
- local.current.stderr?.on("data", (data: Buffer) => {
280
+ child.stderr?.on("data", (data: Buffer) => {
276
281
  if (!inspectorUrlFound) {
277
282
  stderrData += data.toString();
278
283
  const matches =
@@ -288,20 +293,20 @@ function useLocalWorker({
288
293
  process.stderr.write(data);
289
294
  });
290
295
 
291
- local.current.on("exit", (code) => {
296
+ child.on("exit", (code) => {
292
297
  if (code) {
293
298
  logger.error(`Miniflare process exited with code ${code}`);
294
299
  }
295
300
  });
296
301
 
297
- local.current.on("error", (error: Error) => {
302
+ child.on("error", (error: Error) => {
298
303
  logger.error(`Miniflare process failed to spawn`);
299
304
  logger.error(error);
300
305
  });
301
306
 
302
307
  removeSignalExitListener.current = onExit((_code, _signal) => {
303
308
  logger.log("⎔ Shutting down local server.");
304
- local.current?.kill();
309
+ child.kill();
305
310
  local.current = undefined;
306
311
  });
307
312
  }
@@ -328,6 +333,7 @@ function useLocalWorker({
328
333
  ip,
329
334
  bindings.durable_objects?.bindings,
330
335
  bindings.kv_namespaces,
336
+ bindings.r2_buckets,
331
337
  bindings.vars,
332
338
  bindings.services,
333
339
  compatibilityDate,
@@ -53,7 +53,7 @@ export function Remote(props: {
53
53
  zone: string | undefined;
54
54
  host: string | undefined;
55
55
  routes: Route[] | undefined;
56
- onReady?: () => void;
56
+ onReady?: (() => void) | undefined;
57
57
  }) {
58
58
  const [accountId, setAccountId] = useState(props.accountId);
59
59
  const accountChoicesRef = useRef<Promise<ChooseAccountItem[]>>();
@@ -158,7 +158,7 @@ export function useWorker(props: {
158
158
  zone: string | undefined;
159
159
  host: string | undefined;
160
160
  routes: Route[] | undefined;
161
- onReady?: () => void;
161
+ onReady: (() => void) | undefined;
162
162
  }): CfPreviewToken | undefined {
163
163
  const {
164
164
  name,