wrangler 0.0.0-e3cab74 → 0.0.0-e3e3243

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 (66) hide show
  1. package/README.md +1 -1
  2. package/bin/wrangler.js +10 -1
  3. package/miniflare-dist/index.mjs +79 -0
  4. package/miniflare-dist/index.mjs.map +7 -0
  5. package/package.json +10 -7
  6. package/pages/functions/filepath-routing.test.ts +125 -25
  7. package/pages/functions/filepath-routing.ts +6 -3
  8. package/src/__tests__/configuration.test.ts +959 -324
  9. package/src/__tests__/dev.test.tsx +512 -84
  10. package/src/__tests__/guess-worker-format.test.ts +19 -17
  11. package/src/__tests__/helpers/mock-account-id.ts +22 -4
  12. package/src/__tests__/helpers/mock-cfetch.ts +3 -3
  13. package/src/__tests__/helpers/mock-console.ts +17 -9
  14. package/src/__tests__/helpers/mock-kv.ts +9 -16
  15. package/src/__tests__/helpers/mock-stdin.ts +103 -0
  16. package/src/__tests__/helpers/run-in-tmp.ts +3 -0
  17. package/src/__tests__/https-options.test.ts +127 -0
  18. package/src/__tests__/index.test.ts +128 -11
  19. package/src/__tests__/jest.setup.ts +16 -4
  20. package/src/__tests__/kv.test.ts +364 -125
  21. package/src/__tests__/logout.test.ts +15 -13
  22. package/src/__tests__/pages.test.ts +82 -0
  23. package/src/__tests__/publish.test.ts +1838 -735
  24. package/src/__tests__/r2.test.ts +4 -4
  25. package/src/__tests__/route.test.ts +45 -0
  26. package/src/__tests__/secret.test.ts +167 -68
  27. package/src/__tests__/tail.test.ts +2 -4
  28. package/src/__tests__/whoami.test.tsx +21 -7
  29. package/src/bundle.ts +27 -5
  30. package/src/cfetch/internal.ts +11 -5
  31. package/src/config/README.md +6 -6
  32. package/src/config/config.ts +54 -101
  33. package/src/config/environment.ts +67 -1
  34. package/src/config/index.ts +13 -3
  35. package/src/config/validation-helpers.ts +207 -23
  36. package/src/config/validation.ts +344 -129
  37. package/src/{api/preview.ts → create-worker-preview.ts} +39 -20
  38. package/src/{api/form_data.ts → create-worker-upload-form.ts} +1 -1
  39. package/src/dev/dev.tsx +392 -0
  40. package/src/dev/local.tsx +232 -0
  41. package/src/dev/remote.tsx +203 -0
  42. package/src/dev/use-esbuild.ts +104 -0
  43. package/src/entry.ts +192 -9
  44. package/src/https-options.ts +122 -0
  45. package/src/index.tsx +577 -515
  46. package/src/inspect.ts +9 -1
  47. package/src/intl-polyfill.d.ts +139 -0
  48. package/src/{kv.tsx → kv.ts} +97 -53
  49. package/src/miniflare-cli/README.md +30 -0
  50. package/src/miniflare-cli/enum-keys.ts +17 -0
  51. package/src/miniflare-cli/index.ts +37 -0
  52. package/src/module-collection.ts +90 -2
  53. package/src/open-in-browser.ts +14 -2
  54. package/src/pages.tsx +26 -10
  55. package/src/proxy.ts +104 -40
  56. package/src/publish.ts +53 -71
  57. package/src/reporting.ts +25 -9
  58. package/src/selfsigned.d.ts +29 -0
  59. package/src/sites.tsx +68 -29
  60. package/src/user.tsx +163 -85
  61. package/src/{api/worker.ts → worker.ts} +1 -24
  62. package/wrangler-dist/cli.js +5933 -3842
  63. package/wrangler-dist/cli.js.map +3 -3
  64. package/src/__tests__/helpers/mock-user.ts +0 -27
  65. package/src/dev.tsx +0 -961
  66. package/src/guess-worker-format.ts +0 -68
@@ -1,4 +1,5 @@
1
1
  import { writeFileSync } from "node:fs";
2
+ import { Headers } from "undici";
2
3
  import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
