wrangler 0.0.0-ece06ea → 0.0.0-edc4b53

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 (41) hide show
  1. package/package.json +24 -15
  2. package/pages/functions/buildWorker.ts +62 -0
  3. package/pages/functions/filepath-routing.test.ts +39 -0
  4. package/pages/functions/filepath-routing.ts +221 -0
  5. package/pages/functions/identifiers.ts +78 -0
  6. package/pages/functions/routes.ts +158 -0
  7. package/pages/functions/template-worker.ts +144 -0
  8. package/src/__tests__/dev.test.tsx +66 -0
  9. package/src/__tests__/index.test.ts +254 -120
  10. package/src/__tests__/jest.setup.ts +22 -0
  11. package/src/__tests__/kv.test.ts +1106 -0
  12. package/src/__tests__/mock-cfetch.ts +171 -0
  13. package/src/__tests__/mock-dialogs.ts +65 -0
  14. package/src/__tests__/run-in-tmp.ts +19 -0
  15. package/src/__tests__/run-wrangler.ts +32 -0
  16. package/src/api/form_data.ts +71 -98
  17. package/src/api/preview.ts +29 -24
  18. package/src/api/worker.ts +38 -44
  19. package/src/cfetch/index.ts +102 -0
  20. package/src/cfetch/internal.ts +65 -0
  21. package/src/cli.ts +6 -7
  22. package/src/config.ts +476 -113
  23. package/src/dev.tsx +291 -256
  24. package/src/dialogs.tsx +4 -12
  25. package/src/index.tsx +381 -252
  26. package/src/inspect.ts +524 -0
  27. package/src/kv.tsx +110 -45
  28. package/src/module-collection.ts +64 -0
  29. package/src/pages.tsx +900 -213
  30. package/src/proxy.ts +294 -0
  31. package/src/publish.ts +67 -63
  32. package/src/sites.tsx +10 -7
  33. package/src/tail.tsx +5 -3
  34. package/src/user.tsx +31 -33
  35. package/wrangler-dist/cli.js +43444 -56347
  36. package/wrangler-dist/cli.js.map +3 -3
  37. package/src/__tests__/fixtures/init/.gitkeep +0 -0
  38. package/src/__tests__/mock-cfetch.js +0 -46
  39. package/src/api/inspect.ts +0 -441
  40. package/src/cfetch.ts +0 -72
  41. 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) => {
@@ -381,6 +435,20 @@ export async function main(argv: string[]): Promise<void> {
381
435
  type: "string",
382
436
  // TODO: get choices for the toml file?
383
437
  })
