wrangler 0.0.0-e9a1820 → 0.0.0-e9ab55a

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.
package/src/index.tsx CHANGED
@@ -6,6 +6,7 @@ import makeCLI from "yargs";
6
6
  import type Yargs from "yargs";
7
7
  import { findUp } from "find-up";
8
8
  import TOML from "@iarna/toml";
9
+ import { normaliseAndValidateEnvironmentsConfig } from "./config";
9
10
  import type { Config } from "./config";
10
11
  import { confirm, prompt } from "./dialogs";
11
12
  import { version as wranglerVersion } from "../package.json";
@@ -30,7 +31,7 @@ import {
30
31
 
31
32
  import { pages } from "./pages";
32
33
 
33
- import cfetch from "./cfetch";
34
+ import { fetchResult, fetchRaw } from "./cfetch";
34
35
 
35
36
  import publish from "./publish";
36
37
  import path from "path/posix";
@@ -43,6 +44,9 @@ import { setTimeout } from "node:timers/promises";
43
44
  import * as fs from "node:fs";
44
45
  import { execa } from "execa";
45
46
 
47
+ const resetColor = "\x1b[0m";
48
+ const fgGreenColor = "\x1b[32m";
49
+
46
50
  async function readConfig(configPath?: string): Promise<Config> {
47
51
  const config: Config = {};
48
52
  if (!configPath) {
@@ -56,48 +60,7 @@ async function readConfig(configPath?: string): Promise<Config> {
56
60
  Object.assign(config, parsed);
57
61
  }
58
62
 
59
- const inheritedFields = [
60
- "name",
61
- "account_id",
62
- "workers_dev",
63
- "compatibility_date",
64
- "compatibility_flags",
65
- "zone_id",
66
- "routes",
67
- "route",
68
- "jsx_factory",
69
- "jsx_fragment",
70
- "site",
71
- "triggers",
72
- "usage_model",
73
- ];
74
-
75
- Object.keys(config.env || {}).forEach((env) => {
76
- inheritedFields.forEach((field) => {
77
- if (config[field] !== undefined && config.env[env][field] === undefined) {
78
- config.env[env][field] = config[field]; // TODO: - shallow copy?
79
- }
80
- });
81
- });
82
-
83
- const mirroredFields = [
84
- "vars",
85
- "kv_namespaces",
86
- "durable_objects",
87
- "experimental_services",
88
- ];
89
- Object.keys(config.env || {}).forEach((env) => {
90
- mirroredFields.forEach((field) => {
91
- // if it exists on top level, it should exist on env definitions
92
- Object.keys(config[field] || {}).forEach((fieldKey) => {
93
- if (!(fieldKey in config.env[env][field])) {
94
- console.warn(
95
- `In your configuration, "${field}.${fieldKey}" exists at a top level, but not on "env.${env}". This is not what you probably want, since the field "${field}" is not inherited by environments. Please add "${field}.${fieldKey}" to "env.${env}".`
96
- );
97
- }
98
- });
99
- });
100
- });
63
+ normaliseAndValidateEnvironmentsConfig(config);
101
64
 
102
65
  if ("experimental_services" in config) {
103
66
  console.warn(
@@ -421,7 +384,11 @@ export async function main(argv: string[]): Promise<void> {
421
384
  "👂 Start a local server for developing your worker",
422
385
  (yargs) => {
423
386
  return yargs
424
- .positional("filename", { describe: "entry point", type: "string" })
387
+ .positional("filename", {
388
+ describe: "entry point",
389
+ type: "string",
390
+ demandOption: true,
391
+ })
425
392
  .option("name", {
426
393
  describe: "name of the script",
427
394
  type: "string",
@@ -435,6 +402,20 @@ export async function main(argv: string[]): Promise<void> {
435
402
  type: "string",
436
403
  // TODO: get choices for the toml file?
437
404
  })
405
+ .option("compatibility-date", {
406
+ describe: "Date to use for compatibility checks",
407
+ type: "string",
408
+ })
409
+ .option("compatibility-flags", {
410
+ describe: "Flags to use for compatibility checks",
411
+ type: "array",
412
+ alias: "compatibility-flag",
413
+ })
414
+ .option("latest", {
415
+ describe: "Use the latest version of the worker runtime",
416
+ type: "boolean",
417
+ default: true,
418
+ })
438
419
  .option("ip", {
439
420
  describe: "IP address to listen on",
440
421
  type: "string",
@@ -455,7 +436,7 @@ export async function main(argv: string[]): Promise<void> {
455
436
  describe: "Protocol to listen to requests on, defaults to http.",
456
437
  choices: ["http", "https"],
457
438
  })
458
- .option("public", {
439
+ .option("experimental-public", {
459
440
  describe: "Static assets to be served",
460
441
  type: "string",
461
442
  })
@@ -482,39 +463,51 @@ export async function main(argv: string[]): Promise<void> {
482
463
  const { filename, format } = args;
483
464
  const config = args.config as Config;
484
465
 
485
- // -- snip, extract --
466
+ if (args["experimental-public"]) {
467
+ console.warn(
468
+ "🚨 The --experimental-public field is experimental and will change in the future."
469
+ );
470
+ }
471
+
472
+ if (args.public) {
473
+ throw new Error(
474
+ "🚨 The --public field has been renamed to --experimental-public, and will change behaviour in the future."
475
+ );
476
+ }
486
477
 
487
478
  if (!args.local) {
479
+ // -- snip, extract --
488
480
  const loggedIn = await loginOrRefreshIfRequired();
489
481
  if (!loggedIn) {
490
482
  // didn't login, let's just quit
491
483
  console.log("Did not login, quitting...");
492
484
  return;
493
485
  }
486
+
494
487
  if (!config.account_id) {
495
488
  config.account_id = await getAccountId();
496
489
  if (!config.account_id) {
497
490
  throw new Error("No account id found, quitting...");
498
491
  }
499
492
  }
493
+ // -- snip, end --
500
494
  }
501
495
 
502
- // -- snip, end --
503
-
504
- const envRootObj = args.env ? config.env[args.env] || {} : config;
496
+ const environments = config.env ?? {};
497
+ const envRootObj = args.env ? environments[args.env] || {} : config;
505
498
 
506
499
  // TODO: this error shouldn't actually happen,
507
500
  // but we haven't fixed it internally yet
508
501
  if ("durable_objects" in envRootObj) {
509
502
  if (!(args.name || config.name)) {
510
503
  console.warn(
511
- 'A worker with durable objects need to be named, or it may not work as expected. Add a "name" into wrangler.toml, or pass it in the command line with --name.'
504
+ 'A worker with durable objects needs to be named, or it may not work as expected. Add a "name" into wrangler.toml, or pass it in the command line with --name.'
512
505
  );
513
506
  }
514
507
  // TODO: if not already published, publish a draft worker
515
508
  }
516
509
 
517
- render(
510
+ const { waitUntilExit } = render(
518
511
  <Dev
519
512
  name={args.name || config.name}
520
513
  entry={path.relative(process.cwd(), filename)}
@@ -526,9 +519,16 @@ export async function main(argv: string[]): Promise<void> {
526
519
  accountId={config.account_id}
527
520
  site={args.site || config.site?.bucket}
528
521
  port={args.port || config.dev?.port}
529
- public={args.public}
530
- compatibilityDate={config.compatibility_date}
531
- compatibilityFlags={config.compatibility_flags}
522
+ public={args["experimental-public"]}
523
+ compatibilityDate={
524
+ args["compatibility-date"] ||
525
+ config.compatibility_date ||
526
+ new Date().toISOString().substring(0, 10)
527
+ }
528
+ compatibilityFlags={
529
+ (args["compatibility-flags"] as string[]) ||
530
+ config.compatibility_flags
531
+ }
532
532
  usageModel={config.usage_model}
533
533
  bindings={{
534
534
  kv_namespaces: envRootObj.kv_namespaces?.map(
@@ -537,7 +537,7 @@ export async function main(argv: string[]): Promise<void> {
537
537
  // `preview_id` instead of `id` so that they don't
538
538
  // break production data. So here we check that a `preview_id`
539
539
  // has actually been configured.
540
- // This whole block of code will be obsolted in the future
540
+ // This whole block of code will be obsoleted in the future
541
541
  // when we have copy-on-write for previews on edge workers.
542
542
  if (!preview_id) {
543
543
  // TODO: This error has to be a _lot_ better, ideally just asking
@@ -558,6 +558,7 @@ export async function main(argv: string[]): Promise<void> {
558
558
  }}
559
559
  />
560
560
  );
561
+ await waitUntilExit();
561
562
  }
562
563
  );
563
564
 
@@ -579,7 +580,21 @@ export async function main(argv: string[]): Promise<void> {
579
580
  describe: "name to use when uploading",
580
581
  type: "string",
581
582
  })
582
- .option("public", {
583
+ .option("compatibility-date", {
584
+ describe: "Date to use for compatibility checks",
585
+ type: "string",
586
+ })
587
+ .option("compatibility-flags", {
588
+ describe: "Flags to use for compatibility checks",
589
+ type: "array",
590
+ alias: "compatibility-flag",
591
+ })
592
+ .option("latest", {
593
+ describe: "Use the latest version of the worker runtime",
594
+ type: "boolean",
595
+ default: false,
596
+ })
597
+ .option("experimental-public", {
583
598
  describe: "Static assets to be served",
584
599
  type: "string",
585
600
  })
@@ -618,36 +633,58 @@ export async function main(argv: string[]): Promise<void> {
618
633
  "🚫 Local publishing is not yet supported"
619
634
  );
620
635
  }
636
+
637
+ if (args["experimental-public"]) {
638
+ console.warn(
639
+ "🚨 The --experimental-public field is experimental and will change in the future."
640
+ );
641
+ }
642
+ if (args.public) {
643
+ throw new Error(
644
+ "🚨 The --public field has been renamed to --experimental-public, and will change behaviour in the future."
645
+ );
646
+ }
647
+
621
648
  const config = args.config as Config;
622
649
 
623
- // -- snip, extract --
650
+ if (args.latest) {
651
+ console.warn(
652
+ "⚠️ Using the latest version of the Workers runtime. To silence this warning, please choose a specific version of the runtime with --compatibility-date, or add a compatibility_date to your wrangler.toml.\n"
653
+ );
654
+ }
655
+
624
656
  if (!args.local) {
657
+ // -- snip, extract --
625
658
  const loggedIn = await loginOrRefreshIfRequired();
626
659
  if (!loggedIn) {
627
660
  // didn't login, let's just quit
628
661
  console.log("Did not login, quitting...");
629
662
  return;
630
663
  }
664
+
631
665
  if (!config.account_id) {
632
666
  config.account_id = await getAccountId();
633
667
  if (!config.account_id) {
634
668
  throw new Error("No account id found, quitting...");
635
669
  }
636
670
  }
671
+ // -- snip, end --
637
672
  }
638
673
 
639
- // -- snip, end --
640
-
641
674
  await publish({
642
675
  config: args.config as Config,
643
676
  name: args.name,
644
677
  script: args.script,
645
678
  env: args.env,
679
+ compatibilityDate: args.latest
680
+ ? new Date().toISOString().substring(0, 10)
681
+ : args["compatibility-date"],
682
+ compatibilityFlags: args["compatibility-flags"] as string[],
646
683
  triggers: args.triggers,
647
684
  jsxFactory: args["jsx-factory"],
648
685
  jsxFragment: args["jsx-fragment"],
649
686
  routes: args.routes,
650
- public: args.public,
687
+ public: args["experimental-public"],
651
688
  site: args.site,
652
689
  });
653
690
  }
@@ -698,6 +735,12 @@ export async function main(argv: string[]): Promise<void> {
698
735
  // TODO: filter by client ip, which can be 'self' or an ip address
699
736
  },
700
737
  async (args) => {
738
+ if (args.local) {
739
+ throw new NotImplementedError(
740
+ `local mode is not yet supported for this command`
741
+ );
742
+ }
743
+
701
744
  const config = args.config as Config;
702
745
 
703
746
  if (!(args.name || config.name)) {
@@ -708,22 +751,19 @@ export async function main(argv: string[]): Promise<void> {
708
751
  }`;
709
752
 
710
753
  // -- snip, extract --
754
+ const loggedIn = await loginOrRefreshIfRequired();
755
+ if (!loggedIn) {
756
+ // didn't login, let's just quit
757
+ console.log("Did not login, quitting...");
758
+ return;
759
+ }
711
760
 
712
- if (!args.local) {
713
- const loggedIn = await loginOrRefreshIfRequired();
714
- if (!loggedIn) {
715
- // didn't login, let's just quit
716
- console.log("Did not login, quitting...");
717
- return;
718
- }
761
+ if (!config.account_id) {
762
+ config.account_id = await getAccountId();
719
763
  if (!config.account_id) {
720
- config.account_id = await getAccountId();
721
- if (!config.account_id) {
722
- throw new Error("No account id found, quitting...");
723
- }
764
+ throw new Error("No account id found, quitting...");
724
765
  }
725
766
  }
726
-
727
767
  // -- snip, end --
728
768
 
729
769
  const accountId = config.account_id;
@@ -836,7 +876,7 @@ export async function main(argv: string[]): Promise<void> {
836
876
  throw new Error("missing zone id");
837
877
  }
838
878
 
839
- console.log(await cfetch(`/zones/${zone}/workers/routes`));
879
+ console.log(await fetchResult(`/zones/${zone}/workers/routes`));
840
880
  }
841
881
  )
842
882
  .command(
@@ -866,7 +906,7 @@ export async function main(argv: string[]): Promise<void> {
866
906
  }
867
907
 
868
908
  console.log(
869
- await cfetch(`/zones/${zone}/workers/routes/${args.id}`, {
909
+ await fetchResult(`/zones/${zone}/workers/routes/${args.id}`, {
870
910
  method: "DELETE",
871
911
  })
872
912
  );
@@ -929,31 +969,30 @@ export async function main(argv: string[]): Promise<void> {
929
969
  throw new Error("Missing script name");
930
970
  }
931
971
 
932
- // -- snip, extract --
933
-
934
972
  if (!args.local) {
973
+ // -- snip, extract --
935
974
  const loggedIn = await loginOrRefreshIfRequired();
936
975
  if (!loggedIn) {
937
976
  // didn't login, let's just quit
938
977
  console.log("Did not login, quitting...");
939
978
  return;
940
979
  }
980
+
941
981
  if (!config.account_id) {
942
982
  config.account_id = await getAccountId();
943
983
  if (!config.account_id) {
944
984
  throw new Error("No account id found, quitting...");
945
985
  }
946
986
  }
987
+ // -- snip, end --
947
988
  }
948
989
 
949
- // -- snip, end --
950
-
951
990
  const secretValue = await prompt(
952
991
  "Enter a secret value:",
953
992
  "password"
954
993
  );
955
994
  async function submitSecret() {
956
- return await cfetch(
995
+ return await fetchResult(
957
996
  `/accounts/${config.account_id}/workers/scripts/${scriptName}/secrets/`,
958
997
  {
959
998
  method: "PUT",
@@ -972,7 +1011,7 @@ export async function main(argv: string[]): Promise<void> {
972
1011
  } catch (e) {
973
1012
  if (e.code === 10007) {
974
1013
  // upload a draft worker
975
- await cfetch(
1014
+ await fetchResult(
976
1015
  `/accounts/${config.account_id}/workers/scripts/${scriptName}`,
977
1016
  {
978
1017
  method: "PUT",
@@ -1033,32 +1072,31 @@ export async function main(argv: string[]): Promise<void> {
1033
1072
  throw new Error("Missing script name");
1034
1073
  }
1035
1074
 
1036
- // -- snip, extract --
1037
-
1038
1075
  if (!args.local) {
1076
+ // -- snip, extract --
1039
1077
  const loggedIn = await loginOrRefreshIfRequired();
1040
1078
  if (!loggedIn) {
1041
1079
  // didn't login, let's just quit
1042
1080
  console.log("Did not login, quitting...");
1043
1081
  return;
1044
1082
  }
1083
+
1045
1084
  if (!config.account_id) {
1046
1085
  config.account_id = await getAccountId();
1047
1086
  if (!config.account_id) {
1048
1087
  throw new Error("No account id found, quitting...");
1049
1088
  }
1050
1089
  }
1090
+ // -- snip, end --
1051
1091
  }
1052
1092
 
1053
- // -- snip, end --
1054
-
1055
1093
  if (await confirm("Are you sure you want to delete this secret?")) {
1056
1094
  console.log(
1057
1095
  `Deleting the secret ${args.key} on script ${scriptName}.`
1058
1096
  );
1059
1097
 
1060
1098
  console.log(
1061
- await cfetch(
1099
+ await fetchResult(
1062
1100
  `/accounts/${config.account_id}/workers/scripts/${scriptName}/secrets/${args.key}`,
1063
1101
  { method: "DELETE" }
1064
1102
  )
@@ -1095,27 +1133,26 @@ export async function main(argv: string[]): Promise<void> {
1095
1133
  throw new Error("Missing script name");
1096
1134
  }
1097
1135
 
1098
- // -- snip, extract --
1099
-
1100
1136
  if (!args.local) {
1137
+ // -- snip, extract --
1101
1138
  const loggedIn = await loginOrRefreshIfRequired();
1102
1139
  if (!loggedIn) {
1103
1140
  // didn't login, let's just quit
1104
1141
  console.log("Did not login, quitting...");
1105
1142
  return;
1106
1143
  }
1144
+
1107
1145
  if (!config.account_id) {
1108
1146
  config.account_id = await getAccountId();
1109
1147
  if (!config.account_id) {
1110
1148
  throw new Error("No account id found, quitting...");
1111
1149
  }
1112
1150
  }
1151
+ // -- snip, end --
1113
1152
  }
1114
1153
 
1115
- // -- snip, end --
1116
-
1117
1154
  console.log(
1118
- await cfetch(
1155
+ await fetchResult(
1119
1156
  `/accounts/${config.account_id}/workers/scripts/${scriptName}/secrets`
1120
1157
  )
1121
1158
  );
@@ -1139,6 +1176,7 @@ export async function main(argv: string[]): Promise<void> {
1139
1176
  .positional("namespace", {
1140
1177
  describe: "The name of the new namespace",
1141
1178
  type: "string",
1179
+ demandOption: true,
1142
1180
  })
1143
1181
  .option("env", {
1144
1182
  type: "string",
@@ -1184,44 +1222,43 @@ export async function main(argv: string[]): Promise<void> {
1184
1222
  });
1185
1223
  await mf.getKVNamespace(title); // this should "create" the namespace
1186
1224
  console.log(`✨ Success! Created KV namespace ${title}`);
1187
- return;
1188
- }
1189
-
1190
- // -- snip, extract --
1191
-
1192
- if (!args.local) {
1225
+ } else {
1226
+ // -- snip, extract --
1193
1227
  const loggedIn = await loginOrRefreshIfRequired();
1194
1228
  if (!loggedIn) {
1195
1229
  // didn't login, let's just quit
1196
1230
  console.log("Did not login, quitting...");
1197
1231
  return;
1198
1232
  }
1233
+
1199
1234
  if (!config.account_id) {
1200
1235
  config.account_id = await getAccountId();
1201
1236
  if (!config.account_id) {
1202
1237
  throw new Error("No account id found, quitting...");
1203
1238
  }
1204
1239
  }
1205
- }
1240
+ // -- snip, end --
1206
1241
 
1207
- // -- snip, end --
1242
+ // TODO: generate a binding name stripping non alphanumeric chars
1208
1243
 
1209
- // TODO: generate a binding name stripping non alphanumeric chars
1210
-
1211
- console.log(`🌀 Creating namespace with title "${title}"`);
1212
- const namespaceId = await createNamespace(config.account_id, title);
1244
+ console.log(`🌀 Creating namespace with title "${title}"`);
1245
+ const namespaceId = await createNamespace(
1246
+ config.account_id,
1247
+ title
1248
+ );
1213
1249
 
1214
- console.log("✨ Success!");
1215
- const envString = args.env ? ` under [env.${args.env}]` : "";
1216
- const previewString = args.preview ? "preview_" : "";
1217
- console.log(
1218
- `Add the following to your configuration file in your kv_namespaces array${envString}:`
1219
- );
1220
- console.log(
1221
- `{ binding = "${args.namespace}", ${previewString}id = "${namespaceId}" }`
1222
- );
1250
+ console.log("✨ Success!");
1251
+ const envString = args.env ? ` under [env.${args.env}]` : "";
1252
+ const previewString = args.preview ? "preview_" : "";
1253
+ console.log(
1254
+ `Add the following to your configuration file in your kv_namespaces array${envString}:`
1255
+ );
1256
+ console.log(
1257
+ `{ binding = "${args.namespace}", ${previewString}id = "${namespaceId}" }`
1258
+ );
1223
1259
 
1224
- // TODO: automatically write this block to the wrangler.toml config file??
1260
+ // TODO: automatically write this block to the wrangler.toml config file??
1261
+ }
1225
1262
  }
1226
1263
  )
1227
1264
  .command(
@@ -1229,42 +1266,39 @@ export async function main(argv: string[]): Promise<void> {
1229
1266
  "Outputs a list of all KV namespaces associated with your account id.",
1230
1267
  {},
1231
1268
  async (args) => {
1269
+ const config = args.config as Config;
1270
+
1232
1271
  if (args.local) {
1233
1272
  throw new NotImplementedError(
1234
1273
  `local mode is not yet supported for this command`
1235
1274
  );
1236
- }
1237
-
1238
- const config = args.config as Config;
1239
-
1240
- // -- snip, extract --
1241
-
1242
- if (!args.local) {
1275
+ } else {
1276
+ // -- snip, extract --
1243
1277
  const loggedIn = await loginOrRefreshIfRequired();
1244
1278
  if (!loggedIn) {
1245
1279
  // didn't login, let's just quit
1246
1280
  console.log("Did not login, quitting...");
1247
1281
  return;
1248
1282
  }
1283
+
1249
1284
  if (!config.account_id) {
1250
1285
  config.account_id = await getAccountId();
1251
1286
  if (!config.account_id) {
1252
1287
  throw new Error("No account id found, quitting...");
1253
1288
  }
1254
1289
  }
1255
- }
1290
+ // -- snip, end --
1256
1291
 
1257
- // -- snip, end --
1292
+ // TODO: we should show bindings if they exist for given ids
1258
1293
 
1259
- // TODO: we should show bindings if they exist for given ids
1260
-
1261
- console.log(
1262
- JSON.stringify(
1263
- await listNamespaces(config.account_id),
1264
- null,
1265
- " "
1266
- )
1267
- );
1294
+ console.log(
1295
+ JSON.stringify(
1296
+ await listNamespaces(config.account_id),
1297
+ null,
1298
+ " "
1299
+ )
1300
+ );
1301
+ }
1268
1302
  }
1269
1303
  )
1270
1304
  .command(
@@ -1291,31 +1325,23 @@ export async function main(argv: string[]): Promise<void> {
1291
1325
  });
1292
1326
  },
1293
1327
  async (args) => {
1328
+ const config = args.config as Config;
1329
+
1294
1330
  if (args.local) {
1295
1331
  throw new NotImplementedError(
1296
1332
  `local mode is not yet supported for this command`
1297
1333
  );
1298
- }
1299
- const config = args.config as Config;
1300
-
1301
- const id =
1302
- args["namespace-id"] ||
1303
- (args.env
1304
- ? config.env[args.env] || {}
1305
- : config
1306
- ).kv_namespaces.find(
1307
- (namespace) => namespace.binding === args.binding
1308
- )?.[args.preview ? "preview_id" : "id"];
1309
- if (!id) {
1310
- throw new CommandLineArgsError(
1311
- "Not able to delete namespace.\n" +
1312
- `A namespace with binding name "${args.binding}" was not found in the configured "kv_namespaces".`
1313
- );
1314
- }
1315
-
1316
- // -- snip, extract --
1334
+ } else {
1335
+ let id;
1336
+ try {
1337
+ id = getNamespaceId(args);
1338
+ } catch (e) {
1339
+ throw new CommandLineArgsError(
1340
+ "Not able to delete namespace.\n" + e.message
1341
+ );
1342
+ }
1317
1343
 
1318
- if (!args.local) {
1344
+ // -- snip, extract --
1319
1345
  const loggedIn = await loginOrRefreshIfRequired();
1320
1346
  if (!loggedIn) {
1321
1347
  // didn't login, let's just quit
@@ -1329,32 +1355,31 @@ export async function main(argv: string[]): Promise<void> {
1329
1355
  throw new Error("No account id found, quitting...");
1330
1356
  }
1331
1357
  }
1332
- }
1333
-
1334
- // -- snip, end --
1358
+ // -- snip, end --
1335
1359
 
1336
- await cfetch<{ id: string }>(
1337
- `/accounts/${config.account_id}/storage/kv/namespaces/${id}`,
1338
- { method: "DELETE" }
1339
- );
1360
+ await fetchResult<{ id: string }>(
1361
+ `/accounts/${config.account_id}/storage/kv/namespaces/${id}`,
1362
+ { method: "DELETE" }
1363
+ );
1340
1364
 
1341
- // TODO: recommend they remove it from wrangler.toml
1365
+ // TODO: recommend they remove it from wrangler.toml
1342
1366
 
1343
- // test-mf wrangler kv:namespace delete --namespace-id 2a7d3d8b23fc4159b5afa489d6cfd388
1344
- // Are you sure you want to delete namespace 2a7d3d8b23fc4159b5afa489d6cfd388? [y/n]
1345
- // n
1346
- // 💁 Not deleting namespace 2a7d3d8b23fc4159b5afa489d6cfd388
1347
- // ➜ test-mf wrangler kv:namespace delete --namespace-id 2a7d3d8b23fc4159b5afa489d6cfd388
1348
- // Are you sure you want to delete namespace 2a7d3d8b23fc4159b5afa489d6cfd388? [y/n]
1349
- // y
1350
- // 🌀 Deleting namespace 2a7d3d8b23fc4159b5afa489d6cfd388
1351
- // ✨ Success
1352
- // ⚠️ Make sure to remove this "kv-namespace" entry from your configuration file!
1353
- // ➜ test-mf
1367
+ // test-mf wrangler kv:namespace delete --namespace-id 2a7d3d8b23fc4159b5afa489d6cfd388
1368
+ // Are you sure you want to delete namespace 2a7d3d8b23fc4159b5afa489d6cfd388? [y/n]
1369
+ // n
1370
+ // 💁 Not deleting namespace 2a7d3d8b23fc4159b5afa489d6cfd388
1371
+ // ➜ test-mf wrangler kv:namespace delete --namespace-id 2a7d3d8b23fc4159b5afa489d6cfd388
1372
+ // Are you sure you want to delete namespace 2a7d3d8b23fc4159b5afa489d6cfd388? [y/n]
1373
+ // y
1374
+ // 🌀 Deleting namespace 2a7d3d8b23fc4159b5afa489d6cfd388
1375
+ // ✨ Success
1376
+ // ⚠️ Make sure to remove this "kv-namespace" entry from your configuration file!
1377
+ // ➜ test-mf
1354
1378
 
1355
- // TODO: do it automatically
1379
+ // TODO: do it automatically
1356
1380
 
1357
- // TODO: delete the preview namespace as well?
1381
+ // TODO: delete the preview namespace as well?
1382
+ }
1358
1383
  }
1359
1384
  );
1360
1385
  }
@@ -1374,6 +1399,7 @@ export async function main(argv: string[]): Promise<void> {
1374
1399
  .positional("key", {
1375
1400
  type: "string",
1376
1401
  describe: "The key to write to.",
1402
+ demandOption: true,
1377
1403
  })
1378
1404
  .positional("value", {
1379
1405
  type: "string",
@@ -1413,9 +1439,11 @@ export async function main(argv: string[]): Promise<void> {
1413
1439
  },
1414
1440
  async ({ key, ttl, expiration, ...args }) => {
1415
1441
  const namespaceId = getNamespaceId(args);
1442
+ // One of `args.path` and `args.value` must be defined
1416
1443
  const value = args.path
1417
1444
  ? await readFile(args.path, "utf-8")
1418
- : args.value;
1445
+ : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1446
+ args.value!;
1419
1447
  const config = args.config as Config;
1420
1448
 
1421
1449
  if (args.path) {
@@ -1437,11 +1465,8 @@ export async function main(argv: string[]): Promise<void> {
1437
1465
  });
1438
1466
  const ns = await mf.getKVNamespace(namespaceId);
1439
1467
  await ns.put(key, value, { expiration, expirationTtl: ttl });
1440
- return;
1441
- }
1442
- // -- snip, extract --
1443
-
1444
- if (!args.local) {
1468
+ } else {
1469
+ // -- snip, extract --
1445
1470
  const loggedIn = await loginOrRefreshIfRequired();
1446
1471
  if (!loggedIn) {
1447
1472
  // didn't login, let's just quit
@@ -1455,14 +1480,13 @@ export async function main(argv: string[]): Promise<void> {
1455
1480
  throw new Error("No account id found, quitting...");
1456
1481
  }
1457
1482
  }
1458
- }
1459
-
1460
- // -- snip, end --
1483
+ // -- snip, end --
1461
1484
 
1462
- await putKeyValue(config.account_id, namespaceId, key, value, {
1463
- expiration,
1464
- expiration_ttl: ttl,
1465
- });
1485
+ await putKeyValue(config.account_id, namespaceId, key, value, {
1486
+ expiration,
1487
+ expiration_ttl: ttl,
1488
+ });
1489
+ }
1466
1490
  }
1467
1491
  )
1468
1492
  .command(
@@ -1483,6 +1507,12 @@ export async function main(argv: string[]): Promise<void> {
1483
1507
  type: "string",
1484
1508
  describe: "Perform on a specific environment",
1485
1509
  })
1510
+ .option("preview", {
1511
+ type: "boolean",
1512
+ // In the case of listing keys we will default to non-preview mode
1513
+ default: false,
1514
+ describe: "Interact with a preview namespace",
1515
+ })
1486
1516
  .option("prefix", {
1487
1517
  type: "string",
1488
1518
  describe: "A prefix to filter listed keys",
@@ -1504,12 +1534,8 @@ export async function main(argv: string[]): Promise<void> {
1504
1534
  const ns = await mf.getKVNamespace(namespaceId);
1505
1535
  const listResponse = await ns.list({ prefix });
1506
1536
  console.log(JSON.stringify(listResponse.keys, null, " ")); // TODO: paginate, collate
1507
- return;
1508
- }
1509
-
1510
- // -- snip, extract --
1511
-
1512
- if (!args.local) {
1537
+ } else {
1538
+ // -- snip, extract --
1513
1539
  const loggedIn = await loginOrRefreshIfRequired();
1514
1540
  if (!loggedIn) {
1515
1541
  // didn't login, let's just quit
@@ -1523,13 +1549,12 @@ export async function main(argv: string[]): Promise<void> {
1523
1549
  throw new Error("No account id found, quitting...");
1524
1550
  }
1525
1551
  }
1526
- }
1552
+ // -- snip, end --
1527
1553
 
1528
- // -- snip, end --
1529
-
1530
- console.log(
1531
- await listNamespaceKeys(config.account_id, namespaceId, prefix)
1532
- );
1554
+ console.log(
1555
+ await listNamespaceKeys(config.account_id, namespaceId, prefix)
1556
+ );
1557
+ }
1533
1558
  }
1534
1559
  )
1535
1560
  .command(
@@ -1540,6 +1565,7 @@ export async function main(argv: string[]): Promise<void> {
1540
1565
  .positional("key", {
1541
1566
  describe: "The key value to get.",
1542
1567
  type: "string",
1568
+ demandOption: true,
1543
1569
  })
1544
1570
  .option("binding", {
1545
1571
  type: "string",
@@ -1557,6 +1583,12 @@ export async function main(argv: string[]): Promise<void> {
1557
1583
  .option("preview", {
1558
1584
  type: "boolean",
1559
1585
  describe: "Interact with a preview namespace",
1586
+ })
1587
+ .option("preview", {
1588
+ type: "boolean",
1589
+ // In the case of getting key values we will default to non-preview mode
1590
+ default: false,
1591
+ describe: "Interact with a preview namespace",
1560
1592
  });
1561
1593
  },
1562
1594
  async ({ key, ...args }) => {
@@ -1575,9 +1607,8 @@ export async function main(argv: string[]): Promise<void> {
1575
1607
  return;
1576
1608
  }
1577
1609
 
1578
- // -- snip, extract --
1579
-
1580
1610
  if (!args.local) {
1611
+ // -- snip, extract --
1581
1612
  const loggedIn = await loginOrRefreshIfRequired();
1582
1613
  if (!loggedIn) {
1583
1614
  // didn't login, let's just quit
@@ -1591,16 +1622,11 @@ export async function main(argv: string[]): Promise<void> {
1591
1622
  throw new Error("No account id found, quitting...");
1592
1623
  }
1593
1624
  }
1625
+ // -- snip, end --
1594
1626
  }
1595
1627
 
1596
- // -- snip, end --
1597
-
1598
- // annoyingly, the API for this one doesn't return the
1599
- // data in the 'standard' format. goddammit.
1600
- // That's why we have the fallthrough response in cfetch.
1601
- // Oh well.
1602
1628
  console.log(
1603
- await cfetch(
1629
+ await fetchRaw(
1604
1630
  `/accounts/${config.account_id}/storage/kv/namespaces/${namespaceId}/values/${key}`
1605
1631
  )
1606
1632
  );
@@ -1614,6 +1640,7 @@ export async function main(argv: string[]): Promise<void> {
1614
1640
  .positional("key", {
1615
1641
  describe: "The key value to delete",
1616
1642
  type: "string",
1643
+ demandOption: true,
1617
1644
  })
1618
1645
  .option("binding", {
1619
1646
  type: "string",
@@ -1654,9 +1681,8 @@ export async function main(argv: string[]): Promise<void> {
1654
1681
 
1655
1682
  const config = args.config as Config;
1656
1683
 
1657
- // -- snip, extract --
1658
-
1659
1684
  if (!args.local) {
1685
+ // -- snip, extract --
1660
1686
  const loggedIn = await loginOrRefreshIfRequired();
1661
1687
  if (!loggedIn) {
1662
1688
  // didn't login, let's just quit
@@ -1670,11 +1696,10 @@ export async function main(argv: string[]): Promise<void> {
1670
1696
  throw new Error("No account id found, quitting...");
1671
1697
  }
1672
1698
  }
1699
+ // -- snip, end --
1673
1700
  }
1674
1701
 
1675
- // -- snip, end --
1676
-
1677
- await cfetch(
1702
+ await fetchResult(
1678
1703
  `/accounts/${config.account_id}/storage/kv/namespaces/${namespaceId}/values/${key}`,
1679
1704
  { method: "DELETE" }
1680
1705
  );
@@ -1697,6 +1722,7 @@ export async function main(argv: string[]): Promise<void> {
1697
1722
  .positional("filename", {
1698
1723
  describe: `The JSON file of key-value pairs to upload, in form [{"key":..., "value":...}"...]`,
1699
1724
  type: "string",
1725
+ demandOption: true,
1700
1726
  })
1701
1727
  .option("binding", {
1702
1728
  type: "string",
@@ -1752,13 +1778,8 @@ export async function main(argv: string[]): Promise<void> {
1752
1778
  expirationTtl: expiration_ttl,
1753
1779
  });
1754
1780
  }
1755
-
1756
- return;
1757
- }
1758
-
1759
- // -- snip, extract --
1760
-
1761
- if (!args.local) {
1781
+ } else {
1782
+ // -- snip, extract --
1762
1783
  const loggedIn = await loginOrRefreshIfRequired();
1763
1784
  if (!loggedIn) {
1764
1785
  // didn't login, let's just quit
@@ -1772,23 +1793,23 @@ export async function main(argv: string[]): Promise<void> {
1772
1793
  throw new Error("No account id found, quitting...");
1773
1794
  }
1774
1795
  }
1775
- }
1776
-
1777
- // -- snip, end --
1796
+ // -- snip, end --
1778
1797
 
1779
- console.log(
1780
- await putBulkKeyValue(config.account_id, namespaceId, content)
1781
- );
1798
+ console.log(
1799
+ await putBulkKeyValue(config.account_id, namespaceId, content)
1800
+ );
1801
+ }
1782
1802
  }
1783
1803
  )
1784
1804
  .command(
1785
1805
  "delete <filename>",
1786
- "Upload multiple key-value pairs to a namespace",
1806
+ "Delete multiple key-value pairs from a namespace",
1787
1807
  (yargs) => {
1788
1808
  return yargs
1789
1809
  .positional("filename", {
1790
- describe: `The JSON file of key-value pairs to upload, in form ["key1", "key2", ...]`,
1810
+ describe: `The JSON file of keys to delete, in the form ["key1", "key2", ...]`,
1791
1811
  type: "string",
1812
+ demandOption: true,
1792
1813
  })
1793
1814
  .option("binding", {
1794
1815
  type: "string",
@@ -1832,13 +1853,8 @@ export async function main(argv: string[]): Promise<void> {
1832
1853
  for (const key of parsedContent) {
1833
1854
  await ns.delete(key);
1834
1855
  }
1835
-
1836
- return;
1837
- }
1838
-
1839
- // -- snip, extract --
1840
-
1841
- if (!args.local) {
1856
+ } else {
1857
+ // -- snip, extract --
1842
1858
  const loggedIn = await loginOrRefreshIfRequired();
1843
1859
  if (!loggedIn) {
1844
1860
  // didn't login, let's just quit
@@ -1852,13 +1868,16 @@ export async function main(argv: string[]): Promise<void> {
1852
1868
  throw new Error("No account id found, quitting...");
1853
1869
  }
1854
1870
  }
1855
- }
1871
+ // -- snip, end --
1856
1872
 
1857
- // -- snip, end --
1858
-
1859
- console.log(
1860
- await deleteBulkKeyValue(config.account_id, namespaceId, content)
1861
- );
1873
+ console.log(
1874
+ await deleteBulkKeyValue(
1875
+ config.account_id,
1876
+ namespaceId,
1877
+ content
1878
+ )
1879
+ );
1880
+ }
1862
1881
  }
1863
1882
  );
1864
1883
  }
@@ -1895,8 +1914,15 @@ export async function main(argv: string[]): Promise<void> {
1895
1914
  if (e instanceof CommandLineArgsError) {
1896
1915
  wrangler.showHelp("error");
1897
1916
  console.error(""); // Just adds a bit of space
1917
+ console.error(e.message);
1918
+ } else {
1919
+ console.error(e.message);
1920
+ console.error(""); // Just adds a bit of space
1921
+ console.error(
1922
+ `${fgGreenColor}%s${resetColor}`,
1923
+ "If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1924
+ );
1898
1925
  }
1899
- console.error(e.message);
1900
1926
  throw e;
1901
1927
  }
1902
1928
  }