wrangler 0.0.0-e3cab74 → 0.0.0-e3e3243

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 (66) hide show
  1. package/README.md +1 -1
  2. package/bin/wrangler.js +10 -1
  3. package/miniflare-dist/index.mjs +79 -0
  4. package/miniflare-dist/index.mjs.map +7 -0
  5. package/package.json +10 -7
  6. package/pages/functions/filepath-routing.test.ts +125 -25
  7. package/pages/functions/filepath-routing.ts +6 -3
  8. package/src/__tests__/configuration.test.ts +959 -324
  9. package/src/__tests__/dev.test.tsx +512 -84
  10. package/src/__tests__/guess-worker-format.test.ts +19 -17
  11. package/src/__tests__/helpers/mock-account-id.ts +22 -4
  12. package/src/__tests__/helpers/mock-cfetch.ts +3 -3
  13. package/src/__tests__/helpers/mock-console.ts +17 -9
  14. package/src/__tests__/helpers/mock-kv.ts +9 -16
  15. package/src/__tests__/helpers/mock-stdin.ts +103 -0
  16. package/src/__tests__/helpers/run-in-tmp.ts +3 -0
  17. package/src/__tests__/https-options.test.ts +127 -0
  18. package/src/__tests__/index.test.ts +128 -11
  19. package/src/__tests__/jest.setup.ts +16 -4
  20. package/src/__tests__/kv.test.ts +364 -125
  21. package/src/__tests__/logout.test.ts +15 -13
  22. package/src/__tests__/pages.test.ts +82 -0
  23. package/src/__tests__/publish.test.ts +1838 -735
  24. package/src/__tests__/r2.test.ts +4 -4
  25. package/src/__tests__/route.test.ts +45 -0
  26. package/src/__tests__/secret.test.ts +167 -68
  27. package/src/__tests__/tail.test.ts +2 -4
  28. package/src/__tests__/whoami.test.tsx +21 -7
  29. package/src/bundle.ts +27 -5
  30. package/src/cfetch/internal.ts +11 -5
  31. package/src/config/README.md +6 -6
  32. package/src/config/config.ts +54 -101
  33. package/src/config/environment.ts +67 -1
  34. package/src/config/index.ts +13 -3
  35. package/src/config/validation-helpers.ts +207 -23
  36. package/src/config/validation.ts +344 -129
  37. package/src/{api/preview.ts → create-worker-preview.ts} +39 -20
  38. package/src/{api/form_data.ts → create-worker-upload-form.ts} +1 -1
  39. package/src/dev/dev.tsx +392 -0
  40. package/src/dev/local.tsx +232 -0
  41. package/src/dev/remote.tsx +203 -0
  42. package/src/dev/use-esbuild.ts +104 -0
  43. package/src/entry.ts +192 -9
  44. package/src/https-options.ts +122 -0
  45. package/src/index.tsx +577 -515
  46. package/src/inspect.ts +9 -1
  47. package/src/intl-polyfill.d.ts +139 -0
  48. package/src/{kv.tsx → kv.ts} +97 -53
  49. package/src/miniflare-cli/README.md +30 -0
  50. package/src/miniflare-cli/enum-keys.ts +17 -0
  51. package/src/miniflare-cli/index.ts +37 -0
  52. package/src/module-collection.ts +90 -2
  53. package/src/open-in-browser.ts +14 -2
  54. package/src/pages.tsx +26 -10
  55. package/src/proxy.ts +104 -40
  56. package/src/publish.ts +53 -71
  57. package/src/reporting.ts +25 -9
  58. package/src/selfsigned.d.ts +29 -0
  59. package/src/sites.tsx +68 -29
  60. package/src/user.tsx +163 -85
  61. package/src/{api/worker.ts → worker.ts} +1 -24
  62. package/wrangler-dist/cli.js +5933 -3842
  63. package/wrangler-dist/cli.js.map +3 -3
  64. package/src/__tests__/helpers/mock-user.ts +0 -27
  65. package/src/dev.tsx +0 -961
  66. package/src/guess-worker-format.ts +0 -68
package/src/index.tsx CHANGED
@@ -3,17 +3,19 @@ import { writeFile, mkdir } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { setTimeout } from "node:timers/promises";
5
5
  import TOML from "@iarna/toml";
6
+ import chalk from "chalk";
6
7
  import { findUp } from "find-up";
7
8
  import getPort from "get-port";
8
9
  import { render } from "ink";
9
10
  import React from "react";
10
11
  import onExit from "signal-exit";
12
+ import supportsColor from "supports-color";
11
13
  import makeCLI from "yargs";
12
14
  import { version as wranglerVersion } from "../package.json";
13
- import { toFormData } from "./api/form_data";
14
15
  import { fetchResult } from "./cfetch";
15
16
  import { findWranglerToml, readConfig } from "./config";
16
- import Dev from "./dev";
17
+ import { createWorkerUploadForm } from "./create-worker-upload-form";
18
+ import Dev from "./dev/dev";
17
19
  import { confirm, prompt } from "./dialogs";
18
20
  import { getEntry } from "./entry";