438
+ .option("compatibility-date", {
439
+ describe: "Date to use for compatibility checks",
440
+ type: "string",
441
+ })
442
+ .option("compatibility-flags", {
443
+ describe: "Flags to use for compatibility checks",
444
+ type: "array",
445
+ alias: "compatibility-flag",
446
+ })
447
+ .option("latest", {
448
+ describe: "Use the latest version of the worker runtime",
449
+ type: "boolean",
450
+ default: true,
451
+ })
384
452
  .option("ip", {
385
453
  describe: "IP address to listen on",
386
454
  type: "string",
@@ -401,7 +469,7 @@ export async function main(argv: string[]): Promise<void> {
401
469
  describe: "Protocol to listen to requests on, defaults to http.",
402
470
  choices: ["http", "https"],
403
471
  })
404
- .option("public", {
472
+ .option("experimental-public", {
405
473
  describe: "Static assets to be served",
406
474
  type: "string",
407
475
  })
@@ -422,16 +490,24 @@ export async function main(argv: string[]): Promise<void> {
422
490
  .option("jsx-fragment", {
423
491
  describe: "The function that is called for each JSX fragment",
424
492
  type: "string",
425
- })
426
- .option("polyfill-node", {
427
- describe: "Try to polyfill node builtins",
428
- type: "boolean",
429
493
  });
430
494
  },
431
495
  async (args) => {
432
496
  const { filename, format } = args;
433
497
  const config = args.config as Config;
434
498
 
499
+ if (args["experimental-public"]) {
500
+ console.warn(
501
+ "🚨 The --experimental-public field is experimental and will change in the future."
502
+ );
503
+ }
504
+
505
+ if (args.public) {
506
+ throw new Error(
507
+ "🚨 The --public field has been renamed to --experimental-public, and will change behaviour in the future."
508
+ );
509
+ }
510
+
435
511
  // -- snip, extract --
436
512
 
437
513
  if (!args.local) {
@@ -444,8 +520,7 @@ export async function main(argv: string[]): Promise<void> {
444
520
  if (!config.account_id) {
445
521
  config.account_id = await getAccountId();
446
522
  if (!config.account_id) {
447
- console.error("No account id found, quitting...");
448
- return;
523
+ throw new Error("No account id found, quitting...");
449
524
  }
450
525
  }
451
526
  }
@@ -454,17 +529,22 @@ export async function main(argv: string[]): Promise<void> {
454
529
 
455
530
  const envRootObj = args.env ? config.env[args.env] || {} : config;
456
531
 
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
- );
532
+ // TODO: this error shouldn't actually happen,
533
+ // but we haven't fixed it internally yet
534
+ if ("durable_objects" in envRootObj) {
535
+ if (!(args.name || config.name)) {
536
+ console.warn(
537
+ '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.'
538
+ );
539
+ }
540
+ // TODO: if not already published, publish a draft worker
462
541
  }
463
542
 
464
- render(
543
+ const { waitUntilExit } = render(
465
544
  <Dev
466
545
  name={args.name || config.name}
467
- entry={filename}
546
+ entry={path.relative(process.cwd(), filename)}
547
+ buildCommand={config.build || {}}
468
548
  format={format}
469
549
  initialMode={args.local ? "local" : "remote"}
470
550
  jsxFactory={args["jsx-factory"] || envRootObj?.jsx_factory}
@@ -472,40 +552,51 @@ export async function main(argv: string[]): Promise<void> {
472
552
  accountId={config.account_id}
473
553
  site={args.site || config.site?.bucket}
474
554
  port={args.port || config.dev?.port}
475
- polyfillNode={polyfillNode}
476
- public={args.public}
477
- compatibilityDate={config.compatibility_date}
478
- compatibilityFlags={config.compatibility_flags}
555
+ public={args["experimental-public"]}
556
+ compatibilityDate={
557
+ args["compatibility-date"] ||
558
+ config.compatibility_date ||
559
+ new Date().toISOString().substring(0, 10)
560
+ }
561
+ compatibilityFlags={
562
+ (args["compatibility-flags"] as string[]) ||
563
+ config.compatibility_flags
564
+ }
479
565
  usageModel={config.usage_model}
480
- variables={{
481
- ...(envRootObj?.vars || {}),
482
- ...(envRootObj?.kv_namespaces || []).reduce(
483
- (obj, { binding, preview_id }) => {
566
+ bindings={{
567
+ kv_namespaces: envRootObj.kv_namespaces?.map(
568
+ ({ binding, preview_id, id: _id }) => {
569
+ // In `dev`, we make folks use a separate kv namespace called
570
+ // `preview_id` instead of `id` so that they don't
571
+ // break production data. So here we check that a `preview_id`
572
+ // has actually been configured.
573
+ // This whole block of code will be obsoleted in the future
574
+ // when we have copy-on-write for previews on edge workers.
484
575
  if (!preview_id) {
485
576
  // TODO: This error has to be a _lot_ better, ideally just asking
486
577
  // to create a preview namespace for the user automatically
487
578
  throw new Error(
488
- "kv namespaces need a preview id during dev mode"
489
- );
579
+ `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`
580
+ ); // Ugh, I really don't like this message very much
490
581
  }
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
- {}
582
+ return {
583
+ binding,
584
+ id: preview_id,
585
+ };
586
+ }
500
587
  ),
588
+ vars: envRootObj.vars,
589
+ durable_objects: envRootObj.durable_objects,
590
+ services: envRootObj.experimental_services,
501
591
  }}
502
592
  />
503
593
  );
594
+ await waitUntilExit();
504
595
  }
505
596
  );
506
597
 
507
598
  // publish
508
- yargs.command(
599
+ wrangler.command(
509
600
  "publish [script]",
510
601
  "🆙 Publish your Worker to Cloudflare.",
511
602
  (yargs) => {
@@ -522,7 +613,21 @@ export async function main(argv: string[]): Promise<void> {
522
613
  describe: "name to use when uploading",
523
614
  type: "string",
524
615
  })
525
- .option("public", {
616
+ .option("compatibility-date", {
617
+ describe: "Date to use for compatibility checks",
618
+ type: "string",
619
+ })
620
+ .option("compatibility-flags", {
621
+ describe: "Flags to use for compatibility checks",
622
+ type: "array",
623
+ alias: "compatibility-flag",
624
+ })
625
+ .option("latest", {
626
+ describe: "Use the latest version of the worker runtime",
627
+ type: "boolean",
628
+ default: false,
629
+ })
630
+ .option("experimental-public", {
526
631
  describe: "Static assets to be served",
527
632
  type: "string",
528
633
  })
@@ -553,19 +658,34 @@ export async function main(argv: string[]): Promise<void> {
553
658
  .option("jsx-fragment", {
554
659
  describe: "The function that is called for each JSX fragment",
555
660
  type: "string",
556
- })
557
- .option("polyfill-node", {
558
- describe: "Try to polyfill node builtins",
559
- type: "boolean",
560
661
  });
561
662
  },
562
663
  async (args) => {
563
664
  if (args.local) {
564
- console.error("🚫 Local publishing is not yet supported");
565
- return;
665
+ throw new NotImplementedError(
666
+ "🚫 Local publishing is not yet supported"
667
+ );
668
+ }
669
+
670
+ if (args["experimental-public"]) {
671
+ console.warn(
672
+ "🚨 The --experimental-public field is experimental and will change in the future."
673
+ );
674
+ }
675
+ if (args.public) {
676
+ throw new Error(
677
+ "🚨 The --public field has been renamed to --experimental-public, and will change behaviour in the future."
678
+ );
566
679
  }
680
+
567
681
  const config = args.config as Config;
568
682
 
683
+ if (args.latest) {
684
+ console.warn(
685
+ "⚠️ 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"
686
+ );
687
+ }
688
+
569
689
  // -- snip, extract --
570
690
  if (!args.local) {
571
691
  const loggedIn = await loginOrRefreshIfRequired();
@@ -577,39 +697,34 @@ export async function main(argv: string[]): Promise<void> {
577
697
  if (!config.account_id) {
578
698
  config.account_id = await getAccountId();
579
699
  if (!config.account_id) {
580
- console.error("No account id found, quitting...");
581
- return;
700
+ throw new Error("No account id found, quitting...");
582
701
  }
583
702
  }
584
703
  }
585
704
 
586
705
  // -- snip, end --
587
706
 
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
707
  await publish({
596
708
  config: args.config as Config,
597
709
  name: args.name,
598
710
  script: args.script,
599
711
  env: args.env,
712
+ compatibilityDate: args.latest
713
+ ? new Date().toISOString().substring(0, 10)
714
+ : args["compatibility-date"],
715
+ compatibilityFlags: args["compatibility-flags"] as string[],
600
716
  triggers: args.triggers,
601
717
  jsxFactory: args["jsx-factory"],
602
718
  jsxFragment: args["jsx-fragment"],
603
- polyfillNode: args["polyfill-node"],
604
719
  routes: args.routes,
605
- public: args.public,
720
+ public: args["experimental-public"],
606
721
  site: args.site,
607
722
  });
608
723
  }
609
724
  );
610
725
 
611
726
  // tail
612
- yargs.command(
727
+ wrangler.command(
613
728
  "tail [name]",
614
729
  "🦚 Starts a log tailing session for a deployed Worker.",
615
730
  (yargs) => {
@@ -656,8 +771,7 @@ export async function main(argv: string[]): Promise<void> {
656
771
  const config = args.config as Config;
657
772
 
658
773
  if (!(args.name || config.name)) {
659
- console.error("Missing script name");
660
- return;
774
+ throw new Error("Missing script name");
661
775
  }
662
776
  const scriptName = `${args.name || config.name}${
663
777
  args.env ? `-${args.env}` : ""
@@ -675,8 +789,7 @@ export async function main(argv: string[]): Promise<void> {
675
789
  if (!config.account_id) {
676
790
  config.account_id = await getAccountId();
677
791
  if (!config.account_id) {
678
- console.error("No account id found, quitting...");
679
- return;
792
+ throw new Error("No account id found, quitting...");
680
793
  }
681
794
  }
682
795
  }
@@ -700,9 +813,9 @@ export async function main(argv: string[]): Promise<void> {
700
813
  `successfully created tail, expires at ${expiration.toLocaleString()}`
701
814
  );
702
815
 
703
- onExit(() => {
816
+ onExit(async () => {
704
817
  tail.terminate();
705
- deleteTail();
818
+ await deleteTail();
706
819
  });
707
820
 
708
821
  tail.on("message", (data) => {
@@ -727,7 +840,7 @@ export async function main(argv: string[]): Promise<void> {
727
840
  );
728
841
 
729
842
  // preview
730
- yargs.command(
843
+ wrangler.command(
731
844
  "preview [method] [body]",
732
845
  false,
733
846
  (yargs) => {
@@ -753,20 +866,20 @@ export async function main(argv: string[]): Promise<void> {
753
866
  });
754
867
  },
755
868
  () => {
756
- // "🔬 [DEPRECATED] Preview your code temporarily on cloudflareworkers.com"
757
- console.error(
869
+ // "🔬 [DEPRECATED] Preview your code temporarily on https://cloudflareworkers.com"
870
+ throw new DeprecationError(
758
871
  "`wrangler preview` has been deprecated, please refer to TODO://some/path for alternatives"
759
872
  );
760
873
  }
761
874
  );
762
875
 
763
876
  // route
764
- yargs.command(
877
+ wrangler.command(
765
878
  "route",
766
879
  false, // I think we want to hide this command
767
880
  // "➡️ List or delete worker routes",
768
- (yargs) => {
769
- return yargs
881
+ (routeYargs) => {
882
+ return routeYargs
770
883
  .command(
771
884
  "list",
772
885
  "List a route associated with a zone",
@@ -790,11 +903,10 @@ export async function main(argv: string[]): Promise<void> {
790
903
  // TODO: use environment (current wrangler doesn't do so?)
791
904
  const zone = args.zone || (args.config as Config).zone_id;
792
905
  if (!zone) {
793
- console.error("missing zone id");
794
- return;
906
+ throw new Error("missing zone id");
795
907
  }
796
908
 
797
- console.log(await cfetch(`/zones/${zone}/workers/routes`));
909
+ console.log(await fetchResult(`/zones/${zone}/workers/routes`));
798
910
  }
799
911
  )
800
912
  .command(
@@ -824,7 +936,7 @@ export async function main(argv: string[]): Promise<void> {
824
936
  }
825
937
 
826
938
  console.log(
827
- await cfetch(`/zones/${zone}/workers/routes/${args.id}`, {
939
+ await fetchResult(`/zones/${zone}/workers/routes/${args.id}`, {
828
940
  method: "DELETE",
829
941
  })
830
942
  );
@@ -834,7 +946,7 @@ export async function main(argv: string[]): Promise<void> {
834
946
  );
835
947
 
836
948
  // subdomain
837
- yargs.command(
949
+ wrangler.command(
838
950
  "subdomain [name]",
839
951
  false,
840
952
  // "👷 Create or change your workers.dev subdomain.",
@@ -842,18 +954,18 @@ export async function main(argv: string[]): Promise<void> {
842
954
  return yargs.positional("name", { type: "string" });
843
955
  },
844
956
  () => {
845
- console.error(
957
+ throw new DeprecationError(
846
958
  "`wrangler subdomain` has been deprecated, please refer to TODO://some/path for alternatives"
847
959
  );
848
960
  }
849
961
  );
850
962
 
851
963
  // secret
852
- yargs.command(
964
+ wrangler.command(
853
965
  "secret",
854
966
  "🤫 Generate a secret that can be referenced in the worker script",
855
- (yargs) => {
856
- return yargs
967
+ (secretYargs) => {
968
+ return secretYargs
857
969
  .command(
858
970
  "put <key>",
859
971
  "Create or update a secret variable for a script",
@@ -875,16 +987,16 @@ export async function main(argv: string[]): Promise<void> {
875
987
  },
876
988
  async (args) => {
877
989
  if (args.local) {
878
- console.error("--local not implemented for this command yet");
879
- return;
990
+ throw new NotImplementedError(
991
+ "--local not implemented for this command yet"
992
+ );
880
993
  }
881
994
  const config = args.config as Config;
882
995
 
883
996
  // TODO: use environment (how does current wrangler do it?)
884
997
  const scriptName = args.name || config.name;
885
998
  if (!scriptName) {
886
- console.error("Missing script name");
887
- return;
999
+ throw new Error("Missing script name");
888
1000
  }
889
1001
 
890
1002
  // -- snip, extract --
@@ -899,8 +1011,7 @@ export async function main(argv: string[]): Promise<void> {
899
1011
  if (!config.account_id) {
900
1012
  config.account_id = await getAccountId();
901
1013
  if (!config.account_id) {
902
- console.error("No account id found, quitting...");
903
- return;
1014
+ throw new Error("No account id found, quitting...");
904
1015
  }
905
1016
  }
906
1017
  }
@@ -912,7 +1023,7 @@ export async function main(argv: string[]): Promise<void> {
912
1023
  "password"
913
1024
  );
914
1025
  async function submitSecret() {
915
- return await cfetch(
1026
+ return await fetchResult(
916
1027
  `/accounts/${config.account_id}/workers/scripts/${scriptName}/secrets/`,
917
1028
  {
918
1029
  method: "PUT",
@@ -931,7 +1042,7 @@ export async function main(argv: string[]): Promise<void> {
931
1042
  } catch (e) {
932
1043
  if (e.code === 10007) {
933
1044
  // upload a draft worker
934
- await cfetch(
1045
+ await fetchResult(
935
1046
  `/accounts/${config.account_id}/workers/scripts/${scriptName}`,
936
1047
  {
937
1048
  method: "PUT",
@@ -942,7 +1053,11 @@ export async function main(argv: string[]): Promise<void> {
942
1053
  content: `export default { fetch() {} }`,
943
1054
  type: "esm",
944
1055
  },
945
- variables: {},
1056
+ bindings: {
1057
+ kv_namespaces: [],
1058
+ vars: {},
1059
+ durable_objects: { bindings: [] },
1060
+ },
946
1061
  modules: [],
947
1062
  }),
948
1063
  }
@@ -976,8 +1091,9 @@ export async function main(argv: string[]): Promise<void> {
976
1091
  },
977
1092
  async (args) => {
978
1093
  if (args.local) {
979
- console.error("--local not implemented for this command yet");
980
- return;
1094
+ throw new NotImplementedError(
1095
+ "--local not implemented for this command yet"
1096
+ );
981
1097
  }
982
1098
  const config = args.config as Config;
983
1099
 
@@ -999,8 +1115,7 @@ export async function main(argv: string[]): Promise<void> {
999
1115
  if (!config.account_id) {
1000
1116
  config.account_id = await getAccountId();
1001
1117
  if (!config.account_id) {
1002
- console.error("No account id found, quitting...");
1003
- return;
1118
+ throw new Error("No account id found, quitting...");
1004
1119
  }
1005
1120
  }
1006
1121
  }
@@ -1013,7 +1128,7 @@ export async function main(argv: string[]): Promise<void> {
1013
1128
  );
1014
1129
 
1015
1130
  console.log(
1016
- await cfetch(
1131
+ await fetchResult(
1017
1132
  `/accounts/${config.account_id}/workers/scripts/${scriptName}/secrets/${args.key}`,
1018
1133
  { method: "DELETE" }
1019
1134
  )
@@ -1038,16 +1153,16 @@ export async function main(argv: string[]): Promise<void> {
1038
1153
  },
1039
1154
  async (args) => {
1040
1155
  if (args.local) {
1041
- console.error("--local not implemented for this command yet");
1042
- return;
1156
+ throw new NotImplementedError(
1157
+ "--local not implemented for this command yet"
1158
+ );
1043
1159
  }
1044
1160
  const config = args.config as Config;
1045
1161
 
1046
1162
  // TODO: use environment (how does current wrangler do it?)
1047
1163
  const scriptName = args.name || config.name;
1048
1164
  if (!scriptName) {
1049
- console.error("Missing script name");
1050
- return;
1165
+ throw new Error("Missing script name");
1051
1166
  }
1052
1167
 
1053
1168
  // -- snip, extract --
@@ -1062,8 +1177,7 @@ export async function main(argv: string[]): Promise<void> {
1062
1177
  if (!config.account_id) {
1063
1178
  config.account_id = await getAccountId();
1064
1179
  if (!config.account_id) {
1065
- console.error("No account id found, quitting...");
1066
- return;
1180
+ throw new Error("No account id found, quitting...");
1067
1181
  }
1068
1182
  }
1069
1183
  }
@@ -1071,7 +1185,7 @@ export async function main(argv: string[]): Promise<void> {
1071
1185
  // -- snip, end --
1072
1186
 
1073
1187
  console.log(
1074
- await cfetch(
1188
+ await fetchResult(
1075
1189
  `/accounts/${config.account_id}/workers/scripts/${scriptName}/secrets`
1076
1190
  )
1077
1191
  );
@@ -1082,11 +1196,11 @@ export async function main(argv: string[]): Promise<void> {
1082
1196
 
1083
1197
  // kv
1084
1198
  // :namespace
1085
- yargs.command(
1199
+ wrangler.command(
1086
1200
  "kv:namespace",
1087
1201
  "🗂️ Interact with your Workers KV Namespaces",
1088
- (yargs) => {
1089
- return yargs
1202
+ (namespaceYargs) => {
1203
+ return namespaceYargs
1090
1204
  .command(
1091
1205
  "create <namespace>",
1092
1206
  "Create a new namespace",
@@ -1106,13 +1220,19 @@ export async function main(argv: string[]): Promise<void> {
1106
1220
  });
1107
1221
  },
1108
1222
  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(" ")}"?`
1223
+ if (args._.length > 2) {
1224
+ const extraArgs = args._.slice(2).join(" ");
1225
+ throw new CommandLineArgsError(
1226
+ `Unexpected additional positional arguments "${extraArgs}".`
1114
1227
  );
1115
1228
  }
1229
+
1230
+ if (!isValidNamespaceBinding(args.namespace)) {
1231
+ throw new CommandLineArgsError(
1232
+ `The namespace binding name "${args.namespace}" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number.`
1233
+ );
1234
+ }
1235
+
1116
1236
  const config = args.config as Config;
1117
1237
  if (!config.name) {
1118
1238
  console.warn(
@@ -1120,13 +1240,10 @@ export async function main(argv: string[]): Promise<void> {
1120
1240
  );
1121
1241
  }
1122
1242
 
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
- }
1243
+ const name = config.name || "worker";
1244
+ const environment = args.env ? `-${args.env}` : "";
1245
+ const preview = args.preview ? "_preview" : "";
1246
+ const title = `${name}${environment}-${args.namespace}${preview}`;
1130
1247
 
1131
1248
  if (args.local) {
1132
1249
  const { Miniflare } = await import("miniflare");
@@ -1135,7 +1252,7 @@ export async function main(argv: string[]): Promise<void> {
1135
1252
  // TODO: these options shouldn't be required
1136
1253
  script: ` `, // has to be a string with at least one char
1137
1254
  });
1138
- mf.getKVNamespace(title); // this should "create" the namespace
1255
+ await mf.getKVNamespace(title); // this should "create" the namespace
1139
1256
  console.log(`✨ Success! Created KV namespace ${title}`);
1140
1257
  return;
1141
1258
  }
@@ -1152,8 +1269,7 @@ export async function main(argv: string[]): Promise<void> {
1152
1269
  if (!config.account_id) {
1153
1270
  config.account_id = await getAccountId();
1154
1271
  if (!config.account_id) {
1155
- console.error("No account id found, quitting...");
1156
- return;
1272
+ throw new Error("No account id found, quitting...");
1157
1273
  }
1158
1274
  }
1159
1275
  }
@@ -1163,31 +1279,19 @@ export async function main(argv: string[]): Promise<void> {
1163
1279
  // TODO: generate a binding name stripping non alphanumeric chars
1164
1280
 
1165
1281
  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
- );
1282
+ const namespaceId = await createNamespace(config.account_id, title);
1179
1283
 
1180
1284
  console.log("✨ Success!");
1285
+ const envString = args.env ? ` under [env.${args.env}]` : "";
1286
+ const previewString = args.preview ? "preview_" : "";
1181
1287
  console.log(
1182
- `Add the following to your configuration file in your kv_namespaces array${
1183
- args.env ? ` under [env.${args.env}]` : ""
1184
- }:`
1288
+ `Add the following to your configuration file in your kv_namespaces array${envString}:`
1185
1289
  );
1186
1290
  console.log(
1187
- `{ binding = "${args.namespace}", ${
1188
- args.preview ? "preview_" : ""
1189
- }id = "${response.id}" }`
1291
+ `{ binding = "${args.namespace}", ${previewString}id = "${namespaceId}" }`
1190
1292
  );
1293
+
1294
+ // TODO: automatically write this block to the wrangler.toml config file??
1191
1295
  }
1192
1296
  )
1193
1297
  .command(
@@ -1196,8 +1300,9 @@ export async function main(argv: string[]): Promise<void> {
1196
1300
  {},
1197
1301
  async (args) => {
1198
1302
  if (args.local) {
1199
- console.error(`local mode is not yet supported for this command`);
1200
- return;
1303
+ throw new NotImplementedError(
1304
+ `local mode is not yet supported for this command`
1305
+ );
1201
1306
  }
1202
1307
 
1203
1308
  const config = args.config as Config;
@@ -1214,8 +1319,7 @@ export async function main(argv: string[]): Promise<void> {
1214
1319
  if (!config.account_id) {
1215
1320
  config.account_id = await getAccountId();
1216
1321
  if (!config.account_id) {
1217
- console.error("No account id found, quitting...");
1218
- return;
1322
+ throw new Error("No account id found, quitting...");
1219
1323
  }
1220
1324
  }
1221
1325
  }
@@ -1258,21 +1362,19 @@ export async function main(argv: string[]): Promise<void> {
1258
1362
  },
1259
1363
  async (args) => {
1260
1364
  if (args.local) {
1261
- console.error(`local mode is not yet supported for this command`);
1262
- return;
1365
+ throw new NotImplementedError(
1366
+ `local mode is not yet supported for this command`
1367
+ );
1263
1368
  }
1264
1369
  const config = args.config as Config;
1265
1370
 
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");
1371
+ let id;
1372
+ try {
1373
+ id = getNamespaceId(args);
1374
+ } catch (e) {
1375
+ throw new CommandLineArgsError(
1376
+ "Not able to delete namespace.\n" + e.message
1377
+ );
1276
1378
  }
1277
1379
 
1278
1380
  // -- snip, extract --
@@ -1288,15 +1390,14 @@ export async function main(argv: string[]): Promise<void> {
1288
1390
  if (!config.account_id) {
1289
1391
  config.account_id = await getAccountId();
1290
1392
  if (!config.account_id) {
1291
- console.error("No account id found, quitting...");
1292
- return;
1393
+ throw new Error("No account id found, quitting...");
1293
1394
  }
1294
1395
  }
1295
1396
  }
1296
1397
 
1297
1398
  // -- snip, end --
1298
1399
 
1299
- await cfetch<{ id: string }>(
1400
+ await fetchResult<{ id: string }>(
1300
1401
  `/accounts/${config.account_id}/storage/kv/namespaces/${id}`,
1301
1402
  { method: "DELETE" }
1302
1403
  );
@@ -1324,13 +1425,13 @@ export async function main(argv: string[]): Promise<void> {
1324
1425
  );
1325
1426
 
1326
1427
  // :key
1327
- yargs.command(
1428
+ wrangler.command(
1328
1429
  "kv:key",
1329
1430
  "🔑 Individually manage Workers KV key-value pairs",
1330
- (yargs) => {
1331
- return yargs
1431
+ (kvKeyYargs) => {
1432
+ return kvKeyYargs
1332
1433
  .command(
1333
- "put <key> <value>",
1434
+ "put <key> [value]",
1334
1435
  "Writes a single key/value pair to the given namespace.",
1335
1436
  (yargs) => {
1336
1437
  return yargs
@@ -1371,7 +1472,8 @@ export async function main(argv: string[]): Promise<void> {
1371
1472
  .option("path", {
1372
1473
  type: "string",
1373
1474
  describe: "Read value from the file at a given path.",
1374
- });
1475
+ })
1476
+ .check(demandOneOfOption("value", "path"));
1375
1477
  },
1376
1478
  async ({ key, ttl, expiration, ...args }) => {
1377
1479
  const namespaceId = getNamespaceId(args);
@@ -1380,7 +1482,16 @@ export async function main(argv: string[]): Promise<void> {
1380
1482
  : args.value;
1381
1483
  const config = args.config as Config;
1382
1484
 
1383
- console.log(`writing ${key}=${value} to namespace ${namespaceId}`);
1485
+ if (args.path) {
1486
+ console.log(
1487
+ `writing the contents of ${args.path} to the key "${key}" on namespace ${namespaceId}`
1488
+ );
1489
+ } else {
1490
+ console.log(
1491
+ `writing the value "${value}" to key "${key}" on namespace ${namespaceId}`
1492
+ );
1493
+ }
1494
+
1384
1495
  if (args.local) {
1385
1496
  const { Miniflare } = await import("miniflare");
1386
1497
  const mf = new Miniflare({
@@ -1405,8 +1516,7 @@ export async function main(argv: string[]): Promise<void> {
1405
1516
  if (!config.account_id) {
1406
1517
  config.account_id = await getAccountId();
1407
1518
  if (!config.account_id) {
1408
- console.error("No account id found, quitting...");
1409
- return;
1519
+ throw new Error("No account id found, quitting...");
1410
1520
  }
1411
1521
  }
1412
1522
  }
@@ -1437,6 +1547,12 @@ export async function main(argv: string[]): Promise<void> {
1437
1547
  type: "string",
1438
1548
  describe: "Perform on a specific environment",
1439
1549
  })
1550
+ .option("preview", {
1551
+ type: "boolean",
1552
+ // In the case of listing keys we will default to non-preview mode
1553
+ default: false,
1554
+ describe: "Interact with a preview namespace",
1555
+ })
1440
1556
  .option("prefix", {
1441
1557
  type: "string",
1442
1558
  describe: "A prefix to filter listed keys",
@@ -1474,8 +1590,7 @@ export async function main(argv: string[]): Promise<void> {
1474
1590
  if (!config.account_id) {
1475
1591
  config.account_id = await getAccountId();
1476
1592
  if (!config.account_id) {
1477
- console.error("No account id found, quitting...");
1478
- return;
1593
+ throw new Error("No account id found, quitting...");
1479
1594
  }
1480
1595
  }
1481
1596
  }
@@ -1512,6 +1627,12 @@ export async function main(argv: string[]): Promise<void> {
1512
1627
  .option("preview", {
1513
1628
  type: "boolean",
1514
1629
  describe: "Interact with a preview namespace",
1630
+ })
1631
+ .option("preview", {
1632
+ type: "boolean",
1633
+ // In the case of getting key values we will default to non-preview mode
1634
+ default: false,
1635
+ describe: "Interact with a preview namespace",
1515
1636
  });
1516
1637
  },
1517
1638
  async ({ key, ...args }) => {
@@ -1543,20 +1664,15 @@ export async function main(argv: string[]): Promise<void> {
1543
1664
  if (!config.account_id) {
1544
1665
  config.account_id = await getAccountId();
1545
1666
  if (!config.account_id) {
1546
- console.error("No account id found, quitting...");
1547
- return;
1667
+ throw new Error("No account id found, quitting...");
1548
1668
  }
1549
1669
  }
1550
1670
  }
1551
1671
 
1552
1672
  // -- snip, end --
1553
1673
 
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
1674
  console.log(
1559
- await cfetch(
1675
+ await fetchRaw(
1560
1676
  `/accounts/${config.account_id}/storage/kv/namespaces/${namespaceId}/values/${key}`
1561
1677
  )
1562
1678
  );
@@ -1592,6 +1708,10 @@ export async function main(argv: string[]): Promise<void> {
1592
1708
  async ({ key, ...args }) => {
1593
1709
  const namespaceId = getNamespaceId(args);
1594
1710
 
1711
+ console.log(
1712
+ `deleting the key "${key}" on namespace ${namespaceId}`
1713
+ );
1714
+
1595
1715
  if (args.local) {
1596
1716
  const { Miniflare } = await import("miniflare");
1597
1717
  const mf = new Miniflare({
@@ -1619,15 +1739,14 @@ export async function main(argv: string[]): Promise<void> {
1619
1739
  if (!config.account_id) {
1620
1740
  config.account_id = await getAccountId();
1621
1741
  if (!config.account_id) {
1622
- console.error("No account id found, quitting...");
1623
- return;
1742
+ throw new Error("No account id found, quitting...");
1624
1743
  }
1625
1744
  }
1626
1745
  }
1627
1746
 
1628
1747
  // -- snip, end --
1629
1748
 
1630
- await cfetch(
1749
+ await fetchResult(
1631
1750
  `/accounts/${config.account_id}/storage/kv/namespaces/${namespaceId}/values/${key}`,
1632
1751
  { method: "DELETE" }
1633
1752
  );
@@ -1637,11 +1756,11 @@ export async function main(argv: string[]): Promise<void> {
1637
1756
  );
1638
1757
 
1639
1758
  // :bulk
1640
- yargs.command(
1759
+ wrangler.command(
1641
1760
  "kv:bulk",
1642
1761
  "💪 Interact with multiple Workers KV key-value pairs at once",
1643
- (yargs) => {
1644
- return yargs
1762
+ (kvBulkYargs) => {
1763
+ return kvBulkYargs
1645
1764
  .command(
1646
1765
  "put <filename>",
1647
1766
  "Upload multiple key-value pairs to a namespace",
@@ -1681,8 +1800,9 @@ export async function main(argv: string[]): Promise<void> {
1681
1800
  try {
1682
1801
  parsedContent = JSON.parse(content);
1683
1802
  } catch (err) {
1684
- console.error(`could not parse json from ${filename}`);
1685
- throw err;
1803
+ throw new Error(
1804
+ `Could not parse json from ${filename}.\n${err.message ?? err}`
1805
+ );
1686
1806
  }
1687
1807
 
1688
1808
  if (args.local) {
@@ -1721,8 +1841,7 @@ export async function main(argv: string[]): Promise<void> {
1721
1841
  if (!config.account_id) {
1722
1842
  config.account_id = await getAccountId();
1723
1843
  if (!config.account_id) {
1724
- console.error("No account id found, quitting...");
1725
- return;
1844
+ throw new Error("No account id found, quitting...");
1726
1845
  }
1727
1846
  }
1728
1847
  }
@@ -1769,8 +1888,9 @@ export async function main(argv: string[]): Promise<void> {
1769
1888
  try {
1770
1889
  parsedContent = JSON.parse(content);
1771
1890
  } catch (err) {
1772
- console.error(`could not parse json from ${filename}`);
1773
- throw err;
1891
+ throw new Error(
1892
+ `Could not parse json from ${filename}.\n${err.message ?? err}`
1893
+ );
1774
1894
  }
1775
1895
 
1776
1896
  if (args.local) {
@@ -1801,8 +1921,7 @@ export async function main(argv: string[]): Promise<void> {
1801
1921
  if (!config.account_id) {
1802
1922
  config.account_id = await getAccountId();
1803
1923
  if (!config.account_id) {
1804
- console.error("No account id found, quitting...");
1805
- return;
1924
+ throw new Error("No account id found, quitting...");
1806
1925
  }
1807
1926
  }
1808
1927
  }
@@ -1817,10 +1936,11 @@ export async function main(argv: string[]): Promise<void> {
1817
1936
  }
1818
1937
  );
1819
1938
 
1820
- yargs.command("pages", "⚡️ Configure Cloudflare Pages", pages);
1939
+ wrangler.command("pages", "⚡️ Configure Cloudflare Pages", pages);
1821
1940
 
1822
- yargs
1941
+ wrangler
1823
1942
  .option("config", {
1943
+ alias: "c",
1824
1944
  describe: "Path to .toml configuration file",
1825
1945
  type: "string",
1826
1946
  async coerce(arg) {
@@ -1828,18 +1948,27 @@ export async function main(argv: string[]): Promise<void> {
1828
1948
  },
1829
1949
  })
1830
1950
  .option("local", {
1951
+ alias: "l",
1831
1952
  describe: "Run on my machine",
1832
1953
  type: "boolean",
1833
1954
  default: false, // I bet this will a point of contention. We'll revisit it.
1834
1955
  });
1835
1956
 
1836
- yargs.group(["config", "help", "version"], "Flags:");
1837
- yargs.version(wranglerVersion);
1957
+ wrangler.group(["config", "help", "version"], "Flags:");
1958
+ wrangler.help().alias("h", "help");
1959
+ wrangler.version(wranglerVersion).alias("v", "version");
1960
+ wrangler.exitProcess(false);
1961
+
1838
1962
  await initialiseUserConfig();
1839
1963
 
1840
- await yargs.parse(argv, (err, argv, output) => {
1841
- if (output) {
1842
- console.log(output);
1964
+ try {
1965
+ await wrangler.parse();
1966
+ } catch (e) {
1967
+ if (e instanceof CommandLineArgsError) {
1968
+ wrangler.showHelp("error");
1969
+ console.error(""); // Just adds a bit of space
1843
1970
  }
1844
- });
1971
+ console.error(e.message);
1972
+ throw e;
1973
+ }
1845
1974
  }