wrangler 2.0.22 → 2.0.25

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 (75) hide show
  1. package/README.md +20 -2
  2. package/bin/wrangler.js +1 -1
  3. package/miniflare-dist/index.mjs +643 -7
  4. package/package.json +17 -5
  5. package/src/__tests__/configuration.test.ts +89 -17
  6. package/src/__tests__/dev.test.tsx +121 -8
  7. package/src/__tests__/generate.test.ts +93 -0
  8. package/src/__tests__/helpers/mock-cfetch.ts +54 -2
  9. package/src/__tests__/index.test.ts +10 -27
  10. package/src/__tests__/jest.setup.ts +31 -1
  11. package/src/__tests__/kv.test.ts +82 -61
  12. package/src/__tests__/metrics.test.ts +5 -0
  13. package/src/__tests__/publish.test.ts +573 -254
  14. package/src/__tests__/r2.test.ts +173 -71
  15. package/src/__tests__/tail.test.ts +93 -39
  16. package/src/__tests__/user.test.ts +1 -0
  17. package/src/__tests__/validate-dev-props.test.ts +56 -0
  18. package/src/__tests__/version.test.ts +35 -0
  19. package/src/__tests__/whoami.test.tsx +60 -1
  20. package/src/api/dev.ts +49 -9
  21. package/src/bundle.ts +298 -37
  22. package/src/cfetch/internal.ts +34 -2
  23. package/src/config/config.ts +15 -3
  24. package/src/config/environment.ts +40 -8
  25. package/src/config/index.ts +13 -0
  26. package/src/config/validation.ts +111 -9
  27. package/src/create-worker-preview.ts +3 -1
  28. package/src/create-worker-upload-form.ts +25 -0
  29. package/src/dev/dev.tsx +145 -31
  30. package/src/dev/local.tsx +116 -24
  31. package/src/dev/remote.tsx +39 -12
  32. package/src/dev/use-esbuild.ts +28 -0
  33. package/src/dev/validate-dev-props.ts +31 -0
  34. package/src/dev-registry.tsx +160 -0
  35. package/src/dev.tsx +148 -67
  36. package/src/generate.ts +112 -14
  37. package/src/index.tsx +252 -7
  38. package/src/inspect.ts +90 -5
  39. package/src/metrics/index.ts +1 -0
  40. package/src/metrics/metrics-dispatcher.ts +1 -0
  41. package/src/metrics/metrics-usage-headers.ts +24 -0
  42. package/src/metrics/send-event.ts +2 -2
  43. package/src/miniflare-cli/assets.ts +546 -0
  44. package/src/miniflare-cli/index.ts +157 -6
  45. package/src/module-collection.ts +3 -3
  46. package/src/pages/build.tsx +36 -28
  47. package/src/pages/constants.ts +4 -0
  48. package/src/pages/deployments.tsx +10 -10
  49. package/src/pages/dev.tsx +155 -651
  50. package/src/pages/functions/buildPlugin.ts +4 -0
  51. package/src/pages/functions/buildWorker.ts +4 -0
  52. package/src/pages/functions/routes-consolidation.test.ts +66 -0
  53. package/src/pages/functions/routes-consolidation.ts +29 -0
  54. package/src/pages/functions/routes-transformation.test.ts +271 -0
  55. package/src/pages/functions/routes-transformation.ts +125 -0
  56. package/src/pages/projects.tsx +9 -3
  57. package/src/pages/publish.tsx +57 -15
  58. package/src/pages/types.ts +9 -0
  59. package/src/pages/upload.tsx +38 -21
  60. package/src/publish.ts +139 -112
  61. package/src/r2.ts +81 -0
  62. package/src/tail/index.ts +15 -2
  63. package/src/tail/printing.ts +41 -3
  64. package/src/user/choose-account.tsx +20 -11
  65. package/src/user/user.tsx +20 -2
  66. package/src/whoami.tsx +79 -1
  67. package/src/worker.ts +12 -0
  68. package/templates/first-party-worker-module-facade.ts +18 -0
  69. package/templates/format-dev-errors.ts +32 -0
  70. package/templates/pages-shim.ts +9 -0
  71. package/templates/{static-asset-facade.js → serve-static-assets.ts} +21 -7
  72. package/templates/service-bindings-module-facade.js +51 -0
  73. package/templates/service-bindings-sw-facade.js +39 -0
  74. package/wrangler-dist/cli.d.ts +38 -3
  75. package/wrangler-dist/cli.js +45244 -25199
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wrangler",
3
- "version": "2.0.22",
3
+ "version": "2.0.25",
4
4
  "description": "Command-line interface for all things Cloudflare Workers",
