wrangler 0.0.0-e0d2f35 → 0.0.0-e1b8ac4

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 (160) hide show
  1. package/Cloudflare_CA.pem +18 -0
  2. package/README.md +27 -14
  3. package/bin/wrangler.js +48 -4
  4. package/kv-asset-handler.js +1 -0
  5. package/miniflare-dist/index.mjs +4886 -0
  6. package/package.json +58 -31
  7. package/pages/functions/buildPlugin.ts +105 -0
  8. package/pages/functions/buildWorker.ts +93 -4
  9. package/pages/functions/filepath-routing.test.ts +223 -28
  10. package/pages/functions/filepath-routing.ts +92 -131
  11. package/pages/functions/routes.ts +16 -18
  12. package/pages/functions/template-plugin.ts +147 -0
  13. package/pages/functions/template-worker.ts +24 -17
  14. package/src/__tests__/config-cache.test.ts +36 -0
  15. package/src/__tests__/configuration.test.ts +3548 -0
  16. package/src/__tests__/dev.test.tsx +1049 -65
  17. package/src/__tests__/guess-worker-format.test.ts +85 -0
  18. package/src/__tests__/{clipboardy-mock.js → helpers/clipboardy-mock.js} +0 -0
  19. package/src/__tests__/helpers/cmd-shim.d.ts +11 -0
  20. package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
  21. package/src/__tests__/helpers/mock-account-id.ts +48 -0
  22. package/src/__tests__/helpers/mock-bin.ts +36 -0
  23. package/src/__tests__/{mock-cfetch.ts → helpers/mock-cfetch.ts} +50 -12
  24. package/src/__tests__/helpers/mock-console.ts +87 -0
  25. package/src/__tests__/helpers/mock-dialogs.ts +153 -0
  26. package/src/__tests__/helpers/mock-http-server.ts +46 -0
  27. package/src/__tests__/helpers/mock-istty.ts +27 -0
  28. package/src/__tests__/{mock-kv.ts → helpers/mock-kv.ts} +9 -16
  29. package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
  30. package/src/__tests__/helpers/mock-process.ts +24 -0
  31. package/src/__tests__/helpers/mock-stdin.ts +103 -0
  32. package/src/__tests__/helpers/mock-web-socket.ts +29 -0
  33. package/src/__tests__/{run-in-tmp.ts → helpers/run-in-tmp.ts} +11 -1
  34. package/src/__tests__/helpers/run-wrangler.ts +16 -0
  35. package/src/__tests__/helpers/write-worker-source.ts +31 -0
  36. package/src/__tests__/helpers/write-wrangler-toml.ts +17 -0
  37. package/src/__tests__/https-options.test.ts +130 -0
  38. package/src/__tests__/index.test.ts +171 -338
  39. package/src/__tests__/init.test.ts +1992 -0
  40. package/src/__tests__/jest.setup.ts +86 -8
  41. package/src/__tests__/kv.test.ts +745 -449
  42. package/src/__tests__/logger.test.ts +141 -0
  43. package/src/__tests__/package-manager.test.ts +353 -0
  44. package/src/__tests__/pages.test.ts +754 -0
  45. package/src/__tests__/parse.test.ts +330 -0
  46. package/src/__tests__/publish.test.ts +5631 -903
  47. package/src/__tests__/r2.test.ts +233 -0
  48. package/src/__tests__/route.test.ts +45 -0
  49. package/src/__tests__/secret.test.ts +370 -95
  50. package/src/__tests__/tail.test.ts +690 -0
  51. package/src/__tests__/user.test.ts +132 -0
  52. package/src/__tests__/whoami.test.tsx +58 -20
  53. package/src/abort.d.ts +3 -0
  54. package/src/bundle.ts +204 -0
  55. package/src/cfetch/index.ts +30 -20
  56. package/src/cfetch/internal.ts +101 -30
  57. package/src/cli.ts +6 -2
  58. package/src/config/README.md +107 -0
  59. package/src/config/config.ts +243 -0
  60. package/src/config/diagnostics.ts +80 -0
  61. package/src/config/environment.ts +420 -0
  62. package/src/config/index.ts +225 -0
  63. package/src/config/validation-helpers.ts +584 -0
  64. package/src/config/validation.ts +1580 -0
  65. package/src/config-cache.ts +45 -0
  66. package/src/create-worker-preview.ts +185 -0
  67. package/src/create-worker-upload-form.ts +254 -0
  68. package/src/dev/dev-vars.ts +36 -0
  69. package/src/dev/dev.tsx +399 -0
  70. package/src/dev/local.tsx +321 -0
  71. package/src/dev/remote.tsx +242 -0
  72. package/src/dev/use-esbuild.ts +112 -0
  73. package/src/dialogs.tsx +66 -8
  74. package/src/durable.ts +102 -0
  75. package/src/entry.ts +322 -0
  76. package/src/environment-variables.ts +35 -0
  77. package/src/errors.ts +11 -0
  78. package/src/generate-auth-url.ts +33 -0
  79. package/src/generate-random-state.ts +16 -0
  80. package/src/https-options.ts +123 -0
  81. package/src/index.tsx +1848 -1019
  82. package/src/inspect.ts +87 -20
  83. package/src/intl-polyfill.d.ts +139 -0
  84. package/src/{kv.tsx → kv.ts} +199 -67
  85. package/src/logger.ts +73 -0
  86. package/src/miniflare-cli/README.md +30 -0
  87. package/src/miniflare-cli/enum-keys.ts +17 -0
  88. package/src/miniflare-cli/index.ts +41 -0
  89. package/src/miniflare-cli/request-context.ts +40 -0
  90. package/src/module-collection.ts +229 -34
  91. package/src/open-in-browser.ts +17 -0
  92. package/src/package-manager.ts +174 -0
  93. package/src/pages.tsx +1145 -95
  94. package/src/parse.ts +222 -0
  95. package/src/paths.ts +26 -0
  96. package/src/proxy.ts +180 -74
  97. package/src/publish.ts +626 -310
  98. package/src/r2.ts +50 -0
  99. package/src/selfsigned.d.ts +29 -0
  100. package/src/sites.tsx +187 -59
  101. package/src/tail/filters.ts +279 -0
  102. package/src/tail/index.ts +299 -0
  103. package/src/tail/printing.ts +67 -0
  104. package/src/update-check.ts +19 -0
  105. package/src/user.tsx +359 -176
  106. package/src/whoami.tsx +5 -3
  107. package/src/{api/worker.ts → worker.ts} +66 -40
  108. package/src/zones.ts +73 -0
  109. package/templates/gitignore +171 -0
  110. package/templates/new-worker-scheduled.js +17 -0
  111. package/templates/new-worker-scheduled.ts +32 -0
  112. package/templates/new-worker.js +15 -0
  113. package/templates/new-worker.ts +30 -0
  114. package/{static-asset-facade.js → templates/static-asset-facade.js} +3 -7
  115. package/templates/tsconfig.json +105 -0
  116. package/wrangler-dist/cli.js +112889 -110835
  117. package/src/__tests__/logout.test.ts +0 -64
  118. package/src/__tests__/mock-console.ts +0 -34
  119. package/src/__tests__/mock-dialogs.ts +0 -65
  120. package/src/__tests__/mock-user.ts +0 -27
  121. package/src/__tests__/run-wrangler.ts +0 -8
  122. package/src/api/form_data.ts +0 -135
  123. package/src/api/preview.ts +0 -128
  124. package/src/config.ts +0 -641
  125. package/src/dev.tsx +0 -830
  126. package/src/tail.tsx +0 -73
  127. package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
  128. package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
  129. package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
  130. package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
  131. package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
  132. package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
  133. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
  134. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
  135. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
  136. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
  137. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
  138. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
  139. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
  140. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
  141. package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
  142. package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
  143. package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
  144. package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
  145. package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
  146. package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
  147. package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
  148. package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
  149. package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
  150. package/vendor/wrangler-mime/CHANGELOG.md +0 -289
  151. package/vendor/wrangler-mime/LICENSE +0 -21
  152. package/vendor/wrangler-mime/Mime.js +0 -97
  153. package/vendor/wrangler-mime/README.md +0 -187
  154. package/vendor/wrangler-mime/cli.js +0 -46
  155. package/vendor/wrangler-mime/index.js +0 -4
  156. package/vendor/wrangler-mime/lite.js +0 -4
  157. package/vendor/wrangler-mime/package.json +0 -52
  158. package/vendor/wrangler-mime/types/other.js +0 -1
  159. package/vendor/wrangler-mime/types/standard.js +0 -1
  160. package/wrangler-dist/cli.js.map +0 -7
@@ -1,21 +1,30 @@
1
- import { writeFileSync } from "fs";
2
- import type { KVNamespaceInfo } from "../kv";
1
+ import { writeFileSync } from "node:fs";
2
+ import { Headers } from "undici";
3
+ import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
3
4
  import {
4
5
  setMockResponse,
5
- setMockRawResponse,
6
6
  unsetAllMocks,
7
- } from "./mock-cfetch";
8
- import { runWrangler } from "./run-wrangler";
9
- import { runInTempDir } from "./run-in-tmp";
10
- import { mockConsoleMethods } from "./mock-console";
11
- import { mockKeyListRequest } from "./mock-kv";
7
+ unsetMockFetchKVGetValues,
8
+ setMockFetchKVGetValue,
9
+ } from "./helpers/mock-cfetch";
10
+ import { mockConsoleMethods } from "./helpers/mock-console";
11
+ import { clearConfirmMocks, mockConfirm } from "./helpers/mock-dialogs";
12
+ import { mockKeyListRequest } from "./helpers/mock-kv";
13
+ import { mockProcess } from "./helpers/mock-process";
14
+ import { runInTempDir } from "./helpers/run-in-tmp";
15
+ import { runWrangler } from "./helpers/run-wrangler";
16
+ import type { KeyValue, KVNamespaceInfo, NamespaceKeyInfo } from "../kv";
12
17
 
