wrangler 0.0.0-e6733a3 → 0.0.0-e88a54e

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 (99) hide show
  1. package/bin/wrangler.js +2 -2
  2. package/miniflare-dist/index.mjs +79 -0
  3. package/miniflare-dist/index.mjs.map +7 -0
  4. package/package.json +35 -16
  5. package/pages/functions/buildWorker.ts +1 -1
  6. package/pages/functions/filepath-routing.test.ts +212 -28
  7. package/pages/functions/filepath-routing.ts +91 -131
  8. package/pages/functions/routes.ts +11 -18
  9. package/pages/functions/template-worker.ts +19 -33
  10. package/src/__tests__/configuration.test.ts +2796 -0
  11. package/src/__tests__/dev.test.tsx +488 -58
  12. package/src/__tests__/dev2.test.tsx +154 -0
  13. package/src/__tests__/guess-worker-format.test.ts +58 -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} +55 -19
  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 +779 -75
  30. package/src/__tests__/jest.setup.ts +53 -6
  31. package/src/__tests__/kv.test.ts +738 -454
  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 +44 -0
  35. package/src/__tests__/parse.test.ts +322 -0
  36. package/src/__tests__/publish.test.ts +2704 -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 +564 -0
  42. package/src/__tests__/whoami.test.tsx +100 -0
  43. package/src/bundle.ts +142 -0
  44. package/src/cfetch/index.ts +21 -19
  45. package/src/cfetch/internal.ts +94 -24
  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 +366 -0
  51. package/src/config/index.ts +62 -0
  52. package/src/config/validation-helpers.ts +486 -0
  53. package/src/config/validation.ts +1090 -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 +364 -0
  57. package/src/dev/local.tsx +232 -0
  58. package/src/dev/remote.tsx +203 -0
  59. package/src/dev/use-esbuild.ts +100 -0
  60. package/src/dialogs.tsx +2 -2
  61. package/src/entry.ts +208 -0
  62. package/src/environment-variables.ts +33 -0
  63. package/src/https-options.ts +122 -0
  64. package/src/index.tsx +1198 -799
  65. package/src/inspect.ts +159 -66
  66. package/src/{kv.tsx → kv.ts} +117 -65
  67. package/src/miniflare-cli/README.md +30 -0
  68. package/src/miniflare-cli/enum-keys.ts +17 -0
  69. package/src/miniflare-cli/index.ts +37 -0
  70. package/src/module-collection.ts +229 -35
  71. package/src/open-in-browser.ts +25 -0
  72. package/src/package-manager.ts +126 -0
  73. package/src/pages.tsx +58 -42
  74. package/src/parse.ts +183 -0
  75. package/src/paths.ts +26 -0
  76. package/src/proxy.ts +224 -78
  77. package/src/publish.ts +281 -292
  78. package/src/r2.ts +50 -0
  79. package/src/reporting.ts +131 -0
  80. package/src/selfsigned.d.ts +29 -0
  81. package/src/sites.tsx +224 -58
  82. package/src/tail/filters.ts +279 -0
  83. package/src/tail/index.ts +299 -0
  84. package/src/tail/printing.ts +62 -0
  85. package/src/user.tsx +273 -180
  86. package/src/whoami.tsx +69 -0
  87. package/src/{api/worker.ts → worker.ts} +45 -37
  88. package/templates/new-worker.js +15 -0
  89. package/templates/new-worker.ts +15 -0
  90. package/{static-asset-facade.js → templates/static-asset-facade.js} +0 -0
  91. package/templates/tsconfig.json +105 -0
  92. package/wrangler-dist/cli.js +130612 -112486
  93. package/wrangler-dist/cli.js.map +3 -3
  94. package/src/__tests__/run-in-tmp.ts +0 -19
  95. package/src/__tests__/run-wrangler.ts +0 -32
  96. package/src/api/form_data.ts +0 -131
  97. package/src/config.ts +0 -487
  98. package/src/dev.tsx +0 -771
  99. package/src/tail.tsx +0 -73
@@ -1,15 +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";
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";
10
16
 