19
21
  import {
@@ -26,9 +28,11 @@ import {
26
28
  createNamespace,
27
29
  isValidNamespaceBinding,
28
30
  getKeyValue,
31
+ isKeyValue,
32
+ unexpectedKeyValueProps,
29
33
  } from "./kv";
30
34
  import { getPackageManager } from "./package-manager";
31
- import { pages } from "./pages";
35
+ import { pages, pagesBetaWarning } from "./pages";
32
36
  import { formatMessage, ParseError, parseJSON, readFileSync } from "./parse";
33
37
  import publish from "./publish";
34
38
  import { createR2Bucket, deleteR2Bucket, listR2Buckets } from "./r2";
@@ -43,7 +47,6 @@ import {
43
47
  login,
44
48
  logout,
45
49
  listScopes,
46
- initialise as initialiseUserConfig,
47
50
  loginOrRefreshIfRequired,
48
51
  getAccountId,
49
52
  validateScopeKeys,
@@ -53,6 +56,7 @@ import { whoami } from "./whoami";
53
56
  import type { Config } from "./config";
54
57
  import type { TailCLIFilters } from "./tail";
55
58
  import type { RawData } from "ws";
59
+ import type { CommandModule } from "yargs";
56
60
  import type Yargs from "yargs";
57
61
 
58
62
  type ConfigPath = string | undefined;
@@ -79,25 +83,106 @@ ${TOML.stringify({ rules: config.build.upload.rules })}`
79
83
  return rules;
80
84
  }
81
85
 
82
- function isLegacyEnv(args: unknown, config: Config): boolean {
83
- return (
84
- (args as { "legacy-env": boolean | undefined })["legacy-env"] ??
85
- config.legacy_env
86
+ function printWranglerBanner() {
87
+ // Let's not print this in tests
88
+ if (typeof jest !== "undefined") {
89
+ return;
90
+ }
91
+ const text = ` ⛅️ wrangler ${wranglerVersion} `;
92
+
93
+ console.log(
94
+ text +
95
+ "\n" +
96
+ (supportsColor.stdout
97
+ ? chalk.hex("#FF8800")("-".repeat(text.length))
98
+ : "-".repeat(text.length))
86
99
  );
87
100
  }
88
101
 
102
+ function isLegacyEnv(args: unknown, config: Config): boolean {
103
+ return config.legacy_env;
104
+ }
105
+
89
106
  function getScriptName(
90
107
  args: { name: string | undefined; env: string | undefined },
91
108
  config: Config
92
109
  ): string | undefined {
93
- const shortScriptName = args.name ?? config.name;
94
- if (!shortScriptName) {
95
- return;
110
+ if (args.name && isLegacyEnv(args, config) && args.env) {
111
+ throw new CommandLineArgsError(
112
+ "In legacy environment mode you cannot use --name and --env together. If you want to specify a Worker name for a specific environment you can add the following to your wrangler.toml config:" +
113
+ `
114
+ [env.${args.env}]
115
+ name = "${args.name}"
116
+ `
117
+ );
118
+ }
119
+
120
+ return args.name ?? config.name;
121
+ }
122
+
123
+ /**
124
+ * Alternative to the getScriptName() because special Legacy cases allowed "name", and "env" together in Wrangler1
125
+ */
126
+ function getLegacyScriptName(
127
+ args: { name: string | undefined; env: string | undefined },
128
+ config: Config
129
+ ) {
130
+ return args.name && args.env && isLegacyEnv(args, config)
131
+ ? `${args.name}-${args.env}`
132
+ : args.name ?? config.name;
133
+ }
134
+
135
+ /**
136
+ * Ensure that a user is logged in, and a valid account_id is available.
137
+ */
138
+ async function requireAuth(
139
+ config: Config,
140
+ isInteractive = true
141
+ ): Promise<string> {
142
+ const loggedIn = await loginOrRefreshIfRequired(isInteractive);
143
+ if (!loggedIn) {
144
+ // didn't login, let's just quit
145
+ throw new Error("Did not login, quitting...");
96
146
  }
147
+ const accountId = config.account_id || (await getAccountId(isInteractive));
148
+ if (!accountId) {
149
+ throw new Error("No account id found, quitting...");
150
+ }
151
+
152
+ return accountId;
153
+ }
154
+
155
+ /**
156
+ * Get a promise to the streamed input from stdin.
157
+ *
158
+ * This function can be used to grab the incoming stream of data from, say,
159
+ * piping the output of another process into the wrangler process.
160
+ */
161
+ function readFromStdin(): Promise<string> {
162
+ return new Promise((resolve, reject) => {
163
+ const stdin = process.stdin;
164
+ const chunks: string[] = [];
165
+
166
+ // When there is data ready to be read, the `readable` event will be triggered.
167
+ // In the handler for `readable` we call `read()` over and over until all the available data has been read.
168
+ stdin.on("readable", () => {
169
+ let chunk;
170
+ while (null !== (chunk = stdin.read())) {
171
+ chunks.push(chunk);
172
+ }
173
+ });
174
+
175
+ // When the streamed data is complete the `end` event will be triggered.
176
+ // In the handler for `end` we join the chunks together and resolve the promise.
177
+ stdin.on("end", () => {
178
+ resolve(chunks.join(""));
179
+ });
97
180
 
98
- return isLegacyEnv(args, config)
99
- ? `${shortScriptName}${args.env ? `-${args.env}` : ""}`
100
- : shortScriptName;
181
+ // If there is an `error` event then the handler will reject the promise.
182
+ stdin.on("error", (err) => {
183
+ reject(err);
184
+ });
185
+ });
101
186
  }
102
187
 
103
188
  // a helper to demand one of a set of options
@@ -134,6 +219,7 @@ class DeprecationError extends Error {
134
219
 
135
220
  export async function main(argv: string[]): Promise<void> {
136
221
  const wrangler = makeCLI(argv)
222
+ .strict()
137
223
  // We handle errors ourselves in a try-catch around `yargs.parse`.
138
224
  // If you want the "help info" to be displayed then throw an instance of `CommandLineArgsError`.
139
225
  // Otherwise we just log the error that was thrown without any "help info".
@@ -148,7 +234,15 @@ export async function main(argv: string[]): Promise<void> {
148
234
  .scriptName("wrangler")
149
235
  .wrap(null);
150
236
 
151
- // the default is to simply print the help menu
237
+ // Default help command that supports the subcommands
238
+ const subHelp: CommandModule = {
239
+ command: ["*"],
240
+ handler: async (args) => {
241
+ setImmediate(() =>
242
+ wrangler.parse([...args._.map((a) => `${a}`), "--help"])
243
+ );
244
+ },
245
+ };
152
246
  wrangler.command(
153
247
  ["*"],
154
248
  false,
@@ -189,7 +283,7 @@ export async function main(argv: string[]): Promise<void> {
189
283
  () => {
190
284
  // "👯 [DEPRECATED]. Scaffold a Cloudflare Workers project from a public GitHub repository.",
191
285
  throw new DeprecationError(
192
- "`wrangler generate` has been deprecated, please refer to TODO://some/path for alternatives"
286
+ "`wrangler generate` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#generate for alternatives"
193
287
  );
194
288
  }
195
289
  );
@@ -204,6 +298,12 @@ export async function main(argv: string[]): Promise<void> {
204
298
  describe: "The name of your worker",
205
299
  type: "string",
206
300
  })
301
+ .option("type", {
302
+ describe: "The type of worker to create",
303
+ type: "string",
304
+ choices: ["rust", "javascript", "webpack"],
305
+ deprecated: true,
306
+ })
207
307
  .option("yes", {
208
308
  describe: 'Answer "yes" to any prompts for new projects',
209
309
  type: "boolean",
@@ -211,7 +311,8 @@ export async function main(argv: string[]): Promise<void> {
211
311
  });
212
312
  },
213
313
  async (args) => {
214
- if ("type" in args) {
314
+ printWranglerBanner();
315
+ if (args.type) {
215
316
  let message = "The --type option is no longer supported.";
216
317
  if (args.type === "webpack") {
217
318
  message +=
@@ -517,7 +618,7 @@ export async function main(argv: string[]): Promise<void> {
517
618
  () => {
518
619
  // "[DEPRECATED] 🦀 Build your project (if applicable)",
519
620
  throw new DeprecationError(
520
- "`wrangler build` has been deprecated, please refer to TODO://some/path for alternatives"
621
+ "`wrangler build` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#build for alternatives"
521
622
  );
522
623
  }
523
624
  );
@@ -544,6 +645,7 @@ export async function main(argv: string[]): Promise<void> {
544
645
  // TODO: scopes
545
646
  },
546
647
  async (args) => {
648
+ printWranglerBanner();
547
649
  if (args["scopes-list"]) {
548
650
  listScopes();
549
651
  return;
@@ -578,6 +680,7 @@ export async function main(argv: string[]): Promise<void> {
578
680
  // "🚪 Logout from Cloudflare",
579
681
  () => {},
580
682
  async () => {
683
+ printWranglerBanner();
581
684
  await logout();
582
685
  }
583
686
  );
@@ -588,6 +691,7 @@ export async function main(argv: string[]): Promise<void> {
588
691
  "🕵️ Retrieve your user info and test your auth config",
589
692
  () => {},
590
693
  async () => {
694
+ printWranglerBanner();
591
695
  await whoami();
592
696
  }
593
697
  );
@@ -600,7 +704,7 @@ export async function main(argv: string[]): Promise<void> {
600
704
  () => {
601
705
  // "🕵️ Authenticate Wrangler with a Cloudflare API Token",
602
706
  throw new DeprecationError(
603
- "`wrangler config` has been deprecated, please refer to TODO://some/path for alternatives"
707
+ "`wrangler config` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#config for alternatives"
604
708
  );
605
709
  }
606
710
  );
@@ -626,6 +730,7 @@ export async function main(argv: string[]): Promise<void> {
626
730
  .option("env", {
627
731
  describe: "Perform on a specific environment",
628
732
  type: "string",
733
+ alias: "e",
629
734
  })
630
735
  .option("compatibility-date", {
631
736
  describe: "Date to use for compatibility checks",
@@ -642,9 +747,8 @@ export async function main(argv: string[]): Promise<void> {
642
747
  default: true,
643
748
  })
644
749
  .option("ip", {
645
- describe: "IP address to listen on",
750
+ describe: "IP address to listen on, defaults to `localhost`",
646
751
  type: "string",
647
- default: "127.0.0.1",
648
752
  })
649
753
  .option("port", {
650
754
  describe: "Port to listen on",
@@ -654,15 +758,19 @@ export async function main(argv: string[]): Promise<void> {
654
758
  describe: "Port for devtools to connect to",
655
759
  type: "number",
656
760
  })
761
+ .option("routes", {
762
+ describe: "Routes to upload",
763
+ alias: "route",
764
+ type: "array",
765
+ })
657
766
  .option("host", {
658
767
  type: "string",
659
768
  describe:
660
769
  "Host to forward requests to, defaults to the zone of project",
661
770
  })
662
771
  .option("local-protocol", {
663
- default: "http",
664
772
  describe: "Protocol to listen to requests on, defaults to http.",
665
- choices: ["http", "https"],
773
+ choices: ["http", "https"] as const,
666
774
  })
667
775
  .option("experimental-public", {
668
776
  describe: "Static assets to be served",
@@ -685,10 +793,9 @@ export async function main(argv: string[]): Promise<void> {
685
793
  array: true,
686
794
  })
687
795
  .option("upstream-protocol", {
688
- default: "https",
689
796
  describe:
690
797
  "Protocol to forward requests to host on, defaults to https.",
691
- choices: ["http", "https"],
798
+ choices: ["http", "https"] as const,
692
799
  })
693
800
  .option("jsx-factory", {
694
801
  describe: "The function that is called for each JSX element",
@@ -698,6 +805,10 @@ export async function main(argv: string[]): Promise<void> {
698
805
  describe: "The function that is called for each JSX fragment",
699
806
  type: "string",
700
807
  })
808
+ .option("tsconfig", {
809
+ describe: "Path to a custom tsconfig.json file",
810
+ type: "string",
811
+ })
701
812
  .option("local", {
702
813
  alias: "l",
703
814
  describe: "Run on my machine",
@@ -710,11 +821,12 @@ export async function main(argv: string[]): Promise<void> {
710
821
  });
711
822
  },
712
823
  async (args) => {
713
- const config = readConfig(
824
+ printWranglerBanner();
825
+ const configPath =
714
826
  (args.config as ConfigPath) ||
715
- (args.script && findWranglerToml(path.dirname(args.script)))
716
- );
717
- const entry = getEntry(config, "dev", args.script);
827
+ (args.script && findWranglerToml(path.dirname(args.script)));
828
+ const config = readConfig(configPath, args);
829
+ const entry = await getEntry(args, config, "dev");
718
830
 
719
831
  if (args["experimental-public"]) {
720
832
  console.warn(
@@ -728,52 +840,112 @@ export async function main(argv: string[]): Promise<void> {
728
840
  );
729
841
  }
730
842
 
731
- if (config.site?.["entry-point"]) {
843
+ const upstreamProtocol =
844
+ (args["upstream-protocol"] as "http" | "https") ||
845
+ config.dev.upstream_protocol;
846
+ if (upstreamProtocol === "http") {
732
847
  console.warn(
733
- "Deprecation notice: The `site.entry-point` config field is no longer used.\n" +
734
- "The entry-point is specified via the command line (e.g. `wrangler dev path/to/script`).\n" +
735
- "Please remove the `site.entry-point` field from the `wrangler.toml` file."
848
+ "Setting upstream-protocol to http is not currently implemented.\n" +
849
+ "If this is required in your project, please add your use case to the following issue:\n" +
850
+ "https://github.com/cloudflare/wrangler2/issues/583."
736
851
  );
737
852
  }
738
853
 
739
- if (!args.local) {
740
- // -- snip, extract --
741
- const loggedIn = await loginOrRefreshIfRequired();
742
- if (!loggedIn) {
743
- // didn't login, let's just quit
744
- console.log("Did not login, quitting...");
745
- return;
746
- }
854
+ const accountId = !args.local ? await requireAuth(config) : undefined;
747
855
 
748
- if (!config.account_id) {
749
- config.account_id = await getAccountId();
750
- if (!config.account_id) {
751
- throw new Error("No account id found, quitting...");
752
- }
856
+ // TODO: if worker_dev = false and no routes, then error (only for dev)
857
+
858
+ /**
859
+ * Given something that resembles a URL,
860
+ * try to extract a host from it
861
+ */
862
+ function getHost(urlLike: string): string | undefined {
863
+ if (
864
+ !(urlLike.startsWith("http://") || urlLike.startsWith("https://"))
865
+ ) {
866
+ urlLike = "http://" + urlLike;
753
867
  }
754
- // -- snip, end --
868
+ return new URL(urlLike).host;
755
869
  }
756
870
 
757
- const environments = config.env ?? {};
758
- const envRootObj = args.env ? environments[args.env] || {} : config;
871
+ /**
872
+ * Given something that resembles a host,
873
+ * try to infer a zone id from it
874
+ */
875
+ async function getZoneId(host: string): Promise<string | undefined> {
876
+ const zones = await fetchResult<{ id: string }[]>(
877
+ `/zones`,
878
+ {},
879
+ new URLSearchParams({ name: host })
880
+ );
881
+ return zones[0]?.id;
882
+ }
883
+
884
+ // When we're given a host (in one of the above ways), we do 2 things:
885
+ // - We try to extract a host from it
886
+ // - We try to get a zone id from the host
887
+ //
888
+ // So it turns out it's particularly hard to get a 'valid' domain
889
+ // from a string, so we don't even try to validate TLDs, etc.
890
+ // Once we get something that looks like w.x.y.z-ish, we then try to
891
+ // get a zone id for it, by lopping off subdomains until we get a hit
892
+ // from the API. That's it!
893
+
894
+ let zone: { host: string; id: string } | undefined;
895
+
896
+ if (!args.local) {
897
+ const hostLike =
898
+ args.host ||
899
+ config.dev.host ||
900
+ (args.routes && args.routes[0]) ||
901
+ config.route ||
902
+ (config.routes && config.routes[0]);
903
+ const host =
904
+ typeof hostLike === "string" ? getHost(hostLike) : undefined;
905
+ let zoneId: string | undefined;
906
+ const hostPieces =
907
+ typeof host === "string" ? host.split(".") : undefined;
908
+ while (hostPieces && hostPieces.length > 1) {
909
+ zoneId = await getZoneId(hostPieces.join("."));
910
+ if (zoneId) break;
911
+ hostPieces.shift();
912
+ }
913
+ if (host && !zoneId) {
914
+ throw new Error(`Could not find zone for ${hostLike}`);
915
+ }
916
+ zone =
917
+ typeof zoneId === "string" && typeof host === "string"
918
+ ? {
919
+ host,
920
+ id: zoneId,
921
+ }
922
+ : undefined;
923
+ }
759
924
 
760
925
  const { waitUntilExit } = render(
761
926
  <Dev
762
927
  name={getScriptName(args, config)}
763
928
  entry={entry}
764
929
  env={args.env}
765
- zone={undefined}
930
+ zone={zone}
766
931
  rules={getRules(config)}
767
932
  legacyEnv={isLegacyEnv(args, config)}
768
- buildCommand={config.build || {}}
769
- format={args.format || config.build?.upload?.format}
933
+ build={config.build || {}}
770
934
  initialMode={args.local ? "local" : "remote"}
771
- jsxFactory={args["jsx-factory"] || envRootObj?.jsx_factory}
772
- jsxFragment={args["jsx-fragment"] || envRootObj?.jsx_fragment}
935
+ jsxFactory={args["jsx-factory"] || config.jsx_factory}
936
+ jsxFragment={args["jsx-fragment"] || config.jsx_fragment}
937
+ tsconfig={args.tsconfig ?? config.tsconfig}
938
+ upstreamProtocol={upstreamProtocol}
939
+ localProtocol={
940
+ // The typings are not quite clever enough to handle element accesses, only property accesses,
941
+ // so we need to cast here.
942
+ (args["local-protocol"] as "http" | "https") ||
943
+ config.dev.local_protocol
944
+ }
773
945
  enableLocalPersistence={
774
946
  args["experimental-enable-local-persistence"] || false
775
947
  }
776
- accountId={config.account_id}
948
+ accountId={accountId}
777
949
  assetPaths={getAssetPaths(
778
950
  config,
779
951
  args.site,
@@ -783,6 +955,7 @@ export async function main(argv: string[]): Promise<void> {
783
955
  port={
784
956
  args.port || config.dev?.port || (await getPort({ port: 8787 }))
785
957
  }
958
+ ip={args.ip || config.dev.ip}
786
959
  inspectorPort={
787
960
  args["inspector-port"] ?? (await getPort({ port: 9229 }))
788
961
  }
@@ -796,9 +969,9 @@ export async function main(argv: string[]): Promise<void> {
796
969
  (args["compatibility-flags"] as string[]) ||
797
970
  config.compatibility_flags
798
971
  }
799
- usageModel={envRootObj.usage_model}
972
+ usageModel={config.usage_model}
800
973
  bindings={{
801
- kv_namespaces: envRootObj.kv_namespaces?.map(
974
+ kv_namespaces: config.kv_namespaces?.map(
802
975
  ({ binding, preview_id, id: _id }) => {
803
976
  // In `dev`, we make folks use a separate kv namespace called
804
977
  // `preview_id` instead of `id` so that they don't
@@ -819,12 +992,12 @@ export async function main(argv: string[]): Promise<void> {
819
992
  };
820
993
  }
821
994
  ),
822
- vars: envRootObj.vars,
995
+ vars: config.vars,
823
996
  wasm_modules: config.wasm_modules,
824
997
  text_blobs: config.text_blobs,
825
- durable_objects: envRootObj.durable_objects,
826
- r2_buckets: envRootObj.r2_buckets,
827
- unsafe: envRootObj.unsafe?.bindings,
998
+ durable_objects: config.durable_objects,
999
+ r2_buckets: config.r2_buckets,
1000
+ unsafe: config.unsafe?.bindings,
828
1001
  }}
829
1002
  />
830
1003
  );
@@ -841,6 +1014,7 @@ export async function main(argv: string[]): Promise<void> {
841
1014
  .option("env", {
842
1015
  type: "string",
843
1016
  describe: "Perform on a specific environment",
1017
+ alias: "e",
844
1018
  })
845
1019
  .positional("script", {
846
1020
  describe: "The path to an entry point for your worker",
@@ -905,9 +1079,14 @@ export async function main(argv: string[]): Promise<void> {
905
1079
  .option("jsx-fragment", {
906
1080
  describe: "The function that is called for each JSX fragment",
907
1081
  type: "string",
1082
+ })
1083
+ .option("tsconfig", {
1084
+ describe: "Path to a custom tsconfig.json file",
1085
+ type: "string",
908
1086
  });
909
1087
  },
910
1088
  async (args) => {
1089
+ printWranglerBanner();
911
1090
  if (args["experimental-public"]) {
912
1091
  console.warn(
913
1092
  "🚨 The --experimental-public field is experimental and will change in the future."
@@ -919,11 +1098,11 @@ export async function main(argv: string[]): Promise<void> {
919
1098
  );
920
1099
  }
921
1100
 
922
- const config = readConfig(
1101
+ const configPath =
923
1102
  (args.config as ConfigPath) ||
924
- (args.script && findWranglerToml(path.dirname(args.script)))
925
- );
926
- const entry = getEntry(config, "publish", args.script);
1103
+ (args.script && findWranglerToml(path.dirname(args.script)));
1104
+ const config = readConfig(configPath, args);
1105
+ const entry = await getEntry(args, config, "publish");
927
1106
 
928
1107
  if (args.latest) {
929
1108
  console.warn(
@@ -931,21 +1110,7 @@ export async function main(argv: string[]): Promise<void> {
931
1110
  );
932
1111
  }
933
1112
 
934
- // -- snip, extract --
935
- const loggedIn = await loginOrRefreshIfRequired();
936
- if (!loggedIn) {
937
- // didn't login, let's just quit
938
- console.log("Did not login, quitting...");
939
- return;
940
- }
941
-
942
- if (!config.account_id) {
943
- config.account_id = await getAccountId();
944
- if (!config.account_id) {
945
- throw new Error("No account id found, quitting...");
946
- }
947
- }
948
- // -- snip, end --
1113
+ const accountId = await requireAuth(config);
949
1114
 
950
1115
  const assetPaths = getAssetPaths(
951
1116
  config,
@@ -955,9 +1120,9 @@ export async function main(argv: string[]): Promise<void> {
955
1120
  );
956
1121
  await publish({
957
1122
  config,
1123
+ accountId,
958
1124
  name: getScriptName(args, config),
959
1125
  rules: getRules(config),
960
- format: args.format || config.build?.upload?.format,
961
1126
  entry,
962
1127
  env: args.env,
963
1128
  compatibilityDate: args.latest
@@ -967,6 +1132,7 @@ export async function main(argv: string[]): Promise<void> {
967
1132
  triggers: args.triggers,
968
1133
  jsxFactory: args["jsx-factory"],
969
1134
  jsxFragment: args["jsx-fragment"],
1135
+ tsconfig: args.tsconfig,
970
1136
  routes: args.routes,
971
1137
  assetPaths,
972
1138
  legacyEnv: isLegacyEnv(args, config),
@@ -1023,6 +1189,7 @@ export async function main(argv: string[]): Promise<void> {
1023
1189
  .option("env", {
1024
1190
  type: "string",
1025
1191
  describe: "Perform on a specific environment",
1192
+ alias: "e",
1026
1193
  })
1027
1194
  .option("debug", {
1028
1195
  type: "boolean",
@@ -1034,31 +1201,18 @@ export async function main(argv: string[]): Promise<void> {
1034
1201
  );
1035
1202
  },
1036
1203
  async (args) => {
1037
- const config = readConfig(args.config as ConfigPath);
1204
+ if (args.format === "pretty") {
1205
+ printWranglerBanner();
1206
+ }
1207
+ const config = readConfig(args.config as ConfigPath, args);
1038
1208
 
1039
- const scriptName = getScriptName(args, config);
1209
+ const scriptName = getLegacyScriptName(args, config);
1040
1210
 
1041
1211
  if (!scriptName) {
1042
1212
  throw new Error("Missing script name");
1043
1213
  }
1044
1214
 
1045
- // -- snip, extract --
1046
- const loggedIn = await loginOrRefreshIfRequired();
1047
- if (!loggedIn) {
1048
- // didn't login, let's just quit
1049
- console.log("Did not login, quitting...");
1050
- return;
1051
- }
1052
-
1053
- if (!config.account_id) {
1054
- config.account_id = await getAccountId();
1055
- if (!config.account_id) {
1056
- throw new Error("No account id found, quitting...");
1057
- }
1058
- }
1059
- // -- snip, end --
1060
-
1061
- const accountId = config.account_id;
1215
+ const accountId = await requireAuth(config);
1062
1216
 
1063
1217
  const cliFilters: TailCLIFilters = {
1064
1218
  status: args.status as ("ok" | "error" | "canceled")[],
@@ -1163,27 +1317,10 @@ export async function main(argv: string[]): Promise<void> {
1163
1317
  "***************************************************\n"
1164
1318
  );
1165
1319
 
1166
- const config = readConfig(args.config as ConfigPath);
1167
- const entry = getEntry(config, "dev");
1168
-
1169
- // -- snip, extract --
1170
- const loggedIn = await loginOrRefreshIfRequired();
1171
- if (!loggedIn) {
1172
- // didn't login, let's just quit
1173
- console.log("Did not login, quitting...");
1174
- return;
1175
- }
1176
-
1177
- if (!config.account_id) {
1178
- config.account_id = await getAccountId();
1179
- if (!config.account_id) {
1180
- throw new Error("No account id found, quitting...");
1181
- }
1182
- }
1183
- // -- snip, end --
1320
+ const config = readConfig(args.config as ConfigPath, args);
1321
+ const entry = await getEntry({}, config, "dev");
1184
1322
 
1185
- const environments = config.env ?? {};
1186
- const envRootObj = args.env ? environments[args.env] || {} : config;
1323
+ const accountId = await requireAuth(config);
1187
1324
 
1188
1325
  const { waitUntilExit } = render(
1189
1326
  <Dev
@@ -1193,15 +1330,18 @@ export async function main(argv: string[]): Promise<void> {
1193
1330
  env={args.env}
1194
1331
  zone={undefined}
1195
1332
  legacyEnv={isLegacyEnv(args, config)}
1196
- buildCommand={config.build || {}}
1197
- format={config.build?.upload?.format}
1333
+ build={config.build || {}}
1198
1334
  initialMode={args.local ? "local" : "remote"}
1199
- jsxFactory={envRootObj?.jsx_factory}
1200
- jsxFragment={envRootObj?.jsx_fragment}
1335
+ jsxFactory={config.jsx_factory}
1336
+ jsxFragment={config.jsx_fragment}
1337
+ tsconfig={config.tsconfig}
1338
+ upstreamProtocol={config.dev.upstream_protocol}
1339
+ localProtocol={config.dev.local_protocol}
1201
1340
  enableLocalPersistence={false}
1202
- accountId={config.account_id}
1341
+ accountId={accountId}
1203
1342
  assetPaths={undefined}
1204
1343
  port={config.dev?.port}
1344
+ ip={config.dev.ip}
1205
1345
  public={undefined}
1206
1346
  compatibilityDate={
1207
1347
  config.compatibility_date ||
@@ -1213,7 +1353,7 @@ export async function main(argv: string[]): Promise<void> {
1213
1353
  }
1214
1354
  usageModel={config.usage_model}
1215
1355
  bindings={{
1216
- kv_namespaces: envRootObj.kv_namespaces?.map(
1356
+ kv_namespaces: config.kv_namespaces?.map(
1217
1357
  ({ binding, preview_id, id: _id }) => {
1218
1358
  // In `dev`, we make folks use a separate kv namespace called
1219
1359
  // `preview_id` instead of `id` so that they don't
@@ -1234,12 +1374,12 @@ export async function main(argv: string[]): Promise<void> {
1234
1374
  };
1235
1375
  }
1236
1376
  ),
1237
- vars: envRootObj.vars,
1377
+ vars: config.vars,
1238
1378
  wasm_modules: config.wasm_modules,
1239
1379
  text_blobs: config.text_blobs,
1240
- durable_objects: envRootObj.durable_objects,
1241
- r2_buckets: envRootObj.r2_buckets,
1242
- unsafe: envRootObj.unsafe?.bindings,
1380
+ durable_objects: config.durable_objects,
1381
+ r2_buckets: config.r2_buckets,
1382
+ unsafe: config.unsafe?.bindings,
1243
1383
  }}
1244
1384
  inspectorPort={await getPort({ port: 9229 })}
1245
1385
  />
@@ -1248,7 +1388,7 @@ export async function main(argv: string[]): Promise<void> {
1248
1388
  }
1249
1389
  );
1250
1390
 
1251
- // route
1391
+ // [DEPRECATED] route
1252
1392
  wrangler.command(
1253
1393
  "route",
1254
1394
  false, // I think we want to hide this command
@@ -1273,21 +1413,21 @@ export async function main(argv: string[]): Promise<void> {
1273
1413
  type: "string",
1274
1414
  });
1275
1415
  },
1276
- async (args) => {
1277
- console.log(":route list", args);
1278
- // TODO: use environment (current wrangler doesn't do so?)
1279
- // TODO: don't get `zone_id` from config at all and require the command line arg `--zone`.
1280
- const config = readConfig(args.config as ConfigPath);
1281
- const zone = args.zone || config.zone_id;
1282
- if (!zone) {
1283
- throw new Error("missing zone id");
1284
- }
1285
-
1286
- console.log(await fetchResult(`/zones/${zone}/workers/routes`));
1416
+ () => {
1417
+ // "👯 [DEPRECATED]. Use wrangler.toml to manage routes.
1418
+ const deprecationNotice =
1419
+ "`wrangler route list` has been deprecated.";
1420
+ const futureRoutes =
1421
+ "Refer to wrangler.toml for a list of routes the worker will be deployed to upon publishing.";
1422
+ const presentRoutes =
1423
+ "Refer to the Cloudflare Dashboard to see the routes this worker is currently running on.";
1424
+ throw new DeprecationError(
1425
+ `${deprecationNotice}\n${futureRoutes}\n${presentRoutes}`
1426
+ );
1287
1427
  }
1288
1428
  )
1289
1429
  .command(
1290
- "delete <id>",
1430
+ "delete [id]",
1291
1431
  "Delete a route associated with a zone",
1292
1432
  (yargs) => {
1293
1433
  return yargs
@@ -1304,22 +1444,22 @@ export async function main(argv: string[]): Promise<void> {
1304
1444
  describe: "Perform on a specific environment",
1305
1445
  });
1306
1446
  },
1307
- async (args) => {
1308
- console.log(":route delete", args);
1309
- // TODO: use environment (current wrangler doesn't do so?)
1310
- const config = readConfig(args.config as ConfigPath);
1311
- const zone = args.zone || config.zone_id;
1312
- if (!zone) {
1313
- throw new Error("missing zone id");
1314
- }
1315
-
1316
- console.log(
1317
- await fetchResult(`/zones/${zone}/workers/routes/${args.id}`, {
1318
- method: "DELETE",
1319
- })
1320
- );
1447
+ () => {
1448
+ // "👯 [DEPRECATED]. Use wrangler.toml to manage routes.
1449
+ const deprecationNotice =
1450
+ "`wrangler route delete` has been deprecated.";
1451
+ const shouldDo =
1452
+ "Remove the unwanted route(s) from wrangler.toml and run `wrangler publish` to remove your worker from those routes.";
1453
+ throw new DeprecationError(`${deprecationNotice}\n${shouldDo}`);
1321
1454
  }
1322
1455
  );
1456
+ },
1457
+ () => {
1458
+ // "👯 [DEPRECATED]. Use wrangler.toml to manage routes.
1459
+ const deprecationNotice = "`wrangler route` has been deprecated.";
1460
+ const shouldDo =
1461
+ "Please use wrangler.toml and/or `wrangler publish --routes` to modify routes";
1462
+ throw new DeprecationError(`${deprecationNotice}\n${shouldDo}`);
1323
1463
  }
1324
1464
  );
1325
1465
 
@@ -1333,7 +1473,7 @@ export async function main(argv: string[]): Promise<void> {
1333
1473
  },
1334
1474
  () => {
1335
1475
  throw new DeprecationError(
1336
- "`wrangler subdomain` has been deprecated, please refer to TODO://some/path for alternatives"
1476
+ "`wrangler subdomain` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#subdomain for alternatives"
1337
1477
  );
1338
1478
  }
1339
1479
  );
@@ -1344,6 +1484,7 @@ export async function main(argv: string[]): Promise<void> {
1344
1484
  "🤫 Generate a secret that can be referenced in the worker script",
1345
1485
  (secretYargs) => {
1346
1486
  return secretYargs
1487
+ .command(subHelp)
1347
1488
  .command(
1348
1489
  "put <key>",
1349
1490
  "Create or update a secret variable for a script",
@@ -1361,36 +1502,24 @@ export async function main(argv: string[]): Promise<void> {
1361
1502
  type: "string",
1362
1503
  describe:
1363
1504
  "Binds the secret to the Worker of the specific environment",
1505
+ alias: "e",
1364
1506
  });
1365
1507
  },
1366
1508
  async (args) => {
1367
- const config = readConfig(args.config as ConfigPath);
1509
+ printWranglerBanner();
1510
+ const config = readConfig(args.config as ConfigPath, args);
1368
1511
 
1369
- const scriptName = getScriptName(args, config);
1512
+ const scriptName = getLegacyScriptName(args, config);
1370
1513
  if (!scriptName) {
1371
1514
  throw new Error("Missing script name");
1372
1515
  }
1373
1516
 
1374
- // -- snip, extract --
1375
- const loggedIn = await loginOrRefreshIfRequired();
1376
- if (!loggedIn) {
1377
- // didn't login, let's just quit
1378
- console.log("Did not login, quitting...");
1379
- return;
1380
- }
1517
+ const isInteractive = process.stdin.isTTY;
1518
+ const accountId = await requireAuth(config, isInteractive);
1381
1519
 
1382
- if (!config.account_id) {
1383
- config.account_id = await getAccountId();
1384
- if (!config.account_id) {
1385
- throw new Error("No account id found, quitting...");
1386
- }
1387
- }
1388
- // -- snip, end --
1389
-
1390
- const secretValue = await prompt(
1391
- "Enter a secret value:",
1392
- "password"
1393
- );
1520
+ const secretValue = isInteractive
1521
+ ? await prompt("Enter a secret value:", "password")
1522
+ : await readFromStdin();
1394
1523
 
1395
1524
  console.log(
1396
1525
  `🌀 Creating the secret for script ${scriptName} ${
@@ -1401,8 +1530,8 @@ export async function main(argv: string[]): Promise<void> {
1401
1530
  async function submitSecret() {
1402
1531
  const url =
1403
1532
  !args.env || isLegacyEnv(args, config)
1404
- ? `/accounts/${config.account_id}/workers/scripts/${scriptName}/secrets`
1405
- : `/accounts/${config.account_id}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1533
+ ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
1534
+ : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1406
1535
 
1407
1536
  return await fetchResult(url, {
1408
1537
  method: "PUT",
@@ -1415,49 +1544,61 @@ export async function main(argv: string[]): Promise<void> {
1415
1544
  });
1416
1545
  }
1417
1546
 
1547
+ const createDraftWorker = async () => {
1548
+ // TODO: log a warning
1549
+ await fetchResult(
1550
+ !args["legacy-env"] && args.env
1551
+ ? `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}`
1552
+ : `/accounts/${accountId}/workers/scripts/${scriptName}`,
1553
+ {
1554
+ method: "PUT",
1555
+ body: createWorkerUploadForm({
1556
+ name: scriptName,
1557
+ main: {
1558
+ name: scriptName,
1559
+ content: `export default { fetch() {} }`,
1560
+ type: "esm",
1561
+ },
1562
+ bindings: {
1563
+ kv_namespaces: [],
1564
+ vars: {},
1565
+ durable_objects: { bindings: [] },
1566
+ r2_buckets: [],
1567
+ wasm_modules: {},
1568
+ text_blobs: {},
1569
+ unsafe: [],
1570
+ },
1571
+ modules: [],
1572
+ migrations: undefined,
1573
+ compatibility_date: undefined,
1574
+ compatibility_flags: undefined,
1575
+ usage_model: undefined,
1576
+ }),
1577
+ }
1578
+ );
1579
+ };
1580
+
1581
+ function isMissingWorkerError(e: unknown): e is { code: 10007 } {
1582
+ return (
1583
+ typeof e === "object" &&
1584
+ e !== null &&
1585
+ (e as { code: 10007 }).code === 10007
1586
+ );
1587
+ }
1588
+
1418
1589
  try {
1419
1590
  await submitSecret();
1420
1591
  } catch (e) {
1421
- // @ts-expect-error non-standard property on Error
1422
- if (e.code === 10007) {
1423
- // upload a draft worker
1424
- // TODO: log a warning
1425
- await fetchResult(
1426
- !args["legacy-env"] && args.env
1427
- ? `/accounts/${config.account_id}/workers/services/${scriptName}/environments/${args.env}`
1428
- : `/accounts/${config.account_id}/workers/scripts/${scriptName}`,
1429
- {
1430
- method: "PUT",
1431
- body: toFormData({
1432
- name: scriptName,
1433
- main: {
1434
- name: scriptName,
1435
- content: `export default { fetch() {} }`,
1436
- type: "esm",
1437
- },
1438
- bindings: {
1439
- kv_namespaces: [],
1440
- vars: {},
1441
- durable_objects: { bindings: [] },
1442
- r2_buckets: [],
1443
- wasm_modules: {},
1444
- text_blobs: {},
1445
- unsafe: [],
1446
- },
1447
- modules: [],
1448
- migrations: undefined,
1449
- compatibility_date: undefined,
1450
- compatibility_flags: undefined,
1451
- usage_model: undefined,
1452
- }),
1453
- }
1454
- );
1455
-
1456
- // and then try again
1592
+ if (isMissingWorkerError(e)) {
1593
+ // create a draft worker and try again
1594
+ await createDraftWorker();
1457
1595
  await submitSecret();
1458
1596
  // TODO: delete the draft worker if this failed too?
1597
+ } else {
1598
+ throw e;
1459
1599
  }
1460
1600
  }
1601
+
1461
1602
  console.log(`✨ Success! Uploaded secret ${args.key}`);
1462
1603
  }
1463
1604
  )
@@ -1465,6 +1606,7 @@ export async function main(argv: string[]): Promise<void> {
1465
1606
  "delete <key>",
1466
1607
  "Delete a secret variable from a script",
1467
1608
  (yargs) => {
1609
+ printWranglerBanner();
1468
1610
  return yargs
1469
1611
  .positional("key", {
1470
1612
  describe: "The variable name to be accessible in the script",
@@ -1478,31 +1620,18 @@ export async function main(argv: string[]): Promise<void> {
1478
1620
  type: "string",
1479
1621
  describe:
1480
1622
  "Binds the secret to the Worker of the specific environment",
1623
+ alias: "e",
1481
1624
  });
1482
1625
  },
1483
1626
  async (args) => {
1484
- const config = readConfig(args.config as ConfigPath);
1627
+ const config = readConfig(args.config as ConfigPath, args);
1485
1628
 
1486
- const scriptName = getScriptName(args, config);
1629
+ const scriptName = getLegacyScriptName(args, config);
1487
1630
  if (!scriptName) {
1488
1631
  throw new Error("Missing script name");
1489
1632
  }
1490
1633
 
1491
- // -- snip, extract --
1492
- const loggedIn = await loginOrRefreshIfRequired();
1493
- if (!loggedIn) {
1494
- // didn't login, let's just quit
1495
- console.log("Did not login, quitting...");
1496
- return;
1497
- }
1498
-
1499
- if (!config.account_id) {
1500
- config.account_id = await getAccountId();
1501
- if (!config.account_id) {
1502
- throw new Error("No account id found, quitting...");
1503
- }
1504
- }
1505
- // -- snip, end --
1634
+ const accountId = await requireAuth(config);
1506
1635
 
1507
1636
  if (
1508
1637
  await confirm(
@@ -1521,8 +1650,8 @@ export async function main(argv: string[]): Promise<void> {
1521
1650
 
1522
1651
  const url =
1523
1652
  !args.env || isLegacyEnv(args, config)
1524
- ? `/accounts/${config.account_id}/workers/scripts/${scriptName}/secrets`
1525
- : `/accounts/${config.account_id}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1653
+ ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
1654
+ : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1526
1655
 
1527
1656
  await fetchResult(`${url}/${args.key}`, { method: "DELETE" });
1528
1657
  console.log(`✨ Success! Deleted secret ${args.key}`);
@@ -1542,36 +1671,23 @@ export async function main(argv: string[]): Promise<void> {
1542
1671
  type: "string",
1543
1672
  describe:
1544
1673
  "Binds the secret to the Worker of the specific environment.",
1674
+ alias: "e",
1545
1675
  });
1546
1676
  },
1547
1677
  async (args) => {
1548
- const config = readConfig(args.config as ConfigPath);
1678
+ const config = readConfig(args.config as ConfigPath, args);
1549
1679
 
1550
- const scriptName = getScriptName(args, config);
1680
+ const scriptName = getLegacyScriptName(args, config);
1551
1681
  if (!scriptName) {
1552
1682
  throw new Error("Missing script name");
1553
1683
  }
1554
1684
 
1555
- // -- snip, extract --
1556
- const loggedIn = await loginOrRefreshIfRequired();
1557
- if (!loggedIn) {
1558
- // didn't login, let's just quit
1559
- console.log("Did not login, quitting...");
1560
- return;
1561
- }
1562
-
1563
- if (!config.account_id) {
1564
- config.account_id = await getAccountId();
1565
- if (!config.account_id) {
1566
- throw new Error("No account id found, quitting...");
1567
- }
1568
- }
1569
- // -- snip, end --
1685
+ const accountId = await requireAuth(config);
1570
1686
 
1571
1687
  const url =
1572
1688
  !args.env || isLegacyEnv(args, config)
1573
- ? `/accounts/${config.account_id}/workers/scripts/${scriptName}/secrets`
1574
- : `/accounts/${config.account_id}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1689
+ ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
1690
+ : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1575
1691
 
1576
1692
  console.log(JSON.stringify(await fetchResult(url), null, " "));
1577
1693
  }
@@ -1586,6 +1702,7 @@ export async function main(argv: string[]): Promise<void> {
1586
1702
  "🗂️ Interact with your Workers KV Namespaces",
1587
1703
  (namespaceYargs) => {
1588
1704
  return namespaceYargs
1705
+ .command(subHelp)
1589
1706
  .command(
1590
1707
  "create <namespace>",
1591
1708
  "Create a new namespace",
@@ -1599,6 +1716,7 @@ export async function main(argv: string[]): Promise<void> {
1599
1716
  .option("env", {
1600
1717
  type: "string",
1601
1718
  describe: "Perform on a specific environment",
1719
+ alias: "e",
1602
1720
  })
1603
1721
  .option("preview", {
1604
1722
  type: "boolean",
@@ -1606,12 +1724,7 @@ export async function main(argv: string[]): Promise<void> {
1606
1724
  });
1607
1725
  },
1608
1726
  async (args) => {
1609
- if (args._.length > 2) {
1610
- const extraArgs = args._.slice(2).join(" ");
1611
- throw new CommandLineArgsError(
1612
- `Unexpected additional positional arguments "${extraArgs}".`
1613
- );
1614
- }
1727
+ printWranglerBanner();
1615
1728
 
1616
1729
  if (!isValidNamespaceBinding(args.namespace)) {
1617
1730
  throw new CommandLineArgsError(
@@ -1619,7 +1732,7 @@ export async function main(argv: string[]): Promise<void> {
1619
1732
  );
1620
1733
  }
1621
1734
 
1622
- const config = readConfig(args.config as ConfigPath);
1735
+ const config = readConfig(args.config as ConfigPath, args);
1623
1736
  if (!config.name) {
1624
1737
  console.warn(
1625
1738
  "No configured name present, using `worker` as a prefix for the title"
@@ -1631,26 +1744,12 @@ export async function main(argv: string[]): Promise<void> {
1631
1744
  const preview = args.preview ? "_preview" : "";
1632
1745
  const title = `${name}${environment}-${args.namespace}${preview}`;
1633
1746
 
1634
- // -- snip, extract --
1635
- const loggedIn = await loginOrRefreshIfRequired();
1636
- if (!loggedIn) {
1637
- // didn't login, let's just quit
1638
- console.log("Did not login, quitting...");
1639
- return;
1640
- }
1641
-
1642
- if (!config.account_id) {
1643
- config.account_id = await getAccountId();
1644
- if (!config.account_id) {
1645
- throw new Error("No account id found, quitting...");
1646
- }
1647
- }
1648
- // -- snip, end --
1747
+ const accountId = await requireAuth(config);
1649
1748
 
1650
1749
  // TODO: generate a binding name stripping non alphanumeric chars
1651
1750
 
1652
1751
  console.log(`🌀 Creating namespace with title "${title}"`);
1653
- const namespaceId = await createNamespace(config.account_id, title);
1752
+ const namespaceId = await createNamespace(accountId, title);
1654
1753
 
1655
1754
  console.log("✨ Success!");
1656
1755
  const envString = args.env ? ` under [env.${args.env}]` : "";
@@ -1670,32 +1769,14 @@ export async function main(argv: string[]): Promise<void> {
1670
1769
  "Outputs a list of all KV namespaces associated with your account id.",
1671
1770
  {},
1672
1771
  async (args) => {
1673
- const config = readConfig(args.config as ConfigPath);
1674
-
1675
- // -- snip, extract --
1676
- const loggedIn = await loginOrRefreshIfRequired();
1677
- if (!loggedIn) {
1678
- // didn't login, let's just quit
1679
- console.log("Did not login, quitting...");
1680
- return;
1681
- }
1772
+ const config = readConfig(args.config as ConfigPath, args);
1682
1773
 
1683
- if (!config.account_id) {
1684
- config.account_id = await getAccountId();
1685
- if (!config.account_id) {
1686
- throw new Error("No account id found, quitting...");
1687
- }
1688
- }
1689
- // -- snip, end --
1774
+ const accountId = await requireAuth(config);
1690
1775
 
1691
1776
  // TODO: we should show bindings if they exist for given ids
1692
1777
 
1693
1778
  console.log(
1694
- JSON.stringify(
1695
- await listNamespaces(config.account_id),
1696
- null,
1697
- " "
1698
- )
1779
+ JSON.stringify(await listNamespaces(accountId), null, " ")
1699
1780
  );
1700
1781
  }
1701
1782
  )
@@ -1716,6 +1797,7 @@ export async function main(argv: string[]): Promise<void> {
1716
1797
  .option("env", {
1717
1798
  type: "string",
1718
1799
  describe: "Perform on a specific environment",
1800
+ alias: "e",
1719
1801
  })
1720
1802
  .option("preview", {
1721
1803
  type: "boolean",
@@ -1723,7 +1805,8 @@ export async function main(argv: string[]): Promise<void> {
1723
1805
  });
1724
1806
  },
1725
1807
  async (args) => {
1726
- const config = readConfig(args.config as ConfigPath);
1808
+ printWranglerBanner();
1809
+ const config = readConfig(args.config as ConfigPath, args);
1727
1810
 
1728
1811
  let id;
1729
1812
  try {
@@ -1734,24 +1817,10 @@ export async function main(argv: string[]): Promise<void> {
1734
1817
  );
1735
1818
  }
1736
1819
 
1737
- // -- snip, extract --
1738
- const loggedIn = await loginOrRefreshIfRequired();
1739
- if (!loggedIn) {
1740
- // didn't login, let's just quit
1741
- console.log("Did not login, quitting...");
1742
- return;
1743
- }
1744
-
1745
- if (!config.account_id) {
1746
- config.account_id = await getAccountId();
1747
- if (!config.account_id) {
1748
- throw new Error("No account id found, quitting...");
1749
- }
1750
- }
1751
- // -- snip, end --
1820
+ const accountId = await requireAuth(config);
1752
1821
 
1753
1822
  await fetchResult<{ id: string }>(
1754
- `/accounts/${config.account_id}/storage/kv/namespaces/${id}`,
1823
+ `/accounts/${accountId}/storage/kv/namespaces/${id}`,
1755
1824
  { method: "DELETE" }
1756
1825
  );
1757
1826
 
@@ -1783,6 +1852,7 @@ export async function main(argv: string[]): Promise<void> {
1783
1852
  "🔑 Individually manage Workers KV key-value pairs",
1784
1853
  (kvKeyYargs) => {
1785
1854
  return kvKeyYargs
1855
+ .command(subHelp)
1786
1856
  .command(
1787
1857
  "put <key> [value]",
1788
1858
  "Writes a single key/value pair to the given namespace.",
@@ -1809,6 +1879,7 @@ export async function main(argv: string[]): Promise<void> {
1809
1879
  .option("env", {
1810
1880
  type: "string",
1811
1881
  describe: "Perform on a specific environment",
1882
+ alias: "e",
1812
1883
  })
1813
1884
  .option("preview", {
1814
1885
  type: "boolean",
@@ -1830,7 +1901,8 @@ export async function main(argv: string[]): Promise<void> {
1830
1901
  .check(demandOneOfOption("value", "path"));
1831
1902
  },
1832
1903
  async ({ key, ttl, expiration, ...args }) => {
1833
- const config = readConfig(args.config as ConfigPath);
1904
+ printWranglerBanner();
1905
+ const config = readConfig(args.config as ConfigPath, args);
1834
1906
  const namespaceId = getNamespaceId(args, config);
1835
1907
  // One of `args.path` and `args.value` must be defined
1836
1908
  const value = args.path
@@ -1848,23 +1920,11 @@ export async function main(argv: string[]): Promise<void> {
1848
1920
  );
1849
1921
  }
1850
1922
 
1851
- // -- snip, extract --
1852
- const loggedIn = await loginOrRefreshIfRequired();
1853
- if (!loggedIn) {
1854
- // didn't login, let's just quit
1855
- console.log("Did not login, quitting...");
1856
- return;
1857
- }
1858
-
1859
- if (!config.account_id) {
1860
- config.account_id = await getAccountId();
1861
- if (!config.account_id) {
1862
- throw new Error("No account id found, quitting...");
1863
- }
1864
- }
1865
- // -- snip, end --
1923
+ const accountId = await requireAuth(config);
1866
1924
 
1867
- await putKeyValue(config.account_id, namespaceId, key, value, {
1925
+ await putKeyValue(accountId, namespaceId, {
1926
+ key,
1927
+ value,
1868
1928
  expiration,
1869
1929
  expiration_ttl: ttl,
1870
1930
  });
@@ -1887,6 +1947,7 @@ export async function main(argv: string[]): Promise<void> {
1887
1947
  .option("env", {
1888
1948
  type: "string",
1889
1949
  describe: "Perform on a specific environment",
1950
+ alias: "e",
1890
1951
  })
1891
1952
  .option("preview", {
1892
1953
  type: "boolean",
@@ -1901,27 +1962,13 @@ export async function main(argv: string[]): Promise<void> {
1901
1962
  },
1902
1963
  async ({ prefix, ...args }) => {
1903
1964
  // TODO: support for limit+cursor (pagination)
1904
- const config = readConfig(args.config as ConfigPath);
1965
+ const config = readConfig(args.config as ConfigPath, args);
1905
1966
  const namespaceId = getNamespaceId(args, config);
1906
1967
 
1907
- // -- snip, extract --
1908
- const loggedIn = await loginOrRefreshIfRequired();
1909
- if (!loggedIn) {
1910
- // didn't login, let's just quit
1911
- console.log("Did not login, quitting...");
1912
- return;
1913
- }
1914
-
1915
- if (!config.account_id) {
1916
- config.account_id = await getAccountId();
1917
- if (!config.account_id) {
1918
- throw new Error("No account id found, quitting...");
1919
- }
1920
- }
1921
- // -- snip, end --
1968
+ const accountId = await requireAuth(config);
1922
1969
 
1923
1970
  const results = await listNamespaceKeys(
1924
- config.account_id,
1971
+ accountId,
1925
1972
  namespaceId,
1926
1973
  prefix
1927
1974
  );
@@ -1950,6 +1997,7 @@ export async function main(argv: string[]): Promise<void> {
1950
1997
  .option("env", {
1951
1998
  type: "string",
1952
1999
  describe: "Perform on a specific environment",
2000
+ alias: "e",
1953
2001
  })
1954
2002
  .option("preview", {
1955
2003
  type: "boolean",
@@ -1963,26 +2011,12 @@ export async function main(argv: string[]): Promise<void> {
1963
2011
  });
1964
2012
  },
1965
2013
  async ({ key, ...args }) => {
1966
- const config = readConfig(args.config as ConfigPath);
2014
+ const config = readConfig(args.config as ConfigPath, args);
1967
2015
  const namespaceId = getNamespaceId(args, config);
1968
2016
 
1969
- // -- snip, extract --
1970
- const loggedIn = await loginOrRefreshIfRequired();
1971
- if (!loggedIn) {
1972
- // didn't login, let's just quit
1973
- console.log("Did not login, quitting...");
1974
- return;
1975
- }
1976
-
1977
- if (!config.account_id) {
1978
- config.account_id = await getAccountId();
1979
- if (!config.account_id) {
1980
- throw new Error("No account id found, quitting...");
1981
- }
1982
- }
1983
- // -- snip, end --
2017
+ const accountId = await requireAuth(config);
1984
2018
 
1985
- console.log(await getKeyValue(config.account_id, namespaceId, key));
2019
+ console.log(await getKeyValue(accountId, namespaceId, key));
1986
2020
  }
1987
2021
  )
1988
2022
  .command(
@@ -2007,6 +2041,7 @@ export async function main(argv: string[]): Promise<void> {
2007
2041
  .option("env", {
2008
2042
  type: "string",
2009
2043
  describe: "Perform on a specific environment",
2044
+ alias: "e",
2010
2045
  })
2011
2046
  .option("preview", {
2012
2047
  type: "boolean",
@@ -2014,31 +2049,18 @@ export async function main(argv: string[]): Promise<void> {
2014
2049
  });
2015
2050
  },
2016
2051
  async ({ key, ...args }) => {
2017
- const config = readConfig(args.config as ConfigPath);
2052
+ printWranglerBanner();
2053
+ const config = readConfig(args.config as ConfigPath, args);
2018
2054
  const namespaceId = getNamespaceId(args, config);
2019
2055
 
2020
2056
  console.log(
2021
2057
  `deleting the key "${key}" on namespace ${namespaceId}`
2022
2058
  );
2023
2059
 
2024
- // -- snip, extract --
2025
- const loggedIn = await loginOrRefreshIfRequired();
2026
- if (!loggedIn) {
2027
- // didn't login, let's just quit
2028
- console.log("Did not login, quitting...");
2029
- return;
2030
- }
2031
-
2032
- if (!config.account_id) {
2033
- config.account_id = await getAccountId();
2034
- if (!config.account_id) {
2035
- throw new Error("No account id found, quitting...");
2036
- }
2037
- }
2038
- // -- snip, end --
2060
+ const accountId = await requireAuth(config);
2039
2061
 
2040
2062
  await fetchResult(
2041
- `/accounts/${config.account_id}/storage/kv/namespaces/${namespaceId}/values/${key}`,
2063
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${key}`,
2042
2064
  { method: "DELETE" }
2043
2065
  );
2044
2066
  }
@@ -2052,6 +2074,7 @@ export async function main(argv: string[]): Promise<void> {
2052
2074
  "💪 Interact with multiple Workers KV key-value pairs at once",
2053
2075
  (kvBulkYargs) => {
2054
2076
  return kvBulkYargs
2077
+ .command(subHelp)
2055
2078
  .command(
2056
2079
  "put <filename>",
2057
2080
  "Upload multiple key-value pairs to a namespace",
@@ -2074,6 +2097,7 @@ export async function main(argv: string[]): Promise<void> {
2074
2097
  .option("env", {
2075
2098
  type: "string",
2076
2099
  describe: "Perform on a specific environment",
2100
+ alias: "e",
2077
2101
  })
2078
2102
  .option("preview", {
2079
2103
  type: "boolean",
@@ -2081,33 +2105,80 @@ export async function main(argv: string[]): Promise<void> {
2081
2105
  });
2082
2106
  },
2083
2107
  async ({ filename, ...args }) => {
2108
+ printWranglerBanner();
2084
2109
  // The simplest implementation I could think of.
2085
2110
  // This could be made more efficient with a streaming parser/uploader
2086
2111
  // but we'll do that in the future if needed.
2087
2112
 
2088
- const config = readConfig(args.config as ConfigPath);
2113
+ const config = readConfig(args.config as ConfigPath, args);
2089
2114
  const namespaceId = getNamespaceId(args, config);
2090
2115
  const content = parseJSON(readFileSync(filename), filename);
2091
2116
 
2092
- // -- snip, extract --
2093
- const loggedIn = await loginOrRefreshIfRequired();
2094
- if (!loggedIn) {
2095
- // didn't login, let's just quit
2096
- console.log("Did not login, quitting...");
2097
- return;
2117
+ if (!Array.isArray(content)) {
2118
+ throw new Error(
2119
+ `Unexpected JSON input from "${filename}".\n` +
2120
+ `Expected an array of key-value objects but got type "${typeof content}".`
2121
+ );
2098
2122
  }
2099
2123
 
2100
- if (!config.account_id) {
2101
- config.account_id = await getAccountId();
2102
- if (!config.account_id) {
2103
- throw new Error("No account id found, quitting...");
2124
+ const errors: string[] = [];
2125
+ const warnings: string[] = [];
2126
+ for (let i = 0; i < content.length; i++) {
2127
+ const keyValue = content[i];
2128
+ if (typeof keyValue !== "object") {
2129
+ errors.push(
2130
+ `The item at index ${i} is type: "${typeof keyValue}" - ${JSON.stringify(
2131
+ keyValue
2132
+ )}`
2133
+ );
2134
+ } else if (!isKeyValue(keyValue)) {
2135
+ errors.push(
2136
+ `The item at index ${i} is ${JSON.stringify(keyValue)}`
2137
+ );
2138
+ } else {
2139
+ const props = unexpectedKeyValueProps(keyValue);
2140
+ if (props.length > 0) {
2141
+ warnings.push(
2142
+ `The item at index ${i} contains unexpected properties: ${JSON.stringify(
2143
+ props
2144
+ )}.`
2145
+ );
2146
+ }
2104
2147
  }
2105
2148
  }
2106
- // -- snip, end --
2149
+ if (warnings.length > 0) {
2150
+ console.warn(
2151
+ `Unexpected key-value properties in "${filename}".\n` +
2152
+ warnings.join("\n")
2153
+ );
2154
+ }
2155
+ if (errors.length > 0) {
2156
+ throw new Error(
2157
+ `Unexpected JSON input from "${filename}".\n` +
2158
+ `Each item in the array should be an object that matches:\n\n` +
2159
+ `interface KeyValue {\n` +
2160
+ ` key: string;\n` +
2161
+ ` value: string;\n` +
2162
+ ` expiration?: number;\n` +
2163
+ ` expiration_ttl?: number;\n` +
2164
+ ` metadata?: object;\n` +
2165
+ ` base64?: boolean;\n` +
2166
+ `}\n\n` +
2167
+ errors.join("\n")
2168
+ );
2169
+ }
2107
2170
 
2108
- console.log(
2109
- await putBulkKeyValue(config.account_id, namespaceId, content)
2171
+ const accountId = await requireAuth(config);
2172
+ await putBulkKeyValue(
2173
+ accountId,
2174
+ namespaceId,
2175
+ content,
2176
+ (index, total) => {
2177
+ console.log(`Uploaded ${index} of ${total}.`);
2178
+ }
2110
2179
  );
2180
+
2181
+ console.log("Success!");
2111
2182
  }
2112
2183
  )
2113
2184
  .command(
@@ -2132,156 +2203,148 @@ export async function main(argv: string[]): Promise<void> {
2132
2203
  .option("env", {
2133
2204
  type: "string",
2134
2205
  describe: "Perform on a specific environment",
2206
+ alias: "e",
2135
2207
  })
2136
2208
  .option("preview", {
2137
2209
  type: "boolean",
2138
2210
  describe: "Interact with a preview namespace",
2211
+ })
2212
+ .option("force", {
2213
+ type: "boolean",
2214
+ alias: "f",
2215
+ describe: "Do not ask for confirmation before deleting",
2139
2216
  });
2140
2217
  },
2141
2218
  async ({ filename, ...args }) => {
2142
- const config = readConfig(args.config as ConfigPath);
2219
+ printWranglerBanner();
2220
+ const config = readConfig(args.config as ConfigPath, args);
2143
2221
  const namespaceId = getNamespaceId(args, config);
2144
- const content = parseJSON(readFileSync(filename), filename);
2145
2222
 
2146
- // -- snip, extract --
2147
- const loggedIn = await loginOrRefreshIfRequired();
2148
- if (!loggedIn) {
2149
- // didn't login, let's just quit
2150
- console.log("Did not login, quitting...");
2151
- return;
2223
+ if (!args.force) {
2224
+ const result = await confirm(
2225
+ `Are you sure you want to delete all the keys read from "${filename}" from kv-namespace with id "${namespaceId}"?`
2226
+ );
2227
+ if (!result) {
2228
+ console.log(`Not deleting keys read from "${filename}".`);
2229
+ return;
2230
+ }
2231
+ }
2232
+
2233
+ const content = parseJSON(
2234
+ readFileSync(filename),
2235
+ filename
2236
+ ) as string[];
2237
+
2238
+ if (!Array.isArray(content)) {
2239
+ throw new Error(
2240
+ `Unexpected JSON input from "${filename}".\n` +
2241
+ `Expected an array of strings but got:\n${content}`
2242
+ );
2152
2243
  }
2153
2244
 
2154
- if (!config.account_id) {
2155
- config.account_id = await getAccountId();
2156
- if (!config.account_id) {
2157
- throw new Error("No account id found, quitting...");
2245
+ const errors: string[] = [];
2246
+ for (let i = 0; i < content.length; i++) {
2247
+ const key = content[i];
2248
+ if (typeof key !== "string") {
2249
+ errors.push(
2250
+ `The item at index ${i} is type: "${typeof key}" - ${JSON.stringify(
2251
+ key
2252
+ )}`
2253
+ );
2158
2254
  }
2159
2255
  }
2160
- // -- snip, end --
2256
+ if (errors.length > 0) {
2257
+ throw new Error(
2258
+ `Unexpected JSON input from "${filename}".\n` +
2259
+ `Expected an array of strings.\n` +
2260
+ errors.join("\n")
2261
+ );
2262
+ }
2161
2263
 
2162
- console.log(
2163
- await deleteBulkKeyValue(config.account_id, namespaceId, content)
2264
+ const accountId = await requireAuth(config);
2265
+
2266
+ await deleteBulkKeyValue(
2267
+ accountId,
2268
+ namespaceId,
2269
+ content,
2270
+ (index, total) => {
2271
+ console.log(`Deleted ${index} of ${total}.`);
2272
+ }
2164
2273
  );
2274
+
2275
+ console.log("Success!");
2165
2276
  }
2166
2277
  );
2167
2278
  }
2168
2279
  );
2169
2280
 
2170
- wrangler.command("pages", "⚡️ Configure Cloudflare Pages", pages);
2281
+ wrangler.command(
2282
+ "pages",
2283
+ "⚡️ Configure Cloudflare Pages",
2284
+ async (pagesYargs) => {
2285
+ await pages(pagesYargs.command(subHelp).epilogue(pagesBetaWarning));
2286
+ }
2287
+ );
2171
2288
 
2172
2289
  wrangler.command("r2", "📦 Interact with an R2 store", (r2Yargs) => {
2173
- return r2Yargs.command("bucket", "Manage R2 buckets", (r2BucketYargs) => {
2174
- r2BucketYargs.command(
2175
- "create <name>",
2176
- "Create a new R2 bucket",
2177
- (yargs) => {
2178
- return yargs.positional("name", {
2179
- describe: "The name of the new bucket",
2180
- type: "string",
2181
- demandOption: true,
2182
- });
2183
- },
2184
- async (args) => {
2185
- // We expect three values in `_`: `r2`, `bucket`, `create`.
2186
- if (args._.length > 3) {
2187
- const extraArgs = args._.slice(3).join(" ");
2188
- throw new CommandLineArgsError(
2189
- `Unexpected additional positional arguments "${extraArgs}".`
2190
- );
2191
- }
2290
+ return r2Yargs
2291
+ .command(subHelp)
2292
+ .command("bucket", "Manage R2 buckets", (r2BucketYargs) => {
2293
+ r2BucketYargs.command(
2294
+ "create <name>",
2295
+ "Create a new R2 bucket",
2296
+ (yargs) => {
2297
+ return yargs.positional("name", {
2298
+ describe: "The name of the new bucket",
2299
+ type: "string",
2300
+ demandOption: true,
2301
+ });
2302
+ },
2303
+ async (args) => {
2304
+ printWranglerBanner();
2192
2305
 
2193
- const config = readConfig(args.config as ConfigPath);
2306
+ const config = readConfig(args.config as ConfigPath, args);
2194
2307
 
2195
- // -- snip, extract --
2196
- const loggedIn = await loginOrRefreshIfRequired();
2197
- if (!loggedIn) {
2198
- // didn't login, let's just quit
2199
- console.log("Did not login, quitting...");
2200
- return;
2201
- }
2308
+ const accountId = await requireAuth(config);
2202
2309
 
2203
- if (!config.account_id) {
2204
- config.account_id = await getAccountId();
2205
- if (!config.account_id) {
2206
- throw new Error("No account id found, quitting...");
2207
- }
2310
+ console.log(`Creating bucket ${args.name}.`);
2311
+ await createR2Bucket(accountId, args.name);
2312
+ console.log(`Created bucket ${args.name}.`);
2208
2313
  }
2209
- // -- snip, end --
2210
-
2211
- console.log(`Creating bucket ${args.name}.`);
2212
- await createR2Bucket(config.account_id, args.name);
2213
- console.log(`Created bucket ${args.name}.`);
2214
- }
2215
- );
2216
-
2217
- r2BucketYargs.command("list", "List R2 buckets", {}, async (args) => {
2218
- const config = readConfig(args.config as ConfigPath);
2314
+ );
2219
2315
 
2220
- // -- snip, extract --
2221
- const loggedIn = await loginOrRefreshIfRequired();
2222
- if (!loggedIn) {
2223
- // didn't login, let's just quit
2224
- console.log("Did not login, quitting...");
2225
- return;
2226
- }
2316
+ r2BucketYargs.command("list", "List R2 buckets", {}, async (args) => {
2317
+ const config = readConfig(args.config as ConfigPath, args);
2227
2318
 
2228
- if (!config.account_id) {
2229
- config.account_id = await getAccountId();
2230
- if (!config.account_id) {
2231
- throw new Error("No account id found, quitting...");
2232
- }
2233
- }
2234
- // -- snip, end --
2319
+ const accountId = await requireAuth(config);
2235
2320
 
2236
- console.log(
2237
- JSON.stringify(await listR2Buckets(config.account_id), null, 2)
2238
- );
2239
- });
2321
+ console.log(JSON.stringify(await listR2Buckets(accountId), null, 2));
2322
+ });
2240
2323
 
2241
- r2BucketYargs.command(
2242
- "delete <name>",
2243
- "Delete an R2 bucket",
2244
- (yargs) => {
2245
- return yargs.positional("name", {
2246
- describe: "The name of the bucket to delete",
2247
- type: "string",
2248
- demandOption: true,
2249
- });
2250
- },
2251
- async (args) => {
2252
- // We expect three values in `_`: `r2`, `bucket`, `delete`.
2253
- if (args._.length > 3) {
2254
- const extraArgs = args._.slice(3).join(" ");
2255
- throw new CommandLineArgsError(
2256
- `Unexpected additional positional arguments "${extraArgs}".`
2257
- );
2258
- }
2324
+ r2BucketYargs.command(
2325
+ "delete <name>",
2326
+ "Delete an R2 bucket",
2327
+ (yargs) => {
2328
+ return yargs.positional("name", {
2329
+ describe: "The name of the bucket to delete",
2330
+ type: "string",
2331
+ demandOption: true,
2332
+ });
2333
+ },
2334
+ async (args) => {
2335
+ printWranglerBanner();
2259
2336
 
2260
- const config = readConfig(args.config as ConfigPath);
2337
+ const config = readConfig(args.config as ConfigPath, args);
2261
2338
 
2262
- // -- snip, extract --
2263
- const loggedIn = await loginOrRefreshIfRequired();
2264
- if (!loggedIn) {
2265
- // didn't login, let's just quit
2266
- console.log("Did not login, quitting...");
2267
- return;
2268
- }
2339
+ const accountId = await requireAuth(config);
2269
2340
 
2270
- if (!config.account_id) {
2271
- config.account_id = await getAccountId();
2272
- if (!config.account_id) {
2273
- throw new Error("No account id found, quitting...");
2274
- }
2341
+ console.log(`Deleting bucket ${args.name}.`);
2342
+ await deleteR2Bucket(accountId, args.name);
2343
+ console.log(`Deleted bucket ${args.name}.`);
2275
2344
  }
2276
- // -- snip, end --
2277
-
2278
- console.log(`Deleting bucket ${args.name}.`);
2279
- await deleteR2Bucket(config.account_id, args.name);
2280
- console.log(`Deleted bucket ${args.name}.`);
2281
- }
2282
- );
2283
- return r2BucketYargs;
2284
- });
2345
+ );
2346
+ return r2BucketYargs;
2347
+ });
2285
2348
  });
2286
2349
 
2287
2350
  wrangler
@@ -2301,7 +2364,6 @@ export async function main(argv: string[]): Promise<void> {
2301
2364
  wrangler.exitProcess(false);
2302
2365
 
2303
2366
  try {
2304
- await initialiseUserConfig();
2305
2367
  await wrangler.parse();
2306
2368
  } catch (e) {
2307
2369
  if (e instanceof CommandLineArgsError) {