3
4
  import {
4
5
  setMockResponse,
@@ -7,10 +8,11 @@ import {
7
8
  setMockFetchKVGetValue,
8
9
  } from "./helpers/mock-cfetch";
9
10
  import { mockConsoleMethods } from "./helpers/mock-console";
11
+ import { mockConfirm } from "./helpers/mock-dialogs";
10
12
  import { mockKeyListRequest } from "./helpers/mock-kv";
11
13
  import { runInTempDir } from "./helpers/run-in-tmp";
12
14
  import { runWrangler } from "./helpers/run-wrangler";
13
- import type { KVNamespaceInfo, NamespaceKeyInfo } from "../kv";
15
+ import type { KeyValue, KVNamespaceInfo, NamespaceKeyInfo } from "../kv";
14
16
 
15
17
  describe("wrangler", () => {
16
18
  mockAccountId();
@@ -59,7 +61,7 @@ describe("wrangler", () => {
59
61
  --legacy-env Use legacy environments [boolean]
60
62
 
61
63
  Options:
62
- --env Perform on a specific environment [string]
64
+ -e, --env Perform on a specific environment [string]
63
65
  --preview Interact with a preview namespace [boolean]
64
66
 
65
67
  Not enough non-option arguments: got 0, need at least 1"
@@ -70,7 +72,7 @@ describe("wrangler", () => {
70
72
  await expect(
71
73
  runWrangler("kv:namespace create abc def ghi")
72
74
  ).rejects.toThrowErrorMatchingInlineSnapshot(
73
- `"Unexpected additional positional arguments \\"def ghi\\"."`
75
+ `"Unknown arguments: def, ghi"`
74
76
  );
75
77
  expect(std.out).toMatchInlineSnapshot(`""`);
76
78
  expect(std.err).toMatchInlineSnapshot(`
@@ -88,10 +90,10 @@ describe("wrangler", () => {
88
90
  --legacy-env Use legacy environments [boolean]
89
91
 
90
92
  Options:
91
- --env Perform on a specific environment [string]
93
+ -e, --env Perform on a specific environment [string]
92
94
  --preview Interact with a preview namespace [boolean]
93
95
 
94
- Unexpected additional positional arguments \\"def ghi\\"."
96
+ Unknown arguments: def, ghi"
95
97
  `);
96
98
  });
97
99
 
@@ -118,7 +120,7 @@ describe("wrangler", () => {
118
120
  --legacy-env Use legacy environments [boolean]
119
121
 
120
122
  Options:
121
- --env Perform on a specific environment [string]
123
+ -e, --env Perform on a specific environment [string]
122
124
  --preview Interact with a preview namespace [boolean]
123
125
 
124
126
  The namespace binding name \\"abc-def\\" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number."
@@ -284,7 +286,7 @@ describe("wrangler", () => {
284
286
  Options:
285
287
  --binding The name of the namespace to delete [string]
286
288
  --namespace-id The id of the namespace to delete [string]
287
- --env Perform on a specific environment [string]
289
+ -e, --env Perform on a specific environment [string]
288
290
  --preview Interact with a preview namespace [boolean]
289
291
 
290
292
  Not able to delete namespace.
@@ -319,10 +321,7 @@ describe("wrangler", () => {
319
321
  describe("put", () => {
320
322
  function mockKeyPutRequest(
321
323
  expectedNamespaceId: string,
322
- expectedKey: string,
323
- expectedValue: string,
324
- expiration?: number,
325
- expirationTtl?: number
324
+ expectedKV: KeyValue
326
325
  ) {
327
326
  const requests = { count: 0 };
328
327
  setMockResponse(
@@ -332,15 +331,19 @@ describe("wrangler", () => {
332
331
  requests.count++;
333
332
  expect(accountId).toEqual("some-account-id");
334
333
  expect(namespaceId).toEqual(expectedNamespaceId);
335
- expect(key).toEqual(expectedKey);
336
- expect(body).toEqual(expectedValue);
337
- if (expiration !== undefined) {
338
- expect(query.get("expiration")).toEqual(`${expiration}`);
334
+ expect(key).toEqual(expectedKV.key);
335
+ expect(body).toEqual(expectedKV.value);
336
+ if (expectedKV.expiration !== undefined) {
337
+ expect(query.get("expiration")).toEqual(
338
+ `${expectedKV.expiration}`
339
+ );
339
340
  } else {
340
341
  expect(query.has("expiration")).toBe(false);
341
342
  }
342
- if (expirationTtl) {
343
- expect(query.get("expiration_ttl")).toEqual(`${expirationTtl}`);
343
+ if (expectedKV.expiration_ttl) {
344
+ expect(query.get("expiration_ttl")).toEqual(
345
+ `${expectedKV.expiration_ttl}`
346
+ );
344
347
  } else {
345
348
  expect(query.has("expiration_ttl")).toBe(false);
346
349
  }
@@ -351,11 +354,10 @@ describe("wrangler", () => {
351
354
  }
352
355
 
353
356
  it("should put a key in a given namespace specified by namespace-id", async () => {
354
- const requests = mockKeyPutRequest(
355
- "some-namespace-id",
356
- "my-key",
357
- "my-value"
358
- );
357
+ const requests = mockKeyPutRequest("some-namespace-id", {
358
+ key: "my-key",
359
+ value: "my-value",
360
+ });
359
361
 
360
362
  await runWrangler(
361
363
  "kv:key put my-key my-value --namespace-id some-namespace-id"
@@ -370,7 +372,10 @@ describe("wrangler", () => {
370
372
 
371
373
  it("should put a key in a given namespace specified by binding", async () => {
372
374
  writeWranglerConfig();
373
- const requests = mockKeyPutRequest("bound-id", "my-key", "my-value");
375
+ const requests = mockKeyPutRequest("bound-id", {
376
+ key: "my-key",
377
+ value: "my-value",
378
+ });
374
379
  await runWrangler(
375
380
  "kv:key put my-key my-value --binding someBinding --preview false"
376
381
  );
@@ -384,11 +389,10 @@ describe("wrangler", () => {
384
389
 
385
390
  it("should put a key in a given preview namespace specified by binding", async () => {
386
391
  writeWranglerConfig();
387
- const requests = mockKeyPutRequest(
388
- "preview-bound-id",
389
- "my-key",
390
- "my-value"
391
- );
392
+ const requests = mockKeyPutRequest("preview-bound-id", {
393
+ key: "my-key",
394
+ value: "my-value",
395
+ });
392
396
 
393
397
  await runWrangler(
394
398
  "kv:key put my-key my-value --binding someBinding --preview"
@@ -402,13 +406,12 @@ describe("wrangler", () => {
402
406
  });
403
407
 
404
408
  it("should add expiration and ttl properties when putting a key", async () => {
405
- const requests = mockKeyPutRequest(
406
- "some-namespace-id",
407
- "my-key",
408
- "my-value",
409
- 10,
410
- 20
411
- );
409
+ const requests = mockKeyPutRequest("some-namespace-id", {
410
+ key: "my-key",
411
+ value: "my-value",
412
+ expiration: 10,
413
+ expiration_ttl: 20,
414
+ });
412
415
  await runWrangler(
413
416
  "kv:key put my-key my-value --namespace-id some-namespace-id --expiration 10 --ttl 20"
414
417
  );
@@ -421,11 +424,10 @@ describe("wrangler", () => {
421
424
 
422
425
  it("should put a key to the specified environment in a given namespace", async () => {
423
426
  writeWranglerConfig();
424
- const requests = mockKeyPutRequest(
425
- "env-bound-id",
426
- "my-key",
427
- "my-value"
428
- );
427
+ const requests = mockKeyPutRequest("env-bound-id", {
428
+ key: "my-key",
429
+ value: "my-value",
430
+ });
429
431
  await runWrangler(
430
432
  "kv:key put my-key my-value --binding someBinding --env some-environment --preview false"
431
433
  );
@@ -438,11 +440,10 @@ describe("wrangler", () => {
438
440
 
439
441
  it("should put a key with a value loaded from a given path", async () => {
440
442
  writeFileSync("foo.txt", "file-contents", "utf-8");
441
- const requests = mockKeyPutRequest(
442
- "some-namespace-id",
443
- "my-key",
444
- "file-contents"
445
- );
443
+ const requests = mockKeyPutRequest("some-namespace-id", {
444
+ key: "my-key",
445
+ value: "file-contents",
446
+ });
446
447
  await runWrangler(
447
448
  "kv:key put my-key --namespace-id some-namespace-id --path foo.txt"
448
449
  );
@@ -479,7 +480,7 @@ describe("wrangler", () => {
479
480
  Options:
480
481
  --binding The binding of the namespace to write to [string]
481
482
  --namespace-id The id of the namespace to write to [string]
482
- --env Perform on a specific environment [string]
483
+ -e, --env Perform on a specific environment [string]
483
484
  --preview Interact with a preview namespace [boolean]
484
485
  --ttl Time for which the entries should be visible [number]
485
486
  --expiration Time since the UNIX epoch after which the entry expires [number]
@@ -515,7 +516,7 @@ describe("wrangler", () => {
515
516
  Options:
516
517
  --binding The binding of the namespace to write to [string]
517
518
  --namespace-id The id of the namespace to write to [string]
518
- --env Perform on a specific environment [string]
519
+ -e, --env Perform on a specific environment [string]
519
520
  --preview Interact with a preview namespace [boolean]
520
521
  --ttl Time for which the entries should be visible [number]
521
522
  --expiration Time since the UNIX epoch after which the entry expires [number]
@@ -551,7 +552,7 @@ describe("wrangler", () => {
551
552
  Options:
552
553
  --binding The binding of the namespace to write to [string]
553
554
  --namespace-id The id of the namespace to write to [string]
554
- --env Perform on a specific environment [string]
555
+ -e, --env Perform on a specific environment [string]
555
556
  --preview Interact with a preview namespace [boolean]
556
557
  --ttl Time for which the entries should be visible [number]
557
558
  --expiration Time since the UNIX epoch after which the entry expires [number]
@@ -587,7 +588,7 @@ describe("wrangler", () => {
587
588
  Options:
588
589
  --binding The binding of the namespace to write to [string]
589
590
  --namespace-id The id of the namespace to write to [string]
590
- --env Perform on a specific environment [string]
591
+ -e, --env Perform on a specific environment [string]
591
592
  --preview Interact with a preview namespace [boolean]
592
593
  --ttl Time for which the entries should be visible [number]
593
594
  --expiration Time since the UNIX epoch after which the entry expires [number]
@@ -623,7 +624,7 @@ describe("wrangler", () => {
623
624
  Options:
624
625
  --binding The binding of the namespace to write to [string]
625
626
  --namespace-id The id of the namespace to write to [string]
626
- --env Perform on a specific environment [string]
627
+ -e, --env Perform on a specific environment [string]
627
628
  --preview Interact with a preview namespace [boolean]
628
629
  --ttl Time for which the entries should be visible [number]
629
630
  --expiration Time since the UNIX epoch after which the entry expires [number]
@@ -645,18 +646,16 @@ describe("wrangler", () => {
645
646
  expect(std.err).toMatchInlineSnapshot(`
646
647
  "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
647
648
 
648
- %s
649
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
649
+ %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
650
650
  `);
651
651
  });
652
652
 
653
653
  it("should error if a given binding has both preview and non-preview and --preview is not specified", async () => {
654
654
  writeWranglerConfig();
655
- const requests = mockKeyPutRequest(
656
- "preview-bound-id",
657
- "my-key",
658
- "my-value"
659
- );
655
+ const requests = mockKeyPutRequest("preview-bound-id", {
656
+ key: "my-key",
657
+ value: "my-value",
658
+ });
660
659
  await expect(
661
660
  runWrangler("kv:key put my-key my-value --binding someBinding")
662
661
  ).rejects.toThrowErrorMatchingInlineSnapshot(
@@ -666,8 +665,7 @@ describe("wrangler", () => {
666
665
  expect(std.err).toMatchInlineSnapshot(`
667
666
  "someBinding has both a namespace ID and a preview ID. Specify \\"--preview\\" or \\"--preview false\\" to avoid writing data to the wrong namespace.
668
667
 
669
- %s
670
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
668
+ %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
671
669
  `);
672
670
  expect(requests.count).toEqual(0);
673
671
  });
@@ -675,26 +673,26 @@ describe("wrangler", () => {
675
673
 
676
674
  describe("list", () => {
677
675
  it("should list the keys of a namespace specified by namespace-id", async () => {
678
- const keys = ["key-1", "key-2", "key-3"];
676
+ const keys = [
677
+ { name: "key-1" },
678
+ { name: "key-2", expiration: 123456789 },
679
+ { name: "key-3", expiration_ttl: 666 },
680
+ ];
679
681
  mockKeyListRequest("some-namespace-id", keys);
680
682
  await runWrangler("kv:key list --namespace-id some-namespace-id");
681
683
  expect(std.err).toMatchInlineSnapshot(`""`);
682
684
  expect(std.out).toMatchInlineSnapshot(`
683
685
  "[
684
686
  {
685
- \\"name\\": \\"key-1\\",
686
- \\"expiration\\": 123456789,
687
- \\"metadata\\": {}
687
+ \\"name\\": \\"key-1\\"
688
688
  },
689
689
  {
690
690
  \\"name\\": \\"key-2\\",
691
- \\"expiration\\": 123456789,
692
- \\"metadata\\": {}
691
+ \\"expiration\\": 123456789
693
692
  },
694
693
  {
695
694
  \\"name\\": \\"key-3\\",
696
- \\"expiration\\": 123456789,
697
- \\"metadata\\": {}
695
+ \\"expiration_ttl\\": 666
698
696
  }
699
697
  ]"
700
698
  `);
@@ -702,7 +700,7 @@ describe("wrangler", () => {
702
700
 
703
701
  it("should list the keys of a namespace specified by binding", async () => {
704
702
  writeWranglerConfig();
705
- const keys = ["key-1", "key-2", "key-3"];
703
+ const keys = [{ name: "key-1" }, { name: "key-2" }, { name: "key-3" }];
706
704
  mockKeyListRequest("bound-id", keys);
707
705
 
708
706
  await runWrangler("kv:key list --binding someBinding");
@@ -710,19 +708,13 @@ describe("wrangler", () => {
710
708
  expect(std.out).toMatchInlineSnapshot(`
711
709
  "[
712
710
  {
713
- \\"name\\": \\"key-1\\",
714
- \\"expiration\\": 123456789,
715
- \\"metadata\\": {}
711
+ \\"name\\": \\"key-1\\"
716
712
  },
717
713
  {
718
- \\"name\\": \\"key-2\\",
719
- \\"expiration\\": 123456789,
720
- \\"metadata\\": {}
714
+ \\"name\\": \\"key-2\\"
721
715
  },
722
716
  {
723
- \\"name\\": \\"key-3\\",
724
- \\"expiration\\": 123456789,
725
- \\"metadata\\": {}
717
+ \\"name\\": \\"key-3\\"
726
718
  }
727
719
  ]"
728
720
  `);
@@ -730,26 +722,20 @@ describe("wrangler", () => {
730
722
 
731
723
  it("should list the keys of a preview namespace specified by binding", async () => {
732
724
  writeWranglerConfig();
733
- const keys = ["key-1", "key-2", "key-3"];
725
+ const keys = [{ name: "key-1" }, { name: "key-2" }, { name: "key-3" }];
734
726
  mockKeyListRequest("preview-bound-id", keys);
735
727
  await runWrangler("kv:key list --binding someBinding --preview");
736
728
  expect(std.err).toMatchInlineSnapshot(`""`);
737
729
  expect(std.out).toMatchInlineSnapshot(`
738
730
  "[
739
731
  {
740
- \\"name\\": \\"key-1\\",
741
- \\"expiration\\": 123456789,
742
- \\"metadata\\": {}
732
+ \\"name\\": \\"key-1\\"
743
733
  },
744
734
  {
745
- \\"name\\": \\"key-2\\",
746
- \\"expiration\\": 123456789,
747
- \\"metadata\\": {}
735
+ \\"name\\": \\"key-2\\"
748
736
  },
749
737
  {
750
- \\"name\\": \\"key-3\\",
751
- \\"expiration\\": 123456789,
752
- \\"metadata\\": {}
738
+ \\"name\\": \\"key-3\\"
753
739
  }
754
740
  ]"
755
741
  `);
@@ -757,7 +743,7 @@ describe("wrangler", () => {
757
743
 
758
744
  it("should list the keys of a namespace specified by binding, in a given environment", async () => {
759
745
  writeWranglerConfig();
760
- const keys = ["key-1", "key-2", "key-3"];
746
+ const keys = [{ name: "key-1" }, { name: "key-2" }, { name: "key-3" }];
761
747
  mockKeyListRequest("env-bound-id", keys);
762
748
  await runWrangler(
763
749
  "kv:key list --binding someBinding --env some-environment"
@@ -766,19 +752,13 @@ describe("wrangler", () => {
766
752
  expect(std.out).toMatchInlineSnapshot(`
767
753
  "[
768
754
  {
769
- \\"name\\": \\"key-1\\",
770
- \\"expiration\\": 123456789,
771
- \\"metadata\\": {}
755
+ \\"name\\": \\"key-1\\"
772
756
  },
773
757
  {
774
- \\"name\\": \\"key-2\\",
775
- \\"expiration\\": 123456789,
776
- \\"metadata\\": {}
758
+ \\"name\\": \\"key-2\\"
777
759
  },
778
760
  {
779
- \\"name\\": \\"key-3\\",
780
- \\"expiration\\": 123456789,
781
- \\"metadata\\": {}
761
+ \\"name\\": \\"key-3\\"
782
762
  }
783
763
  ]"
784
764
  `);
@@ -786,7 +766,7 @@ describe("wrangler", () => {
786
766
 
787
767
  it("should list the keys of a preview namespace specified by binding, in a given environment", async () => {
788
768
  writeWranglerConfig();
789
- const keys = ["key-1", "key-2", "key-3"];
769
+ const keys = [{ name: "key-1" }, { name: "key-2" }, { name: "key-3" }];
790
770
  mockKeyListRequest("preview-env-bound-id", keys);
791
771
  await runWrangler(
792
772
  "kv:key list --binding someBinding --preview --env some-environment"
@@ -795,19 +775,13 @@ describe("wrangler", () => {
795
775
  expect(std.out).toMatchInlineSnapshot(`
796
776
  "[
797
777
  {
798
- \\"name\\": \\"key-1\\",
799
- \\"expiration\\": 123456789,
800
- \\"metadata\\": {}
778
+ \\"name\\": \\"key-1\\"
801
779
  },
802
780
  {
803
- \\"name\\": \\"key-2\\",
804
- \\"expiration\\": 123456789,
805
- \\"metadata\\": {}
781
+ \\"name\\": \\"key-2\\"
806
782
  },
807
783
  {
808
- \\"name\\": \\"key-3\\",
809
- \\"expiration\\": 123456789,
810
- \\"metadata\\": {}
784
+ \\"name\\": \\"key-3\\"
811
785
  }
812
786
  ]"
813
787
  `);
@@ -824,9 +798,9 @@ describe("wrangler", () => {
824
798
  describe(`cursor - ${blankCursorValue}`, () => {
825
799
  it("should make multiple requests for paginated results", async () => {
826
800
  // Create a lot of mock keys, so that the fetch requests will be paginated
827
- const keys: string[] = [];
801
+ const keys: NamespaceKeyInfo[] = [];
828
802
  for (let i = 0; i < 550; i++) {
829
- keys.push("key-" + i);
803
+ keys.push({ name: "key-" + i });
830
804
  }
831
805
  // Ask for the keys in pages of size 100.
832
806
  const requests = mockKeyListRequest(
@@ -835,13 +809,9 @@ describe("wrangler", () => {
835
809
  100,
836
810
  blankCursorValue
837
811
  );
838
- await runWrangler(
839
- "kv:key list --namespace-id some-namespace-id --limit 100"
840
- );
812
+ await runWrangler("kv:key list --namespace-id some-namespace-id");
841
813
  expect(std.err).toMatchInlineSnapshot(`""`);
842
- expect(
843
- JSON.parse(std.out).map((k: NamespaceKeyInfo) => k.name)
844
- ).toEqual(keys);
814
+ expect(JSON.parse(std.out)).toEqual(keys);
845
815
  expect(requests.count).toEqual(6);
846
816
  });
847
817
  });
@@ -857,8 +827,7 @@ describe("wrangler", () => {
857
827
  expect(std.err).toMatchInlineSnapshot(`
858
828
  "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
859
829
 
860
- %s
861
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
830
+ %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
862
831
  `);
863
832
  expect(std.out).toMatchInlineSnapshot(`""`);
864
833
  });
@@ -918,7 +887,7 @@ describe("wrangler", () => {
918
887
  "my-value"
919
888
  );
920
889
  await runWrangler(
921
- "kv:key get my-key my-value --binding someBinding --env some-environment --preview false"
890
+ "kv:key get my-key --binding someBinding --env some-environment --preview false"
922
891
  );
923
892
  expect(std.out).toMatchInlineSnapshot(`"my-value"`);
924
893
  expect(std.err).toMatchInlineSnapshot(`""`);
@@ -948,7 +917,7 @@ describe("wrangler", () => {
948
917
  Options:
949
918
  --binding The name of the namespace to get from [string]
950
919
  --namespace-id The id of the namespace to get from [string]
951
- --env Perform on a specific environment [string]
920
+ -e, --env Perform on a specific environment [string]
952
921
  --preview Interact with a preview namespace [boolean] [default: false]
953
922
 
954
923
  Not enough non-option arguments: got 0, need at least 1"
@@ -979,7 +948,7 @@ describe("wrangler", () => {
979
948
  Options:
980
949
  --binding The name of the namespace to get from [string]
981
950
  --namespace-id The id of the namespace to get from [string]
982
- --env Perform on a specific environment [string]
951
+ -e, --env Perform on a specific environment [string]
983
952
  --preview Interact with a preview namespace [boolean] [default: false]
984
953
 
985
954
  Exactly one of the arguments binding and namespace-id is required"
@@ -1011,7 +980,7 @@ describe("wrangler", () => {
1011
980
  Options:
1012
981
  --binding The name of the namespace to get from [string]
1013
982
  --namespace-id The id of the namespace to get from [string]
1014
- --env Perform on a specific environment [string]
983
+ -e, --env Perform on a specific environment [string]
1015
984
  --preview Interact with a preview namespace [boolean] [default: false]
1016
985
 
1017
986
  Arguments binding and namespace-id are mutually exclusive"
@@ -1029,8 +998,7 @@ describe("wrangler", () => {
1029
998
  expect(std.err).toMatchInlineSnapshot(`
1030
999
  "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
1031
1000
 
1032
- %s
1033
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1001
+ %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1034
1002
  `);
1035
1003
  });
1036
1004
  });
@@ -1092,8 +1060,7 @@ describe("wrangler", () => {
1092
1060
  expect(std.err).toMatchInlineSnapshot(`
1093
1061
  "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
1094
1062
 
1095
- %s
1096
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1063
+ %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1097
1064
  `);
1098
1065
  });
1099
1066
 
@@ -1120,6 +1087,278 @@ describe("wrangler", () => {
1120
1087
  });
1121
1088
  });
1122
1089
  });
1090
+
1091
+ describe("kv:bulk", () => {
1092
+ describe("put", () => {
1093
+ function mockPutRequest(
1094
+ expectedNamespaceId: string,
1095
+ expectedKeyValues: KeyValue[]
1096
+ ) {
1097
+ const requests = { count: 0 };
1098
+ setMockResponse(
1099
+ "/accounts/:accountId/storage/kv/namespaces/:namespaceId/bulk",
1100
+ "PUT",
1101
+ ([_url, accountId, namespaceId], { body }) => {
1102
+ requests.count++;
1103
+ expect(accountId).toEqual("some-account-id");
1104
+ expect(namespaceId).toEqual(expectedNamespaceId);
1105
+ expect(JSON.parse(body as string)).toEqual(
1106
+ expectedKeyValues.slice(
1107
+ (requests.count - 1) * 5000,
1108
+ requests.count * 5000
1109
+ )
1110
+ );
1111
+ return null;
1112
+ }
1113
+ );
1114
+ return requests;
1115
+ }
1116
+
1117
+ it("should put the key-values parsed from a file", async () => {
1118
+ const keyValues: KeyValue[] = [
1119
+ { key: "someKey1", value: "someValue1" },
1120
+ { key: "ns:someKey2", value: "123", base64: true },
1121
+ { key: "someKey3", value: "someValue3", expiration: 100 },
1122
+ { key: "someKey4", value: "someValue4", expiration_ttl: 500 },
1123
+ ];
1124
+ writeFileSync("./keys.json", JSON.stringify(keyValues));
1125
+ const requests = mockPutRequest("some-namespace-id", keyValues);
1126
+ await runWrangler(
1127
+ `kv:bulk put --namespace-id some-namespace-id keys.json`
1128
+ );
1129
+ expect(requests.count).toEqual(1);
1130
+ expect(std.out).toMatchInlineSnapshot(`"Success!"`);
1131
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1132
+ expect(std.err).toMatchInlineSnapshot(`""`);
1133
+ });
1134
+
1135
+ it("should put the key-values in batches of 5000 parsed from a file", async () => {
1136
+ const keyValues: KeyValue[] = new Array(12000).fill({
1137
+ key: "someKey1",
1138
+ value: "someValue1",
1139
+ });
1140
+ writeFileSync("./keys.json", JSON.stringify(keyValues));
1141
+ const requests = mockPutRequest("some-namespace-id", keyValues);
1142
+ await runWrangler(
1143
+ `kv:bulk put --namespace-id some-namespace-id keys.json`
1144
+ );
1145
+ expect(requests.count).toEqual(3);
1146
+ expect(std.out).toMatchInlineSnapshot(`
1147
+ "Uploaded 0 of 12000.
1148
+ Uploaded 5000 of 12000.
1149
+ Uploaded 10000 of 12000.
1150
+ Uploaded 12000 of 12000.
1151
+ Success!"
1152
+ `);
1153
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1154
+ expect(std.err).toMatchInlineSnapshot(`""`);
1155
+ });
1156
+
1157
+ it("should error if the file is not a JSON array", async () => {
1158
+ const keyValues = { key: "someKey1", value: "someValue1" };
1159
+ writeFileSync("./keys.json", JSON.stringify(keyValues));
1160
+ await expect(
1161
+ runWrangler(`kv:bulk put --namespace-id some-namespace-id keys.json`)
1162
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`
1163
+ "Unexpected JSON input from \\"keys.json\\".
1164
+ Expected an array of key-value objects but got type \\"object\\"."
1165
+ `);
1166
+ expect(std.out).toMatchInlineSnapshot(`""`);
1167
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1168
+ });
1169
+
1170
+ it("should error if the array contains items that are not key-value objects", async () => {
1171
+ const keyValues = [
1172
+ 123,
1173
+ "a string",
1174
+ { key: "someKey" },
1175
+ { value: "someValue" },
1176
+ { key: "someKey1", value: "someValue1", invalid: true },
1177
+ ];
1178
+ writeFileSync("./keys.json", JSON.stringify(keyValues));
1179
+ await expect(
1180
+ runWrangler(`kv:bulk put --namespace-id some-namespace-id keys.json`)
1181
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`
1182
+ "Unexpected JSON input from \\"keys.json\\".
1183
+ Each item in the array should be an object that matches:
1184
+
1185
+ interface KeyValue {
1186
+ key: string;
1187
+ value: string;
1188
+ expiration?: number;
1189
+ expiration_ttl?: number;
1190
+ metadata?: object;
1191
+ base64?: boolean;
1192
+ }
1193
+
1194
+ The item at index 0 is type: \\"number\\" - 123
1195
+ The item at index 1 is type: \\"string\\" - \\"a string\\"
1196
+ The item at index 2 is {\\"key\\":\\"someKey\\"}
1197
+ The item at index 3 is {\\"value\\":\\"someValue\\"}"
1198
+ `);
1199
+
1200
+ expect(std.out).toMatchInlineSnapshot(`""`);
1201
+ expect(std.warn).toMatchInlineSnapshot(`
1202
+ "Unexpected key-value properties in \\"keys.json\\".
1203
+ The item at index 4 contains unexpected properties: [\\"invalid\\"]."
1204
+ `);
1205
+ });
1206
+ });
1207
+
1208
+ describe("delete", () => {
1209
+ function mockDeleteRequest(
1210
+ expectedNamespaceId: string,
1211
+ expectedKeys: string[]
1212
+ ) {
1213
+ const requests = { count: 0 };
1214
+ setMockResponse(
1215
+ "/accounts/:accountId/storage/kv/namespaces/:namespaceId/bulk",
1216
+ "DELETE",
1217
+ ([_url, accountId, namespaceId], { headers, body }) => {
1218
+ requests.count++;
1219
+ expect(accountId).toEqual("some-account-id");
1220
+ expect(namespaceId).toEqual(expectedNamespaceId);
1221
+ expect(new Headers(headers ?? []).get("Content-Type")).toEqual(
1222
+ "application/json"
1223
+ );
1224
+ expect(JSON.parse(body as string)).toEqual(
1225
+ expectedKeys.slice(
1226
+ (requests.count - 1) * 5000,
1227
+ requests.count * 5000
1228
+ )
1229
+ );
1230
+ return null;
1231
+ }
1232
+ );
1233
+ return requests;
1234
+ }
1235
+
1236
+ it("should delete the keys parsed from a file", async () => {
1237
+ const keys = ["someKey1", "ns:someKey2"];
1238
+ writeFileSync("./keys.json", JSON.stringify(keys));
1239
+ mockConfirm({
1240
+ text: `Are you sure you want to delete all the keys read from "keys.json" from kv-namespace with id "some-namespace-id"?`,
1241
+ result: true,
1242
+ });
1243
+ const requests = mockDeleteRequest("some-namespace-id", keys);
1244
+ await runWrangler(
1245
+ `kv:bulk delete --namespace-id some-namespace-id keys.json`
1246
+ );
1247
+ expect(requests.count).toEqual(1);
1248
+ expect(std.out).toMatchInlineSnapshot(`"Success!"`);
1249
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1250
+ expect(std.err).toMatchInlineSnapshot(`""`);
1251
+ });
1252
+
1253
+ it("should delete the keys in batches of 5000 parsed from a file", async () => {
1254
+ const keys = new Array(12000).fill("some-key");
1255
+ writeFileSync("./keys.json", JSON.stringify(keys));
1256
+ mockConfirm({
1257
+ text: `Are you sure you want to delete all the keys read from "keys.json" from kv-namespace with id "some-namespace-id"?`,
1258
+ result: true,
1259
+ });
1260
+ const requests = mockDeleteRequest("some-namespace-id", keys);
1261
+ await runWrangler(
1262
+ `kv:bulk delete --namespace-id some-namespace-id keys.json`
1263
+ );
1264
+ expect(requests.count).toEqual(3);
1265
+ expect(std.out).toMatchInlineSnapshot(`
1266
+ "Deleted 0 of 12000.
1267
+ Deleted 5000 of 12000.
1268
+ Deleted 10000 of 12000.
1269
+ Deleted 12000 of 12000.
1270
+ Success!"
1271
+ `);
1272
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1273
+ expect(std.err).toMatchInlineSnapshot(`""`);
1274
+ });
1275
+
1276
+ it("should not delete the keys if the user confirms no", async () => {
1277
+ const keys = ["someKey1", "ns:someKey2"];
1278
+ writeFileSync("./keys.json", JSON.stringify(keys));
1279
+ mockConfirm({
1280
+ text: `Are you sure you want to delete all the keys read from "keys.json" from kv-namespace with id "some-namespace-id"?`,
1281
+ result: false,
1282
+ });
1283
+ await runWrangler(
1284
+ `kv:bulk delete --namespace-id some-namespace-id keys.json`
1285
+ );
1286
+ expect(std.out).toMatchInlineSnapshot(
1287
+ `"Not deleting keys read from \\"keys.json\\"."`
1288
+ );
1289
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1290
+ expect(std.err).toMatchInlineSnapshot(`""`);
1291
+ });
1292
+
1293
+ it("should delete the keys without asking if --force is provided", async () => {
1294
+ const keys = ["someKey1", "ns:someKey2"];
1295
+ writeFileSync("./keys.json", JSON.stringify(keys));
1296
+ const requests = mockDeleteRequest("some-namespace-id", keys);
1297
+ await runWrangler(
1298
+ `kv:bulk delete --namespace-id some-namespace-id keys.json --force`
1299
+ );
1300
+ expect(requests.count).toEqual(1);
1301
+ expect(std.out).toMatchInlineSnapshot(`"Success!"`);
1302
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1303
+ expect(std.err).toMatchInlineSnapshot(`""`);
1304
+ });
1305
+
1306
+ it("should delete the keys without asking if -f is provided", async () => {
1307
+ const keys = ["someKey1", "ns:someKey2"];
1308
+ writeFileSync("./keys.json", JSON.stringify(keys));
1309
+ const requests = mockDeleteRequest("some-namespace-id", keys);
1310
+ await runWrangler(
1311
+ `kv:bulk delete --namespace-id some-namespace-id keys.json -f`
1312
+ );
1313
+ expect(requests.count).toEqual(1);
1314
+ expect(std.out).toMatchInlineSnapshot(`"Success!"`);
1315
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1316
+ expect(std.err).toMatchInlineSnapshot(`""`);
1317
+ });
1318
+
1319
+ it("should error if the file is not a JSON array", async () => {
1320
+ const keys = 12354;
1321
+ writeFileSync("./keys.json", JSON.stringify(keys));
1322
+ mockConfirm({
1323
+ text: `Are you sure you want to delete all the keys read from "keys.json" from kv-namespace with id "some-namespace-id"?`,
1324
+ result: true,
1325
+ });
1326
+ await expect(
1327
+ runWrangler(
1328
+ `kv:bulk delete --namespace-id some-namespace-id keys.json`
1329
+ )
1330
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`
1331
+ "Unexpected JSON input from \\"keys.json\\".
1332
+ Expected an array of strings but got:
1333
+ 12354"
1334
+ `);
1335
+ expect(std.out).toMatchInlineSnapshot(`""`);
1336
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1337
+ });
1338
+
1339
+ it("should error if the file contains non-string items", async () => {
1340
+ const keys = ["good", 12354, { key: "someKey" }, null];
1341
+ writeFileSync("./keys.json", JSON.stringify(keys));
1342
+ mockConfirm({
1343
+ text: `Are you sure you want to delete all the keys read from "keys.json" from kv-namespace with id "some-namespace-id"?`,
1344
+ result: true,
1345
+ });
1346
+ await expect(
1347
+ runWrangler(
1348
+ `kv:bulk delete --namespace-id some-namespace-id keys.json`
1349
+ )
1350
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`
1351
+ "Unexpected JSON input from \\"keys.json\\".
1352
+ Expected an array of strings.
1353
+ The item at index 1 is type: \\"number\\" - 12354
1354
+ The item at index 2 is type: \\"object\\" - {\\"key\\":\\"someKey\\"}
1355
+ The item at index 3 is type: \\"object\\" - null"
1356
+ `);
1357
+ expect(std.out).toMatchInlineSnapshot(`""`);
1358
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1359
+ });
1360
+ });
1361
+ });
1123
1362
  });
1124
1363
 
1125
1364
  function writeWranglerConfig() {