11
17
  describe("wrangler", () => {
18
+ mockAccountId();
19
+ mockApiToken();
12
20
  runInTempDir();
21
+ const std = mockConsoleMethods();
13
22
 
14
23
  afterEach(() => {
15
24
  unsetAllMocks();
@@ -31,11 +40,13 @@ describe("wrangler", () => {
31
40
  }
32
41
 
33
42
  it("should error if no namespace is given", async () => {
34
- const { error, stdout, stderr } = await runWrangler(
35
- "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"`
36
47
  );
37
- expect(stdout).toMatchInlineSnapshot(`""`);
38
- expect(stderr).toMatchInlineSnapshot(`
48
+ expect(std.out).toMatchInlineSnapshot(`""`);
49
+ expect(std.err).toMatchInlineSnapshot(`
39
50
  "wrangler kv:namespace create <namespace>
40
51
 
41
52
  Create a new namespace
@@ -44,28 +55,27 @@ describe("wrangler", () => {
44
55
  namespace The name of the new namespace [string] [required]
45
56
 
46
57
  Flags:
47
- -c, --config Path to .toml configuration file [string]
48
- -h, --help Show help [boolean]
49
- -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]
50
62
 
51
63
  Options:
52
- -l, --local Run on my machine [boolean] [default: false]
53
- --env Perform on a specific environment [string]
64
+ -e, --env Perform on a specific environment [string]
54
65
  --preview Interact with a preview namespace [boolean]
55
66
 
56
67
  Not enough non-option arguments: got 0, need at least 1"
57
68
  `);
58
- expect(error).toMatchInlineSnapshot(
59
- `[Error: Not enough non-option arguments: got 0, need at least 1]`
60
- );
61
69
  });
62
70
 
63
71
  it("should error if the namespace to create contains spaces", async () => {
64
- const { error, stdout, stderr } = await runWrangler(
65
- "kv:namespace create abc def ghi"
72
+ await expect(
73
+ runWrangler("kv:namespace create abc def ghi")
74
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
75
+ `"Unexpected additional positional arguments \\"def ghi\\"."`
66
76
  );
67
- expect(stdout).toMatchInlineSnapshot(`""`);
68
- expect(stderr).toMatchInlineSnapshot(`
77
+ expect(std.out).toMatchInlineSnapshot(`""`);
78
+ expect(std.err).toMatchInlineSnapshot(`
69
79
  "wrangler kv:namespace create <namespace>
70
80
 
71
81
  Create a new namespace
@@ -74,28 +84,28 @@ describe("wrangler", () => {
74
84
  namespace The name of the new namespace [string] [required]
75
85
 
76
86
  Flags:
77
- -c, --config Path to .toml configuration file [string]
78
- -h, --help Show help [boolean]
79
- -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]
80
91
 
81
92
  Options:
82
- -l, --local Run on my machine [boolean] [default: false]
83
- --env Perform on a specific environment [string]
93
+ -e, --env Perform on a specific environment [string]
84
94
  --preview Interact with a preview namespace [boolean]
85
95
 
86
96
  Unexpected additional positional arguments \\"def ghi\\"."
87
97
  `);
88
- expect(error).toMatchInlineSnapshot(
89
- `[Error: Unexpected additional positional arguments "def ghi".]`
90
- );
91
98
  });
92
99
 
93
100
  it("should error if the namespace to create is not valid", async () => {
94
- const { error, stdout, stderr } = await runWrangler(
95
- "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."`
96
105
  );
97
- expect(stdout).toMatchInlineSnapshot(`""`);
98
- expect(stderr).toMatchInlineSnapshot(`
106
+
107
+ expect(std.out).toMatchInlineSnapshot(`""`);
108
+ expect(std.err).toMatchInlineSnapshot(`
99
109
  "wrangler kv:namespace create <namespace>
100
110
 
101
111
  Create a new namespace
@@ -104,28 +114,23 @@ describe("wrangler", () => {
104
114
  namespace The name of the new namespace [string] [required]
105
115
 
106
116
  Flags:
107
- -c, --config Path to .toml configuration file [string]
108
- -h, --help Show help [boolean]
109
- -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]
110
121
 
111
122
  Options:
112
- -l, --local Run on my machine [boolean] [default: false]
113
- --env Perform on a specific environment [string]
123
+ -e, --env Perform on a specific environment [string]
114
124
  --preview Interact with a preview namespace [boolean]
115
125
 
116
126
  The namespace binding name \\"abc-def\\" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number."
117
127
  `);
118
- expect(error).toMatchInlineSnapshot(
119
- `[Error: The namespace binding name "abc-def" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number.]`
120
- );
121
128
  });
122
129
 
123
130
  it("should create a namespace", async () => {
124
131
  mockCreateRequest("worker-UnitTestNamespace");
125
- const { stdout } = await runWrangler(
126
- "kv:namespace create UnitTestNamespace"
127
- );
128
- expect(stdout).toMatchInlineSnapshot(`
132
+ await runWrangler("kv:namespace create UnitTestNamespace");
133
+ expect(std.out).toMatchInlineSnapshot(`
129
134
  "🌀 Creating namespace with title \\"worker-UnitTestNamespace\\"
130
135
  ✨ Success!
131
136
  Add the following to your configuration file in your kv_namespaces array:
@@ -135,10 +140,8 @@ describe("wrangler", () => {
135
140
 
136
141
  it("should create a preview namespace if configured to do so", async () => {
137
142
  mockCreateRequest("worker-UnitTestNamespace_preview");
138
- const { stdout } = await runWrangler(
139
- "kv:namespace create UnitTestNamespace --preview"
140
- );
141
- expect(stdout).toMatchInlineSnapshot(`
143
+ await runWrangler("kv:namespace create UnitTestNamespace --preview");
144
+ expect(std.out).toMatchInlineSnapshot(`
142
145
  "🌀 Creating namespace with title \\"worker-UnitTestNamespace_preview\\"
143
146
  ✨ Success!
144
147
  Add the following to your configuration file in your kv_namespaces array:
@@ -149,10 +152,8 @@ describe("wrangler", () => {
149
152
  it("should create a namespace using configured worker name", async () => {
150
153
  writeFileSync("./wrangler.toml", 'name = "otherWorker"', "utf-8");
151
154
  mockCreateRequest("otherWorker-UnitTestNamespace");
152
- const { stdout } = await runWrangler(
153
- "kv:namespace create UnitTestNamespace"
154
- );
155
- expect(stdout).toMatchInlineSnapshot(`
155
+ await runWrangler("kv:namespace create UnitTestNamespace");
156
+ expect(std.out).toMatchInlineSnapshot(`
156
157
  "🌀 Creating namespace with title \\"otherWorker-UnitTestNamespace\\"
157
158
  ✨ Success!
158
159
  Add the following to your configuration file in your kv_namespaces array:
@@ -162,10 +163,10 @@ describe("wrangler", () => {
162
163
 
163
164
  it("should create a namespace in an environment if configured to do so", async () => {
164
165
  mockCreateRequest("worker-customEnv-UnitTestNamespace");
165
- const { stdout } = await runWrangler(
166
+ await runWrangler(
166
167
  "kv:namespace create UnitTestNamespace --env customEnv"
167
168
  );
168
- expect(stdout).toMatchInlineSnapshot(`
169
+ expect(std.out).toMatchInlineSnapshot(`
169
170
  "🌀 Creating namespace with title \\"worker-customEnv-UnitTestNamespace\\"
170
171
  ✨ Success!
171
172
  Add the following to your configuration file in your kv_namespaces array under [env.customEnv]:
@@ -196,30 +197,28 @@ describe("wrangler", () => {
196
197
  }
197
198
 
198
199
  it("should list namespaces", async () => {
199
- const KVNamespaces = [
200
+ const kvNamespaces: KVNamespaceInfo[] = [
200
201
  { title: "title-1", id: "id-1" },
201
202
  { title: "title-2", id: "id-2" },
202
203
  ];
203
- mockListRequest(KVNamespaces);
204
- const { error, stdout, stderr } = await runWrangler(
205
- "kv:namespace list"
206
- );
207
- expect(error).toMatchInlineSnapshot(`undefined`);
208
- expect(stderr).toMatchInlineSnapshot(`""`);
209
- const namespaces = JSON.parse(stdout);
210
- expect(namespaces).toEqual(KVNamespaces);
204
+ mockListRequest(kvNamespaces);
205
+ await runWrangler("kv:namespace list");
206
+
207
+ expect(std.err).toMatchInlineSnapshot(`""`);
208
+ const namespaces = JSON.parse(std.out);
209
+ expect(namespaces).toEqual(kvNamespaces);
211
210
  });
212
211
 
213
212
  it("should make multiple requests for paginated results", async () => {
214
213
  // Create a lot of mock namespaces, so that the fetch requests will be paginated
215
- const KVNamespaces = [];
214
+ const kvNamespaces: KVNamespaceInfo[] = [];
216
215
  for (let i = 0; i < 550; i++) {
217
- KVNamespaces.push({ title: "title-" + i, id: "id-" + i });
216
+ kvNamespaces.push({ title: "title-" + i, id: "id-" + i });
218
217
  }
219
- const requests = mockListRequest(KVNamespaces);
220
- const { stdout } = await runWrangler("kv:namespace list");
221
- const namespaces = JSON.parse(stdout);
222
- expect(namespaces).toEqual(KVNamespaces);
218
+ const requests = mockListRequest(kvNamespaces);
219
+ await runWrangler("kv:namespace list");
220
+ const namespaces = JSON.parse(std.out);
221
+ expect(namespaces).toEqual(kvNamespaces);
223
222
  expect(requests.count).toEqual(6);
224
223
  });
225
224
  });
@@ -268,24 +267,26 @@ describe("wrangler", () => {
268
267
 
269
268
  it("should error if a given binding name is not in the configured kv namespaces", async () => {
270
269
  writeWranglerConfig();
271
- const { stderr } = await runWrangler(
272
- `kv:namespace delete --binding otherBinding`
273
- );
274
- 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(`
275
276
  "wrangler kv:namespace delete
276
277
 
277
278
  Deletes a given namespace.
278
279
 
279
280
  Flags:
280
- -c, --config Path to .toml configuration file [string]
281
- -h, --help Show help [boolean]
282
- -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]
283
285
 
284
286
  Options:
285
- -l, --local Run on my machine [boolean] [default: false]
286
287
  --binding The name of the namespace to delete [string]
287
288
  --namespace-id The id of the namespace to delete [string]
288
- --env Perform on a specific environment [string]
289
+ -e, --env Perform on a specific environment [string]
289
290
  --preview Interact with a preview namespace [boolean]
290
291
 
291
292
  Not able to delete namespace.
@@ -296,12 +297,12 @@ describe("wrangler", () => {
296
297
  it("should delete a namespace specified by binding name in a given environment", async () => {
297
298
  writeWranglerConfig();
298
299
  const requests = mockDeleteRequest("env-bound-id");
299
- const { stdout, stderr, error } = await runWrangler(
300
- `kv:namespace delete --binding someBinding --env some-environment --preview false`
300
+ await runWrangler(
301
+ "kv:namespace delete --binding someBinding --env some-environment --preview false"
301
302
  );
302
- expect(stdout).toMatchInlineSnapshot(`""`);
303
- expect(stderr).toMatchInlineSnapshot(`""`);
304
- expect(error).toMatchInlineSnapshot(`undefined`);
303
+
304
+ expect(std.out).toMatchInlineSnapshot(`""`);
305
+ expect(std.err).toMatchInlineSnapshot(`""`);
305
306
  expect(requests.count).toEqual(1);
306
307
  });
307
308
 
@@ -320,10 +321,7 @@ describe("wrangler", () => {
320
321
  describe("put", () => {
321
322
  function mockKeyPutRequest(
322
323
  expectedNamespaceId: string,
323
- expectedKey: string,
324
- expectedValue: string,
325
- expiration?: number,
326
- expirationTtl?: number
324
+ expectedKV: KeyValue
327
325
  ) {
328
326
  const requests = { count: 0 };
329
327
  setMockResponse(
@@ -333,10 +331,22 @@ describe("wrangler", () => {
333
331
  requests.count++;
334
332
  expect(accountId).toEqual("some-account-id");
335
333
  expect(namespaceId).toEqual(expectedNamespaceId);
336
- expect(key).toEqual(expectedKey);
337
- expect(body).toEqual(expectedValue);
338
- expect(query.get("expiration")).toEqual(`${expiration}`);
339
- expect(query.get("expiration_ttl")).toEqual(`${expirationTtl}`);
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
+ );
340
+ } else {
341
+ expect(query.has("expiration")).toBe(false);
342
+ }
343
+ if (expectedKV.expiration_ttl) {
344
+ expect(query.get("expiration_ttl")).toEqual(
345
+ `${expectedKV.expiration_ttl}`
346
+ );
347
+ } else {
348
+ expect(query.has("expiration_ttl")).toBe(false);
349
+ }
340
350
  return null;
341
351
  }
342
352
  );
@@ -344,554 +354,555 @@ describe("wrangler", () => {
344
354
  }
345
355
 
346
356
  it("should put a key in a given namespace specified by namespace-id", async () => {
347
- const requests = mockKeyPutRequest(
348
- "some-namespace-id",
349
- "my-key",
350
- "my-value"
351
- );
352
- 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(
353
363
  "kv:key put my-key my-value --namespace-id some-namespace-id"
354
364
  );
365
+
355
366
  expect(requests.count).toEqual(1);
356
- expect(stdout).toMatchInlineSnapshot(
367
+ expect(std.out).toMatchInlineSnapshot(
357
368
  `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace some-namespace-id"`
358
369
  );
359
- expect(stderr).toMatchInlineSnapshot(`""`);
360
- expect(error).toMatchInlineSnapshot(`undefined`);
370
+ expect(std.err).toMatchInlineSnapshot(`""`);
361
371
  });
362
372
 
363
373
  it("should put a key in a given namespace specified by binding", async () => {
364
374
  writeWranglerConfig();
365
- const requests = mockKeyPutRequest("bound-id", "my-key", "my-value");
366
- const { error, stderr, stdout } = await runWrangler(
375
+ const requests = mockKeyPutRequest("bound-id", {
376
+ key: "my-key",
377
+ value: "my-value",
378
+ });
379
+ await runWrangler(
367
380
  "kv:key put my-key my-value --binding someBinding --preview false"
368
381
  );
369
- expect(stdout).toMatchInlineSnapshot(
382
+
383
+ expect(std.out).toMatchInlineSnapshot(
370
384
  `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace bound-id"`
371
385
  );
372
- expect(stderr).toMatchInlineSnapshot(`""`);
373
- expect(error).toMatchInlineSnapshot(`undefined`);
386
+ expect(std.err).toMatchInlineSnapshot(`""`);
374
387
  expect(requests.count).toEqual(1);
375
388
  });
376
389
 
377
390
  it("should put a key in a given preview namespace specified by binding", async () => {
378
391
  writeWranglerConfig();
379
- const requests = mockKeyPutRequest(
380
- "preview-bound-id",
381
- "my-key",
382
- "my-value"
383
- );
384
- 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(
385
398
  "kv:key put my-key my-value --binding someBinding --preview"
386
399
  );
387
- expect(stdout).toMatchInlineSnapshot(
400
+
401
+ expect(std.out).toMatchInlineSnapshot(
388
402
  `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace preview-bound-id"`
389
403
  );
390
- expect(stderr).toMatchInlineSnapshot(`""`);
391
- expect(error).toMatchInlineSnapshot(`undefined`);
404
+ expect(std.err).toMatchInlineSnapshot(`""`);
392
405
  expect(requests.count).toEqual(1);
393
406
  });
394
407
 
395
408
  it("should add expiration and ttl properties when putting a key", async () => {
396
- const requests = mockKeyPutRequest(
397
- "some-namespace-id",
398
- "my-key",
399
- "my-value",
400
- 10,
401
- 20
402
- );
403
- 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(
404
416
  "kv:key put my-key my-value --namespace-id some-namespace-id --expiration 10 --ttl 20"
405
417
  );
406
418
  expect(requests.count).toEqual(1);
407
- expect(stdout).toMatchInlineSnapshot(
419
+ expect(std.out).toMatchInlineSnapshot(
408
420
  `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace some-namespace-id"`
409
421
  );
410
- expect(stderr).toMatchInlineSnapshot(`""`);
411
- expect(error).toMatchInlineSnapshot(`undefined`);
422
+ expect(std.err).toMatchInlineSnapshot(`""`);
412
423
  });
413
424
 
414
425
  it("should put a key to the specified environment in a given namespace", async () => {
415
426
  writeWranglerConfig();
416
- const requests = mockKeyPutRequest(
417
- "env-bound-id",
418
- "my-key",
419
- "my-value"
420
- );
421
- 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(
422
432
  "kv:key put my-key my-value --binding someBinding --env some-environment --preview false"
423
433
  );
424
- expect(stdout).toMatchInlineSnapshot(
434
+ expect(std.out).toMatchInlineSnapshot(
425
435
  `"writing the value \\"my-value\\" to key \\"my-key\\" on namespace env-bound-id"`
426
436
  );
427
- expect(stderr).toMatchInlineSnapshot(`""`);
428
- expect(error).toMatchInlineSnapshot(`undefined`);
437
+ expect(std.err).toMatchInlineSnapshot(`""`);
429
438
  expect(requests.count).toEqual(1);
430
439
  });
431
440
 
432
441
  it("should put a key with a value loaded from a given path", async () => {
433
442
  writeFileSync("foo.txt", "file-contents", "utf-8");
434
- const requests = mockKeyPutRequest(
435
- "some-namespace-id",
436
- "my-key",
437
- "file-contents"
438
- );
439
- 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(
440
448
  "kv:key put my-key --namespace-id some-namespace-id --path foo.txt"
441
449
  );
442
- expect(stdout).toMatchInlineSnapshot(
450
+ expect(std.out).toMatchInlineSnapshot(
443
451
  `"writing the contents of foo.txt to the key \\"my-key\\" on namespace some-namespace-id"`
444
452
  );
445
- expect(stderr).toMatchInlineSnapshot(`""`);
446
- expect(error).toMatchInlineSnapshot(`undefined`);
453
+ expect(std.err).toMatchInlineSnapshot(`""`);
447
454
  expect(requests.count).toEqual(1);
448
455
  });
449
456
 
450
457
  it("should error if no key is provided", async () => {
451
- 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
+ );
452
463
 
453
- expect(stdout).toMatchInlineSnapshot(`""`);
454
- expect(stderr).toMatchInlineSnapshot(`
464
+ expect(std.out).toMatchInlineSnapshot(`""`);
465
+ expect(std.err).toMatchInlineSnapshot(`
455
466
  "wrangler kv:key put <key> [value]
456
467
 
457
468
  Writes a single key/value pair to the given namespace.
458
469
 
459
470
  Positionals:
460
- key The key to write to. [string] [required]
461
- value The value to write. [string]
471
+ key The key to write to [string] [required]
472
+ value The value to write [string]
462
473
 
463
474
  Flags:
464
- -c, --config Path to .toml configuration file [string]
465
- -h, --help Show help [boolean]
466
- -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]
467
479
 
468
480
  Options:
469
- -l, --local Run on my machine [boolean] [default: false]
470
- --binding The binding of the namespace to write to. [string]
471
- --namespace-id The id of the namespace to write to. [string]
472
- --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]
473
484
  --preview Interact with a preview namespace [boolean]
474
- --ttl Time for which the entries should be visible. [number]
485
+ --ttl Time for which the entries should be visible [number]
475
486
  --expiration Time since the UNIX epoch after which the entry expires [number]
476
- --path Read value from the file at a given path. [string]
487
+ --path Read value from the file at a given path [string]
477
488
 
478
489
  Not enough non-option arguments: got 0, need at least 1"
479
490
  `);
480
- expect(error).toMatchInlineSnapshot(
481
- `[Error: Not enough non-option arguments: got 0, need at least 1]`
482
- );
483
491
  });
484
492
 
485
493
  it("should error if no binding nor namespace is provided", async () => {
486
- const { error, stdout, stderr } = await runWrangler(
487
- "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"`
488
498
  );
489
499
 
490
- expect(stdout).toMatchInlineSnapshot(`""`);
491
- expect(stderr).toMatchInlineSnapshot(`
500
+ expect(std.out).toMatchInlineSnapshot(`""`);
501
+ expect(std.err).toMatchInlineSnapshot(`
492
502
  "wrangler kv:key put <key> [value]
493
503
 
494
504
  Writes a single key/value pair to the given namespace.
495
505
 
496
506
  Positionals:
497
- key The key to write to. [string] [required]
498
- value The value to write. [string]
507
+ key The key to write to [string] [required]
508
+ value The value to write [string]
499
509
 
500
510
  Flags:
501
- -c, --config Path to .toml configuration file [string]
502
- -h, --help Show help [boolean]
503
- -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]
504
515
 
505
516
  Options:
506
- -l, --local Run on my machine [boolean] [default: false]
507
- --binding The binding of the namespace to write to. [string]
508
- --namespace-id The id of the namespace to write to. [string]
509
- --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]
510
520
  --preview Interact with a preview namespace [boolean]
511
- --ttl Time for which the entries should be visible. [number]
521
+ --ttl Time for which the entries should be visible [number]
512
522
  --expiration Time since the UNIX epoch after which the entry expires [number]
513
- --path Read value from the file at a given path. [string]
523
+ --path Read value from the file at a given path [string]
514
524
 
515
525
  Exactly one of the arguments binding and namespace-id is required"
516
526
  `);
517
- expect(error).toMatchInlineSnapshot(
518
- `[Error: Exactly one of the arguments binding and namespace-id is required]`
519
- );
520
527
  });
521
528
 
522
529
  it("should error if both binding and namespace is provided", async () => {
523
- const { error, stdout, stderr } = await runWrangler(
524
- "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"`
525
534
  );
526
535
 
527
- expect(stdout).toMatchInlineSnapshot(`""`);
528
- expect(stderr).toMatchInlineSnapshot(`
536
+ expect(std.out).toMatchInlineSnapshot(`""`);
537
+ expect(std.err).toMatchInlineSnapshot(`
529
538
  "wrangler kv:key put <key> [value]
530
539
 
531
540
  Writes a single key/value pair to the given namespace.
532
541
 
533
542
  Positionals:
534
- key The key to write to. [string] [required]
535
- value The value to write. [string]
543
+ key The key to write to [string] [required]
544
+ value The value to write [string]
536
545
 
537
546
  Flags:
538
- -c, --config Path to .toml configuration file [string]
539
- -h, --help Show help [boolean]
540
- -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]
541
551
 
542
552
  Options:
543
- -l, --local Run on my machine [boolean] [default: false]
544
- --binding The binding of the namespace to write to. [string]
545
- --namespace-id The id of the namespace to write to. [string]
546
- --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]
547
556
  --preview Interact with a preview namespace [boolean]
548
- --ttl Time for which the entries should be visible. [number]
557
+ --ttl Time for which the entries should be visible [number]
549
558
  --expiration Time since the UNIX epoch after which the entry expires [number]
550
- --path Read value from the file at a given path. [string]
559
+ --path Read value from the file at a given path [string]
551
560
 
552
561
  Arguments binding and namespace-id are mutually exclusive"
553
562
  `);
554
- expect(error).toMatchInlineSnapshot(
555
- `[Error: Arguments binding and namespace-id are mutually exclusive]`
556
- );
557
563
  });
558
564
 
559
565
  it("should error if no value nor path is provided", async () => {
560
- const { error, stdout, stderr } = await runWrangler(
561
- "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"`
562
570
  );
563
571
 
564
- expect(stdout).toMatchInlineSnapshot(`""`);
565
- expect(stderr).toMatchInlineSnapshot(`
572
+ expect(std.out).toMatchInlineSnapshot(`""`);
573
+ expect(std.err).toMatchInlineSnapshot(`
566
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
- -c, --config Path to .toml configuration file [string]
576
- -h, --help Show help [boolean]
577
- -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]
578
587
 
579
588
  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]
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]
584
592
  --preview Interact with a preview namespace [boolean]
585
- --ttl Time for which the entries should be visible. [number]
593
+ --ttl Time for which the entries should be visible [number]
586
594
  --expiration Time since the UNIX epoch after which the entry expires [number]
587
- --path Read value from the file at a given path. [string]
595
+ --path Read value from the file at a given path [string]
588
596
 
589
597
  Exactly one of the arguments value and path is required"
590
598
  `);
591
- expect(error).toMatchInlineSnapshot(
592
- `[Error: Exactly one of the arguments value and path is required]`
593
- );
594
599
  });
595
600
 
596
601
  it("should error if both value and path is provided", async () => {
597
- const { error, stdout, stderr } = await runWrangler(
598
- "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"`
599
606
  );
600
607
 
601
- expect(stdout).toMatchInlineSnapshot(`""`);
602
- expect(stderr).toMatchInlineSnapshot(`
608
+ expect(std.out).toMatchInlineSnapshot(`""`);
609
+ expect(std.err).toMatchInlineSnapshot(`
603
610
  "wrangler kv:key put <key> [value]
604
611
 
605
612
  Writes a single key/value pair to the given namespace.
606
613
 
607
614
  Positionals:
608
- key The key to write to. [string] [required]
609
- value The value to write. [string]
615
+ key The key to write to [string] [required]
616
+ value The value to write [string]
610
617
 
611
618
  Flags:
612
- -c, --config Path to .toml configuration file [string]
613
- -h, --help Show help [boolean]
614
- -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]
615
623
 
616
624
  Options:
617
- -l, --local Run on my machine [boolean] [default: false]
618
- --binding The binding of the namespace to write to. [string]
619
- --namespace-id The id of the namespace to write to. [string]
620
- --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]
621
628
  --preview Interact with a preview namespace [boolean]
622
- --ttl Time for which the entries should be visible. [number]
629
+ --ttl Time for which the entries should be visible [number]
623
630
  --expiration Time since the UNIX epoch after which the entry expires [number]
624
- --path Read value from the file at a given path. [string]
631
+ --path Read value from the file at a given path [string]
625
632
 
626
633
  Arguments value and path are mutually exclusive"
627
634
  `);
628
- expect(error).toMatchInlineSnapshot(
629
- `[Error: Arguments value and path are mutually exclusive]`
630
- );
631
635
  });
632
636
 
633
637
  it("should error if a given binding name is not in the configured kv namespaces", async () => {
634
638
  writeWranglerConfig();
635
- const { error, stdout, stderr } = await runWrangler(
636
- `kv:key put key value --binding otherBinding`
637
- );
638
- expect(stdout).toMatchInlineSnapshot(`""`);
639
- expect(stderr).toMatchInlineSnapshot(
639
+ await expect(
640
+ runWrangler("kv:key put key value --binding otherBinding")
641
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
640
642
  `"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."`
641
643
  );
642
- expect(error).toMatchInlineSnapshot(
643
- `[Error: A namespace with binding name "otherBinding" was not found in the configured "kv_namespaces".]`
644
- );
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
+ `);
645
651
  });
646
652
 
647
653
  it("should error if a given binding has both preview and non-preview and --preview is not specified", async () => {
648
654
  writeWranglerConfig();
649
- const requests = mockKeyPutRequest(
650
- "preview-bound-id",
651
- "my-key",
652
- "my-value"
653
- );
654
- const { error, stderr, stdout } = await runWrangler(
655
- "kv:key put my-key my-value --binding someBinding"
656
- );
657
- expect(stdout).toMatchInlineSnapshot(`""`);
658
- 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(
659
662
  `"someBinding has both a namespace ID and a preview ID. Specify \\"--preview\\" or \\"--preview false\\" to avoid writing data to the wrong namespace."`
660
663
  );
661
- expect(error).toMatchInlineSnapshot(
662
- `[Error: someBinding has both a namespace ID and a preview ID. Specify "--preview" or "--preview false" to avoid writing data to the wrong namespace.]`
663
- );
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
+ `);
664
670
  expect(requests.count).toEqual(0);
665
671
  });
666
672
  });
667
673
 
668
674
  describe("list", () => {
669
- function mockKeyListRequest(
670
- expectedNamespaceId: string,
671
- expectedKeys: string[],
672
- keysPerRequest = 1000
673
- ) {
674
- const requests = { count: 0 };
675
- setMockRawResponse(
676
- "/accounts/:accountId/storage/kv/namespaces/:namespaceId/keys",
677
- ([_url, accountId, namespaceId], _init, query) => {
678
- requests.count++;
679
- expect(accountId).toEqual("some-account-id");
680
- expect(namespaceId).toEqual(expectedNamespaceId);
681
- if (expectedKeys.length <= keysPerRequest) {
682
- return createFetchResult(expectedKeys);
683
- } else {
684
- const start = parseInt(query.get("cursor")) || 0;
685
- const end = start + keysPerRequest;
686
- const cursor = end < expectedKeys.length ? end : undefined;
687
- return createFetchResult(
688
- expectedKeys.slice(start, end),
689
- true,
690
- [],
691
- [],
692
- { cursor }
693
- );
694
- }
695
- }
696
- );
697
- return requests;
698
- }
699
-
700
675
  it("should list the keys of a namespace specified by namespace-id", async () => {
701
- 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
+ ];
702
681
  mockKeyListRequest("some-namespace-id", keys);
703
- const { error, stdout, stderr } = await runWrangler(
704
- "kv:key list --namespace-id some-namespace-id"
705
- );
706
- expect(error).toMatchInlineSnapshot(`undefined`);
707
- expect(stderr).toMatchInlineSnapshot(`""`);
708
- expect(stdout).toMatchInlineSnapshot(`
709
- "key-1
710
- key-2
711
- 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
+ ]"
712
698
  `);
713
699
  });
714
700
 
715
701
  it("should list the keys of a namespace specified by binding", async () => {
716
702
  writeWranglerConfig();
717
- const keys = ["key-1", "key-2", "key-3"];
703
+ const keys = [{ name: "key-1" }, { name: "key-2" }, { name: "key-3" }];
718
704
  mockKeyListRequest("bound-id", keys);
719
- const { error, stdout, stderr } = await runWrangler(
720
- "kv:key list --binding someBinding"
721
- );
722
- expect(error).toMatchInlineSnapshot(`undefined`);
723
- expect(stderr).toMatchInlineSnapshot(`""`);
724
- expect(stdout).toMatchInlineSnapshot(`
725
- "key-1
726
- key-2
727
- 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
+ ]"
728
720
  `);
729
721
  });
730
722
 
731
723
  it("should list the keys of a preview namespace specified by binding", async () => {
732
724
  writeWranglerConfig();
733
- const keys = ["key-1", "key-2", "key-3"];
725
+ const keys = [{ name: "key-1" }, { name: "key-2" }, { name: "key-3" }];
734
726
  mockKeyListRequest("preview-bound-id", keys);
735
- const { error, stdout, stderr } = await runWrangler(
736
- "kv:key list --binding someBinding --preview"
737
- );
738
- expect(error).toMatchInlineSnapshot(`undefined`);
739
- expect(stderr).toMatchInlineSnapshot(`""`);
740
- expect(stdout).toMatchInlineSnapshot(`
741
- "key-1
742
- key-2
743
- 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
+ ]"
744
741
  `);
745
742
  });
746
743
 
747
744
  it("should list the keys of a namespace specified by binding, in a given environment", async () => {
748
745
  writeWranglerConfig();
749
- const keys = ["key-1", "key-2", "key-3"];
746
+ const keys = [{ name: "key-1" }, { name: "key-2" }, { name: "key-3" }];
750
747
  mockKeyListRequest("env-bound-id", keys);
751
- const { error, stdout, stderr } = await runWrangler(
748
+ await runWrangler(
752
749
  "kv:key list --binding someBinding --env some-environment"
753
750
  );
754
- expect(error).toMatchInlineSnapshot(`undefined`);
755
- expect(stderr).toMatchInlineSnapshot(`""`);
756
- expect(stdout).toMatchInlineSnapshot(`
757
- "key-1
758
- key-2
759
- 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
+ ]"
760
764
  `);
761
765
  });
762
766
 
763
767
  it("should list the keys of a preview namespace specified by binding, in a given environment", async () => {
764
768
  writeWranglerConfig();
765
- const keys = ["key-1", "key-2", "key-3"];
769
+ const keys = [{ name: "key-1" }, { name: "key-2" }, { name: "key-3" }];
766
770
  mockKeyListRequest("preview-env-bound-id", keys);
767
- const { error, stdout, stderr } = await runWrangler(
771
+ await runWrangler(
768
772
  "kv:key list --binding someBinding --preview --env some-environment"
769
773
  );
770
- expect(error).toMatchInlineSnapshot(`undefined`);
771
- expect(stderr).toMatchInlineSnapshot(`""`);
772
- expect(stdout).toMatchInlineSnapshot(`
773
- "key-1
774
- key-2
775
- 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
+ ]"
776
787
  `);
777
788
  });
778
789
 
779
- it("should make multiple requests for paginated results", async () => {
780
- // Create a lot of mock keys, so that the fetch requests will be paginated
781
- const keys = [];
782
- for (let i = 0; i < 550; i++) {
783
- keys.push("key-" + i);
784
- }
785
- // Ask for the keys in pages of size 100.
786
- const requests = mockKeyListRequest("some-namespace-id", keys, 100);
787
- const { stdout, stderr, error } = await runWrangler(
788
- "kv:key list --namespace-id some-namespace-id --limit 100"
789
- );
790
- expect(error).toMatchInlineSnapshot(`undefined`);
791
- expect(stderr).toMatchInlineSnapshot(`""`);
792
- expect(stdout).toEqual(keys.join("\n"));
793
- expect(requests.count).toEqual(6);
794
- });
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(
813
+ "kv:key list --namespace-id some-namespace-id --limit 100"
814
+ );
815
+ expect(std.err).toMatchInlineSnapshot(`""`);
816
+ expect(JSON.parse(std.out)).toEqual(keys);
817
+ expect(requests.count).toEqual(6);
818
+ });
819
+ });
820
+ }
795
821
 
796
822
  it("should error if a given binding name is not in the configured kv namespaces", async () => {
797
823
  writeWranglerConfig();
798
- const { error, stdout, stderr } = await runWrangler(
799
- "kv:key list --binding otherBinding"
800
- );
801
- expect(error).toMatchInlineSnapshot(
802
- `[Error: A namespace with binding name "otherBinding" was not found in the configured "kv_namespaces".]`
803
- );
804
- expect(stderr).toMatchInlineSnapshot(
824
+ await expect(
825
+ runWrangler("kv:key list --binding otherBinding")
826
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
805
827
  `"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."`
806
828
  );
807
- expect(stdout).toMatchInlineSnapshot(`""`);
829
+ expect(std.err).toMatchInlineSnapshot(`
830
+ "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
831
+
832
+ %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
833
+ `);
834
+ expect(std.out).toMatchInlineSnapshot(`""`);
808
835
  });
809
836
  });
810
837
 
811
838
  describe("get", () => {
812
- function mockKeyGetRequest(
813
- expectedNamespaceId: string,
814
- expectedKey: string,
815
- expectedValue: string
816
- ) {
817
- const requests = { count: 0 };
818
- setMockRawResponse(
819
- "/accounts/:accountId/storage/kv/namespaces/:namespaceId/values/:key",
820
- ([_url, accountId, namespaceId, key]) => {
821
- requests.count++;
822
- expect(accountId).toEqual("some-account-id");
823
- expect(namespaceId).toEqual(expectedNamespaceId);
824
- expect(key).toEqual(expectedKey);
825
- return expectedValue;
826
- }
827
- );
828
- return requests;
829
- }
839
+ afterEach(() => {
840
+ unsetMockFetchKVGetValues();
841
+ });
830
842
 
831
843
  it("should get a key in a given namespace specified by namespace-id", async () => {
832
- const requests = mockKeyGetRequest(
844
+ setMockFetchKVGetValue(
845
+ "some-account-id",
833
846
  "some-namespace-id",
834
847
  "my-key",
835
848
  "my-value"
836
849
  );
837
- const { error, stderr, stdout } = await runWrangler(
838
- "kv:key get my-key --namespace-id some-namespace-id"
839
- );
840
- expect(stdout).toMatchInlineSnapshot(`"my-value"`);
841
- expect(stderr).toMatchInlineSnapshot(`""`);
842
- expect(error).toMatchInlineSnapshot(`undefined`);
843
- expect(requests.count).toEqual(1);
850
+ await runWrangler("kv:key get my-key --namespace-id some-namespace-id");
851
+ expect(std.out).toMatchInlineSnapshot(`"my-value"`);
852
+ expect(std.err).toMatchInlineSnapshot(`""`);
844
853
  });
845
854
 
846
855
  it("should get a key in a given namespace specified by binding", async () => {
847
856
  writeWranglerConfig();
848
- const requests = mockKeyGetRequest("bound-id", "my-key", "my-value");
849
- const { error, stderr, stdout } = await runWrangler(
857
+ setMockFetchKVGetValue(
858
+ "some-account-id",
859
+ "bound-id",
860
+ "my-key",
861
+ "my-value"
862
+ );
863
+ await runWrangler(
850
864
  "kv:key get my-key --binding someBinding --preview false"
851
865
  );
852
- expect(stdout).toMatchInlineSnapshot(`"my-value"`);
853
- expect(stderr).toMatchInlineSnapshot(`""`);
854
- expect(error).toMatchInlineSnapshot(`undefined`);
855
- expect(requests.count).toEqual(1);
866
+ expect(std.out).toMatchInlineSnapshot(`"my-value"`);
867
+ expect(std.err).toMatchInlineSnapshot(`""`);
856
868
  });
857
869
 
858
870
  it("should get a key in a given preview namespace specified by binding", async () => {
859
871
  writeWranglerConfig();
860
- const requests = mockKeyGetRequest(
872
+ setMockFetchKVGetValue(
873
+ "some-account-id",
861
874
  "preview-bound-id",
862
875
  "my-key",
863
876
  "my-value"
864
877
  );
865
- const { error, stderr, stdout } = await runWrangler(
866
- "kv:key get my-key --binding someBinding --preview"
867
- );
868
- expect(stdout).toMatchInlineSnapshot(`"my-value"`);
869
- expect(stderr).toMatchInlineSnapshot(`""`);
870
- expect(error).toMatchInlineSnapshot(`undefined`);
871
- expect(requests.count).toEqual(1);
878
+ await runWrangler("kv:key get my-key --binding someBinding --preview");
879
+ expect(std.out).toMatchInlineSnapshot(`"my-value"`);
880
+ expect(std.err).toMatchInlineSnapshot(`""`);
872
881
  });
873
882
 
874
883
  it("should get a key for the specified environment in a given namespace", async () => {
875
884
  writeWranglerConfig();
876
- const requests = mockKeyGetRequest(
885
+ setMockFetchKVGetValue(
886
+ "some-account-id",
877
887
  "env-bound-id",
878
888
  "my-key",
879
889
  "my-value"
880
890
  );
881
- const { error, stderr, stdout } = await runWrangler(
891
+ await runWrangler(
882
892
  "kv:key get my-key my-value --binding someBinding --env some-environment --preview false"
883
893
  );
884
- expect(stdout).toMatchInlineSnapshot(`"my-value"`);
885
- expect(stderr).toMatchInlineSnapshot(`""`);
886
- expect(error).toMatchInlineSnapshot(`undefined`);
887
- expect(requests.count).toEqual(1);
894
+ expect(std.out).toMatchInlineSnapshot(`"my-value"`);
895
+ expect(std.err).toMatchInlineSnapshot(`""`);
888
896
  });
889
897
 
890
898
  it("should error if no key is provided", async () => {
891
- const { error, stdout, stderr } = await runWrangler("kv:key get");
892
-
893
- expect(stdout).toMatchInlineSnapshot(`""`);
894
- expect(stderr).toMatchInlineSnapshot(`
899
+ await expect(
900
+ runWrangler("kv:key get")
901
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
902
+ `"Not enough non-option arguments: got 0, need at least 1"`
903
+ );
904
+ expect(std.out).toMatchInlineSnapshot(`""`);
905
+ expect(std.err).toMatchInlineSnapshot(`
895
906
  "wrangler kv:key get <key>
896
907
 
897
908
  Reads a single value by key from the given namespace.
@@ -900,29 +911,29 @@ describe("wrangler", () => {
900
911
  key The key value to get. [string] [required]
901
912
 
902
913
  Flags:
903
- -c, --config Path to .toml configuration file [string]
904
- -h, --help Show help [boolean]
905
- -v, --version Show version number [boolean]
914
+ -c, --config Path to .toml configuration file [string]
915
+ -h, --help Show help [boolean]
916
+ -v, --version Show version number [boolean]
917
+ --legacy-env Use legacy environments [boolean]
906
918
 
907
919
  Options:
908
- -l, --local Run on my machine [boolean] [default: false]
909
920
  --binding The name of the namespace to get from [string]
910
921
  --namespace-id The id of the namespace to get from [string]
911
- --env Perform on a specific environment [string]
922
+ -e, --env Perform on a specific environment [string]
912
923
  --preview Interact with a preview namespace [boolean] [default: false]
913
924
 
914
925
  Not enough non-option arguments: got 0, need at least 1"
915
926
  `);
916
- expect(error).toMatchInlineSnapshot(
917
- `[Error: Not enough non-option arguments: got 0, need at least 1]`
918
- );
919
927
  });
920
928
 
921
929
  it("should error if no binding nor namespace is provided", async () => {
922
- const { error, stdout, stderr } = await runWrangler("kv:key get foo");
923
-
924
- expect(stdout).toMatchInlineSnapshot(`""`);
925
- expect(stderr).toMatchInlineSnapshot(`
930
+ await expect(
931
+ runWrangler("kv:key get foo")
932
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
933
+ `"Exactly one of the arguments binding and namespace-id is required"`
934
+ );
935
+ expect(std.out).toMatchInlineSnapshot(`""`);
936
+ expect(std.err).toMatchInlineSnapshot(`
926
937
  "wrangler kv:key get <key>
927
938
 
928
939
  Reads a single value by key from the given namespace.
@@ -931,31 +942,30 @@ describe("wrangler", () => {
931
942
  key The key value to get. [string] [required]
932
943
 
933
944
  Flags:
934
- -c, --config Path to .toml configuration file [string]
935
- -h, --help Show help [boolean]
936
- -v, --version Show version number [boolean]
945
+ -c, --config Path to .toml configuration file [string]
946
+ -h, --help Show help [boolean]
947
+ -v, --version Show version number [boolean]
948
+ --legacy-env Use legacy environments [boolean]
937
949
 
938
950
  Options:
939
- -l, --local Run on my machine [boolean] [default: false]
940
951
  --binding The name of the namespace to get from [string]
941
952
  --namespace-id The id of the namespace to get from [string]
942
- --env Perform on a specific environment [string]
953
+ -e, --env Perform on a specific environment [string]
943
954
  --preview Interact with a preview namespace [boolean] [default: false]
944
955
 
945
956
  Exactly one of the arguments binding and namespace-id is required"
946
957
  `);
947
- expect(error).toMatchInlineSnapshot(
948
- `[Error: Exactly one of the arguments binding and namespace-id is required]`
949
- );
950
958
  });
951
959
 
952
960
  it("should error if both binding and namespace is provided", async () => {
953
- const { error, stdout, stderr } = await runWrangler(
954
- "kv:key get foo --binding x --namespace-id y"
961
+ await expect(
962
+ runWrangler("kv:key get foo --binding x --namespace-id y")
963
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
964
+ `"Arguments binding and namespace-id are mutually exclusive"`
955
965
  );
956
966
 
957
- expect(stdout).toMatchInlineSnapshot(`""`);
958
- expect(stderr).toMatchInlineSnapshot(`
967
+ expect(std.out).toMatchInlineSnapshot(`""`);
968
+ expect(std.err).toMatchInlineSnapshot(`
959
969
  "wrangler kv:key get <key>
960
970
 
961
971
  Reads a single value by key from the given namespace.
@@ -964,36 +974,34 @@ describe("wrangler", () => {
964
974
  key The key value to get. [string] [required]
965
975
 
966
976
  Flags:
967
- -c, --config Path to .toml configuration file [string]
968
- -h, --help Show help [boolean]
969
- -v, --version Show version number [boolean]
977
+ -c, --config Path to .toml configuration file [string]
978
+ -h, --help Show help [boolean]
979
+ -v, --version Show version number [boolean]
980
+ --legacy-env Use legacy environments [boolean]
970
981
 
971
982
  Options:
972
- -l, --local Run on my machine [boolean] [default: false]
973
983
  --binding The name of the namespace to get from [string]
974
984
  --namespace-id The id of the namespace to get from [string]
975
- --env Perform on a specific environment [string]
985
+ -e, --env Perform on a specific environment [string]
976
986
  --preview Interact with a preview namespace [boolean] [default: false]
977
987
 
978
988
  Arguments binding and namespace-id are mutually exclusive"
979
989
  `);
980
- expect(error).toMatchInlineSnapshot(
981
- `[Error: Arguments binding and namespace-id are mutually exclusive]`
982
- );
983
990
  });
984
991
 
985
992
  it("should error if a given binding name is not in the configured kv namespaces", async () => {
986
993
  writeWranglerConfig();
987
- const { error, stdout, stderr } = await runWrangler(
988
- `kv:key get key --binding otherBinding`
989
- );
990
- expect(stdout).toMatchInlineSnapshot(`""`);
991
- expect(stderr).toMatchInlineSnapshot(
994
+ await expect(
995
+ runWrangler("kv:key get key --binding otherBinding")
996
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
992
997
  `"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."`
993
998
  );
994
- expect(error).toMatchInlineSnapshot(
995
- `[Error: A namespace with binding name "otherBinding" was not found in the configured "kv_namespaces".]`
996
- );
999
+ expect(std.out).toMatchInlineSnapshot(`""`);
1000
+ expect(std.err).toMatchInlineSnapshot(`
1001
+ "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
1002
+
1003
+ %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1004
+ `);
997
1005
  });
998
1006
  });
999
1007
 
@@ -1045,25 +1053,29 @@ describe("wrangler", () => {
1045
1053
 
1046
1054
  it("should error if a given binding name is not in the configured kv namespaces", async () => {
1047
1055
  writeWranglerConfig();
1048
- const { stderr } = await runWrangler(
1049
- `kv:key delete --binding otherBinding someKey`
1050
- );
1051
- expect(stderr).toMatchInlineSnapshot(
1056
+ await expect(
1057
+ runWrangler(`kv:key delete --binding otherBinding someKey`)
1058
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
1052
1059
  `"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\"."`
1053
1060
  );
1061
+
1062
+ expect(std.err).toMatchInlineSnapshot(`
1063
+ "A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
1064
+
1065
+ %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1066
+ `);
1054
1067
  });
1055
1068
 
1056
1069
  it("should delete a key in a namespace specified by binding name in a given environment", async () => {
1057
1070
  writeWranglerConfig();
1058
1071
  const requests = mockDeleteRequest("env-bound-id", "someKey");
1059
- const { stdout, stderr, error } = await runWrangler(
1072
+ await runWrangler(
1060
1073
  `kv:key delete --binding someBinding --env some-environment --preview false someKey`
1061
1074
  );
1062
- expect(stdout).toMatchInlineSnapshot(
1075
+ expect(std.out).toMatchInlineSnapshot(
1063
1076
  `"deleting the key \\"someKey\\" on namespace env-bound-id"`
1064
1077
  );
1065
- expect(stderr).toMatchInlineSnapshot(`""`);
1066
- expect(error).toMatchInlineSnapshot(`undefined`);
1078
+ expect(std.err).toMatchInlineSnapshot(`""`);
1067
1079
  expect(requests.count).toEqual(1);
1068
1080
  });
1069
1081
 
@@ -1077,6 +1089,278 @@ describe("wrangler", () => {
1077
1089
  });
1078
1090
  });
1079
1091
  });
1092
+
1093
+ describe("kv:bulk", () => {
1094
+ describe("put", () => {
1095
+ function mockPutRequest(
1096
+ expectedNamespaceId: string,
1097
+ expectedKeyValues: KeyValue[]
1098
+ ) {
1099
+ const requests = { count: 0 };
1100
+ setMockResponse(
1101
+ "/accounts/:accountId/storage/kv/namespaces/:namespaceId/bulk",
1102
+ "PUT",
1103
+ ([_url, accountId, namespaceId], { body }) => {
1104
+ requests.count++;
1105
+ expect(accountId).toEqual("some-account-id");
1106
+ expect(namespaceId).toEqual(expectedNamespaceId);
1107
+ expect(JSON.parse(body as string)).toEqual(
1108
+ expectedKeyValues.slice(
1109
+ (requests.count - 1) * 5000,
1110
+ requests.count * 5000
1111
+ )
1112
+ );
1113
+ return null;
1114
+ }
1115
+ );
1116
+ return requests;
1117
+ }
1118
+
1119
+ it("should put the key-values parsed from a file", async () => {
1120
+ const keyValues: KeyValue[] = [
1121
+ { key: "someKey1", value: "someValue1" },
1122
+ { key: "ns:someKey2", value: "123", base64: true },
1123
+ { key: "someKey3", value: "someValue3", expiration: 100 },
1124
+ { key: "someKey4", value: "someValue4", expiration_ttl: 500 },
1125
+ ];
1126
+ writeFileSync("./keys.json", JSON.stringify(keyValues));
1127
+ const requests = mockPutRequest("some-namespace-id", keyValues);
1128
+ await runWrangler(
1129
+ `kv:bulk put --namespace-id some-namespace-id keys.json`
1130
+ );
1131
+ expect(requests.count).toEqual(1);
1132
+ expect(std.out).toMatchInlineSnapshot(`"Success!"`);
1133
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1134
+ expect(std.err).toMatchInlineSnapshot(`""`);
1135
+ });
1136
+
1137
+ it("should put the key-values in batches of 5000 parsed from a file", async () => {
1138
+ const keyValues: KeyValue[] = new Array(12000).fill({
1139
+ key: "someKey1",
1140
+ value: "someValue1",
1141
+ });
1142
+ writeFileSync("./keys.json", JSON.stringify(keyValues));
1143
+ const requests = mockPutRequest("some-namespace-id", keyValues);
1144
+ await runWrangler(
1145
+ `kv:bulk put --namespace-id some-namespace-id keys.json`
1146
+ );
1147
+ expect(requests.count).toEqual(3);
1148
+ expect(std.out).toMatchInlineSnapshot(`
1149
+ "Uploaded 0 of 12000.
1150
+ Uploaded 5000 of 12000.
1151
+ Uploaded 10000 of 12000.
1152
+ Uploaded 12000 of 12000.
1153
+ Success!"
1154
+ `);
1155
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1156
+ expect(std.err).toMatchInlineSnapshot(`""`);
1157
+ });
1158
+
1159
+ it("should error if the file is not a JSON array", async () => {
1160
+ const keyValues = { key: "someKey1", value: "someValue1" };
1161
+ writeFileSync("./keys.json", JSON.stringify(keyValues));
1162
+ await expect(
1163
+ runWrangler(`kv:bulk put --namespace-id some-namespace-id keys.json`)
1164
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`
1165
+ "Unexpected JSON input from \\"keys.json\\".
1166
+ Expected an array of key-value objects but got type \\"object\\"."
1167
+ `);
1168
+ expect(std.out).toMatchInlineSnapshot(`""`);
1169
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1170
+ });
1171
+
1172
+ it("should error if the array contains items that are not key-value objects", async () => {
1173
+ const keyValues = [
1174
+ 123,
1175
+ "a string",
1176
+ { key: "someKey" },
1177
+ { value: "someValue" },
1178
+ { key: "someKey1", value: "someValue1", invalid: true },
1179
+ ];
1180
+ writeFileSync("./keys.json", JSON.stringify(keyValues));
1181
+ await expect(
1182
+ runWrangler(`kv:bulk put --namespace-id some-namespace-id keys.json`)
1183
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`
1184
+ "Unexpected JSON input from \\"keys.json\\".
1185
+ Each item in the array should be an object that matches:
1186
+
1187
+ interface KeyValue {
1188
+ key: string;
1189
+ value: string;
1190
+ expiration?: number;
1191
+ expiration_ttl?: number;
1192
+ metadata?: object;
1193
+ base64?: boolean;
1194
+ }
1195
+
1196
+ The item at index 0 is type: \\"number\\" - 123
1197
+ The item at index 1 is type: \\"string\\" - \\"a string\\"
1198
+ The item at index 2 is {\\"key\\":\\"someKey\\"}
1199
+ The item at index 3 is {\\"value\\":\\"someValue\\"}"
1200
+ `);
1201
+
1202
+ expect(std.out).toMatchInlineSnapshot(`""`);
1203
+ expect(std.warn).toMatchInlineSnapshot(`
1204
+ "Unexpected key-value properties in \\"keys.json\\".
1205
+ The item at index 4 contains unexpected properties: [\\"invalid\\"]."
1206
+ `);
1207
+ });
1208
+ });
1209
+
1210
+ describe("delete", () => {
1211
+ function mockDeleteRequest(
1212
+ expectedNamespaceId: string,
1213
+ expectedKeys: string[]
1214
+ ) {
1215
+ const requests = { count: 0 };
1216
+ setMockResponse(
1217
+ "/accounts/:accountId/storage/kv/namespaces/:namespaceId/bulk",
1218
+ "DELETE",
1219
+ ([_url, accountId, namespaceId], { headers, body }) => {
1220
+ requests.count++;
1221
+ expect(accountId).toEqual("some-account-id");
1222
+ expect(namespaceId).toEqual(expectedNamespaceId);
1223
+ expect(new Headers(headers ?? []).get("Content-Type")).toEqual(
1224
+ "application/json"
1225
+ );
1226
+ expect(JSON.parse(body as string)).toEqual(
1227
+ expectedKeys.slice(
1228
+ (requests.count - 1) * 5000,
1229
+ requests.count * 5000
1230
+ )
1231
+ );
1232
+ return null;
1233
+ }
1234
+ );
1235
+ return requests;
1236
+ }
1237
+
1238
+ it("should delete the keys parsed from a file", async () => {
1239
+ const keys = ["someKey1", "ns:someKey2"];
1240
+ writeFileSync("./keys.json", JSON.stringify(keys));
1241
+ mockConfirm({
1242
+ text: `Are you sure you want to delete all the keys read from "keys.json" from kv-namespace with id "some-namespace-id"?`,
1243
+ result: true,
1244
+ });
1245
+ const requests = mockDeleteRequest("some-namespace-id", keys);
1246
+ await runWrangler(
1247
+ `kv:bulk delete --namespace-id some-namespace-id keys.json`
1248
+ );
1249
+ expect(requests.count).toEqual(1);
1250
+ expect(std.out).toMatchInlineSnapshot(`"Success!"`);
1251
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1252
+ expect(std.err).toMatchInlineSnapshot(`""`);
1253
+ });
1254
+
1255
+ it("should delete the keys in batches of 5000 parsed from a file", async () => {
1256
+ const keys = new Array(12000).fill("some-key");
1257
+ writeFileSync("./keys.json", JSON.stringify(keys));
1258
+ mockConfirm({
1259
+ text: `Are you sure you want to delete all the keys read from "keys.json" from kv-namespace with id "some-namespace-id"?`,
1260
+ result: true,
1261
+ });
1262
+ const requests = mockDeleteRequest("some-namespace-id", keys);
1263
+ await runWrangler(
1264
+ `kv:bulk delete --namespace-id some-namespace-id keys.json`
1265
+ );
1266
+ expect(requests.count).toEqual(3);
1267
+ expect(std.out).toMatchInlineSnapshot(`
1268
+ "Deleted 0 of 12000.
1269
+ Deleted 5000 of 12000.
1270
+ Deleted 10000 of 12000.
1271
+ Deleted 12000 of 12000.
1272
+ Success!"
1273
+ `);
1274
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1275
+ expect(std.err).toMatchInlineSnapshot(`""`);
1276
+ });
1277
+
1278
+ it("should not delete the keys if the user confirms no", async () => {
1279
+ const keys = ["someKey1", "ns:someKey2"];
1280
+ writeFileSync("./keys.json", JSON.stringify(keys));
1281
+ mockConfirm({
1282
+ text: `Are you sure you want to delete all the keys read from "keys.json" from kv-namespace with id "some-namespace-id"?`,
1283
+ result: false,
1284
+ });
1285
+ await runWrangler(
1286
+ `kv:bulk delete --namespace-id some-namespace-id keys.json`
1287
+ );
1288
+ expect(std.out).toMatchInlineSnapshot(
1289
+ `"Not deleting keys read from \\"keys.json\\"."`
1290
+ );
1291
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1292
+ expect(std.err).toMatchInlineSnapshot(`""`);
1293
+ });
1294
+
1295
+ it("should delete the keys without asking if --force is provided", async () => {
1296
+ const keys = ["someKey1", "ns:someKey2"];
1297
+ writeFileSync("./keys.json", JSON.stringify(keys));
1298
+ const requests = mockDeleteRequest("some-namespace-id", keys);
1299
+ await runWrangler(
1300
+ `kv:bulk delete --namespace-id some-namespace-id keys.json --force`
1301
+ );
1302
+ expect(requests.count).toEqual(1);
1303
+ expect(std.out).toMatchInlineSnapshot(`"Success!"`);
1304
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1305
+ expect(std.err).toMatchInlineSnapshot(`""`);
1306
+ });
1307
+
1308
+ it("should delete the keys without asking if -f is provided", async () => {
1309
+ const keys = ["someKey1", "ns:someKey2"];
1310
+ writeFileSync("./keys.json", JSON.stringify(keys));
1311
+ const requests = mockDeleteRequest("some-namespace-id", keys);
1312
+ await runWrangler(
1313
+ `kv:bulk delete --namespace-id some-namespace-id keys.json -f`
1314
+ );
1315
+ expect(requests.count).toEqual(1);
1316
+ expect(std.out).toMatchInlineSnapshot(`"Success!"`);
1317
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1318
+ expect(std.err).toMatchInlineSnapshot(`""`);
1319
+ });
1320
+
1321
+ it("should error if the file is not a JSON array", async () => {
1322
+ const keys = 12354;
1323
+ writeFileSync("./keys.json", JSON.stringify(keys));
1324
+ mockConfirm({
1325
+ text: `Are you sure you want to delete all the keys read from "keys.json" from kv-namespace with id "some-namespace-id"?`,
1326
+ result: true,
1327
+ });
1328
+ await expect(
1329
+ runWrangler(
1330
+ `kv:bulk delete --namespace-id some-namespace-id keys.json`
1331
+ )
1332
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`
1333
+ "Unexpected JSON input from \\"keys.json\\".
1334
+ Expected an array of strings but got:
1335
+ 12354"
1336
+ `);
1337
+ expect(std.out).toMatchInlineSnapshot(`""`);
1338
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1339
+ });
1340
+
1341
+ it("should error if the file contains non-string items", async () => {
1342
+ const keys = ["good", 12354, { key: "someKey" }, null];
1343
+ writeFileSync("./keys.json", JSON.stringify(keys));
1344
+ mockConfirm({
1345
+ text: `Are you sure you want to delete all the keys read from "keys.json" from kv-namespace with id "some-namespace-id"?`,
1346
+ result: true,
1347
+ });
1348
+ await expect(
1349
+ runWrangler(
1350
+ `kv:bulk delete --namespace-id some-namespace-id keys.json`
1351
+ )
1352
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`
1353
+ "Unexpected JSON input from \\"keys.json\\".
1354
+ Expected an array of strings.
1355
+ The item at index 1 is type: \\"number\\" - 12354
1356
+ The item at index 2 is type: \\"object\\" - {\\"key\\":\\"someKey\\"}
1357
+ The item at index 3 is type: \\"object\\" - null"
1358
+ `);
1359
+ expect(std.out).toMatchInlineSnapshot(`""`);
1360
+ expect(std.warn).toMatchInlineSnapshot(`""`);
1361
+ });
1362
+ });
1363
+ });
1080
1364
  });
1081
1365
 
1082
1366
  function writeWranglerConfig() {