wrangler 2.0.7 → 2.0.8

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.
@@ -7,100 +7,210 @@ const { getPackageManager, getPackageManagerName } =
7
7
  jest.requireActual("../package-manager");
8
8
  interface TestCase {
9
9
  npm: boolean;
10
+ pnpm: boolean;
10
11
  yarn: boolean;
11
12
  npmLockFile: boolean;
12
13
  yarnLockFile: boolean;
14
+ pnpmLockFile: boolean;
13
15
  expectedPackageManager: string;
14
16
  }
15
17
 
16
18
  const testCases: TestCase[] = [
17
- // npm binary - no yarn binary
19
+ // npm binary only
18
20
  {
19
21
  npm: true,
20
22
  yarn: false,
23
+ pnpm: false,
21
24
  npmLockFile: false,
22
25
  yarnLockFile: false,
26
+ pnpmLockFile: false,
23
27
  expectedPackageManager: "npm",
24
28
  },
25
29
  {
26
30
  npm: true,
27
31
  yarn: false,
32
+ pnpm: false,
28
33
  npmLockFile: true,
29
34
  yarnLockFile: false,
35
+ pnpmLockFile: false,
30
36
  expectedPackageManager: "npm",
31
37
  },
32
38
  {
33
39
  npm: true,
34
40
  yarn: false,
41
+ pnpm: false,
35
42
  npmLockFile: false,
36
43
  yarnLockFile: true,
44
+ pnpmLockFile: false,
37
45
  expectedPackageManager: "npm",
38
46
  },
39
47
  {
40
48
  npm: true,
41
49
  yarn: false,
50
+ pnpm: false,
42
51
  npmLockFile: true,
43
52
  yarnLockFile: true,
53
+ pnpmLockFile: false,
44
54
  expectedPackageManager: "npm",
45
55
  },
46
56
 
47
- // yarn binary - no npm binary
57
+ // yarn binary only
48
58
  {
49
59
  npm: false,
50
60
  yarn: true,
61
+ pnpm: false,
51
62
  npmLockFile: false,
52
63
  yarnLockFile: false,
64
+ pnpmLockFile: false,
53
65
  expectedPackageManager: "yarn",
54
66
  },
55
67
  {
56
68
  npm: false,
57
69
  yarn: true,
70
+ pnpm: false,
58
71
  npmLockFile: true,
59
72
  yarnLockFile: false,
73
+ pnpmLockFile: false,
60
74
  expectedPackageManager: "yarn",
61
75
  },
62
76
  {
63
77
  npm: false,
64
78
  yarn: true,
79
+ pnpm: false,
65
80
  npmLockFile: false,
66
81
  yarnLockFile: true,
82
+ pnpmLockFile: false,
67
83
  expectedPackageManager: "yarn",
68
84
  },
69
85
  {
70
86
  npm: false,
71
87
  yarn: true,
88
+ pnpm: false,
72
89
  npmLockFile: true,
73
90
  yarnLockFile: true,
91
+ pnpmLockFile: false,
74
92
  expectedPackageManager: "yarn",
75
93
  },
76
94
 
95
+ // pnpm binary only
96
+ {
97
+ npm: false,
98
+ yarn: false,
99
+ pnpm: true,
100
+ npmLockFile: false,
101
+ yarnLockFile: false,
102
+ pnpmLockFile: true,
103
+ expectedPackageManager: "pnpm",
104
+ },
105
+ {
106
+ npm: false,
107
+ yarn: false,
108
+ pnpm: true,
109
+ npmLockFile: true,
110
+ yarnLockFile: false,
111
+ pnpmLockFile: false,
112
+ expectedPackageManager: "pnpm",
113
+ },
114
+ {
115
+ npm: false,
116
+ yarn: false,
117
+ pnpm: true,
118
+ npmLockFile: false,
119
+ yarnLockFile: true,
120
+ pnpmLockFile: false,
121
+ expectedPackageManager: "pnpm",
122
+ },
123
+ {
124
+ npm: false,
125
+ yarn: false,
126
+ pnpm: true,
127
+ npmLockFile: true,
128
+ yarnLockFile: true,
129
+ pnpmLockFile: true,
130
+ expectedPackageManager: "pnpm",
131
+ },
132
+
77
133
  // npm and yarn binaries
78
134
  {
79
135
  npm: true,
80
136
  yarn: true,
137
+ pnpm: false,
81
138
  npmLockFile: false,
82
139
  yarnLockFile: false,
140
+ pnpmLockFile: false,
83
141
  expectedPackageManager: "npm",
84
142
  },
85
143
  {
86
144
  npm: true,
87
145
  yarn: true,
146
+ pnpm: false,
88
147
  npmLockFile: true,
89
148
  yarnLockFile: false,
149
+ pnpmLockFile: false,
90
150
  expectedPackageManager: "npm",
91
151
  },
92
152
  {
93
153
  npm: true,
94
154
  yarn: true,
155
+ pnpm: false,
95
156
  npmLockFile: false,
96
157
  yarnLockFile: true,
158
+ pnpmLockFile: false,
97
159
  expectedPackageManager: "yarn",
98
160
  },
99
161
  {
100
162
  npm: true,
101
163
  yarn: true,
164
+ pnpm: false,
102
165
  npmLockFile: true,
103
166
  yarnLockFile: true,
167
+ pnpmLockFile: false,
168
+ expectedPackageManager: "npm",
169
+ },
170
+ // npm, yarn and pnpm binaries
171
+ {
172
+ npm: true,
173
+ yarn: true,
174
+ pnpm: true,
175
+ npmLockFile: false,
176
+ yarnLockFile: false,
177
+ pnpmLockFile: false,
178
+ expectedPackageManager: "npm",
179
+ },
180
+ {
181
+ npm: true,
182
+ yarn: true,
183
+ pnpm: true,
184
+ npmLockFile: true,
185
+ yarnLockFile: false,
186
+ pnpmLockFile: false,
187
+ expectedPackageManager: "npm",
188
+ },
189
+ {
190
+ npm: true,
191
+ yarn: true,
192
+ pnpm: true,
193
+ npmLockFile: false,
194
+ yarnLockFile: true,
195
+ pnpmLockFile: false,
196
+ expectedPackageManager: "yarn",
197
+ },
198
+ {
199
+ npm: true,
200
+ yarn: true,
201
+ pnpm: true,
202
+ npmLockFile: false,
203
+ yarnLockFile: false,
204
+ pnpmLockFile: true,
205
+ expectedPackageManager: "pnpm",
206
+ },
207
+ {
208
+ npm: true,
209
+ yarn: true,
210
+ pnpm: true,
211
+ npmLockFile: true,
212
+ yarnLockFile: true,
213
+ pnpmLockFile: true,
104
214
  expectedPackageManager: "npm",
105
215
  },
106
216
  ];