5
5
  "keywords": [
6
6
  "wrangler",
@@ -53,14 +53,21 @@
53
53
  "bundle": "node -r esbuild-register scripts/bundle.ts",
54
54
  "check:type": "tsc",
55
55
  "clean": "rm -rf wrangler-dist miniflare-dist emitted-types",
56
- "dev": "npm run clean && concurrently -c black,blue 'npm run bundle -- --watch' 'npm run check:type -- --watch --preserveWatchOutput'",
56
+ "dev": "npm run clean && concurrently -c black,blue --kill-others-on-fail false 'npm run bundle -- --watch' 'npm run check:type -- --watch --preserveWatchOutput'",
57
57
  "emit-types": "tsc -p tsconfig.emit.json && node -r esbuild-register scripts/emit-types.ts",
58
58
  "prepublishOnly": "SOURCEMAPS=false npm run build",
59
59
  "start": "npm run bundle && NODE_OPTIONS=--enable-source-maps ./bin/wrangler.js",
60
60
  "test": "jest --silent=false --verbose=true",
61
- "test-watch": "npm run test -- --runInBand --testTimeout=50000 --watch"
61
+ "test-watch": "npm run test -- --runInBand --testTimeout=50000 --watch",
62
+ "test:ci": "npm run test -- --verbose=true --coverage"
62
63
  },
