wrangler 0.0.0-fb43821 → 0.0.0-fe5dcfd

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 (41) hide show
  1. package/package.json +25 -10
  2. package/pages/functions/buildWorker.ts +62 -0
  3. package/pages/functions/filepath-routing.test.ts +39 -0
  4. package/pages/functions/filepath-routing.ts +228 -0
  5. package/pages/functions/identifiers.ts +78 -0
  6. package/pages/functions/routes.ts +158 -0
  7. package/pages/functions/template-worker.ts +136 -0
  8. package/src/__tests__/dev.test.tsx +67 -0
  9. package/src/__tests__/index.test.ts +42 -175
  10. package/src/__tests__/jest.setup.ts +22 -0
  11. package/src/__tests__/kv.test.ts +1199 -0
  12. package/src/__tests__/mock-cfetch.ts +173 -0
  13. package/src/__tests__/mock-dialogs.ts +65 -0
  14. package/src/__tests__/publish.test.ts +737 -0
  15. package/src/__tests__/run-in-tmp.ts +19 -0
  16. package/src/__tests__/run-wrangler.ts +32 -0
  17. package/src/__tests__/whoami.test.tsx +130 -0
  18. package/src/api/form_data.ts +66 -76
  19. package/src/api/preview.ts +4 -4
  20. package/src/api/worker.ts +38 -44
  21. package/src/cfetch/index.ts +110 -0
  22. package/src/cfetch/internal.ts +71 -0
  23. package/src/cli.ts +6 -7
  24. package/src/config.ts +634 -110
  25. package/src/dev.tsx +275 -272
  26. package/src/dialogs.tsx +2 -2
  27. package/src/index.tsx +504 -403
  28. package/src/inspect.ts +543 -0
  29. package/src/kv.tsx +109 -44
  30. package/src/pages.tsx +444 -222
  31. package/src/proxy.ts +302 -0
  32. package/src/publish.ts +123 -87
  33. package/src/sites.tsx +169 -41
  34. package/src/tail.tsx +5 -3
  35. package/src/user.tsx +64 -74
  36. package/src/whoami.tsx +69 -0
  37. package/wrangler-dist/cli.js +42792 -42619
  38. package/wrangler-dist/cli.js.map +3 -3
  39. package/src/__tests__/mock-cfetch.js +0 -42
  40. package/src/api/inspect.ts +0 -430
  41. package/src/cfetch.ts +0 -72
