wrangler 0.0.0-e928f94 → 0.0.0-e9a1820

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
@@ -1,10 +1,9 @@
1
1
  import React from "react";
2
2
  import { render } from "ink";
3
- import { Dev } from "./dev";
3
+ import Dev from "./dev";
4
4
  import { readFile } from "node:fs/promises";
5
5
  import makeCLI from "yargs";
6
- import { hideBin } from "yargs/helpers";
7
- import type yargs from "yargs";
6
+ import type Yargs from "yargs";
8
7
  import { findUp } from "find-up";
9
8
  import TOML from "@iarna/toml";
10
9
  import type { Config } from "./config";
@@ -25,6 +24,8 @@ import {
25
24
  putKeyValue,
26
25
  putBulkKeyValue,
27
26
  deleteBulkKeyValue,
27
+ createNamespace,
28
+ isValidNamespaceBinding,
28
29
  } from "./kv";
29
30
 
30
31
  import { pages } from "./pages";
@@ -42,15 +43,15 @@ import { setTimeout } from "node:timers/promises";
42
43
  import * as fs from "node:fs";
43
44
  import { execa } from "execa";
44
45
 
45
- async function readConfig(path?: string): Promise<Config> {
46
+ async function readConfig(configPath?: string): Promise<Config> {
46
47
  const config: Config = {};
47
- if (!path) {
48
- path = await findUp("wrangler.toml");
48
+ if (!configPath) {
49
+ configPath = await findUp("wrangler.toml");
49
50
  // TODO - terminate this early instead of going all the way to the root
50
51
  }
51
52
 
52
- if (path) {
53
- const tml: string = await readFile(path, "utf-8");
53
+ if (configPath) {
54
+ const tml: string = await readFile(configPath, "utf-8");
54
55
  const parsed = TOML.parse(tml) as Config;
55
56
  Object.assign(config, parsed);
56
57
  }
@@ -79,13 +80,18 @@ async function readConfig(path?: string): Promise<Config> {
79
80
  });
80
81
  });
81
82
 
82
- const mirroredFields = ["vars", "kv_namespaces", "durable_objects"];
83
+ const mirroredFields = [
84
+ "vars",
85
+ "kv_namespaces",
86
+ "durable_objects",
87
+ "experimental_services",
88
+ ];
83
89
  Object.keys(config.env || {}).forEach((env) => {
84
90
  mirroredFields.forEach((field) => {
85
- // if it exists on top level, it should exist on env defns
91
+ // if it exists on top level, it should exist on env definitions
86
92
  Object.keys(config[field] || {}).forEach((fieldKey) => {
87
93
  if (!(fieldKey in config.env[env][field])) {
88
- console.error(
94
+ console.warn(
89
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}".`
90
96
  );
91
97
  }
@@ -93,11 +99,17 @@ async function readConfig(path?: string): Promise<Config> {
93
99
  });
94
100
  });
95
101
 
102
+ if ("experimental_services" in config) {
103
+ console.warn(
104
+ "The experimental_services field is only for cloudflare internal usage right now, and is subject to change. Please do not use this on production projects"
105
+ );
106
+ }
107
+
96
108
  // todo: validate, add defaults
97
109
  // let's just do some basics for now
98
110
 
99
111
  // @ts-expect-error we're being sneaky here for now
100
- config.__path__ = path;
112
+ config.__path__ = configPath;
101
113
 
102
114
  return config;
103
115
  }
@@ -105,18 +117,18 @@ async function readConfig(path?: string): Promise<Config> {
105
117
  // a helper to demand one of a set of options
106
118
  // via https://github.com/yargs/yargs/issues/1093#issuecomment-491299261
107
119
  function demandOneOfOption(...options: string[]) {
108
- return function (argv: yargs.Arguments) {
120
+ return function (argv: Yargs.Arguments) {
109
121
  const count = options.filter((option) => argv[option]).length;
110
122
  const lastOption = options.pop();
111
123
 
112
124
  if (count === 0) {
113
- throw new Error(
125
+ throw new CommandLineArgsError(
114
126
  `Exactly one of the arguments ${options.join(
115
127
  ", "
116
128
  )} and ${lastOption} is required`
117
129
  );
118
130
  } else if (count > 1) {
119
- throw new Error(
131
+ throw new CommandLineArgsError(
120
132
  `Arguments ${options.join(
121
133
  ", "
122
134
  )} and ${lastOption} are mutually exclusive`
@@ -127,30 +139,49 @@ function demandOneOfOption(...options: string[]) {
127
139
  };
128
140
  }
129
141
 
142
+ class CommandLineArgsError extends Error {}
143
+ class DeprecationError extends Error {}
144
+ class NotImplementedError extends Error {}
145
+
130
146
  export async function main(argv: string[]): Promise<void> {
131
- const yargs = makeCLI(hideBin(process.argv))
132
- .command(
133
- // the default is to simply print the help menu
134
- ["*"],
135
- false,
136
- () => {},
137
- () => {
138
- yargs.showHelp("log");
147
+ const wrangler = makeCLI(argv)
148
+ // We handle errors ourselves in a try-catch around `yargs.parse`.
149
+ // If you want the "help info" to be displayed then throw an instance of `CommandLineArgsError`.
150
+ // Otherwise we just log the error that was thrown without any "help info".
151
+ .showHelpOnFail(false)
152
+ .fail((msg, error) => {
153
+ if (!error) {
154
+ // If there is only a `msg` then this came from yargs own validation, so wrap in a `CommandLineArgsError`.
155
+ error = new CommandLineArgsError(msg);
139
156
  }
140
- )
157
+ throw error;
158
+ })
141
159
  .scriptName("wrangler")
142
160
  .wrap(null);
143
161
 
144
- // you will note that we use the form for all commands where we use the builder function
145
- // to define options and subcommands. Further we return the result of this builder even
146
- // tho it's not completely necessary. The reason is that it's required for type inference
147
- // of the args in the handle function.I wish we could enforce this pattern, but this
148
- // comment will have to do for now.
162
+ // the default is to simply print the help menu
163
+ wrangler.command(
164
+ ["*"],
165
+ false,
166
+ () => {},
167
+ (args) => {
168
+ if (args._.length > 0) {
169
+ throw new CommandLineArgsError(`Unknown command: ${args._}.`);
170
+ } else {
171
+ wrangler.showHelp("log");
172
+ }
173
+ }
174
+ );
149
175
 
150
- // also annoying that choices[] doesn't get inferred as an enum. bleh.
176
+ // You will note that we use the form for all commands where we use the builder function
177
+ // to define options and subcommands.
178
+ // Further we return the result of this builder even though it's not completely necessary.
179
+ // The reason is that it's required for type inference of the args in the handle function.
180
+ // I wish we could enforce this pattern, but this comment will have to do for now.
181
+ // (It's also annoying that choices[] doesn't get inferred as an enum. 🤷‍♂.)
151
182
 
152
183
  // [DEPRECATED] generate
153
- yargs.command(
184
+ wrangler.command(
154
185
  // we definitely want to move away from us cloning github templates
155
186
  // we can do something better here, let's see
156
187
  "generate [name] [template]",
@@ -168,14 +199,14 @@ export async function main(argv: string[]): Promise<void> {
168
199
  },
169
200
  () => {
170
201
  // "👯 [DEPRECATED]. Scaffold a Cloudflare Workers project from a public GitHub repository.",
171
- console.error(
202
+ throw new DeprecationError(
172
203
  "`wrangler generate` has been deprecated, please refer to TODO://some/path for alternatives"
173
204
  );
174
205
  }
175
206
  );
176
207
 
177
208
  // init
178
- yargs.command(
209
+ wrangler.command(
179
210
  "init [name]",
180
211
  "📥 Create a wrangler.toml configuration file",
181
212
  (yargs) => {
@@ -184,26 +215,42 @@ export async function main(argv: string[]): Promise<void> {
184
215
  type: "string",
185
216
  });
186
217
  },
187
- async () => {
218
+ async (args) => {
219
+ if ("type" in args) {
220
+ let message = "The --type option is no longer supported.";
221
+ if (args.type === "webpack") {
222
+ message +=
223
+ "\nIf you wish to use webpack then you will need to create a custom build.";
224
+ // TODO: Add a link to docs
225
+ }
226
+ throw new CommandLineArgsError(message);
227
+ }
228
+
188
229
  const destination = path.join(process.cwd(), "wrangler.toml");
189
230
  if (fs.existsSync(destination)) {
190
- console.error(`${destination} already exists.`);
191
- } else {
192
- const compatibilityDate = new Date().toISOString().substring(0, 10);
193
- try {
194
- await writeFile(
195
- destination,
196
- `compatibility_date = "${compatibilityDate}"` + "\n"
197
- );
198
- console.log(`✨ Succesfully created wrangler.toml`);
199
- // TODO: suggest next steps?
200
- } catch (err) {
201
- console.error(`Failed to create wrangler.toml`);
202
- console.error(err);
203
- throw err;
231
+ console.warn(`${destination} file already exists!`);
232
+ const result = await confirm(
233
+ "Do you want to continue initializing this project?"
234
+ );
235
+ if (!result) {
236
+ return;
204
237
  }
205
238
  }
206
239
 
240
+ const compatibilityDate = new Date().toISOString().substring(0, 10);
241
+ try {
242
+ await writeFile(
243
+ destination,
244
+ `compatibility_date = "${compatibilityDate}"` + "\n"
245
+ );
246
+ console.log(`✨ Successfully created wrangler.toml`);
247
+ // TODO: suggest next steps?
248
+ } catch (err) {
249
+ throw new Error(
250
+ `Failed to create wrangler.toml.\n${err.message ?? err}`
251
+ );
252
+ }
253
+
207
254
  // if no package.json, ask, and if yes, create one
208
255
  let pathToPackageJson = await findUp("package.json");
209
256
 
@@ -240,9 +287,17 @@ export async function main(argv: string[]): Promise<void> {
240
287
  JSON.stringify(
241
288
  {
242
289
  compilerOptions: {
243
- target: "ES2020",
244
- module: "CommonJS",
245
- lib: ["ES2020"],
290
+ target: "esnext",
291
+ module: "esnext",
292
+ moduleResolution: "node",
293
+ esModuleInterop: true,
294
+ allowJs: true,
295
+ allowSyntheticDefaultImports: true,
296
+ isolatedModules: true,
297
+ noEmit: true,
298
+ lib: ["esnext"],
299
+ jsx: "react",
300
+ resolveJsonModule: true,
246
301
  types: ["@cloudflare/workers-types"],
247
302
  },
248
303
  },
@@ -265,7 +320,7 @@ export async function main(argv: string[]): Promise<void> {
265
320
  );
266
321
 
267
322
  // build
268
- yargs.command(
323
+ wrangler.command(
269
324
  "build",
270
325
  false,
271
326
  (yargs) => {
@@ -275,14 +330,14 @@ export async function main(argv: string[]): Promise<void> {
275
330
  },
276
331
  () => {
277
332
  // "[DEPRECATED] 🦀 Build your project (if applicable)",
278
- console.error(
333
+ throw new DeprecationError(
279
334
  "`wrangler build` has been deprecated, please refer to TODO://some/path for alternatives"
280
335
  );
281
336
  }
282
337
  );
283
338
 
284
339
  // login
285
- yargs.command(
340
+ wrangler.command(
286
341
  // this needs scopes as an option?
287
342
  "login",
288
343
  false, // we don't need to show this in the menu
@@ -319,13 +374,13 @@ export async function main(argv: string[]): Promise<void> {
319
374
  await login();
320
375
 
321
376
  // TODO: would be nice if it optionally saved login
322
- // creds inside node_modules/.cache or something
377
+ // credentials inside node_modules/.cache or something
323
378
  // this way you could have multiple users on a single machine
324
379
  }
325
380
  );
326
381
 
327
382
  // logout
328
- yargs.command(
383
+ wrangler.command(
329
384
  // this needs scopes as an option?
330
385
  "logout",
331
386
  false, // we don't need to show this in the menu
@@ -337,7 +392,7 @@ export async function main(argv: string[]): Promise<void> {
337
392
  );
338
393
 
339
394
  // whoami
340
- yargs.command(
395
+ wrangler.command(
341
396
  "whoami",
342
397
  false, // we don't need to show this the menu
343
398
  // "🕵️ Retrieve your user info and test your auth config",
@@ -348,20 +403,20 @@ export async function main(argv: string[]): Promise<void> {
348
403
  );
349
404
 
350
405
  // config
351
- yargs.command(
406
+ wrangler.command(
352
407
  "config",
353
408
  false,
354
409
  () => {},
355
410
  () => {
356
411
  // "🕵️ Authenticate Wrangler with a Cloudflare API Token",
357
- console.error(
412
+ throw new DeprecationError(
358
413
  "`wrangler config` has been deprecated, please refer to TODO://some/path for alternatives"
359
414
  );
360
415
  }
361
416
  );
362
417
 
363
418
  // dev
364
- yargs.command(
419
+ wrangler.command(
365
420
  "dev <filename>",
366
421
  "👂 Start a local server for developing your worker",
367
422
  (yargs) => {
@@ -439,8 +494,7 @@ export async function main(argv: string[]): Promise<void> {
439
494
  if (!config.account_id) {
440
495
  config.account_id = await getAccountId();
441
496
  if (!config.account_id) {
442
- console.error("No account id found, quitting...");
443
- return;
497
+ throw new Error("No account id found, quitting...");
444
498
  }
445
499
  }
446
500
  }
@@ -463,7 +517,8 @@ export async function main(argv: string[]): Promise<void> {
463
517
  render(
464
518
  <Dev
465
519
  name={args.name || config.name}
466
- entry={filename}
520
+ entry={path.relative(process.cwd(), filename)}
521
+ buildCommand={config.build || {}}
467
522
  format={format}
468
523
  initialMode={args.local ? "local" : "remote"}
469
524
  jsxFactory={args["jsx-factory"] || envRootObj?.jsx_factory}
@@ -475,27 +530,31 @@ export async function main(argv: string[]): Promise<void> {
475
530
  compatibilityDate={config.compatibility_date}
476
531
  compatibilityFlags={config.compatibility_flags}
477
532
  usageModel={config.usage_model}
478
- variables={{
479
- ...(envRootObj?.vars || {}),
480
- ...(envRootObj?.kv_namespaces || []).reduce(
481
- (obj, { binding, preview_id }) => {
533
+ bindings={{
534
+ kv_namespaces: envRootObj.kv_namespaces?.map(
535
+ ({ binding, preview_id, id: _id }) => {
536
+ // In `dev`, we make folks use a separate kv namespace called
537
+ // `preview_id` instead of `id` so that they don't
538
+ // break production data. So here we check that a `preview_id`
539
+ // has actually been configured.
540
+ // This whole block of code will be obsolted in the future
541
+ // when we have copy-on-write for previews on edge workers.
482
542
  if (!preview_id) {
483
543
  // TODO: This error has to be a _lot_ better, ideally just asking
484
544
  // to create a preview namespace for the user automatically
485
545
  throw new Error(
486
- "kv namespaces need a preview id during dev mode"
487
- );
546
+ `In development, you should use a separate kv namespace than the one you'd use in production. Please create a new kv namespace with "wrangler kv:namespace create <name> --preview" and add its id as preview_id to the kv_namespace "${binding}" in your wrangler.toml`
547
+ ); // Ugh, I really don't like this message very much
488
548
  }
489
- return { ...obj, [binding]: { namespaceId: preview_id } };
490
- },
491
- {}
492
- ),
493
- ...(envRootObj?.durable_objects?.bindings || []).reduce(
494
- (obj, { name, class_name, script_name }) => {
495
- return { ...obj, [name]: { class_name, script_name } };
496
- },
497
- {}
549
+ return {
550
+ binding,
551
+ id: preview_id,
552
+ };
553
+ }
498
554
  ),
555
+ vars: envRootObj.vars,
556
+ durable_objects: envRootObj.durable_objects,
557
+ services: envRootObj.experimental_services,
499
558
  }}
500
559
  />
501
560
  );
@@ -503,7 +562,7 @@ export async function main(argv: string[]): Promise<void> {
503
562
  );
504
563
 
505
564
  // publish
506
- yargs.command(
565
+ wrangler.command(
507
566
  "publish [script]",
508
567
  "🆙 Publish your Worker to Cloudflare.",
509
568
  (yargs) => {
@@ -555,8 +614,9 @@ export async function main(argv: string[]): Promise<void> {
555
614
  },
556
615
  async (args) => {
557
616
  if (args.local) {
558
- console.error("🚫 Local publishing is not yet supported");
559
- return;
617
+ throw new NotImplementedError(
618
+ "🚫 Local publishing is not yet supported"
619
+ );
560
620
  }
561
621
  const config = args.config as Config;
562
622
 
@@ -571,8 +631,7 @@ export async function main(argv: string[]): Promise<void> {
571
631
  if (!config.account_id) {
572
632
  config.account_id = await getAccountId();
573
633
  if (!config.account_id) {
574
- console.error("No account id found, quitting...");
575
- return;
634
+ throw new Error("No account id found, quitting...");
576
635
  }
577
636
  }
578
637
  }
@@ -595,7 +654,7 @@ export async function main(argv: string[]): Promise<void> {
595
654
  );
596
655
 
597
656
  // tail
598
- yargs.command(
657
+ wrangler.command(
599
658
  "tail [name]",
600
659
  "🦚 Starts a log tailing session for a deployed Worker.",
601
660
  (yargs) => {
@@ -642,8 +701,7 @@ export async function main(argv: string[]): Promise<void> {
642
701
  const config = args.config as Config;
643
702
 
644
703
  if (!(args.name || config.name)) {
645
- console.error("Missing script name");
646
- return;
704
+ throw new Error("Missing script name");
647
705
  }
648
706
  const scriptName = `${args.name || config.name}${
649
707
  args.env ? `-${args.env}` : ""
@@ -661,8 +719,7 @@ export async function main(argv: string[]): Promise<void> {
661
719
  if (!config.account_id) {
662
720
  config.account_id = await getAccountId();
663
721
  if (!config.account_id) {
664
- console.error("No account id found, quitting...");
665
- return;
722
+ throw new Error("No account id found, quitting...");
666
723
  }
667
724
  }
668
725
  }
@@ -686,9 +743,9 @@ export async function main(argv: string[]): Promise<void> {
686
743
  `successfully created tail, expires at ${expiration.toLocaleString()}`
687
744
  );
688
745
 
689
- onExit(() => {
746
+ onExit(async () => {
690
747
  tail.terminate();
691
- deleteTail();
748
+ await deleteTail();
692
749
  });
693
750
 
694
751
  tail.on("message", (data) => {
@@ -713,7 +770,7 @@ export async function main(argv: string[]): Promise<void> {
713
770
  );
714
771
 
715
772
  // preview
716
- yargs.command(
773
+ wrangler.command(
717
774
  "preview [method] [body]",
718
775
  false,
719
776
  (yargs) => {
@@ -739,20 +796,20 @@ export async function main(argv: string[]): Promise<void> {
739
796
  });
740
797
  },
741
798
  () => {
742
- // "🔬 [DEPRECATED] Preview your code temporarily on cloudflareworkers.com"
743
- console.error(
799
+ // "🔬 [DEPRECATED] Preview your code temporarily on https://cloudflareworkers.com"
800
+ throw new DeprecationError(
744
801
  "`wrangler preview` has been deprecated, please refer to TODO://some/path for alternatives"
745
802
  );
746
803
  }
747
804
  );
748
805
 
749
806
  // route
750
- yargs.command(
807
+ wrangler.command(
751
808
  "route",
752
809
  false, // I think we want to hide this command
753
810
  // "➡️ List or delete worker routes",
754
- (yargs) => {
755
- return yargs
811
+ (routeYargs) => {
812
+ return routeYargs
756
813
  .command(
757
814
  "list",
758
815
  "List a route associated with a zone",
@@ -776,8 +833,7 @@ export async function main(argv: string[]): Promise<void> {
776
833
  // TODO: use environment (current wrangler doesn't do so?)
777
834
  const zone = args.zone || (args.config as Config).zone_id;
778
835
  if (!zone) {
779
- console.error("missing zone id");
780
- return;
836
+ throw new Error("missing zone id");
781
837
  }
782
838
 
783
839
  console.log(await cfetch(`/zones/${zone}/workers/routes`));
@@ -820,7 +876,7 @@ export async function main(argv: string[]): Promise<void> {
820
876
  );
821
877
 
822
878
  // subdomain
823
- yargs.command(
879
+ wrangler.command(
824
880
  "subdomain [name]",
825
881
  false,
826
882
  // "👷 Create or change your workers.dev subdomain.",
@@ -828,18 +884,18 @@ export async function main(argv: string[]): Promise<void> {
828
884
  return yargs.positional("name", { type: "string" });
829
885
  },
830
886
  () => {
831
- console.error(
887
+ throw new DeprecationError(
832
888
  "`wrangler subdomain` has been deprecated, please refer to TODO://some/path for alternatives"
833
889
  );
834
890
  }
835
891
  );
836
892
 
837
893
  // secret
838
- yargs.command(
894
+ wrangler.command(
839
895
  "secret",
840
896
  "🤫 Generate a secret that can be referenced in the worker script",
841
- (yargs) => {
842
- return yargs
897
+ (secretYargs) => {
898
+ return secretYargs
843
899
  .command(
844
900
  "put <key>",
845
901
  "Create or update a secret variable for a script",
@@ -861,16 +917,16 @@ export async function main(argv: string[]): Promise<void> {
861
917
  },
862
918
  async (args) => {
863
919
  if (args.local) {
864
- console.error("--local not implemented for this command yet");
865
- return;
920
+ throw new NotImplementedError(
921
+ "--local not implemented for this command yet"
922
+ );
866
923
  }
867
924
  const config = args.config as Config;
868
925
 
869
926
  // TODO: use environment (how does current wrangler do it?)
870
927
  const scriptName = args.name || config.name;
871
928
  if (!scriptName) {
872
- console.error("Missing script name");
873
- return;
929
+ throw new Error("Missing script name");
874
930
  }
875
931
 
876
932
  // -- snip, extract --
@@ -885,8 +941,7 @@ export async function main(argv: string[]): Promise<void> {
885
941
  if (!config.account_id) {
886
942
  config.account_id = await getAccountId();
887
943
  if (!config.account_id) {
888
- console.error("No account id found, quitting...");
889
- return;
944
+ throw new Error("No account id found, quitting...");
890
945
  }
891
946
  }
892
947
  }
@@ -928,7 +983,11 @@ export async function main(argv: string[]): Promise<void> {
928
983
  content: `export default { fetch() {} }`,
929
984
  type: "esm",
930
985
  },
931
- variables: {},
986
+ bindings: {
987
+ kv_namespaces: [],
988
+ vars: {},
989
+ durable_objects: { bindings: [] },
990
+ },
932
991
  modules: [],
933
992
  }),
934
993
  }
@@ -962,8 +1021,9 @@ export async function main(argv: string[]): Promise<void> {
962
1021
  },
963
1022
  async (args) => {
964
1023
  if (args.local) {
965
- console.error("--local not implemented for this command yet");
966
- return;
1024
+ throw new NotImplementedError(
1025
+ "--local not implemented for this command yet"
1026
+ );
967
1027
  }
968
1028
  const config = args.config as Config;
969
1029
 
@@ -985,8 +1045,7 @@ export async function main(argv: string[]): Promise<void> {
985
1045
  if (!config.account_id) {
986
1046
  config.account_id = await getAccountId();
987
1047
  if (!config.account_id) {
988
- console.error("No account id found, quitting...");
989
- return;
1048
+ throw new Error("No account id found, quitting...");
990
1049
  }
991
1050
  }
992
1051
  }
@@ -1024,16 +1083,16 @@ export async function main(argv: string[]): Promise<void> {
1024
1083
  },
1025
1084
  async (args) => {
1026
1085
  if (args.local) {
1027
- console.error("--local not implemented for this command yet");
1028
- return;
1086
+ throw new NotImplementedError(
1087
+ "--local not implemented for this command yet"
1088
+ );
1029
1089
  }
1030
1090
  const config = args.config as Config;
1031
1091
 
1032
1092
  // TODO: use environment (how does current wrangler do it?)
1033
1093
  const scriptName = args.name || config.name;
1034
1094
  if (!scriptName) {
1035
- console.error("Missing script name");
1036
- return;
1095
+ throw new Error("Missing script name");
1037
1096
  }
1038
1097
 
1039
1098
  // -- snip, extract --
@@ -1048,8 +1107,7 @@ export async function main(argv: string[]): Promise<void> {
1048
1107
  if (!config.account_id) {
1049
1108
  config.account_id = await getAccountId();
1050
1109
  if (!config.account_id) {
1051
- console.error("No account id found, quitting...");
1052
- return;
1110
+ throw new Error("No account id found, quitting...");
1053
1111
  }
1054
1112
  }
1055
1113
  }
@@ -1068,11 +1126,11 @@ export async function main(argv: string[]): Promise<void> {
1068
1126
 
1069
1127
  // kv
1070
1128
  // :namespace
1071
- yargs.command(
1129
+ wrangler.command(
1072
1130
  "kv:namespace",
1073
1131
  "🗂️ Interact with your Workers KV Namespaces",
1074
- (yargs) => {
1075
- return yargs
1132
+ (namespaceYargs) => {
1133
+ return namespaceYargs
1076
1134
  .command(
1077
1135
  "create <namespace>",
1078
1136
  "Create a new namespace",
@@ -1092,13 +1150,19 @@ export async function main(argv: string[]): Promise<void> {
1092
1150
  });
1093
1151
  },
1094
1152
  async (args) => {
1095
- if (args._.length !== 2) {
1096
- throw new Error(
1097
- `Did you forget to add quotes around "${
1098
- args.namespace
1099
- } ${args._.slice(2).join(" ")}"?`
1153
+ if (args._.length > 2) {
1154
+ const extraArgs = args._.slice(2).join(" ");
1155
+ throw new CommandLineArgsError(
1156
+ `Unexpected additional positional arguments "${extraArgs}".`
1100
1157
  );
1101
1158
  }
1159
+
1160
+ if (!isValidNamespaceBinding(args.namespace)) {
1161
+ throw new CommandLineArgsError(
1162
+ `The namespace binding name "${args.namespace}" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number.`
1163
+ );
1164
+ }
1165
+
1102
1166
  const config = args.config as Config;
1103
1167
  if (!config.name) {
1104
1168
  console.warn(
@@ -1106,13 +1170,10 @@ export async function main(argv: string[]): Promise<void> {
1106
1170
  );
1107
1171
  }
1108
1172
 
1109
- const title = `${config.name || "worker"}${
1110
- args.env ? `-${args.env}` : ""
1111
- }-${args.namespace}${args.preview ? "_preview" : ""}`;
1112
-
1113
- if (/[\W]+/.test(args.namespace)) {
1114
- throw new Error("invalid binding name, needs to be js friendly");
1115
- }
1173
+ const name = config.name || "worker";
1174
+ const environment = args.env ? `-${args.env}` : "";
1175
+ const preview = args.preview ? "_preview" : "";
1176
+ const title = `${name}${environment}-${args.namespace}${preview}`;
1116
1177
 
1117
1178
  if (args.local) {
1118
1179
  const { Miniflare } = await import("miniflare");
@@ -1121,7 +1182,7 @@ export async function main(argv: string[]): Promise<void> {
1121
1182
  // TODO: these options shouldn't be required
1122
1183
  script: ` `, // has to be a string with at least one char
1123
1184
  });
1124
- mf.getKVNamespace(title); // this should "create" the namespace
1185
+ await mf.getKVNamespace(title); // this should "create" the namespace
1125
1186
  console.log(`✨ Success! Created KV namespace ${title}`);
1126
1187
  return;
1127
1188
  }
@@ -1138,8 +1199,7 @@ export async function main(argv: string[]): Promise<void> {
1138
1199
  if (!config.account_id) {
1139
1200
  config.account_id = await getAccountId();
1140
1201
  if (!config.account_id) {
1141
- console.error("No account id found, quitting...");
1142
- return;
1202
+ throw new Error("No account id found, quitting...");
1143
1203
  }
1144
1204
  }
1145
1205
  }
@@ -1149,31 +1209,19 @@ export async function main(argv: string[]): Promise<void> {
1149
1209
  // TODO: generate a binding name stripping non alphanumeric chars
1150
1210
 
1151
1211
  console.log(`🌀 Creating namespace with title "${title}"`);
1152
-
1153
- const response = await cfetch<{ id: string }>(
1154
- `/accounts/${config.account_id}/storage/kv/namespaces`,
1155
- {
1156
- method: "POST",
1157
- headers: {
1158
- "Content-Type": "application/json",
1159
- },
1160
- body: JSON.stringify({
1161
- title,
1162
- }),
1163
- }
1164
- );
1212
+ const namespaceId = await createNamespace(config.account_id, title);
1165
1213
 
1166
1214
  console.log("✨ Success!");
1215
+ const envString = args.env ? ` under [env.${args.env}]` : "";
1216
+ const previewString = args.preview ? "preview_" : "";
1167
1217
  console.log(
1168
- `Add the following to your configuration file in your kv_namespaces array${
1169
- args.env ? ` under [env.${args.env}]` : ""
1170
- }:`
1218
+ `Add the following to your configuration file in your kv_namespaces array${envString}:`
1171
1219
  );
1172
1220
  console.log(
1173
- `{ binding = "${args.namespace}", ${
1174
- args.preview ? "preview_" : ""
1175
- }id = "${response.id}" }`
1221
+ `{ binding = "${args.namespace}", ${previewString}id = "${namespaceId}" }`
1176
1222
  );
1223
+
1224
+ // TODO: automatically write this block to the wrangler.toml config file??
1177
1225
  }
1178
1226
  )
1179
1227
  .command(
@@ -1182,8 +1230,9 @@ export async function main(argv: string[]): Promise<void> {
1182
1230
  {},
1183
1231
  async (args) => {
1184
1232
  if (args.local) {
1185
- console.error(`local mode is not yet supported for this command`);
1186
- return;
1233
+ throw new NotImplementedError(
1234
+ `local mode is not yet supported for this command`
1235
+ );
1187
1236
  }
1188
1237
 
1189
1238
  const config = args.config as Config;
@@ -1200,8 +1249,7 @@ export async function main(argv: string[]): Promise<void> {
1200
1249
  if (!config.account_id) {
1201
1250
  config.account_id = await getAccountId();
1202
1251
  if (!config.account_id) {
1203
- console.error("No account id found, quitting...");
1204
- return;
1252
+ throw new Error("No account id found, quitting...");
1205
1253
  }
1206
1254
  }
1207
1255
  }
@@ -1244,8 +1292,9 @@ export async function main(argv: string[]): Promise<void> {
1244
1292
  },
1245
1293
  async (args) => {
1246
1294
  if (args.local) {
1247
- console.error(`local mode is not yet supported for this command`);
1248
- return;
1295
+ throw new NotImplementedError(
1296
+ `local mode is not yet supported for this command`
1297
+ );
1249
1298
  }
1250
1299
  const config = args.config as Config;
1251
1300
 
@@ -1256,9 +1305,12 @@ export async function main(argv: string[]): Promise<void> {
1256
1305
  : config
1257
1306
  ).kv_namespaces.find(
1258
1307
  (namespace) => namespace.binding === args.binding
1259
- )[args.preview ? "preview_id" : "id"];
1308
+ )?.[args.preview ? "preview_id" : "id"];
1260
1309
  if (!id) {
1261
- throw new Error("Are you sure? id not found");
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
+ );
1262
1314
  }
1263
1315
 
1264
1316
  // -- snip, extract --
@@ -1274,8 +1326,7 @@ export async function main(argv: string[]): Promise<void> {
1274
1326
  if (!config.account_id) {
1275
1327
  config.account_id = await getAccountId();
1276
1328
  if (!config.account_id) {
1277
- console.error("No account id found, quitting...");
1278
- return;
1329
+ throw new Error("No account id found, quitting...");
1279
1330
  }
1280
1331
  }
1281
1332
  }
@@ -1310,11 +1361,11 @@ export async function main(argv: string[]): Promise<void> {
1310
1361
  );
1311
1362
 
1312
1363
  // :key
1313
- yargs.command(
1364
+ wrangler.command(
1314
1365
  "kv:key",
1315
1366
  "🔑 Individually manage Workers KV key-value pairs",
1316
- (yargs) => {
1317
- return yargs
1367
+ (kvKeyYargs) => {
1368
+ return kvKeyYargs
1318
1369
  .command(
1319
1370
  "put <key> [value]",
1320
1371
  "Writes a single key/value pair to the given namespace.",
@@ -1401,8 +1452,7 @@ export async function main(argv: string[]): Promise<void> {
1401
1452
  if (!config.account_id) {
1402
1453
  config.account_id = await getAccountId();
1403
1454
  if (!config.account_id) {
1404
- console.error("No account id found, quitting...");
1405
- return;
1455
+ throw new Error("No account id found, quitting...");
1406
1456
  }
1407
1457
  }
1408
1458
  }
@@ -1470,8 +1520,7 @@ export async function main(argv: string[]): Promise<void> {
1470
1520
  if (!config.account_id) {
1471
1521
  config.account_id = await getAccountId();
1472
1522
  if (!config.account_id) {
1473
- console.error("No account id found, quitting...");
1474
- return;
1523
+ throw new Error("No account id found, quitting...");
1475
1524
  }
1476
1525
  }
1477
1526
  }
@@ -1539,8 +1588,7 @@ export async function main(argv: string[]): Promise<void> {
1539
1588
  if (!config.account_id) {
1540
1589
  config.account_id = await getAccountId();
1541
1590
  if (!config.account_id) {
1542
- console.error("No account id found, quitting...");
1543
- return;
1591
+ throw new Error("No account id found, quitting...");
1544
1592
  }
1545
1593
  }
1546
1594
  }
@@ -1548,7 +1596,7 @@ export async function main(argv: string[]): Promise<void> {
1548
1596
  // -- snip, end --
1549
1597
 
1550
1598
  // annoyingly, the API for this one doesn't return the
1551
- // data in the 'standard' format. goddamit.
1599
+ // data in the 'standard' format. goddammit.
1552
1600
  // That's why we have the fallthrough response in cfetch.
1553
1601
  // Oh well.
1554
1602
  console.log(
@@ -1619,8 +1667,7 @@ export async function main(argv: string[]): Promise<void> {
1619
1667
  if (!config.account_id) {
1620
1668
  config.account_id = await getAccountId();
1621
1669
  if (!config.account_id) {
1622
- console.error("No account id found, quitting...");
1623
- return;
1670
+ throw new Error("No account id found, quitting...");
1624
1671
  }
1625
1672
  }
1626
1673
  }
@@ -1637,11 +1684,11 @@ export async function main(argv: string[]): Promise<void> {
1637
1684
  );
1638
1685
 
1639
1686
  // :bulk
1640
- yargs.command(
1687
+ wrangler.command(
1641
1688
  "kv:bulk",
1642
1689
  "💪 Interact with multiple Workers KV key-value pairs at once",
1643
- (yargs) => {
1644
- return yargs
1690
+ (kvBulkYargs) => {
1691
+ return kvBulkYargs
1645
1692
  .command(
1646
1693
  "put <filename>",
1647
1694
  "Upload multiple key-value pairs to a namespace",
@@ -1681,8 +1728,9 @@ export async function main(argv: string[]): Promise<void> {
1681
1728
  try {
1682
1729
  parsedContent = JSON.parse(content);
1683
1730
  } catch (err) {
1684
- console.error(`could not parse json from ${filename}`);
1685
- throw err;
1731
+ throw new Error(
1732
+ `Could not parse json from ${filename}.\n${err.message ?? err}`
1733
+ );
1686
1734
  }
1687
1735
 
1688
1736
  if (args.local) {
@@ -1721,8 +1769,7 @@ export async function main(argv: string[]): Promise<void> {
1721
1769
  if (!config.account_id) {
1722
1770
  config.account_id = await getAccountId();
1723
1771
  if (!config.account_id) {
1724
- console.error("No account id found, quitting...");
1725
- return;
1772
+ throw new Error("No account id found, quitting...");
1726
1773
  }
1727
1774
  }
1728
1775
  }
@@ -1769,8 +1816,9 @@ export async function main(argv: string[]): Promise<void> {
1769
1816
  try {
1770
1817
  parsedContent = JSON.parse(content);
1771
1818
  } catch (err) {
1772
- console.error(`could not parse json from ${filename}`);
1773
- throw err;
1819
+ throw new Error(
1820
+ `Could not parse json from ${filename}.\n${err.message ?? err}`
1821
+ );
1774
1822
  }
1775
1823
 
1776
1824
  if (args.local) {
@@ -1801,8 +1849,7 @@ export async function main(argv: string[]): Promise<void> {
1801
1849
  if (!config.account_id) {
1802
1850
  config.account_id = await getAccountId();
1803
1851
  if (!config.account_id) {
1804
- console.error("No account id found, quitting...");
1805
- return;
1852
+ throw new Error("No account id found, quitting...");
1806
1853
  }
1807
1854
  }
1808
1855
  }
@@ -1817,9 +1864,9 @@ export async function main(argv: string[]): Promise<void> {
1817
1864
  }
1818
1865
  );
1819
1866
 
1820
- yargs.command("pages", "⚡️ Configure Cloudflare Pages", pages);
1867
+ wrangler.command("pages", "⚡️ Configure Cloudflare Pages", pages);
1821
1868
 
1822
- yargs
1869
+ wrangler
1823
1870
  .option("config", {
1824
1871
  alias: "c",
1825
1872
  describe: "Path to .toml configuration file",
@@ -1835,24 +1882,21 @@ export async function main(argv: string[]): Promise<void> {
1835
1882
  default: false, // I bet this will a point of contention. We'll revisit it.
1836
1883
  });
1837
1884
 
1838
- yargs.group(["config", "help", "version"], "Flags:");
1839
- yargs.help().alias("h", "help");
1840
- yargs.version(wranglerVersion).alias("v", "version");
1885
+ wrangler.group(["config", "help", "version"], "Flags:");
1886
+ wrangler.help().alias("h", "help");
1887
+ wrangler.version(wranglerVersion).alias("v", "version");
1888
+ wrangler.exitProcess(false);
1889
+
1841
1890
  await initialiseUserConfig();
1842
1891
 
1843
- await yargs.parse(
1844
- argv
1845
- /* TODO: we'd like to use the parse callback,
1846
- but there's a serious bug with it
1847
- https://github.com/yargs/yargs/issues/1975#issuecomment-874237906
1848
-
1849
- , (err, argv, output) => {
1850
- if (output) {
1851
- console.log(output);
1852
- }
1853
- console.log({ err });
1854
- console.log("what now");
1855
- }
1856
- */
1857
- );
1892
+ try {
1893
+ await wrangler.parse();
1894
+ } catch (e) {
1895
+ if (e instanceof CommandLineArgsError) {
1896
+ wrangler.showHelp("error");
1897
+ console.error(""); // Just adds a bit of space
1898
+ }
1899
+ console.error(e.message);
1900
+ throw e;
1901
+ }
1858
1902
  }