wrangler 0.0.0-e22f9d7 → 0.0.0-e2b7352

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 (100) hide show
  1. package/README.md +3 -3
  2. package/bin/wrangler.js +12 -3
  3. package/miniflare-dist/index.mjs +79 -0
  4. package/miniflare-dist/index.mjs.map +7 -0
  5. package/package.json +33 -16
  6. package/pages/functions/buildWorker.ts +1 -1
  7. package/pages/functions/filepath-routing.test.ts +212 -28
  8. package/pages/functions/filepath-routing.ts +91 -131
  9. package/pages/functions/routes.ts +11 -18
  10. package/pages/functions/template-worker.ts +19 -33
  11. package/src/__tests__/configuration.test.ts +2820 -0
  12. package/src/__tests__/dev.test.tsx +535 -61
  13. package/src/__tests__/guess-worker-format.test.ts +68 -0
  14. package/src/__tests__/{clipboardy-mock.js → helpers/clipboardy-mock.js} +0 -0
  15. package/src/__tests__/helpers/cmd-shim.d.ts +11 -0
  16. package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
  17. package/src/__tests__/helpers/mock-account-id.ts +48 -0
  18. package/src/__tests__/helpers/mock-bin.ts +36 -0
  19. package/src/__tests__/{mock-cfetch.ts → helpers/mock-cfetch.ts} +52 -16
  20. package/src/__tests__/helpers/mock-console.ts +70 -0
  21. package/src/__tests__/{mock-dialogs.ts → helpers/mock-dialogs.ts} +1 -1
  22. package/src/__tests__/helpers/mock-kv.ts +33 -0
  23. package/src/__tests__/helpers/mock-stdin.ts +103 -0
  24. package/src/__tests__/helpers/mock-web-socket.ts +29 -0
  25. package/src/__tests__/helpers/run-in-tmp.ts +32 -0
  26. package/src/__tests__/helpers/run-wrangler.ts +16 -0
  27. package/src/__tests__/helpers/write-wrangler-toml.ts +17 -0
  28. package/src/__tests__/https-options.test.ts +127 -0
  29. package/src/__tests__/index.test.ts +773 -77
  30. package/src/__tests__/jest.setup.ts +53 -6
  31. package/src/__tests__/kv.test.ts +725 -456
  32. package/src/__tests__/logout.test.ts +52 -0
  33. package/src/__tests__/package-manager.test.ts +206 -0
  34. package/src/__tests__/pages.test.ts +82 -0
  35. package/src/__tests__/parse.test.ts +322 -0
  36. package/src/__tests__/publish.test.ts +3509 -0
  37. package/src/__tests__/r2.test.ts +198 -0
  38. package/src/__tests__/route.test.ts +45 -0
  39. package/src/__tests__/secret.test.ts +425 -0
  40. package/src/__tests__/sentry.test.ts +146 -0
  41. package/src/__tests__/tail.test.ts +638 -0
  42. package/src/__tests__/whoami.test.tsx +100 -0
  43. package/src/bundle.ts +155 -0
  44. package/src/cfetch/index.ts +21 -19
  45. package/src/cfetch/internal.ts +92 -22
  46. package/src/cli.ts +10 -0
  47. package/src/config/README.md +107 -0
  48. package/src/config/config.ts +218 -0
  49. package/src/config/diagnostics.ts +80 -0
  50. package/src/config/environment.ts +371 -0
  51. package/src/config/index.ts +62 -0
  52. package/src/config/validation-helpers.ts +554 -0
  53. package/src/config/validation.ts +1232 -0
  54. package/src/{api/preview.ts → create-worker-preview.ts} +61 -23
  55. package/src/create-worker-upload-form.ts +217 -0
  56. package/src/dev/dev.tsx +392 -0
  57. package/src/dev/local.tsx +232 -0
  58. package/src/dev/remote.tsx +203 -0
  59. package/src/dev/use-esbuild.ts +104 -0
  60. package/src/dialogs.tsx +2 -2
  61. package/src/entry.ts +240 -0
  62. package/src/environment-variables.ts +33 -0
  63. package/src/https-options.ts +122 -0
  64. package/src/index.tsx +1218 -803
  65. package/src/inspect.ts +103 -25
  66. package/src/intl-polyfill.d.ts +139 -0
  67. package/src/{kv.tsx → kv.ts} +104 -61
  68. package/src/miniflare-cli/README.md +30 -0
  69. package/src/miniflare-cli/enum-keys.ts +17 -0
  70. package/src/miniflare-cli/index.ts +37 -0
  71. package/src/module-collection.ts +229 -35
  72. package/src/open-in-browser.ts +25 -0
  73. package/src/package-manager.ts +126 -0
  74. package/src/pages.tsx +74 -44
  75. package/src/parse.ts +183 -0
  76. package/src/paths.ts +26 -0
  77. package/src/proxy.ts +177 -39
  78. package/src/publish.ts +286 -300
  79. package/src/r2.ts +50 -0
  80. package/src/reporting.ts +131 -0
  81. package/src/selfsigned.d.ts +29 -0
  82. package/src/sites.tsx +224 -62
  83. package/src/tail/filters.ts +279 -0
  84. package/src/tail/index.ts +299 -0
  85. package/src/tail/printing.ts +62 -0
  86. package/src/user.tsx +278 -178
  87. package/src/whoami.tsx +69 -0
  88. package/src/{api/worker.ts → worker.ts} +45 -37
  89. package/templates/new-worker.js +15 -0
  90. package/templates/new-worker.ts +15 -0
  91. package/{static-asset-facade.js → templates/static-asset-facade.js} +0 -0
  92. package/templates/tsconfig.json +105 -0
  93. package/wrangler-dist/cli.js +130377 -112126
  94. package/wrangler-dist/cli.js.map +3 -3
  95. package/src/__tests__/run-in-tmp.ts +0 -19
  96. package/src/__tests__/run-wrangler.ts +0 -32
  97. package/src/api/form_data.ts +0 -131
  98. package/src/config.ts +0 -487
  99. package/src/dev.tsx +0 -784
  100. package/src/tail.tsx +0 -73
@@ -1,20 +1,24 @@
1
- import { writeFileSync } from "fs";
1
+ import { writeFileSync } from "node:fs";
2
+ import { Headers } from "undici";
3
+ import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
2
4
  import {
3
5
  setMockResponse,
4
- setMockRawResponse,
5
6
  unsetAllMocks,
6
- createFetchResult,
7
- } from "./mock-cfetch";
8
- import { runWrangler } from "./run-wrangler";
9
- import { runInTempDir } from "./run-in-tmp";
10
-
11
- interface KVNamespaceInfo {
12
- title: string;
13
- id: string;
14
- }
7
+ unsetMockFetchKVGetValues,
8
+ setMockFetchKVGetValue,
9
+ } from "./helpers/mock-cfetch";
10
+ import { mockConsoleMethods } from "./helpers/mock-console";
11
+ import { mockConfirm } from "./helpers/mock-dialogs";
12
+ import { mockKeyListRequest } from "./helpers/mock-kv";
13
+ import { runInTempDir } from "./helpers/run-in-tmp";
14
+ import { runWrangler } from "./helpers/run-wrangler";
15
+ import type { KeyValue, KVNamespaceInfo, NamespaceKeyInfo } from "../kv";
15
16
 