@@ -112,12 +222,13 @@ describe("getPackageManager()", () => {
112
222
  describe("no supported package manager", () => {
113
223
  mockYarn(false);
114
224
  mockNpm(false);
225
+ mockPnpm(false);
115
226
 
116
227
  it("should throw an error", async () => {
117
228
  await expect(() =>
118
229
  getPackageManager(process.cwd())
119
230
  ).rejects.toThrowErrorMatchingInlineSnapshot(
120
- `"Unable to find a package manager. Supported managers are: npm and yarn."`
231
+ `"Unable to find a package manager. Supported managers are: npm, yarn, and pnpm."`
121
232
  );
122
233
  });
123
234
  });
@@ -125,16 +236,26 @@ describe("getPackageManager()", () => {
125
236
  for (const {
126
237
  npm,
127
238
  yarn,
239
+ pnpm,
128
240
  npmLockFile,
129
241
  yarnLockFile,
242
+ pnpmLockFile,
130
243
  expectedPackageManager,
131
244
  } of testCases) {
132
245
  describe(
133
- getTestCaseDescription(npm, yarn, npmLockFile, yarnLockFile),
246
+ getTestCaseDescription(
247
+ npm,
248
+ yarn,
249
+ pnpm,
250
+ npmLockFile,
251
+ yarnLockFile,
252
+ pnpmLockFile
253
+ ),
134
254
  () => {
135
255
  mockYarn(yarn);
136
256
  mockNpm(npm);
137
- mockLockFiles(npmLockFile, yarnLockFile);
257
+ mockPnpm(pnpm);
258
+ mockLockFiles(npmLockFile, yarnLockFile, pnpmLockFile);
138
259
 
139
260
  it(`should return the ${expectedPackageManager} package manager`, async () => {
140
261
  const actualPackageManager = await getPackageManager(process.cwd());
@@ -169,10 +290,25 @@ function mockNpm(succeed: boolean): void {
169
290
  afterEach(() => unMock());
170
291
  }
171
292
 
293
+ /**
294
+ * Create a fake pnpm binary
295
+ */
296
+ function mockPnpm(succeed: boolean): void {
297
+ let unMock: () => void;
298
+ beforeEach(async () => {
299
+ unMock = await mockBinary("pnpm", `process.exit(${succeed ? 0 : 1})`);
300
+ });
301
+ afterEach(() => unMock());
302
+ }
303
+
172
304
  /**
173
305
  * Create a fake lock files.
174
306
  */
175
- function mockLockFiles(npmLockFile: boolean, yarnLockFile: boolean) {
307
+ function mockLockFiles(
308
+ npmLockFile: boolean,
309
+ yarnLockFile: boolean,
310
+ pnpmLockFile: boolean
311
+ ) {
176
312
  beforeEach(() => {
177
313
  if (npmLockFile) {
178
314
  writeFileSync("package-lock.json", "");
@@ -180,14 +316,19 @@ function mockLockFiles(npmLockFile: boolean, yarnLockFile: boolean) {
180
316
  if (yarnLockFile) {
181
317
  writeFileSync("yarn.lock", "");
182
318
  }
319
+ if (pnpmLockFile) {
320
+ writeFileSync("pnpm-lock.yaml", "");
321
+ }
183
322
  });
184
323
  }
185
324
 
186
325
  function getTestCaseDescription(
187
326
  npm: boolean,
188
327
  yarn: boolean,
328
+ pnpm: boolean,
189
329
  npmLockFile: boolean,
190
- yarnLockFile: boolean
330
+ yarnLockFile: boolean,
331
+ pnpmLockFile: boolean
191
332
  ): string {
192
333
  const criteria: string[] = [];
193
334
  if (npm) {
@@ -202,5 +343,11 @@ function getTestCaseDescription(
202
343
  if (yarnLockFile) {
203
344
  criteria.push("yarn.lock");
204
345
  }
346
+ if (pnpm) {
347
+ criteria.push("pnpm");
348
+ }
349
+ if (pnpmLockFile) {
350
+ criteria.push("pnpm-lock.yaml");
351
+ }
205
352
  return "using " + criteria.join("; ");
206
353
  }
@@ -79,7 +79,7 @@ describe("pages", () => {
79
79
  expect(std.out).toMatchInlineSnapshot(`
80
80
  "🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose
81
81
 
82
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
82
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
83
83
  `);
84
84
  });
85
85
 
@@ -89,7 +89,7 @@ describe("pages", () => {
89
89
  expect(std.out).toMatchInlineSnapshot(`
90
90
  "🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose
91
91
 
92
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
92
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
93
93
  `);
94
94
  });
95
95
  });
@@ -374,10 +374,10 @@ describe("pages", () => {
374
374
  const body = init.body as FormData;
375
375
  const manifest = JSON.parse(body.get("manifest") as string);
376
376
  expect(manifest).toMatchInlineSnapshot(`
377
- Object {
378
- "/logo.png": "2082190357cfd3617ccfe04f340c6247",
379
- }
380
- `);
377
+ Object {
378
+ "/logo.png": "2082190357cfd3617ccfe04f340c6247",
379
+ }
380
+ `);
381
381
  });
382
382
 
383
383
  return {
@@ -445,10 +445,10 @@ describe("pages", () => {
445
445
  const body = init.body as FormData;
446
446
  const manifest = JSON.parse(body.get("manifest") as string);
447
447
  expect(manifest).toMatchInlineSnapshot(`
448
- Object {
449
- "/logo.txt": "1a98fb08af91aca4a7df1764a2c4ddb0",
450
- }
451
- `);
448
+ Object {
449
+ "/logo.txt": "1a98fb08af91aca4a7df1764a2c4ddb0",
450
+ }
451
+ `);
452
452
  });
453
453
 
454
454
  return {
@@ -533,13 +533,13 @@ describe("pages", () => {
533
533
  const body = init.body as FormData;
534
534
  const manifest = JSON.parse(body.get("manifest") as string);
535
535
  expect(manifest).toMatchInlineSnapshot(`
536
- Object {
537
- "/logo.html": "d96fef225537c9f5e44a3cb27fd0b492",
538
- "/logo.js": "6be321bef99e758250dac034474ddbb8",
539
- "/logo.png": "2082190357cfd3617ccfe04f340c6247",
540
- "/logo.txt": "1a98fb08af91aca4a7df1764a2c4ddb0",
541
- }
542
- `);
536
+ Object {
537
+ "/logo.html": "d96fef225537c9f5e44a3cb27fd0b492",
538
+ "/logo.js": "6be321bef99e758250dac034474ddbb8",
539
+ "/logo.png": "2082190357cfd3617ccfe04f340c6247",
540
+ "/logo.txt": "1a98fb08af91aca4a7df1764a2c4ddb0",
541
+ }
542
+ `);
543
543
  });
544
544
 
545
545
  return {
@@ -1121,7 +1121,7 @@ addEventListener('fetch', event => {});`
1121
1121
 
1122
1122
  expect(std.out).toMatchInlineSnapshot(`
1123
1123
  "
1124
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1124
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
1125
1125
  `);
1126
1126
  expect(std.err).toMatchInlineSnapshot(`
1127
1127
  "X [ERROR] Processing wrangler.toml configuration:
@@ -1279,7 +1279,7 @@ addEventListener('fetch', event => {});`
1279
1279
 
1280
1280
  expect(std.out).toMatchInlineSnapshot(`
1281
1281
  "
1282
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1282
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
1283
1283
  `);
1284
1284
  expect(std.err).toMatchInlineSnapshot(`
1285
1285
  "X [ERROR] Missing entry-point: The entry-point should be specified via the command line (e.g. \`wrangler publish path/to/script\`) or the \`main\` config field.
@@ -1327,6 +1327,56 @@ addEventListener('fetch', event => {});`
1327
1327
  expect(std.err).toMatchInlineSnapshot(`""`);
1328
1328
  });
1329
1329
 
1330
+ it("should not contain backslash for assets with nested directories", async () => {
1331
+ const assets = [
1332
+ { filePath: "assets/subdir/file-1.txt", content: "Content of file-1" },
1333
+ { filePath: "assets/subdir/file-2.txt", content: "Content of file-2" },
1334
+ ];
1335
+ const kvNamespace = {
1336
+ title: "__test-name-workers_sites_assets",
1337
+ id: "__test-name-workers_sites_assets-id",
1338
+ };
1339
+ writeWranglerToml({
1340
+ main: "./index.js",
1341
+ site: {
1342
+ bucket: "assets",
1343
+ },
1344
+ });
1345
+ writeWorkerSource();
1346
+ writeAssets(assets);
1347
+ mockUploadWorkerRequest({
1348
+ expectedBindings: [
1349
+ {
1350
+ name: "__STATIC_CONTENT",
1351
+ namespace_id: "__test-name-workers_sites_assets-id",
1352
+ type: "kv_namespace",
1353
+ },
1354
+ ],
1355
+ expectedModules: {
1356
+ __STATIC_CONTENT_MANIFEST:
1357
+ '{"subdir/file-1.txt":"assets/subdir/file-1.2ca234f380.txt","subdir/file-2.txt":"assets/subdir/file-2.5938485188.txt"}',
1358
+ },
1359
+ });
1360
+ mockSubDomainRequest();
1361
+ mockListKVNamespacesRequest(kvNamespace);
1362
+ mockKeyListRequest(kvNamespace.id, []);
1363
+ mockUploadAssetsToKVRequest(kvNamespace.id, assets);
1364
+
1365
+ await runWrangler("publish");
1366
+
1367
+ expect(std.out).toMatchInlineSnapshot(`
1368
+ "Reading assets/subdir/file-1.txt...
1369
+ Uploading as assets/subdir/file-1.2ca234f380.txt...
1370
+ Reading assets/subdir/file-2.txt...
1371
+ Uploading as assets/subdir/file-2.5938485188.txt...
1372
+ ↗️ Done syncing assets
1373
+ Uploaded test-name (TIMINGS)
1374
+ Published test-name (TIMINGS)
1375
+ test-name.test-sub-domain.workers.dev"
1376
+ `);
1377
+ expect(std.err).toMatchInlineSnapshot(`""`);
1378
+ });
1379
+
1330
1380
  it("when using a service-worker type, it should add an asset manifest as a text_blob, and bind to a namespace", async () => {
1331
1381
  const assets = [
1332
1382
  { filePath: "assets/file-1.txt", content: "Content of file-1" },
@@ -1940,7 +1990,7 @@ addEventListener('fetch', event => {});`
1940
1990
  "Reading assets/large-file.txt...
1941
1991
  Uploading as assets/large-file.0ea0637a45.txt...
1942
1992
 
1943
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1993
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
1944
1994
  `);
1945
1995
  expect(std.err).toMatchInlineSnapshot(`
1946
1996
  "X [ERROR] File assets/too-large-file.txt is too big, it should be under 25 MiB. See https://developers.cloudflare.com/workers/platform/limits#kv-limits
@@ -1980,7 +2030,7 @@ addEventListener('fetch', event => {});`
1980
2030
  expect(std.out).toMatchInlineSnapshot(`
1981
2031
  "Reading assets/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/file.txt...
1982
2032
 
1983
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
2033
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
1984
2034
  `);
1985
2035
  expect(std.err).toMatchInlineSnapshot(`
1986
2036
  "X [ERROR] The asset path key \\"assets/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/file.3da0d0cd12.txt\\" exceeds the maximum key size limit of 512. See https://developers.cloudflare.com/workers/platform/limits#kv-limits\\",
@@ -2691,7 +2741,7 @@ addEventListener('fetch', event => {});`
2691
2741
  expect(std.out).toMatchInlineSnapshot(`
2692
2742
  "Running custom build: node -e \\"console.log('custom build');\\"
2693
2743
 
2694
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
2744
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
2695
2745
  `);
2696
2746
  expect(std.err).toMatchInlineSnapshot(`
2697
2747
  "X [ERROR] The expected output file at \\"index.js\\" was not found after running custom build: node -e \\"console.log('custom build');\\".
@@ -2730,7 +2780,7 @@ addEventListener('fetch', event => {});`
2730
2780
  expect(std.out).toMatchInlineSnapshot(`
2731
2781
  "Running custom build: node -e \\"console.log('custom build');\\"
2732
2782
 
2733
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
2783
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
2734
2784
  `);
2735
2785
  expect(std.err).toMatchInlineSnapshot(`
2736
2786
  "X [ERROR] The expected output file at \\".\\" was not found after running custom build: node -e \\"console.log('custom build');\\".
@@ -2845,7 +2895,15 @@ addEventListener('fetch', event => {});`
2845
2895
 
2846
2896
  - In wrangler.toml, you have configured [durable_objects] exported by this Worker (SomeClass),
2847
2897
  but no [migrations] for them. This may not work as expected until you add a [migrations] section
2848
- to your wrangler.toml. Refer to
2898
+ to your wrangler.toml. Add this configuration to your wrangler.toml:
2899
+
2900
+ \`\`\`
2901
+ [[migrations]]
2902
+ tag = \\"v1\\" # Should be unique for each entry
2903
+ new_classes = [\\"SomeClass\\"]
2904
+ \`\`\`
2905
+
2906
+ Refer to
2849
2907
  https://developers.cloudflare.com/workers/learning/using-durable-objects/#durable-object-migrations-in-wranglertoml
2850
2908
  for more details.
2851
2909
 
@@ -3531,7 +3589,7 @@ addEventListener('fetch', event => {});`
3531
3589
  `);
3532
3590
  expect(std.out).toMatchInlineSnapshot(`
3533
3591
  "
3534
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
3592
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
3535
3593
  `);
3536
3594
  expect(std.err).toMatchInlineSnapshot(`
3537
3595
  "X [ERROR] Processing wrangler.toml configuration:
@@ -3630,7 +3688,7 @@ addEventListener('fetch', event => {});`
3630
3688
  `);
3631
3689
  expect(std.out).toMatchInlineSnapshot(`
3632
3690
  "
3633
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
3691
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
3634
3692
  `);
3635
3693
  expect(std.err).toMatchInlineSnapshot(`
3636
3694
  "X [ERROR] Processing wrangler.toml configuration:
@@ -3764,7 +3822,7 @@ addEventListener('fetch', event => {});`
3764
3822
  `);
3765
3823
  expect(std.out).toMatchInlineSnapshot(`
3766
3824
  "
3767
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
3825
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
3768
3826
  `);
3769
3827
  expect(std.err).toMatchInlineSnapshot(`
3770
3828
  "X [ERROR] Processing wrangler.toml configuration:
@@ -3838,7 +3896,7 @@ addEventListener('fetch', event => {});`
3838
3896
  );
3839
3897
  expect(std.out).toMatchInlineSnapshot(`
3840
3898
  "
3841
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
3899
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
3842
3900
  `);
3843
3901
  expect(std.err).toMatchInlineSnapshot(`
3844
3902
  "X [ERROR] You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code
@@ -3975,7 +4033,7 @@ addEventListener('fetch', event => {});`
3975
4033
  );
3976
4034
  expect(std.out).toMatchInlineSnapshot(`
3977
4035
  "
3978
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
4036
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
3979
4037
  `);
3980
4038
  expect(std.err).toMatchInlineSnapshot(`
3981
4039
  "X [ERROR] You cannot configure [text_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure \`[rules]\` in your wrangler.toml
@@ -4084,7 +4142,7 @@ addEventListener('fetch', event => {});`
4084
4142
  );
4085
4143
  expect(std.out).toMatchInlineSnapshot(`
4086
4144
  "
4087
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
4145
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
4088
4146
  `);
4089
4147
  expect(std.err).toMatchInlineSnapshot(`
4090
4148
  "X [ERROR] You cannot configure [data_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure \`[rules]\` in your wrangler.toml
@@ -148,7 +148,7 @@ describe("wrangler secret", () => {
148
148
  }
149
149
  expect(std.out).toMatchInlineSnapshot(`
150
150
  "
151
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
151
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
152
152
  `);
153
153
  expect(std.err).toMatchInlineSnapshot(`
154
154
  "X [ERROR] Missing script name
@@ -204,7 +204,7 @@ describe("wrangler secret", () => {
204
204
 
205
205
  expect(std.out).toMatchInlineSnapshot(`
206
206
  "
207
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
207
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
208
208
  `);
209
209
  expect(std.warn).toMatchInlineSnapshot(`""`);
210
210
  });
@@ -372,7 +372,7 @@ describe("wrangler secret", () => {
372
372
  }
373
373
  expect(std.out).toMatchInlineSnapshot(`
374
374
  "
375
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
375
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
376
376
  `);
377
377
  expect(std.err).toMatchInlineSnapshot(`
378
378
  "X [ERROR] Missing script name
@@ -468,7 +468,7 @@ describe("wrangler secret", () => {
468
468
  }
469
469
  expect(std.out).toMatchInlineSnapshot(`
470
470
  "
471
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
471
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
472
472
  `);
473
473
  expect(std.err).toMatchInlineSnapshot(`
474
474
  "X [ERROR] Missing script name
@@ -10,6 +10,7 @@ export { getCloudflareAPIBaseURL as getCloudflareApiBaseUrl } from "./internal";
10
10
  export interface FetchError {
11
11
  code: number;
12
12
  message: string;
13
+ error_chain?: FetchError[];
13
14
  }
14
15
  export interface FetchResult<ResponseType = unknown> {
15
16
  success: boolean;
@@ -85,9 +86,9 @@ function throwFetchError(
85
86
  response: FetchResult<unknown>
86
87
  ): never {
87
88
  const error = new ParseError({
88
- text: "Received a bad response from the API",
89
+ text: `A request to the Cloudflare API (${resource}) failed.`,
89
90
  notes: response.errors.map((err) => ({
90
- text: err.code ? `${err.message} [code: ${err.code}]` : err.message,
91
+ text: renderError(err),
91
92
  })),
92
93
  });
93
94
  // add the first error code directly to this error
@@ -104,3 +105,17 @@ function hasCursor(result_info: unknown): result_info is { cursor: string } {
104
105
  const cursor = (result_info as { cursor: string } | undefined)?.cursor;
105
106
  return cursor !== undefined && cursor !== null && cursor !== "";
106
107
  }
108
+
109
+ function renderError(err: FetchError, level = 0): string {
110
+ const chainedMessages =
111
+ err.error_chain
112
+ ?.map(
113
+ (chainedError) =>
114
+ `\n${" ".repeat(level)}- ${renderError(chainedError, level + 1)}`
115
+ )
116
+ .join("\n") ?? "";
117
+ return (
118
+ (err.code ? `${err.message} [code: ${err.code}]` : err.message) +
119
+ chainedMessages
120
+ );
121
+ }
@@ -1,4 +1,5 @@
1
1
  import { fetch, Headers } from "undici";
2
+ import { version as wranglerVersion } from "../../package.json";
2
3
  import { getEnvironmentVariableFactory } from "../environment-variables";
3
4
  import { ParseError, parseJSON } from "../parse";
4
5
  import { getAPIToken, loginOrRefreshIfRequired } from "../user";
@@ -33,6 +34,7 @@ export async function fetchInternal<ResponseType>(
33
34
  const apiToken = requireApiToken();
34
35
  const headers = cloneHeaders(init.headers);
35
36
  addAuthorizationHeaderIfUnspecified(headers, apiToken);
37
+ addUserAgent(headers);
36
38
 
37
39
  const queryString = queryParams ? `?${queryParams.toString()}` : "";
38
40
  const method = init.method ?? "GET";
@@ -105,6 +107,10 @@ function addAuthorizationHeaderIfUnspecified(
105
107
  }
106
108
  }
107
109
 
110
+ function addUserAgent(headers: Record<string, string>): void {
111
+ headers["User-Agent"] = `wrangler/${wranglerVersion}`;
112
+ }
113
+
108
114
  /**
109
115
  * The implementation for fetching a kv value from the cloudflare API.
110
116
  * We special-case this one call, because it's the only API call that