wrangler 0.0.0-ece06ea → 0.0.0-eebdb6c

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 (39) hide show
  1. package/package.json +22 -15
  2. package/pages/functions/buildWorker.ts +57 -0
  3. package/pages/functions/filepath-routing.test.ts +32 -0
  4. package/pages/functions/filepath-routing.ts +225 -0
  5. package/pages/functions/identifiers.ts +78 -0
  6. package/pages/functions/routes.ts +158 -0
  7. package/pages/functions/template-worker.ts +135 -0
  8. package/src/__tests__/index.test.ts +254 -120
  9. package/src/__tests__/jest.setup.ts +22 -0
  10. package/src/__tests__/kv.test.ts +1098 -0
  11. package/src/__tests__/mock-cfetch.ts +171 -0
  12. package/src/__tests__/mock-dialogs.ts +65 -0
  13. package/src/__tests__/run-in-tmp.ts +19 -0
  14. package/src/__tests__/run-wrangler.ts +32 -0
  15. package/src/api/form_data.ts +70 -97
  16. package/src/api/inspect.ts +0 -11
  17. package/src/api/preview.ts +29 -24
  18. package/src/api/worker.ts +34 -40
  19. package/src/cfetch/index.ts +103 -0
  20. package/src/cfetch/internal.ts +69 -0
  21. package/src/cli.ts +6 -7
  22. package/src/config.ts +14 -7
  23. package/src/dev.tsx +274 -131
  24. package/src/dialogs.tsx +4 -12
  25. package/src/index.tsx +304 -245
  26. package/src/kv.tsx +101 -45
  27. package/src/module-collection.ts +64 -0
  28. package/src/pages.tsx +681 -104
  29. package/src/proxy.ts +103 -0
  30. package/src/publish.ts +54 -58
  31. package/src/sites.tsx +5 -6
  32. package/src/tail.tsx +5 -3
  33. package/src/user.tsx +9 -16
  34. package/wrangler-dist/cli.js +36633 -49770
  35. package/wrangler-dist/cli.js.map +3 -3
  36. package/src/__tests__/fixtures/init/.gitkeep +0 -0
  37. package/src/__tests__/mock-cfetch.js +0 -46
  38. package/src/cfetch.ts +0 -72
  39. package/src/util/fetch.ts +0 -74
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,11 +24,13 @@ 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";
31
32
 
32
- import cfetch from "./cfetch";
33
+ import { fetchResult, fetchRaw } from "./cfetch";
33
34
 
34
35
  import publish from "./publish";
35
36
  import path from "path/posix";
@@ -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
  }
@@ -66,7 +67,6 @@ async function readConfig(path?: string): Promise<Config> {
66
67
  "route",
67
68
  "jsx_factory",
68
69
  "jsx_fragment",
69
- "polyfill_node",
70
70
  "site",
71
71
  "triggers",
72
72
  "usage_model",
@@ -80,13 +80,18 @@ async function readConfig(path?: string): Promise<Config> {
80
80
  });
81
81
  });
82
82
 