@@ -0,0 +1,737 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import type { KVNamespaceInfo } from "../kv";
4
+ import { mockKeyListRequest } from "./kv.test";
5
+ import { setMockResponse, unsetAllMocks } from "./mock-cfetch";
6
+ import { runInTempDir } from "./run-in-tmp";
7
+ import { runWrangler } from "./run-wrangler";
8
+
9
+ describe("publish", () => {
10
+ runInTempDir();
11
+
12
+ afterEach(() => {
13
+ unsetAllMocks();
14
+ });
15
+
16
+ describe("entry-points", () => {
17
+ it("should be able to use `index` with no extension as the entry-point", async () => {
18
+ writeWranglerToml();
19
+ writeEsmWorkerSource();
20
+ mockUploadWorkerRequest();
21
+ mockSubDomainRequest();
22
+
23
+ const { stdout, stderr, error } = await runWrangler("publish ./index");
24
+
25
+ expect(stripTimings(stdout)).toMatchInlineSnapshot(`
26
+ "Uploaded
27
+ test-name
28
+ (TIMINGS)
29
+ Deployed
30
+ test-name
31
+ (TIMINGS)
32
+
33
+ test-name.test-sub-domain.workers.dev"
34
+ `);
35
+ expect(stderr).toMatchInlineSnapshot(`""`);
36
+ expect(error).toMatchInlineSnapshot(`undefined`);
37
+ });
38
+
39
+ it("should be able to use the `build.upload.main` config as the entry-point for ESM sources", async () => {
40
+ writeWranglerToml("./index.js");
41
+ writeEsmWorkerSource();
42
+ mockUploadWorkerRequest();
43
+ mockSubDomainRequest();
44
+
45
+ const { stdout, stderr, error } = await runWrangler("publish");
46
+
47
+ expect(stripTimings(stdout)).toMatchInlineSnapshot(`
48
+ "Uploaded
49
+ test-name
50
+ (TIMINGS)
51
+ Deployed
52
+ test-name
53
+ (TIMINGS)
54
+
55
+ test-name.test-sub-domain.workers.dev"
56
+ `);
57
+ expect(stderr).toMatchInlineSnapshot(`""`);
58
+ expect(error).toMatchInlineSnapshot(`undefined`);
59
+ });
60
+
61
+ it("should be able to transpile TypeScript", async () => {
62
+ writeWranglerToml();
63
+ writeEsmWorkerSource({ format: "ts" });
64
+ mockUploadWorkerRequest({ expectedBody: "var foo = 100;" });
65
+ mockSubDomainRequest();
66
+
67
+ const { stdout, stderr, error } = await runWrangler(
68
+ "publish " + fs.realpathSync("./index.ts")
69
+ );
70
+
71
+ expect(stripTimings(stdout)).toMatchInlineSnapshot(`
72
+ "Uploaded
73
+ test-name
74
+ (TIMINGS)
75
+ Deployed
76
+ test-name
77
+ (TIMINGS)
78
+
79
+ test-name.test-sub-domain.workers.dev"
80
+ `);
81
+ expect(stderr).toMatchInlineSnapshot(`""`);
82
+ expect(error).toMatchInlineSnapshot(`undefined`);
83
+ });
84
+
85
+ it("should be able to transpile entry-points in sub-directories", async () => {
86
+ writeWranglerToml();
87
+ writeEsmWorkerSource({ basePath: "./src" });
88
+ mockUploadWorkerRequest({ expectedBody: "var foo = 100;" });
89
+ mockSubDomainRequest();
90
+
91
+ const { stdout, stderr, error } = await runWrangler(
92
+ "publish ./src/index.js"
93
+ );
94
+
95
+ expect(stripTimings(stdout)).toMatchInlineSnapshot(`
96
+ "Uploaded
97
+ test-name
98
+ (TIMINGS)
99
+ Deployed
100
+ test-name
101
+ (TIMINGS)
102
+
103
+ test-name.test-sub-domain.workers.dev"
104
+ `);
105
+ expect(stderr).toMatchInlineSnapshot(`""`);
106
+ expect(error).toMatchInlineSnapshot(`undefined`);
107
+ });
108
+ });
109
+
110
+ describe("asset upload", () => {
111
+ it("should upload all the files in the directory specified by `config.site.bucket`", async () => {
112
+ const assets = [
113
+ { filePath: "assets/file-1.txt", content: "Content of file-1" },
114
+ { filePath: "assets/file-2.txt", content: "Content of file-2" },
115
+ ];
116
+ const kvNamespace = {
117
+ title: "__test-name_sites_assets",
118
+ id: "__test-name_sites_assets-id",
119
+ };
120
+ writeWranglerToml("./index.js", "assets");
121
+ writeEsmWorkerSource();
122
+ writeAssets(assets);
123
+ mockUploadWorkerRequest();
124
+ mockSubDomainRequest();
125
+ mockListKVNamespacesRequest(kvNamespace);
126
+ mockKeyListRequest(kvNamespace.id, []);
127
+ mockUploadAssetsToKVRequest(kvNamespace.id, assets);
128
+ const { stdout, stderr, error } = await runWrangler("publish");
129
+
130
+ expect(stripTimings(stdout)).toMatchInlineSnapshot(`
131
+ "reading assets/file-1.txt...
132
+ uploading as assets/file-1.2ca234f380.txt...
133
+ reading assets/file-2.txt...
134
+ uploading as assets/file-2.5938485188.txt...
135
+ Uploaded
136
+ test-name
137
+ (TIMINGS)
138
+ Deployed
139
+ test-name
140
+ (TIMINGS)
141
+
142
+ test-name.test-sub-domain.workers.dev"
143
+ `);
144
+ expect(stderr).toMatchInlineSnapshot(`""`);
145
+ expect(error).toMatchInlineSnapshot(`undefined`);
146
+ });
147
+
148
+ it("should only upload files that are not already in the KV namespace", async () => {
149
+ const assets = [
150
+ { filePath: "assets/file-1.txt", content: "Content of file-1" },
151
+ { filePath: "assets/file-2.txt", content: "Content of file-2" },
152
+ ];
153
+ const kvNamespace = {
154
+ title: "__test-name_sites_assets",
155
+ id: "__test-name_sites_assets-id",
156
+ };
157
+ writeWranglerToml("./index.js", "assets");
158
+ writeEsmWorkerSource();
159
+ writeAssets(assets);
160
+ mockUploadWorkerRequest();
161
+ mockSubDomainRequest();
162
+ mockListKVNamespacesRequest(kvNamespace);
163
+ // Put file-1 in the KV namespace
164
+ mockKeyListRequest(kvNamespace.id, ["assets/file-1.2ca234f380.txt"]);
165
+ // Check we do not upload file-1
166
+ mockUploadAssetsToKVRequest(
167
+ kvNamespace.id,
168
+ assets.filter((a) => a.filePath !== "assets/file-1.txt")
169
+ );
170
+ const { stdout, stderr, error } = await runWrangler("publish");
171
+
172
+ expect(stripTimings(stdout)).toMatchInlineSnapshot(`
173
+ "reading assets/file-1.txt...
174
+ skipping - already uploaded
175
+ reading assets/file-2.txt...
176
+ uploading as assets/file-2.5938485188.txt...
177
+ Uploaded
178
+ test-name
179
+ (TIMINGS)
180
+ Deployed
181
+ test-name
182
+ (TIMINGS)
183
+
184
+ test-name.test-sub-domain.workers.dev"
185
+ `);
186
+ expect(stderr).toMatchInlineSnapshot(`""`);
187
+ expect(error).toMatchInlineSnapshot(`undefined`);
188
+ });
189
+
190
+ it("should only upload files that match the `site-include` arg", async () => {
191
+ const assets = [
192
+ { filePath: "assets/file-1.txt", content: "Content of file-1" },
193
+ { filePath: "assets/file-2.txt", content: "Content of file-2" },
194
+ ];
195
+ const kvNamespace = {
196
+ title: "__test-name_sites_assets",
197
+ id: "__test-name_sites_assets-id",
198
+ };
199
+ writeWranglerToml("./index.js", "assets");
200
+ writeEsmWorkerSource();
201
+ writeAssets(assets);
202
+ mockUploadWorkerRequest();
203
+ mockSubDomainRequest();
204
+ mockListKVNamespacesRequest(kvNamespace);
205
+ mockKeyListRequest(kvNamespace.id, []);
206
+ // Check we only upload file-1
207
+ mockUploadAssetsToKVRequest(
208
+ kvNamespace.id,
209
+ assets.filter((a) => a.filePath === "assets/file-1.txt")
210
+ );
211
+ const { stdout, stderr, error } = await runWrangler(
212
+ "publish --site-include file-1.txt"
213
+ );
214
+
215
+ expect(stripTimings(stdout)).toMatchInlineSnapshot(`
216
+ "reading assets/file-1.txt...
217
+ uploading as assets/file-1.2ca234f380.txt...
218
+ Uploaded
219
+ test-name
220
+ (TIMINGS)
221
+ Deployed
222
+ test-name
223
+ (TIMINGS)
224
+
225
+ test-name.test-sub-domain.workers.dev"
226
+ `);
227
+ expect(stderr).toMatchInlineSnapshot(`""`);
228
+ expect(error).toMatchInlineSnapshot(`undefined`);
229
+ });
230
+
231
+ it("should not upload files that match the `site-exclude` arg", async () => {
232
+ const assets = [
233
+ { filePath: "assets/file-1.txt", content: "Content of file-1" },
234
+ { filePath: "assets/file-2.txt", content: "Content of file-2" },
235
+ ];
236
+ const kvNamespace = {
237
+ title: "__test-name_sites_assets",
238
+ id: "__test-name_sites_assets-id",
239
+ };
240
+ writeWranglerToml("./index.js", "assets");
241
+ writeEsmWorkerSource();
242
+ writeAssets(assets);
243
+ mockUploadWorkerRequest();
244
+ mockSubDomainRequest();
245
+ mockListKVNamespacesRequest(kvNamespace);
246
+ mockKeyListRequest(kvNamespace.id, []);
247
+ // Check we only upload file-1
248
+ mockUploadAssetsToKVRequest(
249
+ kvNamespace.id,
250
+ assets.filter((a) => a.filePath === "assets/file-1.txt")
251
+ );
252
+ const { stdout, stderr, error } = await runWrangler(
253
+ "publish --site-exclude file-2.txt"
254
+ );
255
+
256
+ expect(stripTimings(stdout)).toMatchInlineSnapshot(`
257
+ "reading assets/file-1.txt...
258
+ uploading as assets/file-1.2ca234f380.txt...
259
+ Uploaded
260
+ test-name
261
+ (TIMINGS)
262
+ Deployed
263
+ test-name
264
+ (TIMINGS)
265
+
266
+ test-name.test-sub-domain.workers.dev"
267
+ `);
268
+ expect(stderr).toMatchInlineSnapshot(`""`);
269
+ expect(error).toMatchInlineSnapshot(`undefined`);
270
+ });
271
+
272
+ it("should only upload files that match the `site.include` config", async () => {
273
+ const assets = [
274
+ { filePath: "assets/file-1.txt", content: "Content of file-1" },
275
+ { filePath: "assets/file-2.txt", content: "Content of file-2" },
276
+ ];
277
+ const kvNamespace = {
278
+ title: "__test-name_sites_assets",
279
+ id: "__test-name_sites_assets-id",
280
+ };
281
+ writeWranglerToml("./index.js", "assets", ["file-1.txt"]);
282
+ writeEsmWorkerSource();
283
+ writeAssets(assets);
284
+ mockUploadWorkerRequest();
285
+ mockSubDomainRequest();
286
+ mockListKVNamespacesRequest(kvNamespace);
287
+ mockKeyListRequest(kvNamespace.id, []);
288
+ // Check we only upload file-1
289
+ mockUploadAssetsToKVRequest(
290
+ kvNamespace.id,
291
+ assets.filter((a) => a.filePath === "assets/file-1.txt")
292
+ );
293
+ const { stdout, stderr, error } = await runWrangler("publish");
294
+
295
+ expect(stripTimings(stdout)).toMatchInlineSnapshot(`
296
+ "reading assets/file-1.txt...
297
+ uploading as assets/file-1.2ca234f380.txt...
298
+ Uploaded
299
+ test-name
300
+ (TIMINGS)
301
+ Deployed
302
+ test-name
303
+ (TIMINGS)
304
+
305
+ test-name.test-sub-domain.workers.dev"
306
+ `);
307
+ expect(stderr).toMatchInlineSnapshot(`""`);
308
+ expect(error).toMatchInlineSnapshot(`undefined`);
309
+ });
310
+
311
+ it("should not upload files that match the `site.exclude` config", async () => {
312
+ const assets = [
313
+ { filePath: "assets/file-1.txt", content: "Content of file-1" },
314
+ { filePath: "assets/file-2.txt", content: "Content of file-2" },
315
+ ];
316
+ const kvNamespace = {
317
+ title: "__test-name_sites_assets",
318
+ id: "__test-name_sites_assets-id",
319
+ };
320
+ writeWranglerToml("./index.js", "assets", undefined, ["file-2.txt"]);
321
+ writeEsmWorkerSource();
322
+ writeAssets(assets);
323
+ mockUploadWorkerRequest();
324
+ mockSubDomainRequest();
325
+ mockListKVNamespacesRequest(kvNamespace);
326
+ mockKeyListRequest(kvNamespace.id, []);
327
+ // Check we only upload file-1
328
+ mockUploadAssetsToKVRequest(
329
+ kvNamespace.id,
330
+ assets.filter((a) => a.filePath === "assets/file-1.txt")
331
+ );
332
+ const { stdout, stderr, error } = await runWrangler("publish");
333
+
334
+ expect(stripTimings(stdout)).toMatchInlineSnapshot(`
335
+ "reading assets/file-1.txt...
336
+ uploading as assets/file-1.2ca234f380.txt...
337
+ Uploaded
338
+ test-name
339
+ (TIMINGS)
340
+ Deployed
341
+ test-name
342
+ (TIMINGS)
343
+
344
+ test-name.test-sub-domain.workers.dev"
345
+ `);
346
+ expect(stderr).toMatchInlineSnapshot(`""`);
347
+ expect(error).toMatchInlineSnapshot(`undefined`);
348
+ });
349
+
350
+ it("should use `site-include` arg over `site.include` config", async () => {
351
+ const assets = [
352
+ { filePath: "assets/file-1.txt", content: "Content of file-1" },
353
+ { filePath: "assets/file-2.txt", content: "Content of file-2" },
354
+ ];
355
+ const kvNamespace = {
356
+ title: "__test-name_sites_assets",
357
+ id: "__test-name_sites_assets-id",
358
+ };
359
+ writeWranglerToml("./index.js", "assets", ["file-2.txt"]);
360
+ writeEsmWorkerSource();
361
+ writeAssets(assets);
362
+ mockUploadWorkerRequest();
363
+ mockSubDomainRequest();
364
+ mockListKVNamespacesRequest(kvNamespace);
365
+ mockKeyListRequest(kvNamespace.id, []);
366
+ // Check we only upload file-1
367
+ mockUploadAssetsToKVRequest(
368
+ kvNamespace.id,
369
+ assets.filter((a) => a.filePath === "assets/file-1.txt")
370
+ );
371
+ const { stdout, stderr, error } = await runWrangler(
372
+ "publish --site-include file-1.txt"
373
+ );
374
+
375
+ expect(stripTimings(stdout)).toMatchInlineSnapshot(`
376
+ "reading assets/file-1.txt...
377
+ uploading as assets/file-1.2ca234f380.txt...
378
+ Uploaded
379
+ test-name
380
+ (TIMINGS)
381
+ Deployed
382
+ test-name
383
+ (TIMINGS)
384
+
385
+ test-name.test-sub-domain.workers.dev"
386
+ `);
387
+ expect(stderr).toMatchInlineSnapshot(`""`);
388
+ expect(error).toMatchInlineSnapshot(`undefined`);
389
+ });
390
+
391
+ it("should use `site-exclude` arg over `site.exclude` config", async () => {
392
+ const assets = [
393
+ { filePath: "assets/file-1.txt", content: "Content of file-1" },
394
+ { filePath: "assets/file-2.txt", content: "Content of file-2" },
395
+ ];
396
+ const kvNamespace = {
397
+ title: "__test-name_sites_assets",
398
+ id: "__test-name_sites_assets-id",
399
+ };
400
+ writeWranglerToml("./index.js", "assets", undefined, [
401
+ "assets/file-1.txt",
402
+ ]);
403
+ writeEsmWorkerSource();
404
+ writeAssets(assets);
405
+ mockUploadWorkerRequest();
406
+ mockSubDomainRequest();
407
+ mockListKVNamespacesRequest(kvNamespace);
408
+ mockKeyListRequest(kvNamespace.id, []);
409
+ // Check we only upload file-1
410
+ mockUploadAssetsToKVRequest(
411
+ kvNamespace.id,
412
+ assets.filter((a) => a.filePath.endsWith("assets/file-1.txt"))
413
+ );
414
+ const { stdout, stderr, error } = await runWrangler(
415
+ "publish --site-exclude file-2.txt"
416
+ );
417
+
418
+ expect(stripTimings(stdout)).toMatchInlineSnapshot(`
419
+ "reading assets/file-1.txt...
420
+ uploading as assets/file-1.2ca234f380.txt...
421
+ Uploaded
422
+ test-name
423
+ (TIMINGS)
424
+ Deployed
425
+ test-name
426
+ (TIMINGS)
427
+
428
+ test-name.test-sub-domain.workers.dev"
429
+ `);
430
+ expect(stderr).toMatchInlineSnapshot(`""`);
431
+ expect(error).toMatchInlineSnapshot(`undefined`);
432
+ });
433
+
434
+ it("should walk directories except node_modules", async () => {
435
+ const assets = [
436
+ {
437
+ filePath: "assets/directory-1/file-1.txt",
438
+ content: "Content of file-1",
439
+ },
440
+ {
441
+ filePath: "assets/node_modules/file-2.txt",
442
+ content: "Content of file-2",
443
+ },
444
+ ];
445
+ const kvNamespace = {
446
+ title: "__test-name_sites_assets",
447
+ id: "__test-name_sites_assets-id",
448
+ };
449
+ writeWranglerToml("./index.js", "assets");
450
+ writeEsmWorkerSource();
451
+ writeAssets(assets);
452
+ mockUploadWorkerRequest();
453
+ mockSubDomainRequest();
454
+ mockListKVNamespacesRequest(kvNamespace);
455
+ mockKeyListRequest(kvNamespace.id, []);
456
+ // Only expect file-1 to be uploaded
457
+ mockUploadAssetsToKVRequest(kvNamespace.id, assets.slice(0, 1));
458
+ const { stdout, stderr, error } = await runWrangler("publish");
459
+
460
+ expect(stripTimings(stdout)).toMatchInlineSnapshot(`
461
+ "reading assets/directory-1/file-1.txt...
462
+ uploading as assets/directory-1/file-1.2ca234f380.txt...
463
+ Uploaded
464
+ test-name
465
+ (TIMINGS)
466
+ Deployed
467
+ test-name
468
+ (TIMINGS)
469
+
470
+ test-name.test-sub-domain.workers.dev"
471
+ `);
472
+ expect(stderr).toMatchInlineSnapshot(`""`);
473
+ expect(error).toMatchInlineSnapshot(`undefined`);
474
+ });
475
+
476
+ it("should skip hidden files and directories except `.well-known`", async () => {
477
+ const assets = [
478
+ {
479
+ filePath: "assets/.hidden-file.txt",
480
+ content: "Content of hidden-file",
481
+ },
482
+ {
483
+ filePath: "assets/.hidden/file-1.txt",
484
+ content: "Content of file-1",
485
+ },
486
+ {
487
+ filePath: "assets/.well-known/file-2.txt",
488
+ content: "Content of file-2",
489
+ },
490
+ ];
491
+ const kvNamespace = {
492
+ title: "__test-name_sites_assets",
493
+ id: "__test-name_sites_assets-id",
494
+ };
495
+ writeWranglerToml("./index.js", "assets");
496
+ writeEsmWorkerSource();
497
+ writeAssets(assets);
498
+ mockUploadWorkerRequest();
499
+ mockSubDomainRequest();
500
+ mockListKVNamespacesRequest(kvNamespace);
501
+ mockKeyListRequest(kvNamespace.id, []);
502
+ // Only expect file-2 to be uploaded
503
+ mockUploadAssetsToKVRequest(kvNamespace.id, assets.slice(2));
504
+ const { stdout, stderr, error } = await runWrangler("publish");
505
+
506
+ expect(stripTimings(stdout)).toMatchInlineSnapshot(`
507
+ "reading assets/.well-known/file-2.txt...
508
+ uploading as assets/.well-known/file-2.5938485188.txt...
509
+ Uploaded
510
+ test-name
511
+ (TIMINGS)
512
+ Deployed
513
+ test-name
514
+ (TIMINGS)
515
+
516
+ test-name.test-sub-domain.workers.dev"
517
+ `);
518
+ expect(stderr).toMatchInlineSnapshot(`""`);
519
+ expect(error).toMatchInlineSnapshot(`undefined`);
520
+ });
521
+
522
+ it("should error if the asset is over 25Mb", async () => {
523
+ const assets = [
524
+ {
525
+ filePath: "assets/large-file.txt",
526
+ // This file is greater than 25MiB when base64 encoded but small enough to be uploaded.
527
+ content: "X".repeat(25 * 1024 * 1024 * 0.8 + 1),
528
+ },
529
+ {
530
+ filePath: "assets/too-large-file.txt",
531
+ content: "X".repeat(25 * 1024 * 1024 + 1),
532
+ },
533
+ ];
534
+ const kvNamespace = {
535
+ title: "__test-name_sites_assets",
536
+ id: "__test-name_sites_assets-id",
537
+ };
538
+ writeWranglerToml("./index.js", "assets", undefined, [
539
+ "assets/file-1.txt",
540
+ ]);
541
+ writeEsmWorkerSource();
542
+ writeAssets(assets);
543
+ mockUploadWorkerRequest();
544
+ mockSubDomainRequest();
545
+ mockListKVNamespacesRequest(kvNamespace);
546
+ mockKeyListRequest(kvNamespace.id, []);
547
+
548
+ const { stdout, stderr, error } = await runWrangler("publish");
549
+
550
+ expect(stdout).toMatchInlineSnapshot(`
551
+ "reading assets/large-file.txt...
552
+ uploading as assets/large-file.0ea0637a45.txt..."
553
+ `);
554
+ expect(stderr).toMatchInlineSnapshot(`
555
+ "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
556
+
557
+ %s
558
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
559
+ `);
560
+ expect(error).toMatchInlineSnapshot(
561
+ `[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]`
562
+ );
563
+ });
564
+
565
+ it("should error if the asset key is over 512 characters", async () => {
566
+ const longFilePathAsset = {
567
+ filePath: "assets/" + "folder/".repeat(100) + "file.txt",
568
+ content: "content of file",
569
+ };
570
+ const kvNamespace = {
571
+ title: "__test-name_sites_assets",
572
+ id: "__test-name_sites_assets-id",
573
+ };
574
+ writeWranglerToml("./index.js", "assets");
575
+ writeEsmWorkerSource();
576
+ writeAssets([longFilePathAsset]);
577
+ mockUploadWorkerRequest();
578
+ mockSubDomainRequest();
579
+ mockListKVNamespacesRequest(kvNamespace);
580
+ mockKeyListRequest(kvNamespace.id, []);
581
+
582
+ const { stdout, stderr, error } = await runWrangler("publish");
583
+
584
+ expect(stdout).toMatchInlineSnapshot(
585
+ `"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..."`
586
+ );
587
+ expect(stderr).toMatchInlineSnapshot(`
588
+ "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\\",
589
+
590
+ %s
591
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
592
+ `);
593
+ expect(error).toMatchInlineSnapshot(
594
+ `[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",]`
595
+ );
596
+ });
597
+ });
598
+ });
599
+
600
+ /** Write a mock wrangler.toml file to disk. */
601
+ function writeWranglerToml(
602
+ main?: string,
603
+ bucket?: string,
604
+ include?: string[],
605
+ exclude?: string[]
606
+ ) {
607
+ fs.writeFileSync(
608
+ "./wrangler.toml",
609
+ [
610
+ `compatibility_date = "2022-01-12"`,
611
+ `name = "test-name"`,
612
+ main !== undefined ? `[build.upload]\nmain = "${main}"` : "",
613
+ bucket || include || exclude ? "[site]" : "",
614
+ bucket !== undefined ? `bucket = "${bucket}"` : "",
615
+ include !== undefined ? `include = ${JSON.stringify(include)}` : "",
616
+ exclude !== undefined ? `exclude = ${JSON.stringify(exclude)}` : "",
617
+ ].join("\n"),
618
+ "utf-8"
619
+ );
620
+ }
621
+
622
+ /** Write a mock Worker script to disk. */
623
+ function writeEsmWorkerSource({
624
+ basePath = ".",
625
+ format = "js",
626
+ }: { basePath?: string; format?: "js" | "ts" } = {}) {
627
+ if (basePath !== ".") {
628
+ fs.mkdirSync(basePath, { recursive: true });
629
+ }
630
+ fs.writeFileSync(
631
+ `${basePath}/index.${format}`,
632
+ [
633
+ `import { foo } from "./another";`,
634
+ `export default {`,
635
+ ` async fetch(request) {`,
636
+ ` return new Response('Hello' + foo);`,
637
+ ` },`,
638
+ `};`,
639
+ ].join("\n")
640
+ );
641
+ fs.writeFileSync(`${basePath}/another.${format}`, `export const foo = 100;`);
642
+ }
643
+
644
+ /** Write mock assets to the file system so they can be uploaded. */
645
+ function writeAssets(assets: { filePath: string; content: string }[]) {
646
+ for (const asset of assets) {
647
+ fs.mkdirSync(path.dirname(asset.filePath), { recursive: true });
648
+ fs.writeFileSync(asset.filePath, asset.content);
649
+ }
650
+ }
651
+
652
+ /** Create a mock handler for the request to upload a worker script. */
653
+ function mockUploadWorkerRequest({
654
+ available_on_subdomain = true,
655
+ expectedBody,
656
+ }: {
657
+ available_on_subdomain?: boolean;
658
+ expectedBody?: string;
659
+ } = {}) {
660
+ setMockResponse(
661
+ "/accounts/:accountId/workers/scripts/:scriptName",
662
+ "PUT",
663
+ async ([_url, accountId, scriptName], { body }, queryParams) => {
664
+ expect(accountId).toEqual("some-account-id");
665
+ expect(scriptName).toEqual("test-name");
666
+ expect(queryParams.get("available_on_subdomains")).toEqual("true");
667
+ if (expectedBody !== undefined) {
668
+ expect(
669
+ await ((body as FormData).get("index.js") as File).text()
670
+ ).toMatch(expectedBody);
671
+ }
672
+ return { available_on_subdomain };
673
+ }
674
+ );
675
+ }
676
+
677
+ /** Create a mock handler for the request to get the account's subdomain. */
678
+ function mockSubDomainRequest(subdomain = "test-sub-domain") {
679
+ setMockResponse("/accounts/:accountId/workers/subdomain", () => {
680
+ return { subdomain };
681
+ });
682
+ }
683
+
684
+ /** Create a mock handler for the request to get a list of all KV namespaces. */
685
+ function mockListKVNamespacesRequest(...namespaces: KVNamespaceInfo[]) {
686
+ setMockResponse(
687
+ "/accounts/:accountId/storage/kv/namespaces",
688
+ "GET",
689
+ ([_url, accountId]) => {
690
+ expect(accountId).toEqual("some-account-id");
691
+ return namespaces;
692
+ }
693
+ );
694
+ }
695
+
696
+ /** Create a mock handler for the request that tries to do a bulk upload of assets to a KV namespace. */
697
+ function mockUploadAssetsToKVRequest(
698
+ expectedNamespaceId: string,
699
+ assets: { filePath: string; content: string }[]
700
+ ) {
701
+ setMockResponse(
702
+ "/accounts/:accountId/storage/kv/namespaces/:namespaceId/bulk",
703
+ "PUT",
704
+ ([_url, accountId, namespaceId], { body }) => {
705
+ expect(accountId).toEqual("some-account-id");
706
+ expect(namespaceId).toEqual(expectedNamespaceId);
707
+ const uploads = JSON.parse(body as string);
708
+ expect(assets.length).toEqual(uploads.length);
709
+ for (let i = 0; i < uploads.length; i++) {
710
+ const asset = assets[i];
711
+ const upload = uploads[i];
712
+ // The asset key consists of:
713
+ // - the basename of the filepath
714
+ // - some hash value
715
+ // - the extension
716
+ const keyMatcher = new RegExp(
717
+ "^" +
718
+ asset.filePath
719
+ .replace(/(\.[^.]+)$/, ".[a-z0-9]+$1")
720
+ .replace(/\./g, "\\.")
721
+ );
722
+ expect(upload.key).toMatch(keyMatcher);
723
+ // The asset value is base64 encoded.
724
+ expect(upload.base64).toBe(true);
725
+ expect(Buffer.from(upload.value, "base64").toString()).toEqual(
726
+ asset.content
727
+ );
728
+ }
729
+ return null;
730
+ }
731
+ );
732
+ }
733
+
734
+ /** Strip timing data out of the stdout, since this is not always deterministic. */
735
+ function stripTimings(stdout: string): string {
736
+ return stdout.replace(/\(\d+\.\d+ sec\)/g, "(TIMINGS)");
737
+ }