63
64
  "jest": {
65
+ "coverageReporters": [
66
+ "json",
67
+ "html",
68
+ "text",
69
+ "cobertura"
70
+ ],
64
71
  "moduleNameMapper": {
65
72
  "clipboardy": "<rootDir>/src/__tests__/helpers/clipboardy-mock.js",
66
73
  "miniflare/cli": "<rootDir>/../../node_modules/miniflare/dist/src/cli.js"
@@ -88,17 +95,20 @@
88
95
  "@esbuild-plugins/node-globals-polyfill": "^0.1.1",
89
96
  "@esbuild-plugins/node-modules-polyfill": "^0.1.4",
90
97
  "blake3-wasm": "^2.1.5",
91
- "esbuild": "0.14.47",
98
+ "chokidar": "^3.5.3",
99
+ "esbuild": "0.14.51",
92
100
  "miniflare": "^2.6.0",
93
101
  "nanoid": "^3.3.3",
94
102
  "path-to-regexp": "^6.2.0",
95
103
  "selfsigned": "^2.0.1",
104
+ "source-map": "^0.7.4",
96
105
  "xxhash-wasm": "^1.0.1"
97
106
  },
98
107
  "devDependencies": {
99
108
  "@iarna/toml": "^3.0.0",
100
109
  "@microsoft/api-extractor": "^7.28.3",
101
110
  "@types/command-exists": "^1.2.0",
111
+ "@types/express": "^4.17.13",
102
112
  "@types/glob-to-regexp": "0.4.1",
103
113
  "@types/mime": "^2.0.3",
104
114
  "@types/prompts": "^2.0.14",
@@ -109,14 +119,16 @@
109
119
  "@types/ws": "^8.5.3",
110
120
  "@types/yargs": "^17.0.10",
111
121
  "@webcontainer/env": "^1.0.1",
112
- "chokidar": "^3.5.3",
122
+ "body-parser": "^1.20.0",
113
123
  "clipboardy": "^3.0.0",
114
124
  "cmd-shim": "^4.1.0",
115
125
  "command-exists": "^1.2.9",
116
126
  "concurrently": "^7.2.2",
127
+ "create-cloudflare": "^1.0.0",
117
128
  "devtools-protocol": "^0.0.955664",
118
129
  "dotenv": "^16.0.0",
119
130
  "execa": "^6.1.0",
131
+ "express": "^4.18.1",
120
132
  "faye-websocket": "^0.11.4",
121
133
  "finalhandler": "^1.2.0",
122
134
  "find-up": "^6.3.0",
@@ -26,7 +26,7 @@ describe("normalizeAndValidateConfig()", () => {
26
26
  compatibility_flags: [],
27
27
  configPath: undefined,
28
28
  dev: {
29
- ip: "localhost",
29
+ ip: "0.0.0.0",
30
30
  local_protocol: "http",
31
31
  port: undefined, // the default of 8787 is set at runtime
32
32
  upstream_protocol: "https",
@@ -40,6 +40,10 @@ describe("normalizeAndValidateConfig()", () => {
40
40
  tsconfig: undefined,
41
41
  kv_namespaces: [],
42
42
  legacy_env: true,
43
+ logfwdr: {
44
+ bindings: [],
45
+ schema: undefined,
46
+ },
43
47
  send_metrics: undefined,
44
48
  main: undefined,
45
49
  migrations: [],
@@ -68,6 +72,7 @@ describe("normalizeAndValidateConfig()", () => {
68
72
  no_bundle: undefined,
69
73
  minify: undefined,
70
74
  node_compat: undefined,
75
+ first_party_worker: undefined,
71
76
  });
72
77
  expect(diagnostics.hasErrors()).toBe(false);
73
78
  expect(diagnostics.hasWarnings()).toBe(false);
@@ -463,6 +468,56 @@ describe("normalizeAndValidateConfig()", () => {
463
468
  });
464
469
 
465
470
  describe("[assets]", () => {
471
+ it("normalizes a string input to an object", () => {
472
+ const { config, diagnostics } = normalizeAndValidateConfig(
473
+ {
474
+ assets: "path/to/assets",
475
+ } as unknown as RawConfig,
476
+ undefined,
477
+ { env: undefined }
478
+ );
479
+
480
+ expect(config.assets).toMatchInlineSnapshot(`
481
+ Object {
482
+ "browser_TTL": undefined,
483
+ "bucket": "path/to/assets",
484
+ "exclude": Array [],
485
+ "include": Array [],
486
+ "serve_single_page_app": false,
487
+ }
488
+ `);
489
+ expect(diagnostics.hasWarnings()).toBe(true);
490
+ expect(diagnostics.hasErrors()).toBe(false);
491
+
492
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
493
+ "Processing wrangler configuration:
494
+ - \\"assets\\" fields are experimental and may change or break at any time."
495
+ `);
496
+ });
497
+
498
+ it("errors when input is not a string or object", () => {
499
+ const { config, diagnostics } = normalizeAndValidateConfig(
500
+ {
501
+ assets: 123,
502
+ } as unknown as RawConfig,
503
+ undefined,
504
+ { env: undefined }
505
+ );
506
+
507
+ expect(config.assets).toBeUndefined();
508
+ expect(diagnostics.hasWarnings()).toBe(true);
509
+ expect(diagnostics.hasErrors()).toBe(true);
510
+
511
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
512
+ "Processing wrangler configuration:
513
+ - \\"assets\\" fields are experimental and may change or break at any time."
514
+ `);
515
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
516
+ "Processing wrangler configuration:
517
+ - Expected the \`assets\` field to be a string or an object, but got number."
518
+ `);
519
+ });
520
+
466
521
  it("should error if `assets` config is missing `bucket`", () => {
467
522
  const expectedConfig: RawConfig = {
468
523
  // @ts-expect-error we're intentionally passing an invalid configuration here
@@ -478,7 +533,9 @@ describe("normalizeAndValidateConfig()", () => {
478
533
  { env: undefined }
479
534
  );
480
535
 
481
- expect(config).toEqual(expect.objectContaining(expectedConfig));
536
+ expect(config.assets).toEqual(
537
+ expect.objectContaining(expectedConfig.assets)
538
+ );
482
539
  expect(diagnostics.hasWarnings()).toBe(true);
483
540
  expect(diagnostics.hasErrors()).toBe(true);
484
541
 
@@ -498,6 +555,8 @@ describe("normalizeAndValidateConfig()", () => {
498
555
  bucket: "BUCKET",
499
556
  include: [222, 333],
500
557
  exclude: [444, 555],
558
+ browser_TTL: "not valid",
559
+ serve_single_page_app: "INVALID",
501
560
  },
502
561
  };
503
562
 
@@ -514,12 +573,14 @@ describe("normalizeAndValidateConfig()", () => {
514
573
  - \\"assets\\" fields are experimental and may change or break at any time."
515
574
  `);
516
575
  expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
517
- "Processing wrangler configuration:
518
- - Expected \\"assets.include.[0]\\" to be of type string but got 222.
519
- - Expected \\"assets.include.[1]\\" to be of type string but got 333.
520
- - Expected \\"assets.exclude.[0]\\" to be of type string but got 444.
521
- - Expected \\"assets.exclude.[1]\\" to be of type string but got 555."
522
- `);
576
+ "Processing wrangler configuration:
577
+ - Expected \\"assets.include.[0]\\" to be of type string but got 222.
578
+ - Expected \\"assets.include.[1]\\" to be of type string but got 333.
579
+ - Expected \\"assets.exclude.[0]\\" to be of type string but got 444.
580
+ - Expected \\"assets.exclude.[1]\\" to be of type string but got 555.
581
+ - Expected \\"assets.browser_TTL\\" to be of type number but got \\"not valid\\".
582
+ - Expected \\"assets.serve_single_page_app\\" to be of type boolean but got \\"INVALID\\"."
583
+ `);
523
584
  });
524
585
  });
525
586
 
@@ -858,6 +919,7 @@ describe("normalizeAndValidateConfig()", () => {
858
919
  no_bundle: true,
859
920
  minify: true,
860
921
  node_compat: true,
922
+ first_party_worker: true,
861
923
  };
862
924
 
863
925
  const { config, diagnostics } = normalizeAndValidateConfig(
@@ -931,6 +993,7 @@ describe("normalizeAndValidateConfig()", () => {
931
993
  no_bundle: "INVALID",
932
994
  minify: "INVALID",
933
995
  node_compat: "INVALID",
996
+ first_party_worker: "INVALID",
934
997
  } as unknown as RawEnvironment;
935
998
 
936
999
  const { config, diagnostics } = normalizeAndValidateConfig(
@@ -997,7 +1060,8 @@ describe("normalizeAndValidateConfig()", () => {
997
1060
  - The field \\"define.DEF1\\" should be a string but got 1777.
998
1061
  - Expected \\"no_bundle\\" to be of type boolean but got \\"INVALID\\".
999
1062
  - Expected \\"minify\\" to be of type boolean but got \\"INVALID\\".
1000
- - Expected \\"node_compat\\" to be of type boolean but got \\"INVALID\\"."
1063
+ - Expected \\"node_compat\\" to be of type boolean but got \\"INVALID\\".
1064
+ - Expected \\"first_party_worker\\" to be of type boolean but got \\"INVALID\\"."
1001
1065
  `);
1002
1066
  });
1003
1067
 
@@ -2311,6 +2375,7 @@ describe("normalizeAndValidateConfig()", () => {
2311
2375
  no_bundle: true,
2312
2376
  minify: true,
2313
2377
  node_compat: true,
2378
+ first_party_worker: true,
2314
2379
  };
2315
2380
 
2316
2381
  const { config, diagnostics } = normalizeAndValidateConfig(
@@ -2354,6 +2419,7 @@ describe("normalizeAndValidateConfig()", () => {
2354
2419
  no_bundle: false,
2355
2420
  minify: false,
2356
2421
  node_compat: false,
2422
+ first_party_worker: false,
2357
2423
  };
2358
2424
  const rawConfig: RawConfig = {
2359
2425
  name: "mock-name",
@@ -2376,6 +2442,7 @@ describe("normalizeAndValidateConfig()", () => {
2376
2442
  no_bundle: true,
2377
2443
  minify: true,
2378
2444
  node_compat: true,
2445
+ first_party_worker: true,
2379
2446
  env: {
2380
2447
  ENV1: rawEnv,
2381
2448
  },
@@ -2637,6 +2704,7 @@ describe("normalizeAndValidateConfig()", () => {
2637
2704
  no_bundle: "INVALID",
2638
2705
  minify: "INVALID",
2639
2706
  node_compat: "INVALID",
2707
+ first_party_worker: "INVALID",
2640
2708
  } as unknown as RawEnvironment;
2641
2709
 
2642
2710
  const { config, diagnostics } = normalizeAndValidateConfig(
@@ -2672,7 +2740,8 @@ describe("normalizeAndValidateConfig()", () => {
2672
2740
  - Expected \\"usage_model\\" field to be one of [\\"bundled\\",\\"unbound\\"] but got \\"INVALID\\".
2673
2741
  - Expected \\"no_bundle\\" to be of type boolean but got \\"INVALID\\".
2674
2742
  - Expected \\"minify\\" to be of type boolean but got \\"INVALID\\".
2675
- - Expected \\"node_compat\\" to be of type boolean but got \\"INVALID\\"."
2743
+ - Expected \\"node_compat\\" to be of type boolean but got \\"INVALID\\".
2744
+ - Expected \\"first_party_worker\\" to be of type boolean but got \\"INVALID\\"."
2676
2745
  `);
2677
2746
  });
2678
2747
 
@@ -3670,6 +3739,7 @@ describe("normalizeAndValidateConfig()", () => {
3670
3739
  it("should remove and warn about deprecated properties", () => {
3671
3740
  const environment: RawEnvironment = {
3672
3741
  zone_id: "ZONE_ID",
3742
+ "kv-namespaces": "BAD_KV_NAMESPACE",
3673
3743
  experimental_services: [
3674
3744
  {
3675
3745
  name: "mock-name",
@@ -3695,14 +3765,16 @@ describe("normalizeAndValidateConfig()", () => {
3695
3765
  expect(diagnostics.hasErrors()).toBe(false);
3696
3766
  expect(diagnostics.hasWarnings()).toBe(true);
3697
3767
  expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
3698
- "Processing wrangler configuration:
3768
+ "Processing wrangler configuration:
3699
3769
 
3700
- - \\"env.ENV1\\" environment configuration
3701
- - Deprecation: \\"zone_id\\":
3702
- This is unnecessary since we can deduce this from routes directly.
3703
- - Deprecation: \\"experimental_services\\":
3704
- The \\"experimental_services\\" field is no longer supported. Simply rename the [experimental_services] field to [services]."
3705
- `);
3770
+ - \\"env.ENV1\\" environment configuration
3771
+ - Deprecation: \\"kv-namespaces\\":
3772
+ The \\"kv-namespaces\\" field is no longer supported, please rename to \\"kv_namespaces\\"
3773
+ - Deprecation: \\"zone_id\\":
3774
+ This is unnecessary since we can deduce this from routes directly.
3775
+ - Deprecation: \\"experimental_services\\":
3776
+ The \\"experimental_services\\" field is no longer supported. Simply rename the [experimental_services] field to [services]."
3777
+ `);
3706
3778
  });
3707
3779
  });
3708
3780
 
@@ -68,6 +68,28 @@ describe("wrangler dev", () => {
68
68
  });
69
69
  });
70
70
 
71
+ describe("usage-model", () => {
72
+ it("should read wrangler.toml's usage_model", async () => {
73
+ writeWranglerToml({
74
+ main: "index.js",
75
+ usage_model: "unbound",
76
+ });
77
+ fs.writeFileSync("index.js", `export default {};`);
78
+ await runWrangler("dev");
79
+ expect((Dev as jest.Mock).mock.calls[0][0].usageModel).toEqual("unbound");
80
+ });
81
+
82
+ it("should read wrangler.toml's usage_model in local mode", async () => {
83
+ writeWranglerToml({
84
+ main: "index.js",
85
+ usage_model: "unbound",
86
+ });
87
+ fs.writeFileSync("index.js", `export default {};`);
88
+ await runWrangler("dev --local");
89
+ expect((Dev as jest.Mock).mock.calls[0][0].usageModel).toEqual("unbound");
90
+ });
91
+ });
92
+
71
93
  describe("entry-points", () => {
72
94
  it("should error if there is no entry-point specified", async () => {
73
95
  writeWranglerToml();
@@ -525,9 +547,9 @@ describe("wrangler dev", () => {
525
547
 
526
548
  await expect(runWrangler("dev")).rejects
527
549
  .toThrowErrorMatchingInlineSnapshot(`
528
- "The expected output file at \\"index.js\\" was not found after running custom build: node -e \\"console.log('custom build');\\".
529
- The \`main\` property in wrangler.toml should point to the file generated by the custom build."
530
- `);
550
+ "The expected output file at \\"index.js\\" was not found after running custom build: node -e \\"console.log('custom build');\\".
551
+ The \`main\` property in wrangler.toml should point to the file generated by the custom build."
552
+ `);
531
553
  expect(std.out).toMatchInlineSnapshot(`
532
554
  "Running custom build: node -e \\"console.log('custom build');\\"
533
555
 
@@ -630,13 +652,13 @@ describe("wrangler dev", () => {
630
652
  });
631
653
 
632
654
  describe("ip", () => {
633
- it("should default ip to localhost", async () => {
655
+ it("should default ip to 0.0.0.0", async () => {
634
656
  writeWranglerToml({
635
657
  main: "index.js",
636
658
  });
637
659
  fs.writeFileSync("index.js", `export default {};`);
638
660
  await runWrangler("dev");
639
- expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("localhost");
661
+ expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("0.0.0.0");
640
662
  expect(std.out).toMatchInlineSnapshot(`""`);
641
663
  expect(std.warn).toMatchInlineSnapshot(`""`);
642
664
  expect(std.err).toMatchInlineSnapshot(`""`);
@@ -673,6 +695,78 @@ describe("wrangler dev", () => {
673
695
  });
674
696
  });
675
697
 
698
+ describe("inspector port", () => {
699
+ it("should use 9229 as the default port", async () => {
700
+ writeWranglerToml({
701
+ main: "index.js",
702
+ });
703
+ fs.writeFileSync("index.js", `export default {};`);
704
+ await runWrangler("dev");
705
+ expect((Dev as jest.Mock).mock.calls[0][0].inspectorPort).toEqual(9229);
706
+ expect(std).toMatchInlineSnapshot(`
707
+ Object {
708
+ "debug": "",
709
+ "err": "",
710
+ "out": "",
711
+ "warn": "",
712
+ }
713
+ `);
714
+ });
715
+
716
+ it("should read --inspector-port", async () => {
717
+ writeWranglerToml({
718
+ main: "index.js",
719
+ });
720
+ fs.writeFileSync("index.js", `export default {};`);
721
+ await runWrangler("dev --inspector-port=9999");
722
+ expect((Dev as jest.Mock).mock.calls[0][0].inspectorPort).toEqual(9999);
723
+ expect(std).toMatchInlineSnapshot(`
724
+ Object {
725
+ "debug": "",
726
+ "err": "",
727
+ "out": "",
728
+ "warn": "",
729
+ }
730
+ `);
731
+ });
732
+
733
+ it("should read dev.inspector_port from wrangler.toml", async () => {
734
+ writeWranglerToml({
735
+ main: "index.js",
736
+ dev: {
737
+ inspector_port: 9999,
738
+ },
739
+ });
740
+ fs.writeFileSync("index.js", `export default {};`);
741
+ await runWrangler("dev");
742
+ expect((Dev as jest.Mock).mock.calls[0][0].inspectorPort).toEqual(9999);
743
+ expect(std).toMatchInlineSnapshot(`
744
+ Object {
745
+ "debug": "",
746
+ "err": "",
747
+ "out": "",
748
+ "warn": "",
749
+ }
750
+ `);
751
+ });
752
+
753
+ it("should error if a bad dev.inspector_port config is provided", async () => {
754
+ writeWranglerToml({
755
+ main: "index.js",
756
+ dev: {
757
+ // @ts-expect-error intentionally bad port
758
+ inspector_port: "some string",
759
+ },
760
+ });
761
+ fs.writeFileSync("index.js", `export default {};`);
762
+ await expect(runWrangler("dev")).rejects
763
+ .toThrowErrorMatchingInlineSnapshot(`
764
+ "Processing wrangler.toml configuration:
765
+ - Expected \\"dev.inspector_port\\" to be of type number but got \\"some string\\"."
766
+ `);
767
+ });
768
+ });
769
+
676
770
  describe("port", () => {
677
771
  it("should default port to 8787 if it is not in use", async () => {
678
772
  writeWranglerToml({
@@ -686,7 +780,7 @@ describe("wrangler dev", () => {
686
780
  expect(std.err).toMatchInlineSnapshot(`""`);
687
781
  });
688
782
 
689
- it("should use to `port` from `wrangler.toml`, if available", async () => {
783
+ it("should use `port` from `wrangler.toml`, if available", async () => {
690
784
  writeWranglerToml({
691
785
  main: "index.js",
692
786
  dev: {
@@ -704,6 +798,22 @@ describe("wrangler dev", () => {
704
798
  expect(std.err).toMatchInlineSnapshot(`""`);
705
799
  });
706
800
 
801
+ it("should error if a bad dev.port config is provided", async () => {
802
+ writeWranglerToml({
803
+ main: "index.js",
804
+ dev: {
805
+ // @ts-expect-error intentionally bad port
806
+ port: "some string",
807
+ },
808
+ });
809
+ fs.writeFileSync("index.js", `export default {};`);
810
+ await expect(runWrangler("dev")).rejects
811
+ .toThrowErrorMatchingInlineSnapshot(`
812
+ "Processing wrangler.toml configuration:
813
+ - Expected \\"dev.port\\" to be of type number but got \\"some string\\"."
814
+ `);
815
+ });
816
+
707
817
  it("should use --port command line arg, if provided", async () => {
708
818
  writeWranglerToml({
709
819
  main: "index.js",
@@ -761,7 +871,7 @@ describe("wrangler dev", () => {
761
871
  });
762
872
  fs.writeFileSync("index.js", `export default {};`);
763
873
  await runWrangler("dev");
764
- expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("localhost");
874
+ expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("0.0.0.0");
765
875
  expect(std.out).toMatchInlineSnapshot(`
766
876
  "Your worker has access to the following bindings:
767
877
  - Durable Objects:
@@ -895,7 +1005,7 @@ describe("wrangler dev", () => {
895
1005
  --compatibility-date Date to use for compatibility checks [string]
896
1006
  --compatibility-flags, --compatibility-flag Flags to use for compatibility checks [array]
897
1007
  --latest Use the latest version of the worker runtime [boolean] [default: true]
898
- --ip IP address to listen on, defaults to \`localhost\` [string]
1008
+ --ip IP address to listen on [string] [default: \\"0.0.0.0\\"]
899
1009
  --port Port to listen on [number]
900
1010
  --inspector-port Port for devtools to connect to [number]
901
1011
  --routes, --route Routes to upload [array]
@@ -950,6 +1060,7 @@ describe("wrangler dev", () => {
950
1060
  it("should error if config.assets and --site are used together", async () => {
951
1061
  writeWranglerToml({
952
1062
  main: "./index.js",
1063
+ // @ts-expect-error we allow string inputs here
953
1064
  assets: "abc",
954
1065
  });
955
1066
  fs.writeFileSync("index.js", `export default {};`);
@@ -963,6 +1074,7 @@ describe("wrangler dev", () => {
963
1074
  it("should error if config.assets and config.site are used together", async () => {
964
1075
  writeWranglerToml({
965
1076
  main: "./index.js",
1077
+ // @ts-expect-error we allow string inputs here
966
1078
  assets: "abc",
967
1079
  site: {
968
1080
  bucket: "xyz",
@@ -1014,6 +1126,7 @@ describe("wrangler dev", () => {
1014
1126
  it("should warn if config.assets is used", async () => {
1015
1127
  writeWranglerToml({
1016
1128
  main: "./index.js",
1129
+ // @ts-expect-error we allow string inputs here
1017
1130
  assets: "./assets",
1018
1131
  });
1019
1132
  fs.writeFileSync("index.js", `export default {};`);
@@ -0,0 +1,93 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import process from "node:process";
4
+ import { setup } from "create-cloudflare";
5
+ import { mockConsoleMethods } from "./helpers/mock-console";
6
+ import { mockConfirm, clearConfirmMocks } from "./helpers/mock-dialogs";
7
+ import { runInTempDir } from "./helpers/run-in-tmp";
8
+ import { runWrangler } from "./helpers/run-wrangler";
9
+
10
+ const createCloudflareMock = setup as jest.Mock;
11
+
12
+ describe("generate", () => {
13
+ runInTempDir();
14
+ const std = mockConsoleMethods();
15
+
16
+ afterEach(() => {
17
+ clearConfirmMocks();
18
+ });
19
+
20
+ it("defers to `wrangler init` when no template is given", async () => {
21
+ mockConfirm(
22
+ {
23
+ text: "Would you like to use git to manage this Worker?",
24
+ result: false,
25
+ },
26
+ {
27
+ text: "No package.json found. Would you like to create one?",
28
+ result: false,
29
+ }
30
+ );
31
+ await runWrangler("generate no-template");
32
+ expect(std.out).toMatchInlineSnapshot(
33
+ `"✨ Created no-template/wrangler.toml"`
34
+ );
35
+ });
36
+
37
+ it("runs `create-cloudflare` when a template is given", async () => {
38
+ await expect(
39
+ runWrangler("generate my-worker some-template")
40
+ ).resolves.toBeUndefined();
41
+
42
+ expect(createCloudflareMock).toBeCalled();
43
+ });
44
+
45
+ it("complains when given the --type argument", async () => {
46
+ await expect(
47
+ runWrangler("generate worker-name worker-template --type rust")
48
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
49
+ `"The --type option is no longer supported."`
50
+ );
51
+ });
52
+
53
+ it("complains when given the --site argument", async () => {
54
+ await expect(runWrangler("generate worker-name worker-template --site"))
55
+ .rejects.toThrowErrorMatchingInlineSnapshot(`
56
+ "The --site option is no longer supported.
57
+ If you wish to create a brand new Worker Sites project then clone the \`worker-sites-template\` starter repository:
58
+
59
+ \`\`\`
60
+ git clone --depth=1 --branch=wrangler2 https://github.com/cloudflare/worker-sites-template worker-name
61
+ cd worker-name
62
+ \`\`\`
63
+
64
+ Find out more about how to create and maintain Sites projects at https://developers.cloudflare.com/workers/platform/sites.
65
+ Have you considered using Cloudflare Pages instead? See https://pages.cloudflare.com/."
66
+ `);
67
+ });
68
+
69
+ it("auto-increments the worker directory name", async () => {
70
+ fs.mkdirSync("my-worker");
71
+ await expect(
72
+ runWrangler("generate my-worker some-template")
73
+ ).resolves.toBeUndefined();
74
+
75
+ expect(createCloudflareMock).lastCalledWith(
76
+ path.resolve(process.cwd(), "my-worker-1"),
77
+ "some-template",
78
+ { debug: false, force: false, init: true }
79
+ );
80
+
81
+ fs.mkdirSync("my-worker-1");
82
+
83
+ await expect(
84
+ runWrangler("generate my-worker some-template")
85
+ ).resolves.toBeUndefined();
86
+
87
+ expect(createCloudflareMock).lastCalledWith(
88
+ path.resolve(process.cwd(), "my-worker-2"),
89
+ "some-template",
90
+ { debug: false, force: false, init: true }
91
+ );
92
+ });
93
+ });
@@ -1,8 +1,10 @@
1
+ import { Readable } from "node:stream";
1
2
  import { URL, URLSearchParams } from "node:url";
2
3
  import { pathToRegexp } from "path-to-regexp";
4
+ import { Response } from "undici";
3
5
  import { getCloudflareApiBaseUrl } from "../../cfetch";
4
6
  import type { FetchResult, FetchError } from "../../cfetch";
5
- import type { RequestInit } from "undici";
7
+ import type { RequestInit, BodyInit, HeadersInit } from "undici";
6
8
 
7
9
  /**
8
10
  * The signature of the function that will handle a mock request.
@@ -180,6 +182,7 @@ export function unsetAllMocks() {
180
182
  */
181
183
 
182
184
  const kvGetMocks = new Map<string, string | Buffer>();
185
+ const r2GetMocks = new Map<string, string | undefined>();
183
186
 
184
187
  /**
185
188
  * @mocked typeof fetchKVGetValue
@@ -206,6 +209,55 @@ export function setMockFetchKVGetValue(
206
209
  kvGetMocks.set(`${accountId}/${namespaceId}/${key}`, value);
207
210
  }
208
211
 
209
- export function unsetMockFetchKVGetValues() {
212
+ /**
213
+ * @mocked typeof fetchR2Objects
214
+ */
215
+ export async function mockFetchR2Objects(
216
+ resource: string,
217
+ bodyInit: {
218
+ body: BodyInit | Readable;
219
+ headers: HeadersInit | undefined;
220
+ method: "PUT" | "GET" | "DELETE";
221
+ }
222
+ ): Promise<Response> {
223
+ /**
224
+ * Here we destroy & removeListeners to "drain" the stream, for testing purposes
225
+ * mimicking the fetch request taking in the stream and draining it.
226
+ */
227
+ if (bodyInit.body instanceof Readable) {
228
+ bodyInit.body.destroy();
229
+ bodyInit.body.removeAllListeners();
230
+ }
231
+
232
+ if (r2GetMocks.has(resource)) {
233
+ const value = r2GetMocks.get(resource);
234
+
235
+ return new Response(value);
236
+ }
237
+ throw new Error(`no mock found for \`r2 object\` - ${resource}`);
238
+ }
239
+
240
+ /**
241
+ * Mock setter for usage within test blocks, companion helper to `mockFetchR2Objects`
242
+ */
243
+ export function setMockFetchR2Objects({
244
+ accountId,
245
+ bucketName,
246
+ objectName,
247
+ mockResponse,
248
+ }: {
249
+ accountId: string;
250
+ bucketName: string;
251
+ objectName: string;
252
+ mockResponse?: string;
253
+ }) {
254
+ r2GetMocks.set(
255
+ `/accounts/${accountId}/r2/buckets/${bucketName}/objects/${objectName}`,
256
+ mockResponse
257
+ );
258
+ }
259
+
260
+ export function unsetSpecialMockFns() {
210
261
  kvGetMocks.clear();
262
+ r2GetMocks.clear();
211
263
  }