83
- const mirroredFields = ["vars", "kv_namespaces", "durable_objects"];
83
+ const mirroredFields = [
84
+ "vars",
85
+ "kv_namespaces",
86
+ "durable_objects",
87
+ "experimental_services",
88
+ ];
84
89
  Object.keys(config.env || {}).forEach((env) => {
85
90
  mirroredFields.forEach((field) => {
86
- // 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
87
92
  Object.keys(config[field] || {}).forEach((fieldKey) => {
88
93
  if (!(fieldKey in config.env[env][field])) {
89
- console.error(
94
+ console.warn(
90
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}".`
91
96
  );
92
97
  }
@@ -94,11 +99,17 @@ async function readConfig(path?: string): Promise<Config> {
94
99
  });
95
100
  });
96
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
+
97
108
  // todo: validate, add defaults
98
109
  // let's just do some basics for now
99
110
 
100
111
  // @ts-expect-error we're being sneaky here for now
101
- config.__path__ = path;
112
+ config.__path__ = configPath;
102
113
 
103
114
  return config;
104
115
  }
@@ -106,18 +117,18 @@ async function readConfig(path?: string): Promise<Config> {
106
117
  // a helper to demand one of a set of options
107
118
  // via https://github.com/yargs/yargs/issues/1093#issuecomment-491299261
108
119
  function demandOneOfOption(...options: string[]) {
109
- return function (argv: yargs.Arguments) {
120
+ return function (argv: Yargs.Arguments) {
110
121
  const count = options.filter((option) => argv[option]).length;
111
122
  const lastOption = options.pop();
112
123
 
113
124
  if (count === 0) {
114
- throw new Error(
125
+ throw new CommandLineArgsError(
115
126
  `Exactly one of the arguments ${options.join(
116
127
  ", "
117
128
  )} and ${lastOption} is required`
118
129
  );
119
130
  } else if (count > 1) {
120
- throw new Error(
131
+ throw new CommandLineArgsError(
121
132
  `Arguments ${options.join(
122
133
  ", "
123
134
  )} and ${lastOption} are mutually exclusive`
@@ -128,30 +139,49 @@ function demandOneOfOption(...options: string[]) {
128
139
  };
129
140
  }
130
141
 
142
+ class CommandLineArgsError extends Error {}
143
+ class DeprecationError extends Error {}
144
+ class NotImplementedError extends Error {}
145
+
131
146
  export async function main(argv: string[]): Promise<void> {
132
- const yargs = makeCLI(hideBin(process.argv))
133
- .command(
134
- // the default is to simply print the help menu
135
- ["*"],
136
- false,
137
- () => {},
138
- () => {
139
- 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);
140
156
  }
141
- )
157
+ throw error;
158
+ })
142
159
  .scriptName("wrangler")
143
160
  .wrap(null);
144
161
 
145
- // you will note that we use the form for all commands where we use the builder function
146
- // to define options and subcommands. Further we return the result of this builder even
147
- // tho it's not completely necessary. The reason is that it's required for type inference
148
- // of the args in the handle function.I wish we could enforce this pattern, but this
149
- // 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
+ );
150
175
 
151
- // 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. 🤷‍♂.)
152
182
 
153
183
  // [DEPRECATED] generate
154
- yargs.command(
184
+ wrangler.command(
155
185
  // we definitely want to move away from us cloning github templates
156
186
  // we can do something better here, let's see
157
187
  "generate [name] [template]",
@@ -169,14 +199,14 @@ export async function main(argv: string[]): Promise<void> {
169
199
  },
170
200
  () => {
171
201
  // "👯 [DEPRECATED]. Scaffold a Cloudflare Workers project from a public GitHub repository.",
172
- console.error(
202
+ throw new DeprecationError(
173
203
  "`wrangler generate` has been deprecated, please refer to TODO://some/path for alternatives"
174
204
  );
175
205
  }
176
206
  );
177
207
 
178
208
  // init
179
- yargs.command(
209
+ wrangler.command(
180
210
  "init [name]",
181
211
  "📥 Create a wrangler.toml configuration file",
182
212
  (yargs) => {
@@ -185,26 +215,42 @@ export async function main(argv: string[]): Promise<void> {
185
215
  type: "string",
186
216
  });
187
217
  },
188
- 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
+
189
229
  const destination = path.join(process.cwd(), "wrangler.toml");
190
230
  if (fs.existsSync(destination)) {
191
- console.error(`${destination} already exists.`);
192
- } else {
193
- const compatibilityDate = new Date().toISOString().substring(0, 10);
194
- try {
195
- await writeFile(
196
- destination,
197
- `compatibility_date = "${compatibilityDate}"` + "\n"
198
- );
199
- console.log(`✨ Succesfully created wrangler.toml`);
200
- // TODO: suggest next steps?
201
- } catch (err) {
202
- console.error(`Failed to create wrangler.toml`);
203
- console.error(err);
204
- 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;
205
237
  }
206
238
  }
207
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
+
208
254
  // if no package.json, ask, and if yes, create one
209
255
  let pathToPackageJson = await findUp("package.json");
210
256
 
@@ -241,9 +287,17 @@ export async function main(argv: string[]): Promise<void> {
241
287
  JSON.stringify(
242
288
  {
243
289
  compilerOptions: {
244
- target: "ES2020",
245
- module: "CommonJS",
246
- 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,
247
301
  types: ["@cloudflare/workers-types"],
248
302
  },
249
303
  },
@@ -266,7 +320,7 @@ export async function main(argv: string[]): Promise<void> {
266
320
  );
267
321
 
268
322
  // build
269
- yargs.command(
323
+ wrangler.command(
270
324
  "build",
271
325
  false,
272
326
  (yargs) => {
@@ -276,14 +330,14 @@ export async function main(argv: string[]): Promise<void> {
276
330
  },
277
331
  () => {
278
332
  // "[DEPRECATED] 🦀 Build your project (if applicable)",
279
- console.error(
333
+ throw new DeprecationError(
280
334
  "`wrangler build` has been deprecated, please refer to TODO://some/path for alternatives"
281
335
  );
282
336
  }
283
337
  );
284
338
 
285
339
  // login
286
- yargs.command(
340
+ wrangler.command(
287
341
  // this needs scopes as an option?
288
342
  "login",
289
343
  false, // we don't need to show this in the menu
@@ -320,13 +374,13 @@ export async function main(argv: string[]): Promise<void> {
320
374
  await login();
321
375
 
322
376
  // TODO: would be nice if it optionally saved login
323
- // creds inside node_modules/.cache or something
377
+ // credentials inside node_modules/.cache or something
324
378
  // this way you could have multiple users on a single machine
325
379
  }
326
380
  );
327
381
 
328
382
  // logout
329
- yargs.command(
383
+ wrangler.command(
330
384
  // this needs scopes as an option?
331
385
  "logout",
332
386
  false, // we don't need to show this in the menu
@@ -338,7 +392,7 @@ export async function main(argv: string[]): Promise<void> {
338
392
  );
339
393
 
340
394
  // whoami
341
- yargs.command(
395
+ wrangler.command(
342
396
  "whoami",
343
397
  false, // we don't need to show this the menu
344
398
  // "🕵️ Retrieve your user info and test your auth config",
@@ -349,20 +403,20 @@ export async function main(argv: string[]): Promise<void> {
349
403
  );
350
404
 
351
405
  // config
352
- yargs.command(
406
+ wrangler.command(
353
407
  "config",
354
408
  false,
355
409
  () => {},
356
410
  () => {
357
411
  // "🕵️ Authenticate Wrangler with a Cloudflare API Token",
358
- console.error(
412
+ throw new DeprecationError(
359
413
  "`wrangler config` has been deprecated, please refer to TODO://some/path for alternatives"
360
414
  );
361
415
  }
362
416
  );
363
417
 
364
418
  // dev
365
- yargs.command(
419
+ wrangler.command(
366
420
  "dev <filename>",
367
421
  "👂 Start a local server for developing your worker",
368
422
  (yargs) => {
@@ -422,10 +476,6 @@ export async function main(argv: string[]): Promise<void> {
422
476
  .option("jsx-fragment", {
423
477
  describe: "The function that is called for each JSX fragment",
424
478
  type: "string",
425
- })
426
- .option("polyfill-node", {
427
- describe: "Try to polyfill node builtins",
428
- type: "boolean",
429
479
  });
430
480
  },
431
481
  async (args) => {
@@ -444,8 +494,7 @@ export async function main(argv: string[]): Promise<void> {
444
494
  if (!config.account_id) {
445
495
  config.account_id = await getAccountId();
446
496
  if (!config.account_id) {
447
- console.error("No account id found, quitting...");
448
- return;
497
+ throw new Error("No account id found, quitting...");
449
498
  }
450
499
  }
451
500
  }
@@ -454,17 +503,22 @@ export async function main(argv: string[]): Promise<void> {
454
503
 
455
504
  const envRootObj = args.env ? config.env[args.env] || {} : config;
456
505
 
457
- const polyfillNode = args["polyfill-node"] ?? config.polyfill_node;
458
- if (polyfillNode) {
459
- console.warn(
460
- "Using polyfills for node builtin modules. These are not meant to be completely reliable, and have serious tradeoffs. Please see https://github.com/ionic-team/rollup-plugin-node-polyfills/ for more details."
461
- );
506
+ // TODO: this error shouldn't actually happen,
507
+ // but we haven't fixed it internally yet
508
+ if ("durable_objects" in envRootObj) {
509
+ if (!(args.name || config.name)) {
510
+ 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.'
512
+ );
513
+ }
514
+ // TODO: if not already published, publish a draft worker
462
515
  }
463
516
 
464
517
  render(
465
518
  <Dev
466
519
  name={args.name || config.name}
467
- entry={filename}
520
+ entry={path.relative(process.cwd(), filename)}
521
+ buildCommand={config.build || {}}
468
522
  format={format}
469
523
  initialMode={args.local ? "local" : "remote"}
470
524
  jsxFactory={args["jsx-factory"] || envRootObj?.jsx_factory}
@@ -472,32 +526,35 @@ export async function main(argv: string[]): Promise<void> {
472
526
  accountId={config.account_id}
473
527
  site={args.site || config.site?.bucket}
474
528
  port={args.port || config.dev?.port}
475
- polyfillNode={polyfillNode}
476
529
  public={args.public}
477
530
  compatibilityDate={config.compatibility_date}
478
531
  compatibilityFlags={config.compatibility_flags}
479
532
  usageModel={config.usage_model}
480
- variables={{
481
- ...(envRootObj?.vars || {}),
482
- ...(envRootObj?.kv_namespaces || []).reduce(
483
- (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 obsoleted in the future
541
+ // when we have copy-on-write for previews on edge workers.
484
542
  if (!preview_id) {
485
543
  // TODO: This error has to be a _lot_ better, ideally just asking
486
544
  // to create a preview namespace for the user automatically
487
545
  throw new Error(
488
- "kv namespaces need a preview id during dev mode"
489
- );
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
490
548
  }
491
- return { ...obj, [binding]: { namespaceId: preview_id } };
492
- },
493
- {}
494
- ),
495
- ...(envRootObj?.durable_objects?.bindings || []).reduce(
496
- (obj, { name, class_name, script_name }) => {
497
- return { ...obj, [name]: { class_name, script_name } };
498
- },
499
- {}
549
+ return {
550
+ binding,
551
+ id: preview_id,
552
+ };
553
+ }
500
554
  ),
555
+ vars: envRootObj.vars,
556
+ durable_objects: envRootObj.durable_objects,
557
+ services: envRootObj.experimental_services,
501
558
  }}
502
559
  />
503
560
  );
@@ -505,7 +562,7 @@ export async function main(argv: string[]): Promise<void> {
505
562
  );
506
563
 
507
564
  // publish
508
- yargs.command(
565
+ wrangler.command(
509
566
  "publish [script]",
510
567
  "🆙 Publish your Worker to Cloudflare.",
511
568
  (yargs) => {
@@ -553,16 +610,13 @@ export async function main(argv: string[]): Promise<void> {
553
610
  .option("jsx-fragment", {
554
611
  describe: "The function that is called for each JSX fragment",
555
612
  type: "string",
556
- })
557
- .option("polyfill-node", {
558
- describe: "Try to polyfill node builtins",
559
- type: "boolean",
560
613
  });
561
614
  },
562
615
  async (args) => {
563
616
  if (args.local) {
564
- console.error("🚫 Local publishing is not yet supported");
565
- return;
617
+ throw new NotImplementedError(
618
+ "🚫 Local publishing is not yet supported"
619
+ );
566
620
  }
567
621
  const config = args.config as Config;
568
622
 
@@ -577,21 +631,13 @@ export async function main(argv: string[]): Promise<void> {
577
631
  if (!config.account_id) {
578
632
  config.account_id = await getAccountId();
579
633
  if (!config.account_id) {
580
- console.error("No account id found, quitting...");
581
- return;
634
+ throw new Error("No account id found, quitting...");
582
635
  }
583
636
  }
584
637
  }
585
638
 
586
639
  // -- snip, end --
587
640
 
588
- const polyfillNode = args["polyfill-node"] ?? config.polyfill_node;
589
- if (polyfillNode) {
590
- console.warn(
591
- "Using polyfills for node builtin modules. These are not meant to be completely reliable, and have serious tradeoffs. Please see https://github.com/ionic-team/rollup-plugin-node-polyfills/ for more details."
592
- );
593
- }
594
-
595
641
  await publish({
596
642
  config: args.config as Config,
597
643
  name: args.name,
@@ -600,7 +646,6 @@ export async function main(argv: string[]): Promise<void> {
600
646
  triggers: args.triggers,
601
647
  jsxFactory: args["jsx-factory"],
602
648
  jsxFragment: args["jsx-fragment"],
603
- polyfillNode: args["polyfill-node"],
604
649
  routes: args.routes,
605
650
  public: args.public,
606
651
  site: args.site,
@@ -609,7 +654,7 @@ export async function main(argv: string[]): Promise<void> {
609
654
  );
610
655
 
611
656
  // tail
612
- yargs.command(
657
+ wrangler.command(
613
658
  "tail [name]",
614
659
  "🦚 Starts a log tailing session for a deployed Worker.",
615
660
  (yargs) => {
@@ -656,8 +701,7 @@ export async function main(argv: string[]): Promise<void> {
656
701
  const config = args.config as Config;
657
702
 
658
703
  if (!(args.name || config.name)) {
659
- console.error("Missing script name");
660
- return;
704
+ throw new Error("Missing script name");
661
705
  }
662
706
  const scriptName = `${args.name || config.name}${
663
707
  args.env ? `-${args.env}` : ""
@@ -675,8 +719,7 @@ export async function main(argv: string[]): Promise<void> {
675
719
  if (!config.account_id) {
676
720
  config.account_id = await getAccountId();
677
721
  if (!config.account_id) {
678
- console.error("No account id found, quitting...");
679
- return;
722
+ throw new Error("No account id found, quitting...");
680
723
  }
681
724
  }
682
725
  }
@@ -700,9 +743,9 @@ export async function main(argv: string[]): Promise<void> {
700
743
  `successfully created tail, expires at ${expiration.toLocaleString()}`
701
744
  );
702
745
 
703
- onExit(() => {
746
+ onExit(async () => {
704
747
  tail.terminate();
705
- deleteTail();
748
+ await deleteTail();
706
749
  });
707
750
 
708
751
  tail.on("message", (data) => {
@@ -727,7 +770,7 @@ export async function main(argv: string[]): Promise<void> {
727
770
  );
728
771
 
729
772
  // preview
730
- yargs.command(
773
+ wrangler.command(
731
774
  "preview [method] [body]",
732
775
  false,
733
776
  (yargs) => {
@@ -753,20 +796,20 @@ export async function main(argv: string[]): Promise<void> {
753
796
  });
754
797
  },
755
798
  () => {
756
- // "🔬 [DEPRECATED] Preview your code temporarily on cloudflareworkers.com"
757
- console.error(
799
+ // "🔬 [DEPRECATED] Preview your code temporarily on https://cloudflareworkers.com"
800
+ throw new DeprecationError(
758
801
  "`wrangler preview` has been deprecated, please refer to TODO://some/path for alternatives"
759
802
  );
760
803
  }
761
804
  );
762
805
 
763
806
  // route
764
- yargs.command(
807
+ wrangler.command(
765
808
  "route",
766
809
  false, // I think we want to hide this command
767
810
  // "➡️ List or delete worker routes",
768
- (yargs) => {
769
- return yargs
811
+ (routeYargs) => {
812
+ return routeYargs
770
813
  .command(
771
814
  "list",
772
815
  "List a route associated with a zone",
@@ -790,11 +833,10 @@ export async function main(argv: string[]): Promise<void> {
790
833
  // TODO: use environment (current wrangler doesn't do so?)
791
834
  const zone = args.zone || (args.config as Config).zone_id;
792
835
  if (!zone) {
793
- console.error("missing zone id");
794
- return;
836
+ throw new Error("missing zone id");
795
837
  }
796
838
 
797
- console.log(await cfetch(`/zones/${zone}/workers/routes`));
839
+ console.log(await fetchResult(`/zones/${zone}/workers/routes`));
798
840
  }
799
841
  )
800
842
  .command(
@@ -824,7 +866,7 @@ export async function main(argv: string[]): Promise<void> {
824
866
  }
825
867
 
826
868
  console.log(
827
- await cfetch(`/zones/${zone}/workers/routes/${args.id}`, {
869
+ await fetchResult(`/zones/${zone}/workers/routes/${args.id}`, {
828
870
  method: "DELETE",
829
871
  })
830
872
  );
@@ -834,7 +876,7 @@ export async function main(argv: string[]): Promise<void> {
834
876
  );
835
877
 
836
878
  // subdomain
837
- yargs.command(
879
+ wrangler.command(
838
880
  "subdomain [name]",
839
881
  false,
840
882
  // "👷 Create or change your workers.dev subdomain.",
@@ -842,18 +884,18 @@ export async function main(argv: string[]): Promise<void> {
842
884
  return yargs.positional("name", { type: "string" });
843
885
  },
844
886
  () => {
845
- console.error(
887
+ throw new DeprecationError(
846
888
  "`wrangler subdomain` has been deprecated, please refer to TODO://some/path for alternatives"
847
889
  );
848
890
  }
849
891
  );
850
892
 
851
893
  // secret
852
- yargs.command(
894
+ wrangler.command(
853
895
  "secret",
854
896
  "🤫 Generate a secret that can be referenced in the worker script",
855
- (yargs) => {
856
- return yargs
897
+ (secretYargs) => {
898
+ return secretYargs
857
899
  .command(
858
900
  "put <key>",
859
901
  "Create or update a secret variable for a script",
@@ -875,16 +917,16 @@ export async function main(argv: string[]): Promise<void> {
875
917
  },
876
918
  async (args) => {
877
919
  if (args.local) {
878
- console.error("--local not implemented for this command yet");
879
- return;
920
+ throw new NotImplementedError(
921
+ "--local not implemented for this command yet"
922
+ );
880
923
  }
881
924
  const config = args.config as Config;
882
925
 
883
926
  // TODO: use environment (how does current wrangler do it?)
884
927
  const scriptName = args.name || config.name;
885
928
  if (!scriptName) {
886
- console.error("Missing script name");
887
- return;
929
+ throw new Error("Missing script name");
888
930
  }
889
931
 
890
932
  // -- snip, extract --
@@ -899,8 +941,7 @@ export async function main(argv: string[]): Promise<void> {
899
941
  if (!config.account_id) {
900
942
  config.account_id = await getAccountId();
901
943
  if (!config.account_id) {
902
- console.error("No account id found, quitting...");
903
- return;
944
+ throw new Error("No account id found, quitting...");
904
945
  }
905
946
  }
906
947
  }
@@ -912,7 +953,7 @@ export async function main(argv: string[]): Promise<void> {
912
953
  "password"
913
954
  );
914
955
  async function submitSecret() {
915
- return await cfetch(
956
+ return await fetchResult(
916
957
  `/accounts/${config.account_id}/workers/scripts/${scriptName}/secrets/`,
917
958
  {
918
959
  method: "PUT",
@@ -931,7 +972,7 @@ export async function main(argv: string[]): Promise<void> {
931
972
  } catch (e) {
932
973
  if (e.code === 10007) {
933
974
  // upload a draft worker
934
- await cfetch(
975
+ await fetchResult(
935
976
  `/accounts/${config.account_id}/workers/scripts/${scriptName}`,
936
977
  {
937
978
  method: "PUT",
@@ -942,7 +983,11 @@ export async function main(argv: string[]): Promise<void> {
942
983
  content: `export default { fetch() {} }`,
943
984
  type: "esm",
944
985
  },
945
- variables: {},
986
+ bindings: {
987
+ kv_namespaces: [],
988
+ vars: {},
989
+ durable_objects: { bindings: [] },
990
+ },
946
991
  modules: [],
947
992
  }),
948
993
  }
@@ -976,8 +1021,9 @@ export async function main(argv: string[]): Promise<void> {
976
1021
  },
977
1022
  async (args) => {
978
1023
  if (args.local) {
979
- console.error("--local not implemented for this command yet");
980
- return;
1024
+ throw new NotImplementedError(
1025
+ "--local not implemented for this command yet"
1026
+ );
981
1027
  }
982
1028
  const config = args.config as Config;
983
1029
 
@@ -999,8 +1045,7 @@ export async function main(argv: string[]): Promise<void> {
999
1045
  if (!config.account_id) {
1000
1046
  config.account_id = await getAccountId();
1001
1047
  if (!config.account_id) {
1002
- console.error("No account id found, quitting...");
1003
- return;
1048
+ throw new Error("No account id found, quitting...");
1004
1049
  }
1005
1050
  }
1006
1051
  }
@@ -1013,7 +1058,7 @@ export async function main(argv: string[]): Promise<void> {
1013
1058
  );
1014
1059
 
1015
1060
  console.log(
1016
- await cfetch(
1061
+ await fetchResult(
1017
1062
  `/accounts/${config.account_id}/workers/scripts/${scriptName}/secrets/${args.key}`,
1018
1063
  { method: "DELETE" }
1019
1064
  )
@@ -1038,16 +1083,16 @@ export async function main(argv: string[]): Promise<void> {
1038
1083
  },
1039
1084
  async (args) => {
1040
1085
  if (args.local) {
1041
- console.error("--local not implemented for this command yet");
1042
- return;
1086
+ throw new NotImplementedError(
1087
+ "--local not implemented for this command yet"
1088
+ );
1043
1089
  }
1044
1090
  const config = args.config as Config;
1045
1091
 
1046
1092
  // TODO: use environment (how does current wrangler do it?)
1047
1093
  const scriptName = args.name || config.name;
1048
1094
  if (!scriptName) {
1049
- console.error("Missing script name");
1050
- return;
1095
+ throw new Error("Missing script name");
1051
1096
  }
1052
1097
 
1053
1098
  // -- snip, extract --
@@ -1062,8 +1107,7 @@ export async function main(argv: string[]): Promise<void> {
1062
1107
  if (!config.account_id) {
1063
1108
  config.account_id = await getAccountId();
1064
1109
  if (!config.account_id) {
1065
- console.error("No account id found, quitting...");
1066
- return;
1110
+ throw new Error("No account id found, quitting...");
1067
1111
  }
1068
1112
  }
1069
1113
  }
@@ -1071,7 +1115,7 @@ export async function main(argv: string[]): Promise<void> {
1071
1115
  // -- snip, end --
1072
1116
 
1073
1117
  console.log(
1074
- await cfetch(
1118
+ await fetchResult(
1075
1119
  `/accounts/${config.account_id}/workers/scripts/${scriptName}/secrets`
1076
1120
  )
1077
1121
  );
@@ -1082,11 +1126,11 @@ export async function main(argv: string[]): Promise<void> {
1082
1126
 
1083
1127
  // kv
1084
1128
  // :namespace
1085
- yargs.command(
1129
+ wrangler.command(
1086
1130
  "kv:namespace",
1087
1131
  "🗂️ Interact with your Workers KV Namespaces",
1088
- (yargs) => {
1089
- return yargs
1132
+ (namespaceYargs) => {
1133
+ return namespaceYargs
1090
1134
  .command(
1091
1135
  "create <namespace>",
1092
1136
  "Create a new namespace",
@@ -1106,13 +1150,19 @@ export async function main(argv: string[]): Promise<void> {
1106
1150
  });
1107
1151
  },
1108
1152
  async (args) => {
1109
- if (args._.length !== 2) {
1110
- throw new Error(
1111
- `Did you forget to add quotes around "${
1112
- args.namespace
1113
- } ${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}".`
1114
1157
  );
1115
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
+
1116
1166
  const config = args.config as Config;
1117
1167
  if (!config.name) {
1118
1168
  console.warn(
@@ -1120,13 +1170,10 @@ export async function main(argv: string[]): Promise<void> {
1120
1170
  );
1121
1171
  }
1122
1172
 
1123
- const title = `${config.name || "worker"}${
1124
- args.env ? `-${args.env}` : ""
1125
- }-${args.namespace}${args.preview ? "_preview" : ""}`;
1126
-
1127
- if (/[\W]+/.test(args.namespace)) {
1128
- throw new Error("invalid binding name, needs to be js friendly");
1129
- }
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}`;
1130
1177
 
1131
1178
  if (args.local) {
1132
1179
  const { Miniflare } = await import("miniflare");
@@ -1135,7 +1182,7 @@ export async function main(argv: string[]): Promise<void> {
1135
1182
  // TODO: these options shouldn't be required
1136
1183
  script: ` `, // has to be a string with at least one char
1137
1184
  });
1138
- mf.getKVNamespace(title); // this should "create" the namespace
1185
+ await mf.getKVNamespace(title); // this should "create" the namespace
1139
1186
  console.log(`✨ Success! Created KV namespace ${title}`);
1140
1187
  return;
1141
1188
  }
@@ -1152,8 +1199,7 @@ export async function main(argv: string[]): Promise<void> {
1152
1199
  if (!config.account_id) {
1153
1200
  config.account_id = await getAccountId();
1154
1201
  if (!config.account_id) {
1155
- console.error("No account id found, quitting...");
1156
- return;
1202
+ throw new Error("No account id found, quitting...");
1157
1203
  }
1158
1204
  }
1159
1205
  }
@@ -1163,31 +1209,19 @@ export async function main(argv: string[]): Promise<void> {
1163
1209
  // TODO: generate a binding name stripping non alphanumeric chars
1164
1210
 
1165
1211
  console.log(`🌀 Creating namespace with title "${title}"`);
1166
-
1167
- const response = await cfetch<{ id: string }>(
1168
- `/accounts/${config.account_id}/storage/kv/namespaces`,
1169
- {
1170
- method: "POST",
1171
- headers: {
1172
- "Content-Type": "application/json",
1173
- },
1174
- body: JSON.stringify({
1175
- title,
1176
- }),
1177
- }
1178
- );
1212
+ const namespaceId = await createNamespace(config.account_id, title);
1179
1213
 
1180
1214
  console.log("✨ Success!");
1215
+ const envString = args.env ? ` under [env.${args.env}]` : "";
1216
+ const previewString = args.preview ? "preview_" : "";
1181
1217
  console.log(
1182
- `Add the following to your configuration file in your kv_namespaces array${
1183
- args.env ? ` under [env.${args.env}]` : ""
1184
- }:`
1218
+ `Add the following to your configuration file in your kv_namespaces array${envString}:`
1185
1219
  );
1186
1220
  console.log(
1187
- `{ binding = "${args.namespace}", ${
1188
- args.preview ? "preview_" : ""
1189
- }id = "${response.id}" }`
1221
+ `{ binding = "${args.namespace}", ${previewString}id = "${namespaceId}" }`
1190
1222
  );
1223
+
1224
+ // TODO: automatically write this block to the wrangler.toml config file??
1191
1225
  }
1192
1226
  )
1193
1227
  .command(
@@ -1196,8 +1230,9 @@ export async function main(argv: string[]): Promise<void> {
1196
1230
  {},
1197
1231
  async (args) => {
1198
1232
  if (args.local) {
1199
- console.error(`local mode is not yet supported for this command`);
1200
- return;
1233
+ throw new NotImplementedError(
1234
+ `local mode is not yet supported for this command`
1235
+ );
1201
1236
  }
1202
1237
 
1203
1238
  const config = args.config as Config;
@@ -1214,8 +1249,7 @@ export async function main(argv: string[]): Promise<void> {
1214
1249
  if (!config.account_id) {
1215
1250
  config.account_id = await getAccountId();
1216
1251
  if (!config.account_id) {
1217
- console.error("No account id found, quitting...");
1218
- return;
1252
+ throw new Error("No account id found, quitting...");
1219
1253
  }
1220
1254
  }
1221
1255
  }
@@ -1258,21 +1292,19 @@ export async function main(argv: string[]): Promise<void> {
1258
1292
  },
1259
1293
  async (args) => {
1260
1294
  if (args.local) {
1261
- console.error(`local mode is not yet supported for this command`);
1262
- return;
1295
+ throw new NotImplementedError(
1296
+ `local mode is not yet supported for this command`
1297
+ );
1263
1298
  }
1264
1299
  const config = args.config as Config;
1265
1300
 
1266
- const id =
1267
- args["namespace-id"] ||
1268
- (args.env
1269
- ? config.env[args.env] || {}
1270
- : config
1271
- ).kv_namespaces.find(
1272
- (namespace) => namespace.binding === args.binding
1273
- )[args.preview ? "preview_id" : "id"];
1274
- if (!id) {
1275
- throw new Error("Are you sure? id not found");
1301
+ let id;
1302
+ try {
1303
+ id = getNamespaceId(args);
1304
+ } catch (e) {
1305
+ throw new CommandLineArgsError(
1306
+ "Not able to delete namespace.\n" + e.message
1307
+ );
1276
1308
  }
1277
1309
 
1278
1310
  // -- snip, extract --
@@ -1288,15 +1320,14 @@ export async function main(argv: string[]): Promise<void> {
1288
1320
  if (!config.account_id) {
1289
1321
  config.account_id = await getAccountId();
1290
1322
  if (!config.account_id) {
1291
- console.error("No account id found, quitting...");
1292
- return;
1323
+ throw new Error("No account id found, quitting...");
1293
1324
  }
1294
1325
  }
1295
1326
  }
1296
1327
 
1297
1328
  // -- snip, end --
1298
1329
 
1299
- await cfetch<{ id: string }>(
1330
+ await fetchResult<{ id: string }>(
1300
1331
  `/accounts/${config.account_id}/storage/kv/namespaces/${id}`,
1301
1332
  { method: "DELETE" }
1302
1333
  );
@@ -1324,13 +1355,13 @@ export async function main(argv: string[]): Promise<void> {
1324
1355
  );
1325
1356
 
1326
1357
  // :key
1327
- yargs.command(
1358
+ wrangler.command(
1328
1359
  "kv:key",
1329
1360
  "🔑 Individually manage Workers KV key-value pairs",
1330
- (yargs) => {
1331
- return yargs
1361
+ (kvKeyYargs) => {
1362
+ return kvKeyYargs
1332
1363
  .command(
1333
- "put <key> <value>",
1364
+ "put <key> [value]",
1334
1365
  "Writes a single key/value pair to the given namespace.",
1335
1366
  (yargs) => {
1336
1367
  return yargs
@@ -1371,7 +1402,8 @@ export async function main(argv: string[]): Promise<void> {
1371
1402
  .option("path", {
1372
1403
  type: "string",
1373
1404
  describe: "Read value from the file at a given path.",
1374
- });
1405
+ })
1406
+ .check(demandOneOfOption("value", "path"));
1375
1407
  },
1376
1408
  async ({ key, ttl, expiration, ...args }) => {
1377
1409
  const namespaceId = getNamespaceId(args);
@@ -1380,7 +1412,16 @@ export async function main(argv: string[]): Promise<void> {
1380
1412
  : args.value;
1381
1413
  const config = args.config as Config;
1382
1414
 
1383
- console.log(`writing ${key}=${value} to namespace ${namespaceId}`);
1415
+ if (args.path) {
1416
+ console.log(
1417
+ `writing the contents of ${args.path} to the key "${key}" on namespace ${namespaceId}`
1418
+ );
1419
+ } else {
1420
+ console.log(
1421
+ `writing the value "${value}" to key "${key}" on namespace ${namespaceId}`
1422
+ );
1423
+ }
1424
+
1384
1425
  if (args.local) {
1385
1426
  const { Miniflare } = await import("miniflare");
1386
1427
  const mf = new Miniflare({
@@ -1405,8 +1446,7 @@ export async function main(argv: string[]): Promise<void> {
1405
1446
  if (!config.account_id) {
1406
1447
  config.account_id = await getAccountId();
1407
1448
  if (!config.account_id) {
1408
- console.error("No account id found, quitting...");
1409
- return;
1449
+ throw new Error("No account id found, quitting...");
1410
1450
  }
1411
1451
  }
1412
1452
  }
@@ -1437,6 +1477,12 @@ export async function main(argv: string[]): Promise<void> {
1437
1477
  type: "string",
1438
1478
  describe: "Perform on a specific environment",
1439
1479
  })
1480
+ .option("preview", {
1481
+ type: "boolean",
1482
+ // In the case of listing keys we will default to non-preview mode
1483
+ default: false,
1484
+ describe: "Interact with a preview namespace",
1485
+ })
1440
1486
  .option("prefix", {
1441
1487
  type: "string",
1442
1488
  describe: "A prefix to filter listed keys",
@@ -1474,8 +1520,7 @@ export async function main(argv: string[]): Promise<void> {
1474
1520
  if (!config.account_id) {
1475
1521
  config.account_id = await getAccountId();
1476
1522
  if (!config.account_id) {
1477
- console.error("No account id found, quitting...");
1478
- return;
1523
+ throw new Error("No account id found, quitting...");
1479
1524
  }
1480
1525
  }
1481
1526
  }
@@ -1512,6 +1557,12 @@ export async function main(argv: string[]): Promise<void> {
1512
1557
  .option("preview", {
1513
1558
  type: "boolean",
1514
1559
  describe: "Interact with a preview namespace",
1560
+ })
1561
+ .option("preview", {
1562
+ type: "boolean",
1563
+ // In the case of getting key values we will default to non-preview mode
1564
+ default: false,
1565
+ describe: "Interact with a preview namespace",
1515
1566
  });
1516
1567
  },
1517
1568
  async ({ key, ...args }) => {
@@ -1543,20 +1594,15 @@ export async function main(argv: string[]): Promise<void> {
1543
1594
  if (!config.account_id) {
1544
1595
  config.account_id = await getAccountId();
1545
1596
  if (!config.account_id) {
1546
- console.error("No account id found, quitting...");
1547
- return;
1597
+ throw new Error("No account id found, quitting...");
1548
1598
  }
1549
1599
  }
1550
1600
  }
1551
1601
 
1552
1602
  // -- snip, end --
1553
1603
 
1554
- // annoyingly, the API for this one doesn't return the
1555
- // data in the 'standard' format. goddamit.
1556
- // That's why we have the fallthrough response in cfetch.
1557
- // Oh well.
1558
1604
  console.log(
1559
- await cfetch(
1605
+ await fetchRaw(
1560
1606
  `/accounts/${config.account_id}/storage/kv/namespaces/${namespaceId}/values/${key}`
1561
1607
  )
1562
1608
  );
@@ -1592,6 +1638,10 @@ export async function main(argv: string[]): Promise<void> {
1592
1638
  async ({ key, ...args }) => {
1593
1639
  const namespaceId = getNamespaceId(args);
1594
1640
 
1641
+ console.log(
1642
+ `deleting the key "${key}" on namespace ${namespaceId}`
1643
+ );
1644
+
1595
1645
  if (args.local) {
1596
1646
  const { Miniflare } = await import("miniflare");
1597
1647
  const mf = new Miniflare({
@@ -1619,15 +1669,14 @@ export async function main(argv: string[]): Promise<void> {
1619
1669
  if (!config.account_id) {
1620
1670
  config.account_id = await getAccountId();
1621
1671
  if (!config.account_id) {
1622
- console.error("No account id found, quitting...");
1623
- return;
1672
+ throw new Error("No account id found, quitting...");
1624
1673
  }
1625
1674
  }
1626
1675
  }
1627
1676
 
1628
1677
  // -- snip, end --
1629
1678
 
1630
- await cfetch(
1679
+ await fetchResult(
1631
1680
  `/accounts/${config.account_id}/storage/kv/namespaces/${namespaceId}/values/${key}`,
1632
1681
  { method: "DELETE" }
1633
1682
  );
@@ -1637,11 +1686,11 @@ export async function main(argv: string[]): Promise<void> {
1637
1686
  );
1638
1687
 
1639
1688
  // :bulk
1640
- yargs.command(
1689
+ wrangler.command(
1641
1690
  "kv:bulk",
1642
1691
  "💪 Interact with multiple Workers KV key-value pairs at once",
1643
- (yargs) => {
1644
- return yargs
1692
+ (kvBulkYargs) => {
1693
+ return kvBulkYargs
1645
1694
  .command(
1646
1695
  "put <filename>",
1647
1696
  "Upload multiple key-value pairs to a namespace",
@@ -1681,8 +1730,9 @@ export async function main(argv: string[]): Promise<void> {
1681
1730
  try {
1682
1731
  parsedContent = JSON.parse(content);
1683
1732
  } catch (err) {
1684
- console.error(`could not parse json from ${filename}`);
1685
- throw err;
1733
+ throw new Error(
1734
+ `Could not parse json from ${filename}.\n${err.message ?? err}`
1735
+ );
1686
1736
  }
1687
1737
 
1688
1738
  if (args.local) {
@@ -1721,8 +1771,7 @@ export async function main(argv: string[]): Promise<void> {
1721
1771
  if (!config.account_id) {
1722
1772
  config.account_id = await getAccountId();
1723
1773
  if (!config.account_id) {
1724
- console.error("No account id found, quitting...");
1725
- return;
1774
+ throw new Error("No account id found, quitting...");
1726
1775
  }
1727
1776
  }
1728
1777
  }
@@ -1769,8 +1818,9 @@ export async function main(argv: string[]): Promise<void> {
1769
1818
  try {
1770
1819
  parsedContent = JSON.parse(content);
1771
1820
  } catch (err) {
1772
- console.error(`could not parse json from ${filename}`);
1773
- throw err;
1821
+ throw new Error(
1822
+ `Could not parse json from ${filename}.\n${err.message ?? err}`
1823
+ );
1774
1824
  }
1775
1825
 
1776
1826
  if (args.local) {
@@ -1801,8 +1851,7 @@ export async function main(argv: string[]): Promise<void> {
1801
1851
  if (!config.account_id) {
1802
1852
  config.account_id = await getAccountId();
1803
1853
  if (!config.account_id) {
1804
- console.error("No account id found, quitting...");
1805
- return;
1854
+ throw new Error("No account id found, quitting...");
1806
1855
  }
1807
1856
  }
1808
1857
  }
@@ -1817,10 +1866,11 @@ export async function main(argv: string[]): Promise<void> {
1817
1866
  }
1818
1867
  );
1819
1868
 
1820
- yargs.command("pages", "⚡️ Configure Cloudflare Pages", pages);
1869
+ wrangler.command("pages", "⚡️ Configure Cloudflare Pages", pages);
1821
1870
 
1822
- yargs
1871
+ wrangler
1823
1872
  .option("config", {
1873
+ alias: "c",
1824
1874
  describe: "Path to .toml configuration file",
1825
1875
  type: "string",
1826
1876
  async coerce(arg) {
@@ -1828,18 +1878,27 @@ export async function main(argv: string[]): Promise<void> {
1828
1878
  },
1829
1879
  })
1830
1880
  .option("local", {
1881
+ alias: "l",
1831
1882
  describe: "Run on my machine",
1832
1883
  type: "boolean",
1833
1884
  default: false, // I bet this will a point of contention. We'll revisit it.
1834
1885
  });
1835
1886
 
1836
- yargs.group(["config", "help", "version"], "Flags:");
1837
- yargs.version(wranglerVersion);
1887
+ wrangler.group(["config", "help", "version"], "Flags:");
1888
+ wrangler.help().alias("h", "help");
1889
+ wrangler.version(wranglerVersion).alias("v", "version");
1890
+ wrangler.exitProcess(false);
1891
+
1838
1892
  await initialiseUserConfig();
1839
1893
 
1840
- await yargs.parse(argv, (err, argv, output) => {
1841
- if (output) {
1842
- console.log(output);
1894
+ try {
1895
+ await wrangler.parse();
1896
+ } catch (e) {
1897
+ if (e instanceof CommandLineArgsError) {
1898
+ wrangler.showHelp("error");
1899
+ console.error(""); // Just adds a bit of space
1843
1900
  }
1844
- });
1901
+ console.error(e.message);
1902
+ throw e;
1903
+ }
1845
1904
  }