16
17
  describe("wrangler", () => {
18
+ mockAccountId();
19
+ mockApiToken();
17
20
  runInTempDir();
21
+ const std = mockConsoleMethods();
18
22
 
19
23
  afterEach(() => {
20
24
  unsetAllMocks();
@@ -36,11 +40,13 @@ describe("wrangler", () => {
36
40
  }
37
41
 
38
42
  it("should error if no namespace is given", async () => {
39
- const { error, stdout, stderr } = await runWrangler(
40
- "kv:namespace create"
43
+ await expect(
44
+ runWrangler("kv:namespace create")
45
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
46
+ `"Not enough non-option arguments: got 0, need at least 1"`
41
47
  );
42
- expect(stdout).toMatchInlineSnapshot(`""`);
43
- expect(stderr).toMatchInlineSnapshot(`
48
+ expect(std.out).toMatchInlineSnapshot(`""`);
49
+ expect(std.err).toMatchInlineSnapshot(`
44
50
  "wrangler kv:namespace create <namespace>
45
51
 
46
52
  Create a new namespace
@@ -49,28 +55,27 @@ describe("wrangler", () => {
49
55
  namespace The name of the new namespace [string] [required]
50
56
 
51
57
  Flags:
52
- -c, --config Path to .toml configuration file [string]
53
- -h, --help Show help [boolean]
54
- -v, --version Show version number [boolean]
58
+ -c, --config Path to .toml configuration file [string]
59
+ -h, --help Show help [boolean]
60
+ -v, --version Show version number [boolean]
61
+ --legacy-env Use legacy environments [boolean]
55
62
 
56
63
  Options:
57
- -l, --local Run on my machine [boolean] [default: false]
58
- --env Perform on a specific environment [string]
64
+ -e, --env Perform on a specific environment [string]
59
65
  --preview Interact with a preview namespace [boolean]
60
66
 
61
67
  Not enough non-option arguments: got 0, need at least 1"
62
68
  `);
63
- expect(error).toMatchInlineSnapshot(
64
- `[Error: Not enough non-option arguments: got 0, need at least 1]`
65
- );
66
69
  });
67
70
 
68
71
  it("should error if the namespace to create contains spaces", async () => {
69
- const { error, stdout, stderr } = await runWrangler(
70
- "kv:namespace create abc def ghi"
72
+ await expect(
73
+ runWrangler("kv:namespace create abc def ghi")
74
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
75
+ `"Unknown arguments: def, ghi"`
71
76
  );
72
- expect(stdout).toMatchInlineSnapshot(`""`);
73
- expect(stderr).toMatchInlineSnapshot(`
77
+ expect(std.out).toMatchInlineSnapshot(`""`);
78
+ expect(std.err).toMatchInlineSnapshot(`
74
79
  "wrangler kv:namespace create <namespace>
75
80
 
76
81
  Create a new namespace
@@ -79,28 +84,28 @@ describe("wrangler", () => {
79
84
  namespace The name of the new namespace [string] [required]
80
85
 
81
86
  Flags:
82
- -c, --config Path to .toml configuration file [string]
83
- -h, --help Show help [boolean]
84
- -v, --version Show version number [boolean]
87
+ -c, --config Path to .toml configuration file [string]
88
+ -h, --help Show help [boolean]
89
+ -v, --version Show version number [boolean]
90
+ --legacy-env Use legacy environments [boolean]
85
91
 
86
92
  Options:
87
- -l, --local Run on my machine [boolean] [default: false]
88
- --env Perform on a specific environment [string]
93
+ -e, --env Perform on a specific environment [string]
89
94
  --preview Interact with a preview namespace [boolean]
90
95
 
91
- Unexpected additional positional arguments \\"def ghi\\"."
96
+ Unknown arguments: def, ghi"
92
97
  `);
93
- expect(error).toMatchInlineSnapshot(
94
- `[Error: Unexpected additional positional arguments "def ghi".]`
95
- );
96
98
  });
97
99
 
98
100
  it("should error if the namespace to create is not valid", async () => {
99
- const { error, stdout, stderr } = await runWrangler(
100
- "kv:namespace create abc-def"
101
+ await expect(
102
+ runWrangler("kv:namespace create abc-def")
103
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
104
+ `"The namespace binding name \\"abc-def\\" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number."`
101
105
  );
102
- expect(stdout).toMatchInlineSnapshot(`""`);
103
- expect(stderr).toMatchInlineSnapshot(`
106
+
107
+ expect(std.out).toMatchInlineSnapshot(`""`);
108
+ expect(std.err).toMatchInlineSnapshot(`
104
109
  "wrangler kv:namespace create <namespace>
105
110
 
106
111
  Create a new namespace
@@ -109,28 +114,23 @@ describe("wrangler", () => {
109
114
  namespace The name of the new namespace [string] [required]
110
115
 
111
116
  Flags:
112
- -c, --config Path to .toml configuration file [string]
113
- -h, --help Show help [boolean]
114
- -v, --version Show version number [boolean]
117
+ -c, --config Path to .toml configuration file [string]
118
+ -h, --help Show help [boolean]
119
+ -v, --version Show version number [boolean]
120
+ --legacy-env Use legacy environments [boolean]
115
121
 
116
122
  Options:
117
- -l, --local Run on my machine [boolean] [default: false]
118
- --env Perform on a specific environment [string]
123
+ -e, --env Perform on a specific environment [string]
119
124
  --preview Interact with a preview namespace [boolean]
120
125
 
121
126
  The namespace binding name \\"abc-def\\" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number."
122
127
  `);
123
- expect(error).toMatchInlineSnapshot(
124
- `[Error: The namespace binding name "abc-def" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number.]`
125
- );
126
128
  });
127
129
 
128
130
  it("should create a namespace", async () => {
129
131
  mockCreateRequest("worker-UnitTestNamespace");
130
- const { stdout } = await runWrangler(
131
- "kv:namespace create UnitTestNamespace"
132
- );
133
- expect(stdout).toMatchInlineSnapshot(`
132
+ await runWrangler("kv:namespace create UnitTestNamespace");
133
+ expect(std.out).toMatchInlineSnapshot(`
134
134
  "🌀 Creating namespace with title \\"worker-UnitTestNamespace\\"
135
135
  ✨ Success!
136
136
  Add the following to your configuration file in your kv_namespaces array:
@@ -140,10 +140,8 @@ describe("wrangler", () => {
140
140
 
141
141
  it("should create a preview namespace if configured to do so", async () => {
142
142
  mockCreateRequest("worker-UnitTestNamespace_preview");
143
- const { stdout } = await runWrangler(
144
- "kv:namespace create UnitTestNamespace --preview"
145
- );
146
- expect(stdout).toMatchInlineSnapshot(`
143
+ await runWrangler("kv:namespace create UnitTestNamespace --preview");
144
+ expect(std.out).toMatchInlineSnapshot(`
147
145
  "🌀 Creating namespace with title \\"worker-UnitTestNamespace_preview\\"
148
146
  ✨ Success!
149
147
  Add the following to your configuration file in your kv_namespaces array:
@@ -154,10 +152,8 @@ describe("wrangler", () => {
154
152
  it("should create a namespace using configured worker name", async () => {
155
153
  writeFileSync("./wrangler.toml", 'name = "otherWorker"', "utf-8");
156
154
  mockCreateRequest("otherWorker-UnitTestNamespace");
157
- const { stdout } = await runWrangler(
158
- "kv:namespace create UnitTestNamespace"
159
- );
160
- expect(stdout).toMatchInlineSnapshot(`
155
+ await runWrangler("kv:namespace create UnitTestNamespace");
156
+ expect(std.out).toMatchInlineSnapshot(`
161
157
  "🌀 Creating namespace with title \\"otherWorker-UnitTestNamespace\\"
162
158
  ✨ Success!
163
159
  Add the following to your configuration file in your kv_namespaces array:
@@ -167,10 +163,10 @@ describe("wrangler", () => {
167
163
 
168
164
  it("should create a namespace in an environment if configured to do so", async () => {
169
165
  mockCreateRequest("worker-customEnv-UnitTestNamespace");
170
- const { stdout } = await runWrangler(
166
+ await runWrangler(
171
167
  "kv:namespace create UnitTestNamespace --env customEnv"
172
168
  );
173
- expect(stdout).toMatchInlineSnapshot(`
169
+ expect(std.out).toMatchInlineSnapshot(`
174
170
  "🌀 Creating namespace with title \\"worker-customEnv-UnitTestNamespace\\"
175
171
  ✨ Success!
176
172
  Add the following to your configuration file in your kv_namespaces array under [env.customEnv]:
@@ -206,12 +202,10 @@ describe("wrangler", () => {
206
202
  { title: "title-2", id: "id-2" },
207
203
  ];
208
204
  mockListRequest(kvNamespaces);
209
- const { error, stdout, stderr } = await runWrangler(
210
- "kv:namespace list"
211
- );
212
- expect(error).toMatchInlineSnapshot(`undefined`);
213
- expect(stderr).toMatchInlineSnapshot(`""`);
214
- const namespaces = JSON.parse(stdout);
205
+ await runWrangler("kv:namespace list");
206
+
207
+ expect(std.err).toMatchInlineSnapshot(`""`);
208
+ const namespaces = JSON.parse(std.out);
215
209
  expect(namespaces).toEqual(kvNamespaces);
216
210
  });
217
211
 
@@ -222,8 +216,8 @@ describe("wrangler", () => {
222
216
  kvNamespaces.push({ title: "title-" + i, id: "id-" + i });
223
217
  }
224
218
  const requests = mockListRequest(kvNamespaces);
225
- const { stdout } = await runWrangler("kv:namespace list");
226
- const namespaces = JSON.parse(stdout);
219
+ await runWrangler("kv:namespace list");
220
+ const namespaces = JSON.parse(std.out);
227
221
  expect(namespaces).toEqual(kvNamespaces);
228
222
  expect(requests.count).toEqual(6);
229
223
  });
@@ -273,24 +267,26 @@ describe("wrangler", () => {
273
267
 
274
268
  it("should error if a given binding name is not in the configured kv namespaces", async () => {
275
269
  writeWranglerConfig();
276
- const { stderr } = await runWrangler(
277
- `kv:namespace delete --binding otherBinding`
278
- );
279
- expect(stderr).toMatchInlineSnapshot(`
270
+ await expect(runWrangler("kv:namespace delete --binding otherBinding"))
271
+ .rejects.toThrowErrorMatchingInlineSnapshot(`
272
+ "Not able to delete namespace.
273
+ A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."
274
+ `);
275
+ expect(std.err).toMatchInlineSnapshot(`
280
276
  "wrangler kv:namespace delete
281
277
 
282
278
  Deletes a given namespace.
283
279
 
284
280
  Flags:
285
- -c, --config Path to .toml configuration file [string]
286
- -h, --help Show help [boolean]
287
- -v, --version Show version number [boolean]
281
+ -c, --config Path to .toml configuration file [string]
282
+ -h, --help Show help [boolean]
283
+ -v, --version Show version number [boolean]
284
+ --legacy-env Use legacy environments [boolean]
288
285
 
289
286
  Options:
290
- -l, --local Run on my machine [boolean] [default: false]
291
287
  --binding The name of the namespace to delete [string]
292
288
  --namespace-id The id of the namespace to delete [string]
293
- --env Perform on a specific environment [string]
289
+ -e, --env Perform on a specific environment [string]
294
290
  --preview Interact with a preview namespace [boolean]
295
291
 
296
292
  Not able to delete namespace.
@@ -301,12 +297,12 @@ describe("wrangler", () => {
301
297
  it("should delete a namespace specified by binding name in a given environment", async () => {
302
298
  writeWranglerConfig();
303
299
  const requests = mockDeleteRequest("env-bound-id");
304
- const { stdout, stderr, error } = await runWrangler(
305
- `kv:namespace delete --binding someBinding --env some-environment --preview false`
300
+ await runWrangler(
301
+ "kv:namespace delete --binding someBinding --env some-environment --preview false"
306
302
  );
307
- expect(stdout).toMatchInlineSnapshot(`""`);
308
- expect(stderr).toMatchInlineSnapshot(`""`);
309
- expect(error).toMatchInlineSnapshot(`undefined`);
303
+
304
+ expect(std.out).toMatchInlineSnapshot(`""`);
305
+ expect(std.err).toMatchInlineSnapshot(`""`);
310
306
  expect(requests.count).toEqual(1);
311
307
  });
312
308
 
@@ -325,10 +321,7 @@ describe("wrangler", () => {
325
321
  describe("put", () => {
326
322
  function mockKeyPutRequest(
327
323
  expectedNamespaceId: string,
328
- expectedKey: string,
329
- expectedValue: string,
330
- expiration?: number,
331
- expirationTtl?: number
324
+ expectedKV: KeyValue
332
325
  ) {
333
326
  const requests = { count: 0 };
334
327
  setMockResponse(
@@ -338,15 +331,19 @@ describe("wrangler", () => {
338
331
  requests.count++;
339
332
  expect(accountId).toEqual("some-account-id");
340
333
  expect(namespaceId).toEqual(expectedNamespaceId);
341
- expect(key).toEqual(expectedKey);
342
- expect(body).toEqual(expectedValue);
343
- if (expiration !== undefined) {
344
- 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
+ );
345
340
  } else {
346
341
  expect(query.has("expiration")).toBe(false);
347
342
  }
348
- if (expirationTtl) {
349
- expect(query.get("expiration_ttl")).toEqual(`${expirationTtl}`);
343
+ if (expectedKV.expiration_ttl) {
344
+ expect(query.get("expiration_ttl")).toEqual(
345
+ `${expectedKV.expiration_ttl}`
346
+ );
350
347
  } else {
351
348
  expect(query.has("expiration_ttl")).toBe(false);
352
349
  }
@@ -357,554 +354,553 @@ describe("wrangler", () => {
357
354
  }
358
355
 
359
356
  it("should put a key in a given namespace specified by namespace-id", async () => {
360
- const requests = mockKeyPutRequest(
361
- "some-namespace-id",
362
- "my-key",
363
- "my-value"
364
- );
365
- const { error, stderr, stdout } = await runWrangler(
357
+ const requests = mockKeyPutRequest("some-namespace-id", {
358
+ key: "my-key",
359
+ value: "my-value",
360
+ });
361
+
362
+ await runWrangler(
366
363
  "kv:key put my-key my-value --namespace-id some-namespace-id"
367
364
  );
365
+
368
366
  expect(requests.count).toEqual(1);
369
- expect(stdout).toMatchInlineSnapshot(
367
+ expect(std.out).toMatchInlineSnapshot(
370
368
  `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace some-namespace-id"`
371
369
  );
372
- expect(stderr).toMatchInlineSnapshot(`""`);
373
- expect(error).toMatchInlineSnapshot(`undefined`);
370
+ expect(std.err).toMatchInlineSnapshot(`""`);
374
371
  });
375
372
 
376
373
  it("should put a key in a given namespace specified by binding", async () => {
377
374
  writeWranglerConfig();
378
- const requests = mockKeyPutRequest("bound-id", "my-key", "my-value");
379
- const { error, stderr, stdout } = await runWrangler(
375
+ const requests = mockKeyPutRequest("bound-id", {
376
+ key: "my-key",
377
+ value: "my-value",
378
+ });
379
+ await runWrangler(
380
380
  "kv:key put my-key my-value --binding someBinding --preview false"
381
381
  );
382
- expect(stdout).toMatchInlineSnapshot(
382
+
383
+ expect(std.out).toMatchInlineSnapshot(
383
384
  `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace bound-id"`
384
385
  );
385
- expect(stderr).toMatchInlineSnapshot(`""`);
386
- expect(error).toMatchInlineSnapshot(`undefined`);
386
+ expect(std.err).toMatchInlineSnapshot(`""`);
387
387
  expect(requests.count).toEqual(1);
388
388
  });
389
389
 
390
390
  it("should put a key in a given preview namespace specified by binding", async () => {
391
391
  writeWranglerConfig();
392
- const requests = mockKeyPutRequest(
393
- "preview-bound-id",
394
- "my-key",
395
- "my-value"
396
- );
397
- const { error, stderr, stdout } = await runWrangler(
392
+ const requests = mockKeyPutRequest("preview-bound-id", {
393
+ key: "my-key",
394
+ value: "my-value",
395
+ });
396
+
397
+ await runWrangler(
398
398
  "kv:key put my-key my-value --binding someBinding --preview"
399
399
  );
400
- expect(stdout).toMatchInlineSnapshot(
400
+
401
+ expect(std.out).toMatchInlineSnapshot(
401
402
  `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace preview-bound-id"`
402
403
  );
403
- expect(stderr).toMatchInlineSnapshot(`""`);
404
- expect(error).toMatchInlineSnapshot(`undefined`);
404
+ expect(std.err).toMatchInlineSnapshot(`""`);
405
405
  expect(requests.count).toEqual(1);
406
406
  });
407
407
 
408
408
  it("should add expiration and ttl properties when putting a key", async () => {
409
- const requests = mockKeyPutRequest(
410
- "some-namespace-id",
411
- "my-key",
412
- "my-value",
413
- 10,
414
- 20
415
- );
416
- const { error, stderr, stdout } = await runWrangler(
409
+ const requests = mockKeyPutRequest("some-namespace-id", {
410
+ key: "my-key",
411
+ value: "my-value",
412
+ expiration: 10,
413
+ expiration_ttl: 20,
414
+ });
415
+ await runWrangler(
417
416
  "kv:key put my-key my-value --namespace-id some-namespace-id --expiration 10 --ttl 20"
418
417
  );
419
418
  expect(requests.count).toEqual(1);
420
- expect(stdout).toMatchInlineSnapshot(
419
+ expect(std.out).toMatchInlineSnapshot(
421
420
  `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace some-namespace-id"`
422
421
  );
423
- expect(stderr).toMatchInlineSnapshot(`""`);
424
- expect(error).toMatchInlineSnapshot(`undefined`);
422
+ expect(std.err).toMatchInlineSnapshot(`""`);
425
423
  });
426
424
 
427
425
  it("should put a key to the specified environment in a given namespace", async () => {
428
426
  writeWranglerConfig();
429
- const requests = mockKeyPutRequest(
430
- "env-bound-id",
431
- "my-key",
432
- "my-value"
433
- );
434
- const { error, stderr, stdout } = await runWrangler(
427
+ const requests = mockKeyPutRequest("env-bound-id", {
428
+ key: "my-key",
429
+ value: "my-value",
430
+ });
431
+ await runWrangler(
435
432
  "kv:key put my-key my-value --binding someBinding --env some-environment --preview false"
436
433
  );
437
- expect(stdout).toMatchInlineSnapshot(
434
+ expect(std.out).toMatchInlineSnapshot(
438
435
  `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace env-bound-id"`
439
436
  );
440
- expect(stderr).toMatchInlineSnapshot(`""`);
441
- expect(error).toMatchInlineSnapshot(`undefined`);
437
+ expect(std.err).toMatchInlineSnapshot(`""`);
442
438
  expect(requests.count).toEqual(1);
443
439
  });
444
440
 
445
441
  it("should put a key with a value loaded from a given path", async () => {
446
442
  writeFileSync("foo.txt", "file-contents", "utf-8");
447
- const requests = mockKeyPutRequest(
448
- "some-namespace-id",
449
- "my-key",
450
- "file-contents"
451
- );
452
- const { error, stderr, stdout } = await runWrangler(
443
+ const requests = mockKeyPutRequest("some-namespace-id", {
444
+ key: "my-key",
445
+ value: "file-contents",
446
+ });
447
+ await runWrangler(
453
448
  "kv:key put my-key --namespace-id some-namespace-id --path foo.txt"
454
449
  );
455
- expect(stdout).toMatchInlineSnapshot(
450
+ expect(std.out).toMatchInlineSnapshot(
456
451
  `"writing the contents of foo.txt to the key \\"my-key\\" on namespace some-namespace-id"`
457
452
  );
458
- expect(stderr).toMatchInlineSnapshot(`""`);
459
- expect(error).toMatchInlineSnapshot(`undefined`);
453
+ expect(std.err).toMatchInlineSnapshot(`""`);
460
454
  expect(requests.count).toEqual(1);
461
455
  });
462
456
 
463
457
  it("should error if no key is provided", async () => {
464
- const { error, stdout, stderr } = await runWrangler("kv:key put");
458
+ await expect(
459
+ runWrangler("kv:key put")
460
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
461
+ `"Not enough non-option arguments: got 0, need at least 1"`
462
+ );
465
463
 
466
- expect(stdout).toMatchInlineSnapshot(`""`);
467
- expect(stderr).toMatchInlineSnapshot(`
464
+ expect(std.out).toMatchInlineSnapshot(`""`);
465
+ expect(std.err).toMatchInlineSnapshot(`
468
466
  "wrangler kv:key put <key> [value]
469
467
 
470
468
  Writes a single key/value pair to the given namespace.
471
469
 
472
470
  Positionals:
473
- key The key to write to. [string] [required]
474
- value The value to write. [string]
471
+ key The key to write to [string] [required]
472
+ value The value to write [string]
475
473
 
476
474
  Flags:
477
- -c, --config Path to .toml configuration file [string]
478
- -h, --help Show help [boolean]
479
- -v, --version Show version number [boolean]
475
+ -c, --config Path to .toml configuration file [string]
476
+ -h, --help Show help [boolean]
477
+ -v, --version Show version number [boolean]
478
+ --legacy-env Use legacy environments [boolean]
480
479
 
481
480
  Options:
482
- -l, --local Run on my machine [boolean] [default: false]
483
- --binding The binding of the namespace to write to. [string]
484
- --namespace-id The id of the namespace to write to. [string]
485
- --env Perform on a specific environment [string]
481
+ --binding The binding of the namespace to write to [string]
482
+ --namespace-id The id of the namespace to write to [string]
483
+ -e, --env Perform on a specific environment [string]
486
484
  --preview Interact with a preview namespace [boolean]
487
- --ttl Time for which the entries should be visible. [number]
485
+ --ttl Time for which the entries should be visible [number]
488
486
  --expiration Time since the UNIX epoch after which the entry expires [number]
489
- --path Read value from the file at a given path. [string]
487
+ --path Read value from the file at a given path [string]
490
488
 
491
489
  Not enough non-option arguments: got 0, need at least 1"
492
490
  `);
493
- expect(error).toMatchInlineSnapshot(
494
- `[Error: Not enough non-option arguments: got 0, need at least 1]`
495
- );
496
491
  });
497
492
 
498
493
  it("should error if no binding nor namespace is provided", async () => {
499
- const { error, stdout, stderr } = await runWrangler(
500
- "kv:key put foo bar"
494
+ await expect(
495
+ runWrangler("kv:key put foo bar")
496
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
497
+ `"Exactly one of the arguments binding and namespace-id is required"`
501
498
  );
502
499
 
503
- expect(stdout).toMatchInlineSnapshot(`""`);
504
- expect(stderr).toMatchInlineSnapshot(`
500
+ expect(std.out).toMatchInlineSnapshot(`""`);
501
+ expect(std.err).toMatchInlineSnapshot(`
505
502
  "wrangler kv:key put <key> [value]
506
503
 
507
504
  Writes a single key/value pair to the given namespace.
508
505
 
509
506
  Positionals:
510
- key The key to write to. [string] [required]
511
- value The value to write. [string]
507
+ key The key to write to [string] [required]
508
+ value The value to write [string]
512
509
 
513
510
  Flags:
514
- -c, --config Path to .toml configuration file [string]
515
- -h, --help Show help [boolean]
516
- -v, --version Show version number [boolean]
511
+ -c, --config Path to .toml configuration file [string]
512
+ -h, --help Show help [boolean]
513
+ -v, --version Show version number [boolean]
514
+ --legacy-env Use legacy environments [boolean]
517
515
 
518
516
  Options:
519
- -l, --local Run on my machine [boolean] [default: false]
520
- --binding The binding of the namespace to write to. [string]
521
- --namespace-id The id of the namespace to write to. [string]
522
- --env Perform on a specific environment [string]
517
+ --binding The binding of the namespace to write to [string]
518
+ --namespace-id The id of the namespace to write to [string]
519
+ -e, --env Perform on a specific environment [string]
523
520
  --preview Interact with a preview namespace [boolean]
524
- --ttl Time for which the entries should be visible. [number]
521
+ --ttl Time for which the entries should be visible [number]
525
522
  --expiration Time since the UNIX epoch after which the entry expires [number]
526
- --path Read value from the file at a given path. [string]
523
+ --path Read value from the file at a given path [string]
527
524
 
528
525
  Exactly one of the arguments binding and namespace-id is required"
529
526
  `);
530
- expect(error).toMatchInlineSnapshot(
531
- `[Error: Exactly one of the arguments binding and namespace-id is required]`
532
- );
533
527
  });
534
528
 
535
529
  it("should error if both binding and namespace is provided", async () => {
536
- const { error, stdout, stderr } = await runWrangler(
537
- "kv:key put foo bar --binding x --namespace-id y"
530
+ await expect(
531
+ runWrangler("kv:key put foo bar --binding x --namespace-id y")
532
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
533
+ `"Arguments binding and namespace-id are mutually exclusive"`
538
534
  );
539
535
 
540
- expect(stdout).toMatchInlineSnapshot(`""`);
541
- expect(stderr).toMatchInlineSnapshot(`
536
+ expect(std.out).toMatchInlineSnapshot(`""`);
537
+ expect(std.err).toMatchInlineSnapshot(`
542
538
  "wrangler kv:key put <key> [value]
543
539
 
544
540
  Writes a single key/value pair to the given namespace.
545
541
 
546
542
  Positionals:
547
- key The key to write to. [string] [required]
548
- value The value to write. [string]
543
+ key The key to write to [string] [required]
544
+ value The value to write [string]
549
545
 
550
546
  Flags:
551
- -c, --config Path to .toml configuration file [string]
552
- -h, --help Show help [boolean]
553
- -v, --version Show version number [boolean]
547
+ -c, --config Path to .toml configuration file [string]
548
+ -h, --help Show help [boolean]
549
+ -v, --version Show version number [boolean]
550
+ --legacy-env Use legacy environments [boolean]
554
551
 
555
552
  Options:
556
- -l, --local Run on my machine [boolean] [default: false]
557
- --binding The binding of the namespace to write to. [string]
558
- --namespace-id The id of the namespace to write to. [string]
559
- --env Perform on a specific environment [string]
553
+ --binding The binding of the namespace to write to [string]
554
+ --namespace-id The id of the namespace to write to [string]
555
+ -e, --env Perform on a specific environment [string]
560
556
  --preview Interact with a preview namespace [boolean]
561
- --ttl Time for which the entries should be visible. [number]
557
+ --ttl Time for which the entries should be visible [number]
562
558
  --expiration Time since the UNIX epoch after which the entry expires [number]
563
- --path Read value from the file at a given path. [string]
559
+ --path Read value from the file at a given path [string]
564
560
 
565
561
  Arguments binding and namespace-id are mutually exclusive"
566
562
  `);
567
- expect(error).toMatchInlineSnapshot(
568
- `[Error: Arguments binding and namespace-id are mutually exclusive]`
569
- );
570
563
  });
571
564
 
572
565
  it("should error if no value nor path is provided", async () => {
573
- const { error, stdout, stderr } = await runWrangler(
574
- "kv:key put key --namespace-id 12345"
566
+ await expect(
567
+ runWrangler("kv:key put key --namespace-id 12345")
568
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
569
+ `"Exactly one of the arguments value and path is required"`
575
570
  );
576
571
 
577
- expect(stdout).toMatchInlineSnapshot(`""`);
578
- expect(stderr).toMatchInlineSnapshot(`
572
+ expect(std.out).toMatchInlineSnapshot(`""`);
573
+ expect(std.err).toMatchInlineSnapshot(`
579
574
  "wrangler kv:key put <key> [value]
580
575
 
581
576
  Writes a single key/value pair to the given namespace.
582
577
 
583
578
  Positionals:
584
- key The key to write to. [string] [required]
585
- value The value to write. [string]
579
+ key The key to write to [string] [required]
580
+ value The value to write [string]
586
581
 
587
582
  Flags:
588
- -c, --config Path to .toml configuration file [string]
589
- -h, --help Show help [boolean]
590
- -v, --version Show version number [boolean]
583
+ -c, --config Path to .toml configuration file [string]
584
+ -h, --help Show help [boolean]
585
+ -v, --version Show version number [boolean]
586
+ --legacy-env Use legacy environments [boolean]
591
587
 
592
588
  Options:
593
- -l, --local Run on my machine [boolean] [default: false]
594
- --binding The binding of the namespace to write to. [string]
595
- --namespace-id The id of the namespace to write to. [string]
596
- --env Perform on a specific environment [string]
589
+ --binding The binding of the namespace to write to [string]
590
+ --namespace-id The id of the namespace to write to [string]
591
+ -e, --env Perform on a specific environment [string]
597
592
  --preview Interact with a preview namespace [boolean]
598
- --ttl Time for which the entries should be visible. [number]
593
+ --ttl Time for which the entries should be visible [number]
599
594
  --expiration Time since the UNIX epoch after which the entry expires [number]
600
- --path Read value from the file at a given path. [string]
595
+ --path Read value from the file at a given path [string]
601
596
 
602
597
  Exactly one of the arguments value and path is required"
603
598
  `);
604
- expect(error).toMatchInlineSnapshot(
605
- `[Error: Exactly one of the arguments value and path is required]`
606
- );
607
599
  });
608
600
 
609
601
  it("should error if both value and path is provided", async () => {
610
- const { error, stdout, stderr } = await runWrangler(
611
- "kv:key put key value --path xyz --namespace-id 12345"
602
+ await expect(
603
+ runWrangler("kv:key put key value --path xyz --namespace-id 12345")
604
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
605
+ `"Arguments value and path are mutually exclusive"`
612
606
  );
613
607
 
614
- expect(stdout).toMatchInlineSnapshot(`""`);
615
- expect(stderr).toMatchInlineSnapshot(`
608
+ expect(std.out).toMatchInlineSnapshot(`""`);
609
+ expect(std.err).toMatchInlineSnapshot(`
616
610
  "wrangler kv:key put <key> [value]
617
611
 
618
612
  Writes a single key/value pair to the given namespace.
619
613
 
620
614
  Positionals:
621
- key The key to write to. [string] [required]
622
- value The value to write. [string]
615
+ key The key to write to [string] [required]
616
+ value The value to write [string]
623
617
 
624
618
  Flags:
625
- -c, --config Path to .toml configuration file [string]
626
- -h, --help Show help [boolean]
627
- -v, --version Show version number [boolean]
619
+ -c, --config Path to .toml configuration file [string]
620
+ -h, --help Show help [boolean]
621
+ -v, --version Show version number [boolean]
622
+ --legacy-env Use legacy environments [boolean]
628
623
 
629
624
  Options:
630
- -l, --local Run on my machine [boolean] [default: false]
631
- --binding The binding of the namespace to write to. [string]
632
- --namespace-id The id of the namespace to write to. [string]
633
- --env Perform on a specific environment [string]
625
+ --binding The binding of the namespace to write to [string]
626
+ --namespace-id The id of the namespace to write to [string]
627
+ -e, --env Perform on a specific environment [string]
634
628
  --preview Interact with a preview namespace [boolean]
635
- --ttl Time for which the entries should be visible. [number]
629
+ --ttl Time for which the entries should be visible [number]
636
630
  --expiration Time since the UNIX epoch after which the entry expires [number]
637
- --path Read value from the file at a given path. [string]
631
+ --path Read value from the file at a given path [string]
638
632
 
639
633
  Arguments value and path are mutually exclusive"
640
634
  `);
641
- expect(error).toMatchInlineSnapshot(
642
- `[Error: Arguments value and path are mutually exclusive]`
643
- );
644
635
  });
645
636
 
646
637
  it("should error if a given binding name is not in the configured kv namespaces", async () => {
647
638
  writeWranglerConfig();
648
- const { error, stdout, stderr } = await runWrangler(
649
- `kv:key put key value --binding otherBinding`
650
- );
651
- expect(stdout).toMatchInlineSnapshot(`""`);
652
- expect(stderr).toMatchInlineSnapshot(
639
+ await expect(
640
+ runWrangler("kv:key put key value --binding otherBinding")
641
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
653
642
  `"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."`
654
643
  );
655
- expect(error).toMatchInlineSnapshot(
656
- `[Error: A namespace with binding name "otherBinding" was not found in the configured "kv_namespaces".]`
657
- );
644
+
645
+ expect(std.out).toMatchInlineSnapshot(`""`);
646
+ expect(std.err).toMatchInlineSnapshot(`
647
+ "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
648
+
649
+ %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
650
+ `);
658
651
  });
659
652
 
660
653
  it("should error if a given binding has both preview and non-preview and --preview is not specified", async () => {
661
654
  writeWranglerConfig();
662
- const requests = mockKeyPutRequest(
663
- "preview-bound-id",
664
- "my-key",
665
- "my-value"
666
- );
667
- const { error, stderr, stdout } = await runWrangler(
668
- "kv:key put my-key my-value --binding someBinding"
669
- );
670
- expect(stdout).toMatchInlineSnapshot(`""`);
671
- expect(stderr).toMatchInlineSnapshot(
655
+ const requests = mockKeyPutRequest("preview-bound-id", {
656
+ key: "my-key",
657
+ value: "my-value",
658
+ });
659
+ await expect(
660
+ runWrangler("kv:key put my-key my-value --binding someBinding")
661
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
672
662
  `"someBinding has both a namespace ID and a preview ID. Specify \\"--preview\\" or \\"--preview false\\" to avoid writing data to the wrong namespace."`
673
663
  );
674
- expect(error).toMatchInlineSnapshot(
675
- `[Error: someBinding has both a namespace ID and a preview ID. Specify "--preview" or "--preview false" to avoid writing data to the wrong namespace.]`
676
- );
664
+ expect(std.out).toMatchInlineSnapshot(`""`);
665
+ expect(std.err).toMatchInlineSnapshot(`
666
+ "someBinding has both a namespace ID and a preview ID. Specify \\"--preview\\" or \\"--preview false\\" to avoid writing data to the wrong namespace.
667
+
668
+ %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
669
+ `);
677
670
  expect(requests.count).toEqual(0);
678
671
  });
679
672
  });
680
673
 
681
674
  describe("list", () => {
682
- function mockKeyListRequest(
683
- expectedNamespaceId: string,
684
- expectedKeys: string[],
685
- keysPerRequest = 1000
686
- ) {
687
- const requests = { count: 0 };
688
- setMockRawResponse(
689
- "/accounts/:accountId/storage/kv/namespaces/:namespaceId/keys",
690
- ([_url, accountId, namespaceId], _init, query) => {
691
- requests.count++;
692
- expect(accountId).toEqual("some-account-id");
693
- expect(namespaceId).toEqual(expectedNamespaceId);
694
- if (expectedKeys.length <= keysPerRequest) {
695
- return createFetchResult(expectedKeys);
696
- } else {
697
- const start = parseInt(query.get("cursor") ?? "0") || 0;
698
- const end = start + keysPerRequest;
699
- const cursor = end < expectedKeys.length ? end : undefined;
700
- return createFetchResult(
701
- expectedKeys.slice(start, end),
702
- true,
703
- [],
704
- [],
705
- { cursor }
706
- );
707
- }
708
- }
709
- );
710
- return requests;
711
- }
712
-
713
675
  it("should list the keys of a namespace specified by namespace-id", async () => {
714
- 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
+ ];
715
681
  mockKeyListRequest("some-namespace-id", keys);
716
- const { error, stdout, stderr } = await runWrangler(
717
- "kv:key list --namespace-id some-namespace-id"
718
- );
719
- expect(error).toMatchInlineSnapshot(`undefined`);
720
- expect(stderr).toMatchInlineSnapshot(`""`);
721
- expect(stdout).toMatchInlineSnapshot(`
722
- "key-1
723
- key-2
724
- key-3"
682
+ await runWrangler("kv:key list --namespace-id some-namespace-id");
683
+ expect(std.err).toMatchInlineSnapshot(`""`);
684
+ expect(std.out).toMatchInlineSnapshot(`
685
+ "[
686
+ {
687
+ \\"name\\": \\"key-1\\"
688
+ },
689
+ {
690
+ \\"name\\": \\"key-2\\",
691
+ \\"expiration\\": 123456789
692
+ },
693
+ {
694
+ \\"name\\": \\"key-3\\",
695
+ \\"expiration_ttl\\": 666
696
+ }
697
+ ]"
725
698
  `);
726
699
  });
727
700
 
728
701
  it("should list the keys of a namespace specified by binding", async () => {
729
702
  writeWranglerConfig();
730
- const keys = ["key-1", "key-2", "key-3"];
703
+ const keys = [{ name: "key-1" }, { name: "key-2" }, { name: "key-3" }];
731
704
  mockKeyListRequest("bound-id", keys);
732
- const { error, stdout, stderr } = await runWrangler(
733
- "kv:key list --binding someBinding"
734
- );
735
- expect(error).toMatchInlineSnapshot(`undefined`);
736
- expect(stderr).toMatchInlineSnapshot(`""`);
737
- expect(stdout).toMatchInlineSnapshot(`
738
- "key-1
739
- key-2
740
- key-3"
705
+
706
+ await runWrangler("kv:key list --binding someBinding");
707
+ expect(std.err).toMatchInlineSnapshot(`""`);
708
+ expect(std.out).toMatchInlineSnapshot(`
709
+ "[
710
+ {
711
+ \\"name\\": \\"key-1\\"
712
+ },
713
+ {
714
+ \\"name\\": \\"key-2\\"
715
+ },
716
+ {
717
+ \\"name\\": \\"key-3\\"
718
+ }
719
+ ]"
741
720
  `);
742
721
  });
743
722
 
744
723
  it("should list the keys of a preview namespace specified by binding", async () => {
745
724
  writeWranglerConfig();
746
- const keys = ["key-1", "key-2", "key-3"];
725
+ const keys = [{ name: "key-1" }, { name: "key-2" }, { name: "key-3" }];
747
726
  mockKeyListRequest("preview-bound-id", keys);
748
- const { error, stdout, stderr } = await runWrangler(
749
- "kv:key list --binding someBinding --preview"
750
- );
751
- expect(error).toMatchInlineSnapshot(`undefined`);
752
- expect(stderr).toMatchInlineSnapshot(`""`);
753
- expect(stdout).toMatchInlineSnapshot(`
754
- "key-1
755
- key-2
756
- key-3"
727
+ await runWrangler("kv:key list --binding someBinding --preview");
728
+ expect(std.err).toMatchInlineSnapshot(`""`);
729
+ expect(std.out).toMatchInlineSnapshot(`
730
+ "[
731
+ {
732
+ \\"name\\": \\"key-1\\"
733
+ },
734
+ {
735
+ \\"name\\": \\"key-2\\"
736
+ },
737
+ {
738
+ \\"name\\": \\"key-3\\"
739
+ }
740
+ ]"
757
741
  `);
758
742
  });
759
743
 
760
744
  it("should list the keys of a namespace specified by binding, in a given environment", async () => {
761
745
  writeWranglerConfig();
762
- const keys = ["key-1", "key-2", "key-3"];
746
+ const keys = [{ name: "key-1" }, { name: "key-2" }, { name: "key-3" }];
763
747
  mockKeyListRequest("env-bound-id", keys);
764
- const { error, stdout, stderr } = await runWrangler(
748
+ await runWrangler(
765
749
  "kv:key list --binding someBinding --env some-environment"
766
750
  );
767
- expect(error).toMatchInlineSnapshot(`undefined`);
768
- expect(stderr).toMatchInlineSnapshot(`""`);
769
- expect(stdout).toMatchInlineSnapshot(`
770
- "key-1
771
- key-2
772
- key-3"
751
+ expect(std.err).toMatchInlineSnapshot(`""`);
752
+ expect(std.out).toMatchInlineSnapshot(`
753
+ "[
754
+ {
755
+ \\"name\\": \\"key-1\\"
756
+ },
757
+ {
758
+ \\"name\\": \\"key-2\\"
759
+ },
760
+ {
761
+ \\"name\\": \\"key-3\\"
762
+ }
763
+ ]"
773
764
  `);
774
765
  });
775
766
 
776
767
  it("should list the keys of a preview namespace specified by binding, in a given environment", async () => {
777
768
  writeWranglerConfig();
778
- const keys = ["key-1", "key-2", "key-3"];
769
+ const keys = [{ name: "key-1" }, { name: "key-2" }, { name: "key-3" }];
779
770
  mockKeyListRequest("preview-env-bound-id", keys);
780
- const { error, stdout, stderr } = await runWrangler(
771
+ await runWrangler(
781
772
  "kv:key list --binding someBinding --preview --env some-environment"
782
773
  );
783
- expect(error).toMatchInlineSnapshot(`undefined`);
784
- expect(stderr).toMatchInlineSnapshot(`""`);
785
- expect(stdout).toMatchInlineSnapshot(`
786
- "key-1
787
- key-2
788
- key-3"
774
+ expect(std.err).toMatchInlineSnapshot(`""`);
775
+ expect(std.out).toMatchInlineSnapshot(`
776
+ "[
777
+ {
778
+ \\"name\\": \\"key-1\\"
779
+ },
780
+ {
781
+ \\"name\\": \\"key-2\\"
782
+ },
783
+ {
784
+ \\"name\\": \\"key-3\\"
785
+ }
786
+ ]"
789
787
  `);
790
788
  });
791
789
 
792
- it("should make multiple requests for paginated results", async () => {
793
- // Create a lot of mock keys, so that the fetch requests will be paginated
794
- const keys: string[] = [];
795
- for (let i = 0; i < 550; i++) {
796
- keys.push("key-" + i);
797
- }
798
- // Ask for the keys in pages of size 100.
799
- const requests = mockKeyListRequest("some-namespace-id", keys, 100);
800
- const { stdout, stderr, error } = await runWrangler(
801
- "kv:key list --namespace-id some-namespace-id --limit 100"
802
- );
803
- expect(error).toMatchInlineSnapshot(`undefined`);
804
- expect(stderr).toMatchInlineSnapshot(`""`);
805
- expect(stdout).toEqual(keys.join("\n"));
806
- expect(requests.count).toEqual(6);
807
- });
790
+ // We'll run the next test with variations on the cursor
791
+ // that's returned on cloudflare's API after all results
792
+ // have been drained.
793
+ for (const blankCursorValue of [undefined, null, ""] as [
794
+ undefined,
795
+ null,
796
+ ""
797
+ ]) {
798
+ describe(`cursor - ${blankCursorValue}`, () => {
799
+ it("should make multiple requests for paginated results", async () => {
800
+ // Create a lot of mock keys, so that the fetch requests will be paginated
801
+ const keys: NamespaceKeyInfo[] = [];
802
+ for (let i = 0; i < 550; i++) {
803
+ keys.push({ name: "key-" + i });
804
+ }
805
+ // Ask for the keys in pages of size 100.
806
+ const requests = mockKeyListRequest(
807
+ "some-namespace-id",
808
+ keys,
809
+ 100,
810
+ blankCursorValue
811
+ );
812
+ await runWrangler("kv:key list --namespace-id some-namespace-id");
813
+ expect(std.err).toMatchInlineSnapshot(`""`);
814
+ expect(JSON.parse(std.out)).toEqual(keys);
815
+ expect(requests.count).toEqual(6);
816
+ });
817
+ });
818
+ }
808
819
 
809
820
  it("should error if a given binding name is not in the configured kv namespaces", async () => {
810
821
  writeWranglerConfig();
811
- const { error, stdout, stderr } = await runWrangler(
812
- "kv:key list --binding otherBinding"
813
- );
814
- expect(error).toMatchInlineSnapshot(
815
- `[Error: A namespace with binding name "otherBinding" was not found in the configured "kv_namespaces".]`
816
- );
817
- expect(stderr).toMatchInlineSnapshot(
822
+ await expect(
823
+ runWrangler("kv:key list --binding otherBinding")
824
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
818
825
  `"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."`
819
826
  );
820
- expect(stdout).toMatchInlineSnapshot(`""`);
827
+ expect(std.err).toMatchInlineSnapshot(`
828
+ "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
829
+
830
+ %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
831
+ `);
832
+ expect(std.out).toMatchInlineSnapshot(`""`);
821
833
  });
822
834
  });
823
835
 
824
836
  describe("get", () => {
825
- function mockKeyGetRequest(
826
- expectedNamespaceId: string,
827
- expectedKey: string,
828
- expectedValue: string
829
- ) {
830
- const requests = { count: 0 };
831
- setMockRawResponse(
832
- "/accounts/:accountId/storage/kv/namespaces/:namespaceId/values/:key",
833
- ([_url, accountId, namespaceId, key]) => {
834
- requests.count++;
835
- expect(accountId).toEqual("some-account-id");
836
- expect(namespaceId).toEqual(expectedNamespaceId);
837
- expect(key).toEqual(expectedKey);
838
- return expectedValue;
839
- }
840
- );
841
- return requests;
842
- }
837
+ afterEach(() => {
838
+ unsetMockFetchKVGetValues();
839
+ });
843
840
 
844
841
  it("should get a key in a given namespace specified by namespace-id", async () => {
845
- const requests = mockKeyGetRequest(
842
+ setMockFetchKVGetValue(
843
+ "some-account-id",
846
844
  "some-namespace-id",
847
845
  "my-key",
848
846
  "my-value"
849
847
  );
850
- const { error, stderr, stdout } = await runWrangler(
851
- "kv:key get my-key --namespace-id some-namespace-id"
852
- );
853
- expect(stdout).toMatchInlineSnapshot(`"my-value"`);
854
- expect(stderr).toMatchInlineSnapshot(`""`);
855
- expect(error).toMatchInlineSnapshot(`undefined`);
856
- expect(requests.count).toEqual(1);
848
+ await runWrangler("kv:key get my-key --namespace-id some-namespace-id");
849
+ expect(std.out).toMatchInlineSnapshot(`"my-value"`);
850
+ expect(std.err).toMatchInlineSnapshot(`""`);
857
851
  });
858
852
 
859
853
  it("should get a key in a given namespace specified by binding", async () => {
860
854
  writeWranglerConfig();
861
- const requests = mockKeyGetRequest("bound-id", "my-key", "my-value");
862
- const { error, stderr, stdout } = await runWrangler(
855
+ setMockFetchKVGetValue(
856
+ "some-account-id",
857
+ "bound-id",
858
+ "my-key",
859
+ "my-value"
860
+ );
861
+ await runWrangler(
863
862
  "kv:key get my-key --binding someBinding --preview false"
864
863
  );
865
- expect(stdout).toMatchInlineSnapshot(`"my-value"`);
866
- expect(stderr).toMatchInlineSnapshot(`""`);
867
- expect(error).toMatchInlineSnapshot(`undefined`);
868
- expect(requests.count).toEqual(1);
864
+ expect(std.out).toMatchInlineSnapshot(`"my-value"`);
865
+ expect(std.err).toMatchInlineSnapshot(`""`);
869
866
  });
870
867
 
871
868
  it("should get a key in a given preview namespace specified by binding", async () => {
872
869
  writeWranglerConfig();
873
- const requests = mockKeyGetRequest(
870
+ setMockFetchKVGetValue(
871
+ "some-account-id",
874
872
  "preview-bound-id",
875
873
  "my-key",
876
874
  "my-value"
877
875
  );
878
- const { error, stderr, stdout } = await runWrangler(
879
- "kv:key get my-key --binding someBinding --preview"
880
- );
881
- expect(stdout).toMatchInlineSnapshot(`"my-value"`);
882
- expect(stderr).toMatchInlineSnapshot(`""`);
883
- expect(error).toMatchInlineSnapshot(`undefined`);
884
- expect(requests.count).toEqual(1);
876
+ await runWrangler("kv:key get my-key --binding someBinding --preview");
877
+ expect(std.out).toMatchInlineSnapshot(`"my-value"`);
878
+ expect(std.err).toMatchInlineSnapshot(`""`);
885
879
  });
886
880
 
887
881
  it("should get a key for the specified environment in a given namespace", async () => {
888
882
  writeWranglerConfig();
889
- const requests = mockKeyGetRequest(
883
+ setMockFetchKVGetValue(
884
+ "some-account-id",
890
885
  "env-bound-id",
891
886
  "my-key",
892
887
  "my-value"
893
888
  );
894
- const { error, stderr, stdout } = await runWrangler(
895
- "kv:key get my-key my-value --binding someBinding --env some-environment --preview false"
889
+ await runWrangler(
890
+ "kv:key get my-key --binding someBinding --env some-environment --preview false"
896
891
  );
897
- expect(stdout).toMatchInlineSnapshot(`"my-value"`);
898
- expect(stderr).toMatchInlineSnapshot(`""`);
899
- expect(error).toMatchInlineSnapshot(`undefined`);
900
- expect(requests.count).toEqual(1);
892
+ expect(std.out).toMatchInlineSnapshot(`"my-value"`);
893
+ expect(std.err).toMatchInlineSnapshot(`""`);
901
894
  });
902
895
 
903
896
  it("should error if no key is provided", async () => {
904
- const { error, stdout, stderr } = await runWrangler("kv:key get");
905
-
906
- expect(stdout).toMatchInlineSnapshot(`""`);
907
- expect(stderr).toMatchInlineSnapshot(`
897
+ await expect(
898
+ runWrangler("kv:key get")
899
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
900
+ `"Not enough non-option arguments: got 0, need at least 1"`
901
+ );
902
+ expect(std.out).toMatchInlineSnapshot(`""`);
903
+ expect(std.err).toMatchInlineSnapshot(`
908
904
  "wrangler kv:key get <key>
909
905
 
910
906
  Reads a single value by key from the given namespace.
@@ -913,29 +909,29 @@ describe("wrangler", () => {
913
909
  key The key value to get. [string] [required]
914
910
 
915
911
  Flags:
916
- -c, --config Path to .toml configuration file [string]
917
- -h, --help Show help [boolean]
918
- -v, --version Show version number [boolean]
912
+ -c, --config Path to .toml configuration file [string]
913
+ -h, --help Show help [boolean]
914
+ -v, --version Show version number [boolean]
915
+ --legacy-env Use legacy environments [boolean]
919
916
 
920
917
  Options:
921
- -l, --local Run on my machine [boolean] [default: false]
922
918
  --binding The name of the namespace to get from [string]
923
919
  --namespace-id The id of the namespace to get from [string]
924
- --env Perform on a specific environment [string]
920
+ -e, --env Perform on a specific environment [string]
925
921
  --preview Interact with a preview namespace [boolean] [default: false]
926
922
 
927
923
  Not enough non-option arguments: got 0, need at least 1"
928
924
  `);
929
- expect(error).toMatchInlineSnapshot(
930
- `[Error: Not enough non-option arguments: got 0, need at least 1]`
931
- );
932
925
  });
933
926
 
934
927
  it("should error if no binding nor namespace is provided", async () => {
935
- const { error, stdout, stderr } = await runWrangler("kv:key get foo");
936
-
937
- expect(stdout).toMatchInlineSnapshot(`""`);
938
- expect(stderr).toMatchInlineSnapshot(`
928
+ await expect(
929
+ runWrangler("kv:key get foo")
930
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
931
+ `"Exactly one of the arguments binding and namespace-id is required"`
932
+ );
933
+ expect(std.out).toMatchInlineSnapshot(`""`);
934
+ expect(std.err).toMatchInlineSnapshot(`
939
935
  "wrangler kv:key get <key>
940
936
 
941
937
  Reads a single value by key from the given namespace.
@@ -944,31 +940,30 @@ describe("wrangler", () => {
944
940
  key The key value to get. [string] [required]
945
941
 
946
942
  Flags:
947
- -c, --config Path to .toml configuration file [string]
948
- -h, --help Show help [boolean]
949
- -v, --version Show version number [boolean]
943
+ -c, --config Path to .toml configuration file [string]
944
+ -h, --help Show help [boolean]
945
+ -v, --version Show version number [boolean]
946
+ --legacy-env Use legacy environments [boolean]
950
947
 
951
948
  Options:
952
- -l, --local Run on my machine [boolean] [default: false]
953
949
  --binding The name of the namespace to get from [string]
954
950
  --namespace-id The id of the namespace to get from [string]
955
- --env Perform on a specific environment [string]
951
+ -e, --env Perform on a specific environment [string]
956
952
  --preview Interact with a preview namespace [boolean] [default: false]
957
953
 
958
954
  Exactly one of the arguments binding and namespace-id is required"
959
955
  `);
960
- expect(error).toMatchInlineSnapshot(
961
- `[Error: Exactly one of the arguments binding and namespace-id is required]`
962
- );
963
956
  });
964
957
 
965
958
  it("should error if both binding and namespace is provided", async () => {
966
- const { error, stdout, stderr } = await runWrangler(
967
- "kv:key get foo --binding x --namespace-id y"
959
+ await expect(
960
+ runWrangler("kv:key get foo --binding x --namespace-id y")
961
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
962
+ `"Arguments binding and namespace-id are mutually exclusive"`
968
963
  );
969
964
 
970
- expect(stdout).toMatchInlineSnapshot(`""`);
971
- expect(stderr).toMatchInlineSnapshot(`
965
+ expect(std.out).toMatchInlineSnapshot(`""`);
966
+ expect(std.err).toMatchInlineSnapshot(`
972
967
  "wrangler kv:key get <key>
973
968
 
974
969
  Reads a single value by key from the given namespace.
@@ -977,36 +972,34 @@ describe("wrangler", () => {
977
972
  key The key value to get. [string] [required]
978
973
 
979
974
  Flags:
980
- -c, --config Path to .toml configuration file [string]
981
- -h, --help Show help [boolean]
982
- -v, --version Show version number [boolean]
975
+ -c, --config Path to .toml configuration file [string]
976
+ -h, --help Show help [boolean]
977
+ -v, --version Show version number [boolean]
978
+ --legacy-env Use legacy environments [boolean]
983
979
 
984
980
  Options:
985
- -l, --local Run on my machine [boolean] [default: false]
986
981
  --binding The name of the namespace to get from [string]
987
982
  --namespace-id The id of the namespace to get from [string]
988
- --env Perform on a specific environment [string]
983
+ -e, --env Perform on a specific environment [string]
989
984
  --preview Interact with a preview namespace [boolean] [default: false]
990
985
 
991
986
  Arguments binding and namespace-id are mutually exclusive"
992
987
  `);
993
- expect(error).toMatchInlineSnapshot(
994
- `[Error: Arguments binding and namespace-id are mutually exclusive]`
995
- );
996
988
  });
997
989
 
998
990
  it("should error if a given binding name is not in the configured kv namespaces", async () => {
999
991
  writeWranglerConfig();
1000
- const { error, stdout, stderr } = await runWrangler(
1001
- `kv:key get key --binding otherBinding`
1002
- );
1003
- expect(stdout).toMatchInlineSnapshot(`""`);
1004
- expect(stderr).toMatchInlineSnapshot(
992
+ await expect(
993
+ runWrangler("kv:key get key --binding otherBinding")
994
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
1005
995
  `"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."`
1006
996
  );
1007
- expect(error).toMatchInlineSnapshot(
1008
- `[Error: A namespace with binding name "otherBinding" was not found in the configured "kv_namespaces".]`
1009
- );
997
+ expect(std.out).toMatchInlineSnapshot(`""`);
998
+ expect(std.err).toMatchInlineSnapshot(`
999
+ "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
1000
+
1001
+ %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1002
+ `);
1010
1003
  });
1011
1004
  });
1012
1005
 
@@ -1058,25 +1051,29 @@ describe("wrangler", () => {
1058
1051
 
1059
1052
  it("should error if a given binding name is not in the configured kv namespaces", async () => {
1060
1053
  writeWranglerConfig();
1061
- const { stderr } = await runWrangler(
1062
- `kv:key delete --binding otherBinding someKey`
1063
- );
1064
- expect(stderr).toMatchInlineSnapshot(
1054
+ await expect(
1055
+ runWrangler(`kv:key delete --binding otherBinding someKey`)
1056
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
1065
1057
  `"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."`
1066
1058
  );
1059
+
1060
+ expect(std.err).toMatchInlineSnapshot(`
1061
+ "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
1062
+
1063
+ %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1064
+ `);
1067
1065
  });
1068
1066
 
1069
1067
  it("should delete a key in a namespace specified by binding name in a given environment", async () => {
1070
1068
  writeWranglerConfig();
1071
1069
  const requests = mockDeleteRequest("env-bound-id", "someKey");
1072
- const { stdout, stderr, error } = await runWrangler(
1070
+ await runWrangler(
1073
1071
  `kv:key delete --binding someBinding --env some-environment --preview false someKey`
1074
1072
  );
1075
- expect(stdout).toMatchInlineSnapshot(
1073
+ expect(std.out).toMatchInlineSnapshot(
1076
1074
  `"deleting the key \\"someKey\\" on namespace env-bound-id"`
1077
1075
  );
1078
- expect(stderr).toMatchInlineSnapshot(`""`);
1079
- expect(error).toMatchInlineSnapshot(`undefined`);
1076
+ expect(std.err).toMatchInlineSnapshot(`""`);
1080
1077
  expect(requests.count).toEqual(1);
1081
1078
  });
1082
1079
 
@@ -1090,6 +1087,278 @@ describe("wrangler", () => {
1090
1087
  });
1091
1088
  });
1092
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
+ });
1093
1362
  });
1094
1363
 
1095
1364
  function writeWranglerConfig() {