13
18
  describe("wrangler", () => {
19
+ mockAccountId();
20
+ mockApiToken();
14
21
  runInTempDir();
15
22
  const std = mockConsoleMethods();
23
+ const proc = mockProcess();
16
24
 
17
25
  afterEach(() => {
18
26
  unsetAllMocks();
27
+ clearConfirmMocks();
19
28
  });
20
29
 
21
30
  describe("kv:namespace", () => {
@@ -34,15 +43,14 @@ describe("wrangler", () => {
34
43
  }
35
44
 
36
45
  it("should error if no namespace is given", async () => {
37
- let error: Error | undefined;
38
- try {
39
- await runWrangler("kv:namespace create");
40
- } catch (e) {
41
- error = e;
42
- }
43
- expect(std.out).toMatchInlineSnapshot(`""`);
44
- expect(std.err).toMatchInlineSnapshot(`
45
- "wrangler kv:namespace create <namespace>
46
+ await expect(
47
+ runWrangler("kv:namespace create")
48
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
49
+ `"Not enough non-option arguments: got 0, need at least 1"`
50
+ );
51
+ expect(std.out).toMatchInlineSnapshot(`
52
+ "
53
+ wrangler kv:namespace create <namespace>
46
54
 
47
55
  Create a new namespace
48
56
 
@@ -55,27 +63,25 @@ describe("wrangler", () => {
55
63
  -v, --version Show version number [boolean]
56
64
 
57
65
  Options:
58
- -l, --local Run on my machine [boolean] [default: false]
59
- --env Perform on a specific environment [string]
60
- --preview Interact with a preview namespace [boolean]
66
+ -e, --env Perform on a specific environment [string]
67
+ --preview Interact with a preview namespace [boolean]"
68
+ `);
69
+ expect(std.err).toMatchInlineSnapshot(`
70
+ "X [ERROR] Not enough non-option arguments: got 0, need at least 1
61
71
 
62
- Not enough non-option arguments: got 0, need at least 1"
72
+ "
63
73
  `);
64
- expect(error).toMatchInlineSnapshot(
65
- `[Error: Not enough non-option arguments: got 0, need at least 1]`
66
- );
67
74
  });
68
75
 
69
76
  it("should error if the namespace to create contains spaces", async () => {
70
- let error: Error | undefined;
71
- try {
72
- await runWrangler("kv:namespace create abc def ghi");
73
- } catch (e) {
74
- error = e;
75
- }
76
- expect(std.out).toMatchInlineSnapshot(`""`);
77
- expect(std.err).toMatchInlineSnapshot(`
78
- "wrangler kv:namespace create <namespace>
77
+ await expect(
78
+ runWrangler("kv:namespace create abc def ghi")
79
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
80
+ `"Unknown arguments: def, ghi"`
81
+ );
82
+ expect(std.out).toMatchInlineSnapshot(`
83
+ "
84
+ wrangler kv:namespace create <namespace>
79
85
 
80
86
  Create a new namespace
81
87
 
@@ -88,28 +94,26 @@ describe("wrangler", () => {
88
94
  -v, --version Show version number [boolean]
89
95
 
90
96
  Options:
91
- -l, --local Run on my machine [boolean] [default: false]
92
- --env Perform on a specific environment [string]
93
- --preview Interact with a preview namespace [boolean]
97
+ -e, --env Perform on a specific environment [string]
98
+ --preview Interact with a preview namespace [boolean]"
99
+ `);
100
+ expect(std.err).toMatchInlineSnapshot(`
101
+ "X [ERROR] Unknown arguments: def, ghi
94
102
 
95
- Unexpected additional positional arguments \\"def ghi\\"."
103
+ "
96
104
  `);
97
- expect(error).toMatchInlineSnapshot(
98
- `[Error: Unexpected additional positional arguments "def ghi".]`
99
- );
100
105
  });
101
106
 
102
107
  it("should error if the namespace to create is not valid", async () => {
103
- let error: Error | undefined;
104
- try {
105
- await runWrangler("kv:namespace create abc-def");
106
- } catch (e) {
107
- error = e;
108
- }
108
+ await expect(
109
+ runWrangler("kv:namespace create abc-def")
110
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
111
+ `"The namespace binding name \\"abc-def\\" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number."`
112
+ );
109
113
 
110
- expect(std.out).toMatchInlineSnapshot(`""`);
111
- expect(std.err).toMatchInlineSnapshot(`
112
- "wrangler kv:namespace create <namespace>
114
+ expect(std.out).toMatchInlineSnapshot(`
115
+ "
116
+ wrangler kv:namespace create <namespace>
113
117
 
114
118
  Create a new namespace
115
119
 
@@ -122,15 +126,14 @@ describe("wrangler", () => {
122
126
  -v, --version Show version number [boolean]
123
127
 
124
128
  Options:
125
- -l, --local Run on my machine [boolean] [default: false]
126
- --env Perform on a specific environment [string]
127
- --preview Interact with a preview namespace [boolean]
129
+ -e, --env Perform on a specific environment [string]
130
+ --preview Interact with a preview namespace [boolean]"
131
+ `);
132
+ expect(std.err).toMatchInlineSnapshot(`
133
+ "X [ERROR] The namespace binding name \\"abc-def\\" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number.
128
134
 
129
- The namespace binding name \\"abc-def\\" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number."
135
+ "
130
136
  `);
131
- expect(error).toMatchInlineSnapshot(
132
- `[Error: The namespace binding name "abc-def" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number.]`
133
- );
134
137
  });
135
138
 
136
139
  it("should create a namespace", async () => {
@@ -156,11 +159,11 @@ describe("wrangler", () => {
156
159
  });
157
160
 
158
161
  it("should create a namespace using configured worker name", async () => {
159
- writeFileSync("./wrangler.toml", 'name = "otherWorker"', "utf-8");
160
- mockCreateRequest("otherWorker-UnitTestNamespace");
162
+ writeFileSync("./wrangler.toml", 'name = "other-worker"', "utf-8");
163
+ mockCreateRequest("other-worker-UnitTestNamespace");
161
164
  await runWrangler("kv:namespace create UnitTestNamespace");
162
165
  expect(std.out).toMatchInlineSnapshot(`
163
- "🌀 Creating namespace with title \\"otherWorker-UnitTestNamespace\\"
166
+ "🌀 Creating namespace with title \\"other-worker-UnitTestNamespace\\"
164
167
  ✨ Success!
165
168
  Add the following to your configuration file in your kv_namespaces array:
166
169
  { binding = \\"UnitTestNamespace\\", id = \\"some-namespace-id\\" }"
@@ -273,35 +276,17 @@ describe("wrangler", () => {
273
276
 
274
277
  it("should error if a given binding name is not in the configured kv namespaces", async () => {
275
278
  writeWranglerConfig();
276
- let error: Error | undefined;
277
- try {
278
- await runWrangler("kv:namespace delete --binding otherBinding");
279
- } catch (e) {
280
- error = e;
281
- }
282
- expect(error).toMatchInlineSnapshot(`
283
- [Error: Not able to delete namespace.
284
- A namespace with binding name "otherBinding" was not found in the configured "kv_namespaces".]
285
- `);
279
+ await expect(runWrangler("kv:namespace delete --binding otherBinding"))
280
+ .rejects.toThrowErrorMatchingInlineSnapshot(`
281
+ "Not able to delete namespace.
282
+ A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."
283
+ `);
286
284
  expect(std.err).toMatchInlineSnapshot(`
287
- "wrangler kv:namespace delete
285
+ "X [ERROR] Not able to delete namespace.
288
286
 
289
- Deletes a given namespace.
287
+ A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
290
288
 
291
- Flags:
292
- -c, --config Path to .toml configuration file [string]
293
- -h, --help Show help [boolean]
294
- -v, --version Show version number [boolean]
295
-
296
- Options:
297
- -l, --local Run on my machine [boolean] [default: false]
298
- --binding The name of the namespace to delete [string]
299
- --namespace-id The id of the namespace to delete [string]
300
- --env Perform on a specific environment [string]
301
- --preview Interact with a preview namespace [boolean]
302
-
303
- Not able to delete namespace.
304
- A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."
289
+ "
305
290
  `);
306
291
  });
307
292
 
@@ -332,10 +317,7 @@ describe("wrangler", () => {
332
317
  describe("put", () => {
333
318
  function mockKeyPutRequest(
334
319
  expectedNamespaceId: string,
335
- expectedKey: string,
336
- expectedValue: string,
337
- expiration?: number,
338
- expirationTtl?: number
320
+ expectedKV: KeyValue
339
321
  ) {
340
322
  const requests = { count: 0 };
341
323
  setMockResponse(
@@ -345,15 +327,19 @@ describe("wrangler", () => {
345
327
  requests.count++;
346
328
  expect(accountId).toEqual("some-account-id");
347
329
  expect(namespaceId).toEqual(expectedNamespaceId);
348
- expect(key).toEqual(expectedKey);
349
- expect(body).toEqual(expectedValue);
350
- if (expiration !== undefined) {
351
- expect(query.get("expiration")).toEqual(`${expiration}`);
330
+ expect(key).toEqual(expectedKV.key);
331
+ expect(body).toEqual(expectedKV.value);
332
+ if (expectedKV.expiration !== undefined) {
333
+ expect(query.get("expiration")).toEqual(
334
+ `${expectedKV.expiration}`
335
+ );
352
336
  } else {
353
337
  expect(query.has("expiration")).toBe(false);
354
338
  }
355
- if (expirationTtl) {
356
- expect(query.get("expiration_ttl")).toEqual(`${expirationTtl}`);
339
+ if (expectedKV.expiration_ttl) {
340
+ expect(query.get("expiration_ttl")).toEqual(
341
+ `${expectedKV.expiration_ttl}`
342
+ );
357
343
  } else {
358
344
  expect(query.has("expiration_ttl")).toBe(false);
359
345
  }
@@ -364,11 +350,10 @@ describe("wrangler", () => {
364
350
  }
365
351
 
366
352
  it("should put a key in a given namespace specified by namespace-id", async () => {
367
- const requests = mockKeyPutRequest(
368
- "some-namespace-id",
369
- "my-key",
370
- "my-value"
371
- );
353
+ const requests = mockKeyPutRequest("some-namespace-id", {
354
+ key: "my-key",
355
+ value: "my-value",
356
+ });
372
357
 
373
358
  await runWrangler(
374
359
  "kv:key put my-key my-value --namespace-id some-namespace-id"
@@ -376,20 +361,38 @@ describe("wrangler", () => {
376
361
 
377
362
  expect(requests.count).toEqual(1);
378
363
  expect(std.out).toMatchInlineSnapshot(
379
- `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace some-namespace-id"`
364
+ `"Writing the value \\"my-value\\" to key \\"my-key\\" on namespace some-namespace-id."`
365
+ );
366
+ expect(std.err).toMatchInlineSnapshot(`""`);
367
+ });
368
+
369
+ it("should encode the key in the api request to put a value", async () => {
370
+ const requests = mockKeyPutRequest("DS9", {
371
+ key: "%2Fmy-key",
372
+ value: "my-value",
373
+ });
374
+
375
+ await runWrangler("kv:key put /my-key my-value --namespace-id DS9");
376
+
377
+ expect(requests.count).toEqual(1);
378
+ expect(std.out).toMatchInlineSnapshot(
379
+ `"Writing the value \\"my-value\\" to key \\"/my-key\\" on namespace DS9."`
380
380
  );
381
381
  expect(std.err).toMatchInlineSnapshot(`""`);
382
382
  });
383
383
 
384
384
  it("should put a key in a given namespace specified by binding", async () => {
385
385
  writeWranglerConfig();
386
- const requests = mockKeyPutRequest("bound-id", "my-key", "my-value");
386
+ const requests = mockKeyPutRequest("bound-id", {
387
+ key: "my-key",
388
+ value: "my-value",
389
+ });
387
390
  await runWrangler(
388
391
  "kv:key put my-key my-value --binding someBinding --preview false"
389
392
  );
390
393
 
391
394
  expect(std.out).toMatchInlineSnapshot(
392
- `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace bound-id"`
395
+ `"Writing the value \\"my-value\\" to key \\"my-key\\" on namespace bound-id."`
393
396
  );
394
397
  expect(std.err).toMatchInlineSnapshot(`""`);
395
398
  expect(requests.count).toEqual(1);
@@ -397,99 +400,108 @@ describe("wrangler", () => {
397
400
 
398
401
  it("should put a key in a given preview namespace specified by binding", async () => {
399
402
  writeWranglerConfig();
400
- const requests = mockKeyPutRequest(
401
- "preview-bound-id",
402
- "my-key",
403
- "my-value"
404
- );
403
+ const requests = mockKeyPutRequest("preview-bound-id", {
404
+ key: "my-key",
405
+ value: "my-value",
406
+ });
405
407
 
406
408
  await runWrangler(
407
409
  "kv:key put my-key my-value --binding someBinding --preview"
408
410
  );
409
411
 
410
412
  expect(std.out).toMatchInlineSnapshot(
411
- `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace preview-bound-id"`
413
+ `"Writing the value \\"my-value\\" to key \\"my-key\\" on namespace preview-bound-id."`
412
414
  );
413
415
  expect(std.err).toMatchInlineSnapshot(`""`);
414
416
  expect(requests.count).toEqual(1);
415
417
  });
416
418
 
417
419
  it("should add expiration and ttl properties when putting a key", async () => {
418
- const requests = mockKeyPutRequest(
419
- "some-namespace-id",
420
- "my-key",
421
- "my-value",
422
- 10,
423
- 20
424
- );
425
- let error: Error | undefined;
426
- try {
427
- await runWrangler(
428
- "kv:key put my-key my-value --namespace-id some-namespace-id --expiration 10 --ttl 20"
429
- );
430
- } catch (e) {
431
- error = e;
432
- }
433
-
420
+ const requests = mockKeyPutRequest("some-namespace-id", {
421
+ key: "my-key",
422
+ value: "my-value",
423
+ expiration: 10,
424
+ expiration_ttl: 20,
425
+ });
426
+ await runWrangler(
427
+ "kv:key put my-key my-value --namespace-id some-namespace-id --expiration 10 --ttl 20"
428
+ );
434
429
  expect(requests.count).toEqual(1);
435
430
  expect(std.out).toMatchInlineSnapshot(
436
- `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace some-namespace-id"`
431
+ `"Writing the value \\"my-value\\" to key \\"my-key\\" on namespace some-namespace-id."`
437
432
  );
438
433
  expect(std.err).toMatchInlineSnapshot(`""`);
439
- expect(error).toMatchInlineSnapshot(`undefined`);
440
434
  });
441
435
 
442
436
  it("should put a key to the specified environment in a given namespace", async () => {
443
437
  writeWranglerConfig();
444
- const requests = mockKeyPutRequest(
445
- "env-bound-id",
446
- "my-key",
447
- "my-value"
448
- );
438
+ const requests = mockKeyPutRequest("env-bound-id", {
439
+ key: "my-key",
440
+ value: "my-value",
441
+ });
449
442
  await runWrangler(
450
443
  "kv:key put my-key my-value --binding someBinding --env some-environment --preview false"
451
444
  );
452
445
  expect(std.out).toMatchInlineSnapshot(
453
- `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace env-bound-id"`
446
+ `"Writing the value \\"my-value\\" to key \\"my-key\\" on namespace env-bound-id."`
454
447
  );
455
448
  expect(std.err).toMatchInlineSnapshot(`""`);
456
449
  expect(requests.count).toEqual(1);
457
450
  });
458
451
 
459
452
  it("should put a key with a value loaded from a given path", async () => {
460
- writeFileSync("foo.txt", "file-contents", "utf-8");
461
- const requests = mockKeyPutRequest(
462
- "some-namespace-id",
463
- "my-key",
464
- "file-contents"
465
- );
453
+ const buf = Buffer.from("file-contents", "utf-8");
454
+ writeFileSync("foo.txt", buf);
455
+ const requests = mockKeyPutRequest("some-namespace-id", {
456
+ key: "my-key",
457
+ value: buf,
458
+ });
466
459
  await runWrangler(
467
460
  "kv:key put my-key --namespace-id some-namespace-id --path foo.txt"
468
461
  );
469
462
  expect(std.out).toMatchInlineSnapshot(
470
- `"writing the contents of foo.txt to the key \\"my-key\\" on namespace some-namespace-id"`
463
+ `"Writing the contents of foo.txt to the key \\"my-key\\" on namespace some-namespace-id."`
464
+ );
465
+ expect(std.err).toMatchInlineSnapshot(`""`);
466
+ expect(requests.count).toEqual(1);
467
+ });
468
+
469
+ it("should put a key with a binary value loaded from a given path", async () => {
470
+ const buf = Buffer.from(
471
+ "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAiSURBVHgB7coxEQAACMPAgH/PgAM6dGwu49fA/deIBXrgAj2cAhIFT4QxAAAAAElFTkSuQmCC",
472
+ "base64"
473
+ );
474
+ writeFileSync("test.png", buf);
475
+ const requests = mockKeyPutRequest("another-namespace-id", {
476
+ key: "my-key",
477
+ value: buf,
478
+ });
479
+ await runWrangler(
480
+ "kv:key put my-key --namespace-id another-namespace-id --path test.png"
481
+ );
482
+ expect(std.out).toMatchInlineSnapshot(
483
+ `"Writing the contents of test.png to the key \\"my-key\\" on namespace another-namespace-id."`
471
484
  );
472
485
  expect(std.err).toMatchInlineSnapshot(`""`);
473
486
  expect(requests.count).toEqual(1);
474
487
  });
475
488
 
476
489
  it("should error if no key is provided", async () => {
477
- let error: Error | undefined;
478
- try {
479
- await runWrangler("kv:key put");
480
- } catch (e) {
481
- error = e;
482
- }
490
+ await expect(
491
+ runWrangler("kv:key put")
492
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
493
+ `"Not enough non-option arguments: got 0, need at least 1"`
494
+ );
483
495
 
484
- expect(std.out).toMatchInlineSnapshot(`""`);
485
- expect(std.err).toMatchInlineSnapshot(`
486
- "wrangler kv:key put <key> [value]
496
+ expect(std.out).toMatchInlineSnapshot(`
497
+ "
498
+ wrangler kv:key put <key> [value]
487
499
 
488
500
  Writes a single key/value pair to the given namespace.
489
501
 
490
502
  Positionals:
491
- key The key to write to. [string] [required]
492
- value The value to write. [string]
503
+ key The key to write to [string] [required]
504
+ value The value to write [string]
493
505
 
494
506
  Flags:
495
507
  -c, --config Path to .toml configuration file [string]
@@ -497,39 +509,37 @@ describe("wrangler", () => {
497
509
  -v, --version Show version number [boolean]
498
510
 
499
511
  Options:
500
- -l, --local Run on my machine [boolean] [default: false]
501
- --binding The binding of the namespace to write to. [string]
502
- --namespace-id The id of the namespace to write to. [string]
503
- --env Perform on a specific environment [string]
512
+ --binding The binding of the namespace to write to [string]
513
+ --namespace-id The id of the namespace to write to [string]
514
+ -e, --env Perform on a specific environment [string]
504
515
  --preview Interact with a preview namespace [boolean]
505
- --ttl Time for which the entries should be visible. [number]
516
+ --ttl Time for which the entries should be visible [number]
506
517
  --expiration Time since the UNIX epoch after which the entry expires [number]
507
- --path Read value from the file at a given path. [string]
518
+ --path Read value from the file at a given path [string]"
519
+ `);
520
+ expect(std.err).toMatchInlineSnapshot(`
521
+ "X [ERROR] Not enough non-option arguments: got 0, need at least 1
508
522
 
509
- Not enough non-option arguments: got 0, need at least 1"
523
+ "
510
524
  `);
511
- expect(error).toMatchInlineSnapshot(
512
- `[Error: Not enough non-option arguments: got 0, need at least 1]`
513
- );
514
525
  });
515
526
 
516
527
  it("should error if no binding nor namespace is provided", async () => {
517
- let error: Error | undefined;
518
- try {
519
- await runWrangler("kv:key put foo bar");
520
- } catch (e) {
521
- error = e;
522
- }
528
+ await expect(
529
+ runWrangler("kv:key put foo bar")
530
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
531
+ `"Exactly one of the arguments binding and namespace-id is required"`
532
+ );
523
533
 
524
- expect(std.out).toMatchInlineSnapshot(`""`);
525
- expect(std.err).toMatchInlineSnapshot(`
526
- "wrangler kv:key put <key> [value]
534
+ expect(std.out).toMatchInlineSnapshot(`
535
+ "
536
+ wrangler kv:key put <key> [value]
527
537
 
528
538
  Writes a single key/value pair to the given namespace.
529
539
 
530
540
  Positionals:
531
- key The key to write to. [string] [required]
532
- value The value to write. [string]
541
+ key The key to write to [string] [required]
542
+ value The value to write [string]
533
543
 
534
544
  Flags:
535
545
  -c, --config Path to .toml configuration file [string]
@@ -537,39 +547,37 @@ describe("wrangler", () => {
537
547
  -v, --version Show version number [boolean]
538
548
 
539
549
  Options:
540
- -l, --local Run on my machine [boolean] [default: false]
541
- --binding The binding of the namespace to write to. [string]
542
- --namespace-id The id of the namespace to write to. [string]
543
- --env Perform on a specific environment [string]
550
+ --binding The binding of the namespace to write to [string]
551
+ --namespace-id The id of the namespace to write to [string]
552
+ -e, --env Perform on a specific environment [string]
544
553
  --preview Interact with a preview namespace [boolean]
545
- --ttl Time for which the entries should be visible. [number]
554
+ --ttl Time for which the entries should be visible [number]
546
555
  --expiration Time since the UNIX epoch after which the entry expires [number]
547
- --path Read value from the file at a given path. [string]
556
+ --path Read value from the file at a given path [string]"
557
+ `);
558
+ expect(std.err).toMatchInlineSnapshot(`
559
+ "X [ERROR] Exactly one of the arguments binding and namespace-id is required
548
560
 
549
- Exactly one of the arguments binding and namespace-id is required"
561
+ "
550
562
  `);
551
- expect(error).toMatchInlineSnapshot(
552
- `[Error: Exactly one of the arguments binding and namespace-id is required]`
553
- );
554
563
  });
555
564
 
556
565
  it("should error if both binding and namespace is provided", async () => {
557
- let error: Error | undefined;
558
- try {
559
- await runWrangler("kv:key put foo bar --binding x --namespace-id y");
560
- } catch (e) {
561
- error = e;
562
- }
566
+ await expect(
567
+ runWrangler("kv:key put foo bar --binding x --namespace-id y")
568
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
569
+ `"Arguments binding and namespace-id are mutually exclusive"`
570
+ );
563
571
 
564
- expect(std.out).toMatchInlineSnapshot(`""`);
565
- expect(std.err).toMatchInlineSnapshot(`
566
- "wrangler kv:key put <key> [value]
572
+ expect(std.out).toMatchInlineSnapshot(`
573
+ "
574
+ wrangler kv:key put <key> [value]
567
575
 
568
576
  Writes a single key/value pair to the given namespace.
569
577
 
570
578
  Positionals:
571
- key The key to write to. [string] [required]
572
- value The value to write. [string]
579
+ key The key to write to [string] [required]
580
+ value The value to write [string]
573
581
 
574
582
  Flags:
575
583
  -c, --config Path to .toml configuration file [string]
@@ -577,39 +585,37 @@ describe("wrangler", () => {
577
585
  -v, --version Show version number [boolean]
578
586
 
579
587
  Options:
580
- -l, --local Run on my machine [boolean] [default: false]
581
- --binding The binding of the namespace to write to. [string]
582
- --namespace-id The id of the namespace to write to. [string]
583
- --env Perform on a specific environment [string]
588
+ --binding The binding of the namespace to write to [string]
589
+ --namespace-id The id of the namespace to write to [string]
590
+ -e, --env Perform on a specific environment [string]
584
591
  --preview Interact with a preview namespace [boolean]
585
- --ttl Time for which the entries should be visible. [number]
592
+ --ttl Time for which the entries should be visible [number]
586
593
  --expiration Time since the UNIX epoch after which the entry expires [number]
587
- --path Read value from the file at a given path. [string]
594
+ --path Read value from the file at a given path [string]"
595
+ `);
596
+ expect(std.err).toMatchInlineSnapshot(`
597
+ "X [ERROR] Arguments binding and namespace-id are mutually exclusive
588
598
 
589
- Arguments binding and namespace-id are mutually exclusive"
599
+ "
590
600
  `);
591
- expect(error).toMatchInlineSnapshot(
592
- `[Error: Arguments binding and namespace-id are mutually exclusive]`
593
- );
594
601
  });
595
602
 
596
603
  it("should error if no value nor path is provided", async () => {
597
- let error: Error | undefined;
598
- try {
599
- await runWrangler("kv:key put key --namespace-id 12345");
600
- } catch (e) {
601
- error = e;
602
- }
604
+ await expect(
605
+ runWrangler("kv:key put key --namespace-id 12345")
606
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
607
+ `"Exactly one of the arguments value and path is required"`
608
+ );
603
609
 
604
- expect(std.out).toMatchInlineSnapshot(`""`);
605
- expect(std.err).toMatchInlineSnapshot(`
606
- "wrangler kv:key put <key> [value]
610
+ expect(std.out).toMatchInlineSnapshot(`
611
+ "
612
+ wrangler kv:key put <key> [value]
607
613
 
608
614
  Writes a single key/value pair to the given namespace.
609
615
 
610
616
  Positionals:
611
- key The key to write to. [string] [required]
612
- value The value to write. [string]
617
+ key The key to write to [string] [required]
618
+ value The value to write [string]
613
619
 
614
620
  Flags:
615
621
  -c, --config Path to .toml configuration file [string]
@@ -617,41 +623,37 @@ describe("wrangler", () => {
617
623
  -v, --version Show version number [boolean]
618
624
 
619
625
  Options:
620
- -l, --local Run on my machine [boolean] [default: false]
621
- --binding The binding of the namespace to write to. [string]
622
- --namespace-id The id of the namespace to write to. [string]
623
- --env Perform on a specific environment [string]
626
+ --binding The binding of the namespace to write to [string]
627
+ --namespace-id The id of the namespace to write to [string]
628
+ -e, --env Perform on a specific environment [string]
624
629
  --preview Interact with a preview namespace [boolean]
625
- --ttl Time for which the entries should be visible. [number]
630
+ --ttl Time for which the entries should be visible [number]
626
631
  --expiration Time since the UNIX epoch after which the entry expires [number]
627
- --path Read value from the file at a given path. [string]
632
+ --path Read value from the file at a given path [string]"
633
+ `);
634
+ expect(std.err).toMatchInlineSnapshot(`
635
+ "X [ERROR] Exactly one of the arguments value and path is required
628
636
 
629
- Exactly one of the arguments value and path is required"
637
+ "
630
638
  `);
631
- expect(error).toMatchInlineSnapshot(
632
- `[Error: Exactly one of the arguments value and path is required]`
633
- );
634
639
  });
635
640
 
636
641
  it("should error if both value and path is provided", async () => {
637
- let error: Error | undefined;
638
- try {
639
- await runWrangler(
640
- "kv:key put key value --path xyz --namespace-id 12345"
641
- );
642
- } catch (e) {
643
- error = e;
644
- }
642
+ await expect(
643
+ runWrangler("kv:key put key value --path xyz --namespace-id 12345")
644
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
645
+ `"Arguments value and path are mutually exclusive"`
646
+ );
645
647
 
646
- expect(std.out).toMatchInlineSnapshot(`""`);
647
- expect(std.err).toMatchInlineSnapshot(`
648
- "wrangler kv:key put <key> [value]
648
+ expect(std.out).toMatchInlineSnapshot(`
649
+ "
650
+ wrangler kv:key put <key> [value]
649
651
 
650
652
  Writes a single key/value pair to the given namespace.
651
653
 
652
654
  Positionals:
653
- key The key to write to. [string] [required]
654
- value The value to write. [string]
655
+ key The key to write to [string] [required]
656
+ value The value to write [string]
655
657
 
656
658
  Flags:
657
659
  -c, --config Path to .toml configuration file [string]
@@ -659,92 +661,86 @@ describe("wrangler", () => {
659
661
  -v, --version Show version number [boolean]
660
662
 
661
663
  Options:
662
- -l, --local Run on my machine [boolean] [default: false]
663
- --binding The binding of the namespace to write to. [string]
664
- --namespace-id The id of the namespace to write to. [string]
665
- --env Perform on a specific environment [string]
664
+ --binding The binding of the namespace to write to [string]
665
+ --namespace-id The id of the namespace to write to [string]
666
+ -e, --env Perform on a specific environment [string]
666
667
  --preview Interact with a preview namespace [boolean]
667
- --ttl Time for which the entries should be visible. [number]
668
+ --ttl Time for which the entries should be visible [number]
668
669
  --expiration Time since the UNIX epoch after which the entry expires [number]
669
- --path Read value from the file at a given path. [string]
670
+ --path Read value from the file at a given path [string]"
671
+ `);
672
+ expect(std.err).toMatchInlineSnapshot(`
673
+ "X [ERROR] Arguments value and path are mutually exclusive
670
674
 
671
- Arguments value and path are mutually exclusive"
675
+ "
672
676
  `);
673
- expect(error).toMatchInlineSnapshot(
674
- `[Error: Arguments value and path are mutually exclusive]`
675
- );
676
677
  });
677
678
 
678
679
  it("should error if a given binding name is not in the configured kv namespaces", async () => {
679
680
  writeWranglerConfig();
680
- let error: Error | undefined;
681
- try {
682
- await runWrangler("kv:key put key value --binding otherBinding");
683
- } catch (e) {
684
- error = e;
685
- }
681
+ await expect(
682
+ runWrangler("kv:key put key value --binding otherBinding")
683
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
684
+ `"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."`
685
+ );
686
686
 
687
- expect(std.out).toMatchInlineSnapshot(`""`);
687
+ expect(std.out).toMatchInlineSnapshot(`
688
+ "
689
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
690
+ `);
688
691
  expect(std.err).toMatchInlineSnapshot(`
689
- "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
692
+ "X [ERROR] A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
690
693
 
691
- %s
692
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
694
+ "
693
695
  `);
694
- expect(error).toMatchInlineSnapshot(
695
- `[Error: A namespace with binding name "otherBinding" was not found in the configured "kv_namespaces".]`
696
- );
697
696
  });
698
697
 
699
698
  it("should error if a given binding has both preview and non-preview and --preview is not specified", async () => {
700
699
  writeWranglerConfig();
701
- const requests = mockKeyPutRequest(
702
- "preview-bound-id",
703
- "my-key",
704
- "my-value"
700
+ const requests = mockKeyPutRequest("preview-bound-id", {
701
+ key: "my-key",
702
+ value: "my-value",
703
+ });
704
+ await expect(
705
+ runWrangler("kv:key put my-key my-value --binding someBinding")
706
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
707
+ `"someBinding has both a namespace ID and a preview ID. Specify \\"--preview\\" or \\"--preview false\\" to avoid writing data to the wrong namespace."`
705
708
  );
706
- let error: Error | undefined;
707
- try {
708
- await runWrangler("kv:key put my-key my-value --binding someBinding");
709
- } catch (e) {
710
- error = e;
711
- }
712
- expect(std.out).toMatchInlineSnapshot(`""`);
709
+ expect(std.out).toMatchInlineSnapshot(`
710
+ "
711
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
712
+ `);
713
713
  expect(std.err).toMatchInlineSnapshot(`
714
- "someBinding has both a namespace ID and a preview ID. Specify \\"--preview\\" or \\"--preview false\\" to avoid writing data to the wrong namespace.
714
+ "X [ERROR] someBinding has both a namespace ID and a preview ID. Specify \\"--preview\\" or \\"--preview false\\" to avoid writing data to the wrong namespace.
715
715
 
716
- %s
717
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
716
+ "
718
717
  `);
719
- expect(error).toMatchInlineSnapshot(
720
- `[Error: someBinding has both a namespace ID and a preview ID. Specify "--preview" or "--preview false" to avoid writing data to the wrong namespace.]`
721
- );
722
718
  expect(requests.count).toEqual(0);
723
719
  });
724
720
  });
725
721
 
726
722
  describe("list", () => {
727
723
  it("should list the keys of a namespace specified by namespace-id", async () => {
728
- const keys = ["key-1", "key-2", "key-3"];
724
+ const keys = [
725
+ { name: "key-1" },
726
+ { name: "key-2", expiration: 123456789 },
727
+ { name: "key-3", expiration_ttl: 666 },
728
+ ];
729
729
  mockKeyListRequest("some-namespace-id", keys);
730
730
  await runWrangler("kv:key list --namespace-id some-namespace-id");
731
731
  expect(std.err).toMatchInlineSnapshot(`""`);
732
732
  expect(std.out).toMatchInlineSnapshot(`
733
733
  "[
734
734
  {
735
- \\"name\\": \\"key-1\\",
736
- \\"expiration\\": 123456789,
737
- \\"metadata\\": {}
735
+ \\"name\\": \\"key-1\\"
738
736
  },
739
737
  {
740
738
  \\"name\\": \\"key-2\\",
741
- \\"expiration\\": 123456789,
742
- \\"metadata\\": {}
739
+ \\"expiration\\": 123456789
743
740
  },
744
741
  {
745
742
  \\"name\\": \\"key-3\\",
746
- \\"expiration\\": 123456789,
747
- \\"metadata\\": {}
743
+ \\"expiration_ttl\\": 666
748
744
  }
749
745
  ]"
750
746
  `);
@@ -752,7 +748,7 @@ describe("wrangler", () => {
752
748
 
753
749
  it("should list the keys of a namespace specified by binding", async () => {
754
750
  writeWranglerConfig();
755
- const keys = ["key-1", "key-2", "key-3"];
751
+ const keys = [{ name: "key-1" }, { name: "key-2" }, { name: "key-3" }];
756
752
  mockKeyListRequest("bound-id", keys);
757
753
 
758
754
  await runWrangler("kv:key list --binding someBinding");
@@ -760,19 +756,13 @@ describe("wrangler", () => {
760
756
  expect(std.out).toMatchInlineSnapshot(`
761
757
  "[
762
758
  {
763
- \\"name\\": \\"key-1\\",
764
- \\"expiration\\": 123456789,
765
- \\"metadata\\": {}
759
+ \\"name\\": \\"key-1\\"
766
760
  },
767
761
  {
768
- \\"name\\": \\"key-2\\",
769
- \\"expiration\\": 123456789,
770
- \\"metadata\\": {}
762
+ \\"name\\": \\"key-2\\"
771
763
  },
772
764
  {
773
- \\"name\\": \\"key-3\\",
774
- \\"expiration\\": 123456789,
775
- \\"metadata\\": {}
765
+ \\"name\\": \\"key-3\\"
776
766
  }
777
767
  ]"
778
768
  `);
@@ -780,26 +770,20 @@ describe("wrangler", () => {
780
770
 
781
771
  it("should list the keys of a preview namespace specified by binding", async () => {
782
772
  writeWranglerConfig();
783
- const keys = ["key-1", "key-2", "key-3"];
773
+ const keys = [{ name: "key-1" }, { name: "key-2" }, { name: "key-3" }];
784
774
  mockKeyListRequest("preview-bound-id", keys);
785
775
  await runWrangler("kv:key list --binding someBinding --preview");
786
776
  expect(std.err).toMatchInlineSnapshot(`""`);
787
777
  expect(std.out).toMatchInlineSnapshot(`
788
778
  "[
789
779
  {
790
- \\"name\\": \\"key-1\\",
791
- \\"expiration\\": 123456789,
792
- \\"metadata\\": {}
780
+ \\"name\\": \\"key-1\\"
793
781
  },
794
782
  {
795
- \\"name\\": \\"key-2\\",
796
- \\"expiration\\": 123456789,
797
- \\"metadata\\": {}
783
+ \\"name\\": \\"key-2\\"
798
784
  },
799
785
  {
800
- \\"name\\": \\"key-3\\",
801
- \\"expiration\\": 123456789,
802
- \\"metadata\\": {}
786
+ \\"name\\": \\"key-3\\"
803
787
  }
804
788
  ]"
805
789
  `);
@@ -807,7 +791,7 @@ describe("wrangler", () => {
807
791
 
808
792
  it("should list the keys of a namespace specified by binding, in a given environment", async () => {
809
793
  writeWranglerConfig();
810
- const keys = ["key-1", "key-2", "key-3"];
794
+ const keys = [{ name: "key-1" }, { name: "key-2" }, { name: "key-3" }];
811
795
  mockKeyListRequest("env-bound-id", keys);
812
796
  await runWrangler(
813
797
  "kv:key list --binding someBinding --env some-environment"
@@ -816,19 +800,13 @@ describe("wrangler", () => {
816
800
  expect(std.out).toMatchInlineSnapshot(`
817
801
  "[
818
802
  {
819
- \\"name\\": \\"key-1\\",
820
- \\"expiration\\": 123456789,
821
- \\"metadata\\": {}
803
+ \\"name\\": \\"key-1\\"
822
804
  },
823
805
  {
824
- \\"name\\": \\"key-2\\",
825
- \\"expiration\\": 123456789,
826
- \\"metadata\\": {}
806
+ \\"name\\": \\"key-2\\"
827
807
  },
828
808
  {
829
- \\"name\\": \\"key-3\\",
830
- \\"expiration\\": 123456789,
831
- \\"metadata\\": {}
809
+ \\"name\\": \\"key-3\\"
832
810
  }
833
811
  ]"
834
812
  `);
@@ -836,7 +814,7 @@ describe("wrangler", () => {
836
814
 
837
815
  it("should list the keys of a preview namespace specified by binding, in a given environment", async () => {
838
816
  writeWranglerConfig();
839
- const keys = ["key-1", "key-2", "key-3"];
817
+ const keys = [{ name: "key-1" }, { name: "key-2" }, { name: "key-3" }];
840
818
  mockKeyListRequest("preview-env-bound-id", keys);
841
819
  await runWrangler(
842
820
  "kv:key list --binding someBinding --preview --env some-environment"
@@ -845,19 +823,13 @@ describe("wrangler", () => {
845
823
  expect(std.out).toMatchInlineSnapshot(`
846
824
  "[
847
825
  {
848
- \\"name\\": \\"key-1\\",
849
- \\"expiration\\": 123456789,
850
- \\"metadata\\": {}
826
+ \\"name\\": \\"key-1\\"
851
827
  },
852
828
  {
853
- \\"name\\": \\"key-2\\",
854
- \\"expiration\\": 123456789,
855
- \\"metadata\\": {}
829
+ \\"name\\": \\"key-2\\"
856
830
  },
857
831
  {
858
- \\"name\\": \\"key-3\\",
859
- \\"expiration\\": 123456789,
860
- \\"metadata\\": {}
832
+ \\"name\\": \\"key-3\\"
861
833
  }
862
834
  ]"
863
835
  `);
@@ -874,9 +846,9 @@ describe("wrangler", () => {
874
846
  describe(`cursor - ${blankCursorValue}`, () => {
875
847
  it("should make multiple requests for paginated results", async () => {
876
848
  // Create a lot of mock keys, so that the fetch requests will be paginated
877
- const keys: string[] = [];
849
+ const keys: NamespaceKeyInfo[] = [];
878
850
  for (let i = 0; i < 550; i++) {
879
- keys.push("key-" + i);
851
+ keys.push({ name: "key-" + i });
880
852
  }
881
853
  // Ask for the keys in pages of size 100.
882
854
  const requests = mockKeyListRequest(
@@ -885,11 +857,9 @@ describe("wrangler", () => {
885
857
  100,
886
858
  blankCursorValue
887
859
  );
888
- await runWrangler(
889
- "kv:key list --namespace-id some-namespace-id --limit 100"
890
- );
860
+ await runWrangler("kv:key list --namespace-id some-namespace-id");
891
861
  expect(std.err).toMatchInlineSnapshot(`""`);
892
- expect(JSON.parse(std.out).map((k) => k.name)).toEqual(keys);
862
+ expect(JSON.parse(std.out)).toEqual(keys);
893
863
  expect(requests.count).toEqual(6);
894
864
  });
895
865
  });
@@ -897,106 +867,122 @@ describe("wrangler", () => {
897
867
 
898
868
  it("should error if a given binding name is not in the configured kv namespaces", async () => {
899
869
  writeWranglerConfig();
900
- let error: Error | undefined;
901
- try {
902
- await runWrangler("kv:key list --binding otherBinding");
903
- } catch (e) {
904
- error = e;
905
- }
906
- expect(error).toMatchInlineSnapshot(
907
- `[Error: A namespace with binding name "otherBinding" was not found in the configured "kv_namespaces".]`
870
+ await expect(
871
+ runWrangler("kv:key list --binding otherBinding")
872
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
873
+ `"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."`
908
874
  );
909
875
  expect(std.err).toMatchInlineSnapshot(`
910
- "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
876
+ "X [ERROR] A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
911
877
 
912
- %s
913
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
878
+ "
879
+ `);
880
+ expect(std.out).toMatchInlineSnapshot(`
881
+ "
882
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
914
883
  `);
915
- expect(std.out).toMatchInlineSnapshot(`""`);
916
884
  });
917
885
  });
918
886
 
919
887
  describe("get", () => {
920
- function mockKeyGetRequest(
921
- expectedNamespaceId: string,
922
- expectedKey: string,
923
- expectedValue: string
924
- ) {
925
- const requests = { count: 0 };
926
- setMockRawResponse(
927
- "/accounts/:accountId/storage/kv/namespaces/:namespaceId/values/:key",
928
- ([_url, accountId, namespaceId, key]) => {
929
- requests.count++;
930
- expect(accountId).toEqual("some-account-id");
931
- expect(namespaceId).toEqual(expectedNamespaceId);
932
- expect(key).toEqual(expectedKey);
933
- return expectedValue;
934
- }
935
- );
936
- return requests;
937
- }
888
+ afterEach(() => {
889
+ unsetMockFetchKVGetValues();
890
+ });
938
891
 
939
892
  it("should get a key in a given namespace specified by namespace-id", async () => {
940
- const requests = mockKeyGetRequest(
893
+ setMockFetchKVGetValue(
894
+ "some-account-id",
941
895
  "some-namespace-id",
942
896
  "my-key",
943
897
  "my-value"
944
898
  );
945
899
  await runWrangler("kv:key get my-key --namespace-id some-namespace-id");
946
- expect(std.out).toMatchInlineSnapshot(`"my-value"`);
900
+ expect(proc.write).toEqual(Buffer.from("my-value"));
901
+ expect(std.err).toMatchInlineSnapshot(`""`);
902
+ });
903
+
904
+ it("should get a binary file by key in a given namespace specified by namespace-id", async () => {
905
+ const buf = Buffer.from(
906
+ "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAiSURBVHgB7coxEQAACMPAgH/PgAM6dGwu49fA/deIBXrgAj2cAhIFT4QxAAAAAElFTkSuQmCC",
907
+ "base64"
908
+ );
909
+ setMockFetchKVGetValue(
910
+ "some-account-id",
911
+ "some-namespace-id",
912
+ "my-key",
913
+ buf
914
+ );
915
+ await runWrangler("kv:key get my-key --namespace-id some-namespace-id");
916
+ expect(proc.write).toEqual(buf);
947
917
  expect(std.err).toMatchInlineSnapshot(`""`);
948
- expect(requests.count).toEqual(1);
949
918
  });
950
919
 
951
920
  it("should get a key in a given namespace specified by binding", async () => {
952
921
  writeWranglerConfig();
953
- const requests = mockKeyGetRequest("bound-id", "my-key", "my-value");
922
+ setMockFetchKVGetValue(
923
+ "some-account-id",
924
+ "bound-id",
925
+ "my-key",
926
+ "my-value"
927
+ );
954
928
  await runWrangler(
955
929
  "kv:key get my-key --binding someBinding --preview false"
956
930
  );
957
- expect(std.out).toMatchInlineSnapshot(`"my-value"`);
931
+ expect(proc.write).toEqual(Buffer.from("my-value"));
958
932
  expect(std.err).toMatchInlineSnapshot(`""`);
959
- expect(requests.count).toEqual(1);
960
933
  });
961
934
 
962
935
  it("should get a key in a given preview namespace specified by binding", async () => {
963
936
  writeWranglerConfig();
964
- const requests = mockKeyGetRequest(
937
+ setMockFetchKVGetValue(
938
+ "some-account-id",
965
939
  "preview-bound-id",
966
940
  "my-key",
967
941
  "my-value"
968
942
  );
969
943
  await runWrangler("kv:key get my-key --binding someBinding --preview");
970
- expect(std.out).toMatchInlineSnapshot(`"my-value"`);
944
+ expect(proc.write).toEqual(Buffer.from("my-value"));
971
945
  expect(std.err).toMatchInlineSnapshot(`""`);
972
- expect(requests.count).toEqual(1);
973
946
  });
974
947
 
975
948
  it("should get a key for the specified environment in a given namespace", async () => {
976
949
  writeWranglerConfig();
977
- const requests = mockKeyGetRequest(
950
+ setMockFetchKVGetValue(
951
+ "some-account-id",
978
952
  "env-bound-id",
979
953
  "my-key",
980
954
  "my-value"
981
955
  );
982
956
  await runWrangler(
983
- "kv:key get my-key my-value --binding someBinding --env some-environment --preview false"
957
+ "kv:key get my-key --binding someBinding --env some-environment --preview false"
984
958
  );
985
- expect(std.out).toMatchInlineSnapshot(`"my-value"`);
959
+ expect(proc.write).toEqual(Buffer.from("my-value"));
960
+ expect(std.err).toMatchInlineSnapshot(`""`);
961
+ });
962
+
963
+ it("should encode the key in the api request to get a value", async () => {
964
+ setMockFetchKVGetValue(
965
+ "some-account-id",
966
+ "some-namespace-id",
967
+ "%2Fmy%2Ckey",
968
+ "my-value"
969
+ );
970
+ await runWrangler(
971
+ "kv:key get /my,key --namespace-id some-namespace-id"
972
+ );
973
+ expect(proc.write).toEqual(Buffer.from("my-value"));
986
974
  expect(std.err).toMatchInlineSnapshot(`""`);
987
- expect(requests.count).toEqual(1);
988
975
  });
989
976
 
990
977
  it("should error if no key is provided", async () => {
991
- let error: Error | undefined;
992
- try {
993
- await runWrangler("kv:key get");
994
- } catch (e) {
995
- error = e;
996
- }
997
- expect(std.out).toMatchInlineSnapshot(`""`);
998
- expect(std.err).toMatchInlineSnapshot(`
999
- "wrangler kv:key get <key>
978
+ await expect(
979
+ runWrangler("kv:key get")
980
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
981
+ `"Not enough non-option arguments: got 0, need at least 1"`
982
+ );
983
+ expect(std.out).toMatchInlineSnapshot(`
984
+ "
985
+ wrangler kv:key get <key>
1000
986
 
1001
987
  Reads a single value by key from the given namespace.
1002
988
 
@@ -1009,29 +995,27 @@ describe("wrangler", () => {
1009
995
  -v, --version Show version number [boolean]
1010
996
 
1011
997
  Options:
1012
- -l, --local Run on my machine [boolean] [default: false]
1013
998
  --binding The name of the namespace to get from [string]
1014
999
  --namespace-id The id of the namespace to get from [string]
1015
- --env Perform on a specific environment [string]
1016
- --preview Interact with a preview namespace [boolean] [default: false]
1000
+ -e, --env Perform on a specific environment [string]
1001
+ --preview Interact with a preview namespace [boolean] [default: false]"
1002
+ `);
1003
+ expect(std.err).toMatchInlineSnapshot(`
1004
+ "X [ERROR] Not enough non-option arguments: got 0, need at least 1
1017
1005
 
1018
- Not enough non-option arguments: got 0, need at least 1"
1006
+ "
1019
1007
  `);
1020
- expect(error).toMatchInlineSnapshot(
1021
- `[Error: Not enough non-option arguments: got 0, need at least 1]`
1022
- );
1023
1008
  });
1024
1009
 
1025
1010
  it("should error if no binding nor namespace is provided", async () => {
1026
- let error: Error | undefined;
1027
- try {
1028
- await runWrangler("kv:key get foo");
1029
- } catch (e) {
1030
- error = e;
1031
- }
1032
- expect(std.out).toMatchInlineSnapshot(`""`);
1033
- expect(std.err).toMatchInlineSnapshot(`
1034
- "wrangler kv:key get <key>
1011
+ await expect(
1012
+ runWrangler("kv:key get foo")
1013
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
1014
+ `"Exactly one of the arguments binding and namespace-id is required"`
1015
+ );
1016
+ expect(std.out).toMatchInlineSnapshot(`
1017
+ "
1018
+ wrangler kv:key get <key>
1035
1019
 
1036
1020
  Reads a single value by key from the given namespace.
1037
1021
 
@@ -1044,30 +1028,28 @@ describe("wrangler", () => {
1044
1028
  -v, --version Show version number [boolean]
1045
1029
 
1046
1030
  Options:
1047
- -l, --local Run on my machine [boolean] [default: false]
1048
1031
  --binding The name of the namespace to get from [string]
1049
1032
  --namespace-id The id of the namespace to get from [string]
1050
- --env Perform on a specific environment [string]
1051
- --preview Interact with a preview namespace [boolean] [default: false]
1033
+ -e, --env Perform on a specific environment [string]
1034
+ --preview Interact with a preview namespace [boolean] [default: false]"
1035
+ `);
1036
+ expect(std.err).toMatchInlineSnapshot(`
1037
+ "X [ERROR] Exactly one of the arguments binding and namespace-id is required
1052
1038
 
1053
- Exactly one of the arguments binding and namespace-id is required"
1039
+ "
1054
1040
  `);
1055
- expect(error).toMatchInlineSnapshot(
1056
- `[Error: Exactly one of the arguments binding and namespace-id is required]`
1057
- );
1058
1041
  });
1059
1042
 
1060
1043
  it("should error if both binding and namespace is provided", async () => {
1061
- let error: Error | undefined;
1062
- try {
1063
- await runWrangler("kv:key get foo --binding x --namespace-id y");
1064
- } catch (e) {
1065
- error = e;
1066
- }
1044
+ await expect(
1045
+ runWrangler("kv:key get foo --binding x --namespace-id y")
1046
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
1047
+ `"Arguments binding and namespace-id are mutually exclusive"`
1048
+ );
1067
1049
 
1068
- expect(std.out).toMatchInlineSnapshot(`""`);
1069
- expect(std.err).toMatchInlineSnapshot(`
1070
- "wrangler kv:key get <key>
1050
+ expect(std.out).toMatchInlineSnapshot(`
1051
+ "
1052
+ wrangler kv:key get <key>
1071
1053
 
1072
1054
  Reads a single value by key from the given namespace.
1073
1055
 
@@ -1080,37 +1062,34 @@ describe("wrangler", () => {
1080
1062
  -v, --version Show version number [boolean]
1081
1063
 
1082
1064
  Options:
1083
- -l, --local Run on my machine [boolean] [default: false]
1084
1065
  --binding The name of the namespace to get from [string]
1085
1066
  --namespace-id The id of the namespace to get from [string]
1086
- --env Perform on a specific environment [string]
1087
- --preview Interact with a preview namespace [boolean] [default: false]
1067
+ -e, --env Perform on a specific environment [string]
1068
+ --preview Interact with a preview namespace [boolean] [default: false]"
1069
+ `);
1070
+ expect(std.err).toMatchInlineSnapshot(`
1071
+ "X [ERROR] Arguments binding and namespace-id are mutually exclusive
1088
1072
 
1089
- Arguments binding and namespace-id are mutually exclusive"
1073
+ "
1090
1074
  `);
1091
- expect(error).toMatchInlineSnapshot(
1092
- `[Error: Arguments binding and namespace-id are mutually exclusive]`
1093
- );
1094
1075
  });
1095
1076
 
1096
1077
  it("should error if a given binding name is not in the configured kv namespaces", async () => {
1097
1078
  writeWranglerConfig();
1098
- let error: Error | undefined;
1099
- try {
1100
- await runWrangler("kv:key get key --binding otherBinding");
1101
- } catch (e) {
1102
- error = e;
1103
- }
1104
- expect(std.out).toMatchInlineSnapshot(`""`);
1079
+ await expect(
1080
+ runWrangler("kv:key get key --binding otherBinding")
1081
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
1082
+ `"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."`
1083
+ );
1084
+ expect(std.out).toMatchInlineSnapshot(`
1085
+ "
1086
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
1087
+ `);
1105
1088
  expect(std.err).toMatchInlineSnapshot(`
1106
- "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
1089
+ "X [ERROR] A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
1107
1090
 
1108
- %s
1109
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1091
+ "
1110
1092
  `);
1111
- expect(error).toMatchInlineSnapshot(
1112
- `[Error: A namespace with binding name "otherBinding" was not found in the configured "kv_namespaces".]`
1113
- );
1114
1093
  });
1115
1094
  });
1116
1095
 
@@ -1142,6 +1121,16 @@ describe("wrangler", () => {
1142
1121
  expect(requests.count).toEqual(1);
1143
1122
  });
1144
1123
 
1124
+ it("should encode the key in the api request to delete a value", async () => {
1125
+ const requests = mockDeleteRequest("voyager", "%2FNCC-74656");
1126
+ await runWrangler(`kv:key delete --namespace-id voyager /NCC-74656`);
1127
+ expect(requests.count).toEqual(1);
1128
+ expect(std.out).toMatchInlineSnapshot(
1129
+ `"Deleting the key \\"/NCC-74656\\" on namespace voyager."`
1130
+ );
1131
+ expect(std.err).toMatchInlineSnapshot(`""`);
1132
+ });
1133
+
1145
1134
  it("should delete a key in a namespace specified by binding name", async () => {
1146
1135
  writeWranglerConfig();
1147
1136
  const requests = mockDeleteRequest("bound-id", "someKey");
@@ -1162,22 +1151,17 @@ describe("wrangler", () => {
1162
1151
 
1163
1152
  it("should error if a given binding name is not in the configured kv namespaces", async () => {
1164
1153
  writeWranglerConfig();
1165
- let error: Error | undefined;
1166
- try {
1167
- await runWrangler(`kv:key delete --binding otherBinding someKey`);
1168
- } catch (e) {
1169
- error = e;
1170
- }
1154
+ await expect(
1155
+ runWrangler(`kv:key delete --binding otherBinding someKey`)
1156
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
1157
+ `"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."`
1158
+ );
1171
1159
 
1172
1160
  expect(std.err).toMatchInlineSnapshot(`
1173
- "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
1161
+ "X [ERROR] A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
1174
1162
 
1175
- %s
1176
- If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1163
+ "
1177
1164
  `);
1178
- expect(error).toMatchInlineSnapshot(
1179
- `[Error: A namespace with binding name "otherBinding" was not found in the configured "kv_namespaces".]`
1180
- );
1181
1165
  });
1182
1166
 
1183
1167
  it("should delete a key in a namespace specified by binding name in a given environment", async () => {
@@ -1187,7 +1171,7 @@ describe("wrangler", () => {
1187
1171
  `kv:key delete --binding someBinding --env some-environment --preview false someKey`
1188
1172
  );
1189
1173
  expect(std.out).toMatchInlineSnapshot(
1190
- `"deleting the key \\"someKey\\" on namespace env-bound-id"`
1174
+ `"Deleting the key \\"someKey\\" on namespace env-bound-id."`
1191
1175
  );
1192
1176
  expect(std.err).toMatchInlineSnapshot(`""`);
1193
1177
  expect(requests.count).toEqual(1);
@@ -1203,13 +1187,325 @@ describe("wrangler", () => {
1203
1187
  });
1204
1188
  });
1205
1189
  });
1190
+
1191
+ describe("kv:bulk", () => {
1192
+ describe("put", () => {
1193
+ function mockPutRequest(
1194
+ expectedNamespaceId: string,
1195
+ expectedKeyValues: KeyValue[]
1196
+ ) {
1197
+ const requests = { count: 0 };
1198
+ setMockResponse(
1199
+ "/accounts/:accountId/storage/kv/namespaces/:namespaceId/bulk",
1200
+ "PUT",
1201
+ ([_url, accountId, namespaceId], { body }) => {
1202
+ requests.count++;
1203
+ expect(accountId).toEqual("some-account-id");
1204
+ expect(namespaceId).toEqual(expectedNamespaceId);
1205
+ expect(JSON.parse(body as string)).toEqual(
1206
+ expectedKeyValues.slice(
1207
+ (requests.count - 1) * 5000,
1208
+ requests.count * 5000
1209
+ )
1210
+ );
1211
+ return null;
1212
+ }
1213
+ );
1214
+ return requests;
1215
+ }
1216
+
1217
+ it("should put the key-values parsed from a file", async () => {
1218
+ const keyValues: KeyValue[] = [
1219
+ { key: "someKey1", value: "someValue1" },
1220
+ { key: "ns:someKey2", value: "123", base64: true },
1221
+ { key: "someKey3", value: "someValue3", expiration: 100 },
1222
+ { key: "someKey4", value: "someValue4", expiration_ttl: 500 },
1223
+ ];
1224
+ writeFileSync("./keys.json", JSON.stringify(keyValues));
1225
+ const requests = mockPutRequest("some-namespace-id", keyValues);
1226
+ await runWrangler(
1227
+ `kv:bulk put --namespace-id some-namespace-id keys.json`
1228
+ );
1229
+ expect(requests.count).toEqual(1);
1230
+ expect(std.out).toMatchInlineSnapshot(`"Success!"`);
1231
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1232
+ expect(std.err).toMatchInlineSnapshot(`""`);
1233
+ });
1234
+
1235
+ it("should put the key-values in batches of 5000 parsed from a file", async () => {
1236
+ const keyValues: KeyValue[] = new Array(12000).fill({
1237
+ key: "someKey1",
1238
+ value: "someValue1",
1239
+ });
1240
+ writeFileSync("./keys.json", JSON.stringify(keyValues));
1241
+ const requests = mockPutRequest("some-namespace-id", keyValues);
1242
+ await runWrangler(
1243
+ `kv:bulk put --namespace-id some-namespace-id keys.json`
1244
+ );
1245
+ expect(requests.count).toEqual(3);
1246
+ expect(std.out).toMatchInlineSnapshot(`
1247
+ "Uploaded 0% (0 out of 12,000)
1248
+ Uploaded 41% (5,000 out of 12,000)
1249
+ Uploaded 83% (10,000 out of 12,000)
1250
+ Uploaded 100% (12,000 out of 12,000)
1251
+ Success!"
1252
+ `);
1253
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1254
+ expect(std.err).toMatchInlineSnapshot(`""`);
1255
+ });
1256
+
1257
+ it("should error if the file is not a JSON array", async () => {
1258
+ const keyValues = { key: "someKey1", value: "someValue1" };
1259
+ writeFileSync("./keys.json", JSON.stringify(keyValues));
1260
+ await expect(
1261
+ runWrangler(`kv:bulk put --namespace-id some-namespace-id keys.json`)
1262
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`
1263
+ "Unexpected JSON input from \\"keys.json\\".
1264
+ Expected an array of key-value objects but got type \\"object\\"."
1265
+ `);
1266
+ expect(std.out).toMatchInlineSnapshot(`
1267
+ "
1268
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
1269
+ `);
1270
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1271
+ });
1272
+
1273
+ it("should error if the array contains items that are not key-value objects", async () => {
1274
+ const keyValues = [
1275
+ 123,
1276
+ "a string",
1277
+ { key: "someKey" },
1278
+ { value: "someValue" },
1279
+ // add a valid object here to make sure it's not included
1280
+ { key: "someKey1", value: "someValue1" },
1281
+ // this one will only add a warning
1282
+ { key: "someKey1", value: "someValue1", invalid: true },
1283
+ // back to the invalid ones
1284
+ { key: 123, value: "somevalue" },
1285
+ { key: "somekey", value: 123 },
1286
+ { key: "someKey1", value: "someValue1", expiration: "string" },
1287
+ { key: "someKey1", value: "someValue1", expiration_ttl: "string" },
1288
+ {
1289
+ key: 123,
1290
+ value: {
1291
+ a: {
1292
+ nested: "object",
1293
+ },
1294
+ },
1295
+ },
1296
+ { key: "someKey1", value: "someValue1", metadata: 123 },
1297
+ { key: "someKey1", value: "someValue1", base64: "string" },
1298
+ ];
1299
+ writeFileSync("./keys.json", JSON.stringify(keyValues));
1300
+ await expect(
1301
+ runWrangler(`kv:bulk put --namespace-id some-namespace-id keys.json`)
1302
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`
1303
+ "Unexpected JSON input from \\"keys.json\\".
1304
+ Each item in the array should be an object that matches:
1305
+
1306
+ interface KeyValue {
1307
+ key: string;
1308
+ value: string;
1309
+ expiration?: number;
1310
+ expiration_ttl?: number;
1311
+ metadata?: object;
1312
+ base64?: boolean;
1313
+ }
1314
+
1315
+ The item at index 0 is 123
1316
+ The item at index 1 is \\"a string\\"
1317
+ The item at index 2 is {\\"key\\":\\"someKey\\"}
1318
+ The item at index 3 is {\\"value\\":\\"someValue\\"}
1319
+ The item at index 6 is {\\"key\\":123,\\"value\\":\\"somevalue\\"}
1320
+ The item at index 7 is {\\"key\\":\\"somekey\\",\\"value\\":123}
1321
+ The item at index 8 is {\\"key\\":\\"someKey1\\",\\"value\\":\\"someValue1\\",\\"expiration\\":\\"string\\"}
1322
+ The item at index 9 is {\\"key\\":\\"someKey1\\",\\"value\\":\\"someValue1\\",\\"expiration_ttl\\":\\"string\\"}
1323
+ The item at index 10 is {\\"key\\":123,\\"value\\":{\\"a\\":{\\"nested\\":\\"object\\"}}}
1324
+ The item at index 11 is {\\"key\\":\\"someKey1\\",\\"value\\":\\"someValue1\\",\\"metadata\\":123}
1325
+ The item at index 12 is {\\"key\\":\\"someKey1\\",\\"value\\":\\"someValue1\\",\\"base64\\":\\"string\\"}"
1326
+ `);
1327
+
1328
+ expect(std.out).toMatchInlineSnapshot(`
1329
+ "
1330
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
1331
+ `);
1332
+ expect(std.warn).toMatchInlineSnapshot(`
1333
+ "▲ [WARNING] Unexpected key-value properties in \\"keys.json\\".
1334
+
1335
+ The item at index 5 contains unexpected properties: [\\"invalid\\"].
1336
+
1337
+ "
1338
+ `);
1339
+ });
1340
+ });
1341
+
1342
+ describe("delete", () => {
1343
+ function mockDeleteRequest(
1344
+ expectedNamespaceId: string,
1345
+ expectedKeys: string[]
1346
+ ) {
1347
+ const requests = { count: 0 };
1348
+ setMockResponse(
1349
+ "/accounts/:accountId/storage/kv/namespaces/:namespaceId/bulk",
1350
+ "DELETE",
1351
+ ([_url, accountId, namespaceId], { headers, body }) => {
1352
+ requests.count++;
1353
+ expect(accountId).toEqual("some-account-id");
1354
+ expect(namespaceId).toEqual(expectedNamespaceId);
1355
+ expect(new Headers(headers ?? []).get("Content-Type")).toEqual(
1356
+ "application/json"
1357
+ );
1358
+ expect(JSON.parse(body as string)).toEqual(
1359
+ expectedKeys.slice(
1360
+ (requests.count - 1) * 5000,
1361
+ requests.count * 5000
1362
+ )
1363
+ );
1364
+ return null;
1365
+ }
1366
+ );
1367
+ return requests;
1368
+ }
1369
+
1370
+ it("should delete the keys parsed from a file", async () => {
1371
+ const keys = ["someKey1", "ns:someKey2"];
1372
+ writeFileSync("./keys.json", JSON.stringify(keys));
1373
+ mockConfirm({
1374
+ text: `Are you sure you want to delete all the keys read from "keys.json" from kv-namespace with id "some-namespace-id"?`,
1375
+ result: true,
1376
+ });
1377
+ const requests = mockDeleteRequest("some-namespace-id", keys);
1378
+ await runWrangler(
1379
+ `kv:bulk delete --namespace-id some-namespace-id keys.json`
1380
+ );
1381
+ expect(requests.count).toEqual(1);
1382
+ expect(std.out).toMatchInlineSnapshot(`"Success!"`);
1383
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1384
+ expect(std.err).toMatchInlineSnapshot(`""`);
1385
+ });
1386
+
1387
+ it("should delete the keys in batches of 5000 parsed from a file", async () => {
1388
+ const keys = new Array(12000).fill("some-key");
1389
+ writeFileSync("./keys.json", JSON.stringify(keys));
1390
+ mockConfirm({
1391
+ text: `Are you sure you want to delete all the keys read from "keys.json" from kv-namespace with id "some-namespace-id"?`,
1392
+ result: true,
1393
+ });
1394
+ const requests = mockDeleteRequest("some-namespace-id", keys);
1395
+ await runWrangler(
1396
+ `kv:bulk delete --namespace-id some-namespace-id keys.json`
1397
+ );
1398
+ expect(requests.count).toEqual(3);
1399
+ expect(std.out).toMatchInlineSnapshot(`
1400
+ "Deleted 0% (0 out of 12,000)
1401
+ Deleted 41% (5,000 out of 12,000)
1402
+ Deleted 83% (10,000 out of 12,000)
1403
+ Deleted 100% (12,000 out of 12,000)
1404
+ Success!"
1405
+ `);
1406
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1407
+ expect(std.err).toMatchInlineSnapshot(`""`);
1408
+ });
1409
+
1410
+ it("should not delete the keys if the user confirms no", async () => {
1411
+ const keys = ["someKey1", "ns:someKey2"];
1412
+ writeFileSync("./keys.json", JSON.stringify(keys));
1413
+ mockConfirm({
1414
+ text: `Are you sure you want to delete all the keys read from "keys.json" from kv-namespace with id "some-namespace-id"?`,
1415
+ result: false,
1416
+ });
1417
+ await runWrangler(
1418
+ `kv:bulk delete --namespace-id some-namespace-id keys.json`
1419
+ );
1420
+ expect(std.out).toMatchInlineSnapshot(
1421
+ `"Not deleting keys read from \\"keys.json\\"."`
1422
+ );
1423
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1424
+ expect(std.err).toMatchInlineSnapshot(`""`);
1425
+ });
1426
+
1427
+ it("should delete the keys without asking if --force is provided", async () => {
1428
+ const keys = ["someKey1", "ns:someKey2"];
1429
+ writeFileSync("./keys.json", JSON.stringify(keys));
1430
+ const requests = mockDeleteRequest("some-namespace-id", keys);
1431
+ await runWrangler(
1432
+ `kv:bulk delete --namespace-id some-namespace-id keys.json --force`
1433
+ );
1434
+ expect(requests.count).toEqual(1);
1435
+ expect(std.out).toMatchInlineSnapshot(`"Success!"`);
1436
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1437
+ expect(std.err).toMatchInlineSnapshot(`""`);
1438
+ });
1439
+
1440
+ it("should delete the keys without asking if -f is provided", async () => {
1441
+ const keys = ["someKey1", "ns:someKey2"];
1442
+ writeFileSync("./keys.json", JSON.stringify(keys));
1443
+ const requests = mockDeleteRequest("some-namespace-id", keys);
1444
+ await runWrangler(
1445
+ `kv:bulk delete --namespace-id some-namespace-id keys.json -f`
1446
+ );
1447
+ expect(requests.count).toEqual(1);
1448
+ expect(std.out).toMatchInlineSnapshot(`"Success!"`);
1449
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1450
+ expect(std.err).toMatchInlineSnapshot(`""`);
1451
+ });
1452
+
1453
+ it("should error if the file is not a JSON array", async () => {
1454
+ const keys = 12354;
1455
+ writeFileSync("./keys.json", JSON.stringify(keys));
1456
+ mockConfirm({
1457
+ text: `Are you sure you want to delete all the keys read from "keys.json" from kv-namespace with id "some-namespace-id"?`,
1458
+ result: true,
1459
+ });
1460
+ await expect(
1461
+ runWrangler(
1462
+ `kv:bulk delete --namespace-id some-namespace-id keys.json`
1463
+ )
1464
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`
1465
+ "Unexpected JSON input from \\"keys.json\\".
1466
+ Expected an array of strings but got:
1467
+ 12354"
1468
+ `);
1469
+ expect(std.out).toMatchInlineSnapshot(`
1470
+ "
1471
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
1472
+ `);
1473
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1474
+ });
1475
+
1476
+ it("should error if the file contains non-string items", async () => {
1477
+ const keys = ["good", 12354, { key: "someKey" }, null];
1478
+ writeFileSync("./keys.json", JSON.stringify(keys));
1479
+ mockConfirm({
1480
+ text: `Are you sure you want to delete all the keys read from "keys.json" from kv-namespace with id "some-namespace-id"?`,
1481
+ result: true,
1482
+ });
1483
+ await expect(
1484
+ runWrangler(
1485
+ `kv:bulk delete --namespace-id some-namespace-id keys.json`
1486
+ )
1487
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`
1488
+ "Unexpected JSON input from \\"keys.json\\".
1489
+ Expected an array of strings.
1490
+ The item at index 1 is type: \\"number\\" - 12354
1491
+ The item at index 2 is type: \\"object\\" - {\\"key\\":\\"someKey\\"}
1492
+ The item at index 3 is type: \\"object\\" - null"
1493
+ `);
1494
+ expect(std.out).toMatchInlineSnapshot(`
1495
+ "
1496
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
1497
+ `);
1498
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1499
+ });
1500
+ });
1501
+ });
1206
1502
  });
1207
1503
 
1208
1504
  function writeWranglerConfig() {
1209
1505
  writeFileSync(
1210
1506
  "./wrangler.toml",
1211
1507
  [
1212
- 'name = "otherWorker"',
1508
+ 'name = "other-worker"',
1213
1509
  "kv_namespaces = [",
1214
1510
  ' { binding = "someBinding", id = "bound-id", preview_id = "preview-bound-id" }',
1215
1511
  "]",