wrangler 0.0.0-e2b7352 → 0.0.0-e2f9bb2

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 (86) hide show
  1. package/README.md +24 -13
  2. package/bin/wrangler.js +3 -0
  3. package/miniflare-dist/index.mjs +4828 -21
  4. package/package.json +29 -22
  5. package/pages/functions/buildPlugin.ts +92 -0
  6. package/pages/functions/buildWorker.ts +80 -4
  7. package/pages/functions/filepath-routing.test.ts +11 -0
  8. package/pages/functions/filepath-routing.ts +8 -0
  9. package/pages/functions/routes.ts +6 -1
  10. package/pages/functions/template-plugin.ts +146 -0
  11. package/pages/functions/template-worker.ts +21 -8
  12. package/src/__tests__/config-cache.test.ts +36 -0
  13. package/src/__tests__/configuration.test.ts +386 -81
  14. package/src/__tests__/dev.test.tsx +242 -21
  15. package/src/__tests__/guess-worker-format.test.ts +18 -1
  16. package/src/__tests__/helpers/mock-cfetch.ts +5 -1
  17. package/src/__tests__/helpers/mock-console.ts +20 -3
  18. package/src/__tests__/helpers/mock-dialogs.ts +24 -0
  19. package/src/__tests__/helpers/mock-http-server.ts +46 -0
  20. package/src/__tests__/helpers/mock-istty.ts +27 -0
  21. package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
  22. package/src/__tests__/helpers/run-in-tmp.ts +7 -0
  23. package/src/__tests__/helpers/write-worker-source.ts +31 -0
  24. package/src/__tests__/https-options.test.ts +5 -2
  25. package/src/__tests__/index.test.ts +25 -759
  26. package/src/__tests__/init.test.ts +1042 -0
  27. package/src/__tests__/jest.setup.ts +4 -4
  28. package/src/__tests__/kv.test.ts +130 -56
  29. package/src/__tests__/logger.test.ts +115 -0
  30. package/src/__tests__/pages.test.ts +290 -43
  31. package/src/__tests__/publish.test.ts +1699 -333
  32. package/src/__tests__/r2.test.ts +24 -8
  33. package/src/__tests__/route.test.ts +4 -4
  34. package/src/__tests__/secret.test.ts +55 -34
  35. package/src/__tests__/tail.test.ts +97 -45
  36. package/src/__tests__/user.test.ts +131 -0
  37. package/src/__tests__/whoami.test.tsx +14 -9
  38. package/src/bundle.ts +15 -1
  39. package/src/cfetch/internal.ts +0 -2
  40. package/src/cli.ts +5 -11
  41. package/src/config/README.md +1 -1
  42. package/src/config/config.ts +20 -4
  43. package/src/config/environment.ts +26 -3
  44. package/src/config/index.ts +2 -1
  45. package/src/config/validation-helpers.ts +26 -5
  46. package/src/config/validation.ts +233 -41
  47. package/src/config-cache.ts +45 -0
  48. package/src/create-worker-preview.ts +22 -13
  49. package/src/create-worker-upload-form.ts +32 -9
  50. package/src/dev/dev-vars.ts +36 -0
  51. package/src/dev/dev.tsx +60 -34
  52. package/src/dev/local.tsx +101 -21
  53. package/src/dev/remote.tsx +23 -13
  54. package/src/dev/use-esbuild.ts +19 -5
  55. package/src/dialogs.tsx +16 -6
  56. package/src/entry.ts +38 -11
  57. package/src/environment-variables.ts +3 -1
  58. package/src/errors.ts +11 -0
  59. package/src/https-options.ts +3 -2
  60. package/src/index.tsx +529 -303
  61. package/src/inspect.ts +17 -11
  62. package/src/logger.ts +66 -0
  63. package/src/miniflare-cli/index.ts +4 -0
  64. package/src/miniflare-cli/request-context.ts +40 -0
  65. package/src/module-collection.ts +7 -7
  66. package/src/open-in-browser.ts +5 -4
  67. package/src/package-manager.ts +9 -8
  68. package/src/pages.tsx +860 -55
  69. package/src/parse.ts +1 -1
  70. package/src/proxy.ts +25 -11
  71. package/src/publish.ts +260 -141
  72. package/src/sites.tsx +27 -14
  73. package/src/tail/index.ts +1 -1
  74. package/src/tail/printing.ts +13 -8
  75. package/src/update-check.ts +19 -0
  76. package/src/user.tsx +104 -38
  77. package/src/whoami.tsx +2 -1
  78. package/src/worker.ts +15 -6
  79. package/templates/gitignore +171 -0
  80. package/templates/static-asset-facade.js +1 -1
  81. package/wrangler-dist/cli.js +20948 -50169
  82. package/miniflare-dist/index.mjs.map +0 -7
  83. package/src/__tests__/logout.test.ts +0 -52
  84. package/src/__tests__/sentry.test.ts +0 -146
  85. package/src/reporting.ts +0 -131
  86. package/wrangler-dist/cli.js.map +0 -7
package/src/index.tsx CHANGED
@@ -4,6 +4,7 @@ import path from "node:path";
4
4
  import { setTimeout } from "node:timers/promises";
5
5
  import TOML from "@iarna/toml";
6
6
  import chalk from "chalk";
7
+ import { execa } from "execa";
7
8
  import { findUp } from "find-up";
8
9
  import getPort from "get-port";
9
10
  import { render } from "ink";
@@ -16,8 +17,10 @@ import { fetchResult } from "./cfetch";
16
17
  import { findWranglerToml, readConfig } from "./config";
17
18
  import { createWorkerUploadForm } from "./create-worker-upload-form";
18
19
  import Dev from "./dev/dev";
20
+ import { getVarsForDev } from "./dev/dev-vars";
19
21
  import { confirm, prompt } from "./dialogs";
20
22
  import { getEntry } from "./entry";
23
+ import { DeprecationError } from "./errors";
21
24
  import {
22
25
  getNamespaceId,
23
26
  listNamespaces,
@@ -31,9 +34,16 @@ import {
31
34
  isKeyValue,
32
35
  unexpectedKeyValueProps,
33
36
  } from "./kv";
37
+ import { logger } from "./logger";
34
38
  import { getPackageManager } from "./package-manager";
35
39
  import { pages, pagesBetaWarning } from "./pages";
36
- import { formatMessage, ParseError, parseJSON, readFileSync } from "./parse";
40
+ import {
41
+ formatMessage,
42
+ ParseError,
43
+ parseJSON,
44
+ parseTOML,
45
+ readFileSync,
46
+ } from "./parse";
37
47
  import publish from "./publish";
38
48
  import { createR2Bucket, deleteR2Bucket, listR2Buckets } from "./r2";
39
49
  import { getAssetPaths } from "./sites";
@@ -43,13 +53,13 @@ import {
43
53
  prettyPrintLogs,
44
54
  translateCLICommandToFilterMessage,
45
55
  } from "./tail";
56
+ import { updateCheck } from "./update-check";
46
57
  import {
47
58
  login,
48
59
  logout,
49
60
  listScopes,
50
- loginOrRefreshIfRequired,
51
- getAccountId,
52
61
  validateScopeKeys,
62
+ requireAuth,
53
63
  } from "./user";
54
64
  import { whoami } from "./whoami";
55
65
 
@@ -74,8 +84,8 @@ function getRules(config: Config): Config["rules"] {
74
84
  }
75
85
 
76
86
  if (config.build?.upload?.rules) {
77
- console.warn(
78
- `Deprecation notice: The \`build.upload.rules\` config field is no longer used, the rules should be specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration file, and add this:
87
+ logger.warn(
88
+ `Deprecation: The \`build.upload.rules\` config field is no longer used, the rules should be specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration file, and add this:
79
89
 
80
90
  ${TOML.stringify({ rules: config.build.upload.rules })}`
81
91
  );
@@ -83,14 +93,15 @@ ${TOML.stringify({ rules: config.build.upload.rules })}`
83
93
  return rules;
84
94
  }
85
95
 
86
- function printWranglerBanner() {
96
+ async function printWranglerBanner() {
87
97
  // Let's not print this in tests
88
98
  if (typeof jest !== "undefined") {
89
99
  return;
90
100
  }
91
- const text = ` ⛅️ wrangler ${wranglerVersion} `;
92
101
 
93
- console.log(
102
+ const text = ` ⛅️ wrangler ${wranglerVersion} ${await updateCheck()}`;
103
+
104
+ logger.log(
94
105
  text +
95
106
  "\n" +
96
107
  (supportsColor.stdout
@@ -99,7 +110,9 @@ function printWranglerBanner() {
99
110
  );
100
111
  }
101
112
 
102
- function isLegacyEnv(args: unknown, config: Config): boolean {
113
+ function isLegacyEnv(config: Config): boolean {
114
+ // We only read from config here, because we've already accounted for
115
+ // args["legacy-env"] in https://github.com/cloudflare/wrangler2/blob/b24aeb5722370c2e04bce97a84a1fa1e55725d79/packages/wrangler/src/config/validation.ts#L94-L98
103
116
  return config.legacy_env;
104
117
  }
105
118
 
@@ -107,7 +120,7 @@ function getScriptName(
107
120
  args: { name: string | undefined; env: string | undefined },
108
121
  config: Config
109
122
  ): string | undefined {
110
- if (args.name && isLegacyEnv(args, config) && args.env) {
123
+ if (args.name && isLegacyEnv(config) && args.env) {
111
124
  throw new CommandLineArgsError(
112
125
  "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
126
  `
@@ -127,31 +140,11 @@ function getLegacyScriptName(
127
140
  args: { name: string | undefined; env: string | undefined },
128
141
  config: Config
129
142
  ) {
130
- return args.name && args.env && isLegacyEnv(args, config)
143
+ return args.name && args.env && isLegacyEnv(config)
131
144
  ? `${args.name}-${args.env}`
132
145
  : args.name ?? config.name;
133
146
  }
134
147
 
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...");
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
148
  /**
156
149
  * Get a promise to the streamed input from stdin.
157
150
  *
@@ -211,11 +204,6 @@ function demandOneOfOption(...options: string[]) {
211
204
  }
212
205
 
213
206
  class CommandLineArgsError extends Error {}
214
- class DeprecationError extends Error {
215
- constructor(message: string) {
216
- super(`DEPRECATION WARNING:\n${message}`);
217
- }
218
- }
219
207
 
220
208
  export async function main(argv: string[]): Promise<void> {
221
209
  const wrangler = makeCLI(argv)
@@ -225,8 +213,9 @@ export async function main(argv: string[]): Promise<void> {
225
213
  // Otherwise we just log the error that was thrown without any "help info".
226
214
  .showHelpOnFail(false)
227
215
  .fail((msg, error) => {
228
- if (!error) {
229
- // If there is only a `msg` then this came from yargs own validation, so wrap in a `CommandLineArgsError`.
216
+ if (!error || error.name === "YError") {
217
+ // If there is no error or the error is a "YError", then this came from yargs own validation
218
+ // Wrap it in a `CommandLineArgsError` so that we can handle it appropriately further up.
230
219
  error = new CommandLineArgsError(msg);
231
220
  }
232
221
  throw error;
@@ -276,14 +265,19 @@ export async function main(argv: string[]): Promise<void> {
276
265
  default: "worker",
277
266
  })
278
267
  .positional("template", {
279
- describe: "a link to a GitHub template",
268
+ describe: "The URL of a GitHub template",
280
269
  default: "https://github.com/cloudflare/worker-template",
281
270
  });
282
271
  },
283
- () => {
272
+ (generateArgs) => {
284
273
  // "👯 [DEPRECATED]. Scaffold a Cloudflare Workers project from a public GitHub repository.",
285
274
  throw new DeprecationError(
286
- "`wrangler generate` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#generate for alternatives"
275
+ "`wrangler generate` has been deprecated.\n" +
276
+ "Try running `wrangler init` to generate a basic Worker, or cloning the template repository instead:\n\n" +
277
+ "```\n" +
278
+ `git clone ${generateArgs.template}\n` +
279
+ "```\n\n" +
280
+ "Please refer to https://developers.cloudflare.com/workers/wrangler/deprecations/#generate for more information."
287
281
  );
288
282
  }
289
283
  );
@@ -302,6 +296,12 @@ export async function main(argv: string[]): Promise<void> {
302
296
  describe: "The type of worker to create",
303
297
  type: "string",
304
298
  choices: ["rust", "javascript", "webpack"],
299
+ hidden: true,
300
+ deprecated: true,
301
+ })
302
+ .option("site", {
303
+ hidden: true,
304
+ type: "boolean",
305
305
  deprecated: true,
306
306
  })
307
307
  .option("yes", {
@@ -311,7 +311,7 @@ export async function main(argv: string[]): Promise<void> {
311
311
  });
312
312
  },
313
313
  async (args) => {
314
- printWranglerBanner();
314
+ await printWranglerBanner();
315
315
  if (args.type) {
316
316
  let message = "The --type option is no longer supported.";
317
317
  if (args.type === "webpack") {
@@ -322,9 +322,30 @@ export async function main(argv: string[]): Promise<void> {
322
322
  throw new CommandLineArgsError(message);
323
323
  }
324
324
 
325
- // TODO: make sure args.name is a valid identifier for a worker name
325
+ const creationDirectory = path.resolve(process.cwd(), args.name ?? "");
326
+
327
+ if (args.site) {
328
+ const gitDirectory =
329
+ creationDirectory !== process.cwd()
330
+ ? path.basename(creationDirectory)
331
+ : "my-site";
332
+ const message =
333
+ "The --site option is no longer supported.\n" +
334
+ "If you wish to create a brand new Worker Sites project then clone the `worker-sites-template` starter repository:\n\n" +
335
+ "```\n" +
336
+ `git clone --depth=1 --branch=wrangler2 https://github.com/cloudflare/worker-sites-template ${gitDirectory}\n` +
337
+ `cd ${gitDirectory}\n` +
338
+ "```\n\n" +
339
+ "Find out more about how to create and maintain Sites projects at https://developers.cloudflare.com/workers/platform/sites.\n" +
340
+ "Have you considered using Cloudflare Pages instead? See https://pages.cloudflare.com/.";
341
+ throw new CommandLineArgsError(message);
342
+ }
326
343
 
327
- const creationDirectory = path.join(process.cwd(), args.name ?? "");
344
+ // TODO: make sure args.name is a valid identifier for a worker name
345
+ const workerName = path
346
+ .basename(creationDirectory)
347
+ .toLowerCase()
348
+ .replaceAll(/[^a-z0-9\-_]/gm, "-");
328
349
 
329
350
  const packageManager = await getPackageManager(creationDirectory);
330
351
 
@@ -336,10 +357,9 @@ export async function main(argv: string[]): Promise<void> {
336
357
  "./wrangler.toml"
337
358
  );
338
359
  let justCreatedWranglerToml = false;
339
- const workerName =
340
- args.name || path.basename(path.resolve(process.cwd()));
360
+
341
361
  if (fs.existsSync(wranglerTomlDestination)) {
342
- console.warn(`${wranglerTomlDestination} file already exists!`);
362
+ logger.warn(`${wranglerTomlDestination} file already exists!`);
343
363
  const shouldContinue = await confirm(
344
364
  "Do you want to continue initializing this project?"
345
365
  );
@@ -349,6 +369,7 @@ export async function main(argv: string[]): Promise<void> {
349
369
  } else {
350
370
  await mkdir(creationDirectory, { recursive: true });
351
371
  const compatibilityDate = new Date().toISOString().substring(0, 10);
372
+
352
373
  try {
353
374
  await writeFile(
354
375
  wranglerTomlDestination,
@@ -357,7 +378,8 @@ export async function main(argv: string[]): Promise<void> {
357
378
  compatibility_date: compatibilityDate,
358
379
  }) + "\n"
359
380
  );
360
- console.log(`✨ Successfully created wrangler.toml`);
381
+
382
+ logger.log(`✨ Successfully created wrangler.toml`);
361
383
  justCreatedWranglerToml = true;
362
384
  } catch (err) {
363
385
  throw new Error(
@@ -366,9 +388,27 @@ export async function main(argv: string[]): Promise<void> {
366
388
  }
367
389
  }
368
390
 
391
+ const yesFlag = args.yes ?? false;
392
+
393
+ const isInsideGitProject = Boolean(await findUp(".git"));
394
+ const isGitInstalled = (await execa("git", ["--version"])).exitCode === 0;
395
+ if (!isInsideGitProject && isGitInstalled) {
396
+ const shouldInitGit =
397
+ yesFlag ||
398
+ (await confirm("Would you like to use git to manage this Worker?"));
399
+ if (shouldInitGit) {
400
+ await execa("git", ["init"], { cwd: creationDirectory });
401
+ await writeFile(
402
+ path.join(creationDirectory, ".gitignore"),
403
+ readFileSync(path.join(__dirname, "../templates/gitignore"))
404
+ );
405
+ logger.log(`✨ Initialized git repository`);
406
+ }
407
+ }
408
+
369
409
  let pathToPackageJson = await findUp("package.json");
370
410
  let shouldCreatePackageJson = false;
371
- const yesFlag = args.yes ?? false;
411
+
372
412
  if (!pathToPackageJson) {
373
413
  // If no package.json exists, ask to create one
374
414
  shouldCreatePackageJson =
@@ -393,8 +433,9 @@ export async function main(argv: string[]): Promise<void> {
393
433
  " "
394
434
  ) + "\n"
395
435
  );
436
+
396
437
  await packageManager.install();
397
- console.log(`✨ Created package.json`);
438
+ logger.log(`✨ Created package.json`);
398
439
  pathToPackageJson = path.join(creationDirectory, "package.json");
399
440
  } else {
400
441
  return;
@@ -419,7 +460,7 @@ export async function main(argv: string[]): Promise<void> {
419
460
  ));
420
461
  if (shouldInstall) {
421
462
  await packageManager.addDevDeps(`wrangler@${wranglerVersion}`);
422
- console.log(`✨ Installed wrangler`);
463
+ logger.log(`✨ Installed wrangler`);
423
464
  }
424
465
  }
425
466
  }
@@ -440,7 +481,7 @@ export async function main(argv: string[]): Promise<void> {
440
481
  "typescript"
441
482
  );
442
483
 
443
- console.log(
484
+ logger.log(
444
485
  `✨ Created tsconfig.json, installed @cloudflare/workers-types into devDependencies`
445
486
  );
446
487
  pathToTSConfig = path.join(creationDirectory, "tsconfig.json");
@@ -468,7 +509,7 @@ export async function main(argv: string[]): Promise<void> {
468
509
  // it could be complicated in existing projects
469
510
  // and we don't want to break them. Instead, we simply
470
511
  // tell the user that they need to update their tsconfig.json
471
- console.log(
512
+ logger.log(
472
513
  `✨ Installed @cloudflare/workers-types.\nPlease add "@cloudflare/workers-types" to compilerOptions.types in your tsconfig.json`
473
514
  );
474
515
  }
@@ -492,7 +533,7 @@ export async function main(argv: string[]): Promise<void> {
492
533
  ) {
493
534
  if (isCreatingWranglerToml) {
494
535
  // rewrite wrangler.toml with main = "path/to/script"
495
- const parsedWranglerToml = TOML.parse(
536
+ const parsedWranglerToml = parseTOML(
496
537
  readFileSync(wranglerTomlDestination)
497
538
  );
498
539
  fs.writeFileSync(
@@ -527,21 +568,21 @@ export async function main(argv: string[]): Promise<void> {
527
568
  2
528
569
  ) + "\n"
529
570
  );
530
- console.log(
571
+ logger.log(
531
572
  `\nTo start developing your Worker, run \`${
532
573
  isNamedWorker ? `cd ${args.name} && ` : ""
533
574
  }npm start\``
534
575
  );
535
- console.log(
576
+ logger.log(
536
577
  `To publish your Worker to the Internet, run \`npm run publish\``
537
578
  );
538
579
  } else {
539
- console.log(
580
+ logger.log(
540
581
  `\nTo start developing your Worker, run \`npx wrangler dev\`${
541
582
  isCreatingWranglerToml ? "" : ` ${scriptPath}`
542
583
  }`
543
584
  );
544
- console.log(
585
+ logger.log(
545
586
  `To publish your Worker to the Internet, run \`npx wrangler publish\`${
546
587
  isCreatingWranglerToml ? "" : ` ${scriptPath}`
547
588
  }`
@@ -568,7 +609,7 @@ export async function main(argv: string[]): Promise<void> {
568
609
  readFileSync(path.join(__dirname, "../templates/new-worker.ts"))
569
610
  );
570
611
 
571
- console.log(`✨ Created src/index.ts`);
612
+ logger.log(`✨ Created src/index.ts`);
572
613
 
573
614
  await writePackageJsonScriptsAndUpdateWranglerToml(
574
615
  shouldWritePackageJsonScripts,
@@ -592,7 +633,7 @@ export async function main(argv: string[]): Promise<void> {
592
633
  readFileSync(path.join(__dirname, "../templates/new-worker.js"))
593
634
  );
594
635
 
595
- console.log(`✨ Created src/index.js`);
636
+ logger.log(`✨ Created src/index.js`);
596
637
 
597
638
  await writePackageJsonScriptsAndUpdateWranglerToml(
598
639
  shouldWritePackageJsonScripts,
@@ -623,79 +664,6 @@ export async function main(argv: string[]): Promise<void> {
623
664
  }
624
665
  );
625
666
 
626
- // login
627
- wrangler.command(
628
- // this needs scopes as an option?
629
- "login",
630
- false, // we don't need to show this in the menu
631
- // "🔓 Login to Cloudflare",
632
- (yargs) => {
633
- // TODO: This needs some copy editing
634
- // I mean, this entire app does, but this too.
635
- return yargs
636
- .option("scopes-list", {
637
- describe: "List all the available OAuth scopes with descriptions",
638
- })
639
- .option("scopes", {
640
- describe: "Pick the set of applicable OAuth scopes when logging in",
641
- array: true,
642
- type: "string",
643
- });
644
-
645
- // TODO: scopes
646
- },
647
- async (args) => {
648
- printWranglerBanner();
649
- if (args["scopes-list"]) {
650
- listScopes();
651
- return;
652
- }
653
- if (args.scopes) {
654
- if (args.scopes.length === 0) {
655
- // don't allow no scopes to be passed, that would be weird
656
- listScopes();
657
- return;
658
- }
659
- if (!validateScopeKeys(args.scopes)) {
660
- throw new CommandLineArgsError(
661
- `One of ${args.scopes} is not a valid authentication scope. Run "wrangler login --list-scopes" to see the valid scopes.`
662
- );
663
- }
664
- await login({ scopes: args.scopes });
665
- return;
666
- }
667
- await login();
668
-
669
- // TODO: would be nice if it optionally saved login
670
- // credentials inside node_modules/.cache or something
671
- // this way you could have multiple users on a single machine
672
- }
673
- );
674
-
675
- // logout
676
- wrangler.command(
677
- // this needs scopes as an option?
678
- "logout",
679
- false, // we don't need to show this in the menu
680
- // "🚪 Logout from Cloudflare",
681
- () => {},
682
- async () => {
683
- printWranglerBanner();
684
- await logout();
685
- }
686
- );
687
-
688
- // whoami
689
- wrangler.command(
690
- "whoami",
691
- "🕵️ Retrieve your user info and test your auth config",
692
- () => {},
693
- async () => {
694
- printWranglerBanner();
695
- await whoami();
696
- }
697
- );
698
-
699
667
  // config
700
668
  wrangler.command(
701
669
  "config",
@@ -722,24 +690,30 @@ export async function main(argv: string[]): Promise<void> {
722
690
  .option("name", {
723
691
  describe: "Name of the worker",
724
692
  type: "string",
693
+ requiresArg: true,
725
694
  })
726
695
  .option("format", {
727
696
  choices: ["modules", "service-worker"] as const,
728
697
  describe: "Choose an entry type",
698
+ deprecated: true,
729
699
  })
730
700
  .option("env", {
731
701
  describe: "Perform on a specific environment",
732
702
  type: "string",
703
+ requiresArg: true,
733
704
  alias: "e",
734
705
  })
735
706
  .option("compatibility-date", {
736
707
  describe: "Date to use for compatibility checks",
737
708
  type: "string",
709
+ requiresArg: true,
738
710
  })
739
711
  .option("compatibility-flags", {
740
712
  describe: "Flags to use for compatibility checks",
741
- type: "array",
742
713
  alias: "compatibility-flag",
714
+ type: "string",
715
+ requiresArg: true,
716
+ array: true,
743
717
  })
744
718
  .option("latest", {
745
719
  describe: "Use the latest version of the worker runtime",
@@ -749,6 +723,7 @@ export async function main(argv: string[]): Promise<void> {
749
723
  .option("ip", {
750
724
  describe: "IP address to listen on, defaults to `localhost`",
751
725
  type: "string",
726
+ requiresArg: true,
752
727
  })
753
728
  .option("port", {
754
729
  describe: "Port to listen on",
@@ -761,10 +736,13 @@ export async function main(argv: string[]): Promise<void> {
761
736
  .option("routes", {
762
737
  describe: "Routes to upload",
763
738
  alias: "route",
764
- type: "array",
739
+ type: "string",
740
+ requiresArg: true,
741
+ array: true,
765
742
  })
766
743
  .option("host", {
767
744
  type: "string",
745
+ requiresArg: true,
768
746
  describe:
769
747
  "Host to forward requests to, defaults to the zone of project",
770
748
  })
@@ -775,21 +753,25 @@ export async function main(argv: string[]): Promise<void> {
775
753
  .option("experimental-public", {
776
754
  describe: "Static assets to be served",
777
755
  type: "string",
756
+ requiresArg: true,
778
757
  })
779
758
  .option("site", {
780
759
  describe: "Root folder of static assets for Workers Sites",
781
760
  type: "string",
761
+ requiresArg: true,
782
762
  })
783
763
  .option("site-include", {
784
764
  describe:
785
765
  "Array of .gitignore-style patterns that match file or directory names from the sites directory. Only matched items will be uploaded.",
786
766
  type: "string",
767
+ requiresArg: true,
787
768
  array: true,
788
769
  })
789
770
  .option("site-exclude", {
790
771
  describe:
791
772
  "Array of .gitignore-style patterns that match file or directory names from the sites directory. Matched items will not be uploaded.",
792
773
  type: "string",
774
+ requiresArg: true,
793
775
  array: true,
794
776
  })
795
777
  .option("upstream-protocol", {
@@ -800,14 +782,17 @@ export async function main(argv: string[]): Promise<void> {
800
782
  .option("jsx-factory", {
801
783
  describe: "The function that is called for each JSX element",
802
784
  type: "string",
785
+ requiresArg: true,
803
786
  })
804
787
  .option("jsx-fragment", {
805
788
  describe: "The function that is called for each JSX fragment",
806
789
  type: "string",
790
+ requiresArg: true,
807
791
  })
808
792
  .option("tsconfig", {
809
793
  describe: "Path to a custom tsconfig.json file",
810
794
  type: "string",
795
+ requiresArg: true,
811
796
  })
812
797
  .option("local", {
813
798
  alias: "l",
@@ -815,36 +800,54 @@ export async function main(argv: string[]): Promise<void> {
815
800
  type: "boolean",
816
801
  default: false, // I bet this will a point of contention. We'll revisit it.
817
802
  })
803
+ .option("minify", {
804
+ describe: "Minify the script",
805
+ type: "boolean",
806
+ })
807
+ .option("node-compat", {
808
+ describe: "Enable node.js compatibility",
809
+ type: "boolean",
810
+ })
818
811
  .option("experimental-enable-local-persistence", {
819
812
  describe: "Enable persistence for this session (only for local mode)",
820
813
  type: "boolean",
814
+ })
815
+ .option("inspect", {
816
+ describe: "Enable dev tools",
817
+ type: "boolean",
818
+ deprecated: true,
821
819
  });
822
820
  },
823
821
  async (args) => {
824
- printWranglerBanner();
822
+ await printWranglerBanner();
825
823
  const configPath =
826
824
  (args.config as ConfigPath) ||
827
825
  (args.script && findWranglerToml(path.dirname(args.script)));
828
826
  const config = readConfig(configPath, args);
829
827
  const entry = await getEntry(args, config, "dev");
830
828
 
829
+ if (args.inspect) {
830
+ logger.warn(
831
+ "Passing --inspect is unnecessary, now you can always connect to devtools."
832
+ );
833
+ }
834
+
831
835
  if (args["experimental-public"]) {
832
- console.warn(
833
- "🚨 The --experimental-public field is experimental and will change in the future."
836
+ logger.warn(
837
+ "The --experimental-public field is experimental and will change in the future."
834
838
  );
835
839
  }
836
840
 
837
841
  if (args.public) {
838
842
  throw new Error(
839
- "🚨 The --public field has been renamed to --experimental-public, and will change behaviour in the future."
843
+ "The --public field has been renamed to --experimental-public, and will change behaviour in the future."
840
844
  );
841
845
  }
842
846
 
843
847
  const upstreamProtocol =
844
- (args["upstream-protocol"] as "http" | "https") ||
845
- config.dev.upstream_protocol;
848
+ args["upstream-protocol"] || config.dev.upstream_protocol;
846
849
  if (upstreamProtocol === "http") {
847
- console.warn(
850
+ logger.warn(
848
851
  "Setting upstream-protocol to http is not currently implemented.\n" +
849
852
  "If this is required in your project, please add your use case to the following issue:\n" +
850
853
  "https://github.com/cloudflare/wrangler2/issues/583."
@@ -900,19 +903,33 @@ export async function main(argv: string[]): Promise<void> {
900
903
  (args.routes && args.routes[0]) ||
901
904
  config.route ||
902
905
  (config.routes && config.routes[0]);
906
+
907
+ let zoneId: string | undefined =
908
+ typeof hostLike === "object" && "zone_id" in hostLike
909
+ ? hostLike.zone_id
910
+ : undefined;
911
+
903
912
  const host =
904
- typeof hostLike === "string" ? getHost(hostLike) : undefined;
905
- let zoneId: string | undefined;
913
+ typeof hostLike === "string"
914
+ ? getHost(hostLike)
915
+ : typeof hostLike === "object"
916
+ ? "zone_name" in hostLike
917
+ ? getHost(hostLike.zone_name)
918
+ : getHost(hostLike.pattern)
919
+ : undefined;
920
+
906
921
  const hostPieces =
907
922
  typeof host === "string" ? host.split(".") : undefined;
908
- while (hostPieces && hostPieces.length > 1) {
923
+
924
+ while (!zoneId && hostPieces && hostPieces.length > 1) {
909
925
  zoneId = await getZoneId(hostPieces.join("."));
910
- if (zoneId) break;
911
926
  hostPieces.shift();
912
927
  }
928
+
913
929
  if (host && !zoneId) {
914
930
  throw new Error(`Could not find zone for ${hostLike}`);
915
931
  }
932
+
916
933
  zone =
917
934
  typeof zoneId === "string" && typeof host === "string"
918
935
  ? {
@@ -922,6 +939,13 @@ export async function main(argv: string[]): Promise<void> {
922
939
  : undefined;
923
940
  }
924
941
 
942
+ const nodeCompat = args.nodeCompat ?? config.node_compat;
943
+ if (nodeCompat) {
944
+ logger.warn(
945
+ "Enabling node.js compatibility mode for builtins and globals. This is experimental and has serious tradeoffs. Please see https://github.com/ionic-team/rollup-plugin-node-polyfills/ for more details."
946
+ );
947
+ }
948
+
925
949
  const { waitUntilExit } = render(
926
950
  <Dev
927
951
  name={getScriptName(args, config)}
@@ -929,19 +953,16 @@ export async function main(argv: string[]): Promise<void> {
929
953
  env={args.env}
930
954
  zone={zone}
931
955
  rules={getRules(config)}
932
- legacyEnv={isLegacyEnv(args, config)}
956
+ legacyEnv={isLegacyEnv(config)}
957
+ minify={args.minify ?? config.minify}
958
+ nodeCompat={nodeCompat}
933
959
  build={config.build || {}}
934
960
  initialMode={args.local ? "local" : "remote"}
935
961
  jsxFactory={args["jsx-factory"] || config.jsx_factory}
936
962
  jsxFragment={args["jsx-fragment"] || config.jsx_fragment}
937
963
  tsconfig={args.tsconfig ?? config.tsconfig}
938
964
  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
- }
965
+ localProtocol={args["local-protocol"] || config.dev.local_protocol}
945
966
  enableLocalPersistence={
946
967
  args["experimental-enable-local-persistence"] || false
947
968
  }
@@ -960,14 +981,12 @@ export async function main(argv: string[]): Promise<void> {
960
981
  args["inspector-port"] ?? (await getPort({ port: 9229 }))
961
982
  }
962
983
  public={args["experimental-public"]}
963
- compatibilityDate={
964
- args["compatibility-date"] ||
965
- config.compatibility_date ||
966
- new Date().toISOString().substring(0, 10)
967
- }
984
+ compatibilityDate={getDevCompatibilityDate(
985
+ config,
986
+ args["compatibility-date"]
987
+ )}
968
988
  compatibilityFlags={
969
- (args["compatibility-flags"] as string[]) ||
970
- config.compatibility_flags
989
+ args["compatibility-flags"] || config.compatibility_flags
971
990
  }
972
991
  usageModel={config.usage_model}
973
992
  bindings={{
@@ -992,13 +1011,29 @@ export async function main(argv: string[]): Promise<void> {
992
1011
  };
993
1012
  }
994
1013
  ),
995
- vars: config.vars,
1014
+ vars: getVarsForDev(config),
996
1015
  wasm_modules: config.wasm_modules,
997
1016
  text_blobs: config.text_blobs,
1017
+ data_blobs: config.data_blobs,
998
1018
  durable_objects: config.durable_objects,
999
- r2_buckets: config.r2_buckets,
1019
+ r2_buckets: config.r2_buckets?.map(
1020
+ ({ binding, preview_bucket_name, bucket_name: _bucket_name }) => {
1021
+ // same idea as kv namespace preview id,
1022
+ // same copy-on-write TODO
1023
+ if (!preview_bucket_name) {
1024
+ throw new Error(
1025
+ `In development, you should use a separate r2 bucket than the one you'd use in production. Please create a new r2 bucket with "wrangler r2 bucket create <name>" and add its name as preview_bucket_name to the r2_buckets "${binding}" in your wrangler.toml`
1026
+ );
1027
+ }
1028
+ return {
1029
+ binding,
1030
+ bucket_name: preview_bucket_name,
1031
+ };
1032
+ }
1033
+ ),
1000
1034
  unsafe: config.unsafe?.bindings,
1001
1035
  }}
1036
+ crons={config.triggers.crons}
1002
1037
  />
1003
1038
  );
1004
1039
  await waitUntilExit();
@@ -1013,29 +1048,41 @@ export async function main(argv: string[]): Promise<void> {
1013
1048
  return yargs
1014
1049
  .option("env", {
1015
1050
  type: "string",
1051
+ requiresArg: true,
1016
1052
  describe: "Perform on a specific environment",
1017
1053
  alias: "e",
1018
1054
  })
1019
1055
  .positional("script", {
1020
1056
  describe: "The path to an entry point for your worker",
1021
1057
  type: "string",
1058
+ requiresArg: true,
1022
1059
  })
1023
1060
  .option("name", {
1024
1061
  describe: "Name of the worker",
1025
1062
  type: "string",
1063
+ requiresArg: true,
1064
+ })
1065
+ .option("outdir", {
1066
+ describe: "Output directory for the bundled worker",
1067
+ type: "string",
1068
+ requiresArg: true,
1026
1069
  })
1027
1070
  .option("format", {
1028
1071
  choices: ["modules", "service-worker"] as const,
1029
1072
  describe: "Choose an entry type",
1073
+ deprecated: true,
1030
1074
  })
1031
1075
  .option("compatibility-date", {
1032
1076
  describe: "Date to use for compatibility checks",
1033
1077
  type: "string",
1078
+ requiresArg: true,
1034
1079
  })
1035
1080
  .option("compatibility-flags", {
1036
1081
  describe: "Flags to use for compatibility checks",
1037
- type: "array",
1038
1082
  alias: "compatibility-flag",
1083
+ type: "string",
1084
+ requiresArg: true,
1085
+ array: true,
1039
1086
  })
1040
1087
  .option("latest", {
1041
1088
  describe: "Use the latest version of the worker runtime",
@@ -1045,56 +1092,79 @@ export async function main(argv: string[]): Promise<void> {
1045
1092
  .option("experimental-public", {
1046
1093
  describe: "Static assets to be served",
1047
1094
  type: "string",
1095
+ requiresArg: true,
1048
1096
  })
1049
1097
  .option("site", {
1050
1098
  describe: "Root folder of static assets for Workers Sites",
1051
1099
  type: "string",
1100
+ requiresArg: true,
1052
1101
  })
1053
1102
  .option("site-include", {
1054
1103
  describe:
1055
1104
  "Array of .gitignore-style patterns that match file or directory names from the sites directory. Only matched items will be uploaded.",
1056
1105
  type: "string",
1106
+ requiresArg: true,
1057
1107
  array: true,
1058
1108
  })
1059
1109
  .option("site-exclude", {
1060
1110
  describe:
1061
1111
  "Array of .gitignore-style patterns that match file or directory names from the sites directory. Matched items will not be uploaded.",
1062
1112
  type: "string",
1113
+ requiresArg: true,
1063
1114
  array: true,
1064
1115
  })
1065
1116
  .option("triggers", {
1066
1117
  describe: "cron schedules to attach",
1067
1118
  alias: ["schedule", "schedules"],
1068
- type: "array",
1119
+ type: "string",
1120
+ requiresArg: true,
1121
+ array: true,
1069
1122
  })
1070
1123
  .option("routes", {
1071
1124
  describe: "Routes to upload",
1072
1125
  alias: "route",
1073
- type: "array",
1126
+ type: "string",
1127
+ requiresArg: true,
1128
+ array: true,
1074
1129
  })
1075
1130
  .option("jsx-factory", {
1076
1131
  describe: "The function that is called for each JSX element",
1077
1132
  type: "string",
1133
+ requiresArg: true,
1078
1134
  })
1079
1135
  .option("jsx-fragment", {
1080
1136
  describe: "The function that is called for each JSX fragment",
1081
1137
  type: "string",
1138
+ requiresArg: true,
1082
1139
  })
1083
1140
  .option("tsconfig", {
1084
1141
  describe: "Path to a custom tsconfig.json file",
1085
1142
  type: "string",
1143
+ requiresArg: true,
1144
+ })
1145
+ .option("minify", {
1146
+ describe: "Minify the script",
1147
+ type: "boolean",
1148
+ })
1149
+ .option("node-compat", {
1150
+ describe: "Enable node.js compatibility",
1151
+ type: "boolean",
1152
+ })
1153
+ .option("dry-run", {
1154
+ describe: "Don't actually publish",
1155
+ type: "boolean",
1086
1156
  });
1087
1157
  },
1088
1158
  async (args) => {
1089
- printWranglerBanner();
1159
+ await printWranglerBanner();
1090
1160
  if (args["experimental-public"]) {
1091
- console.warn(
1092
- "🚨 The --experimental-public field is experimental and will change in the future."
1161
+ logger.warn(
1162
+ "The --experimental-public field is experimental and will change in the future."
1093
1163
  );
1094
1164
  }
1095
1165
  if (args.public) {
1096
1166
  throw new Error(
1097
- "🚨 The --public field has been renamed to --experimental-public, and will change behaviour in the future."
1167
+ "The --public field has been renamed to --experimental-public, and will change behaviour in the future."
1098
1168
  );
1099
1169
  }
1100
1170
 
@@ -1105,8 +1175,8 @@ export async function main(argv: string[]): Promise<void> {
1105
1175
  const entry = await getEntry(args, config, "publish");
1106
1176
 
1107
1177
  if (args.latest) {
1108
- console.warn(
1109
- "⚠️ 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"
1178
+ logger.warn(
1179
+ "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"
1110
1180
  );
1111
1181
  }
1112
1182
 
@@ -1128,15 +1198,19 @@ export async function main(argv: string[]): Promise<void> {
1128
1198
  compatibilityDate: args.latest
1129
1199
  ? new Date().toISOString().substring(0, 10)
1130
1200
  : args["compatibility-date"],
1131
- compatibilityFlags: args["compatibility-flags"] as string[],
1201
+ compatibilityFlags: args["compatibility-flags"],
1132
1202
  triggers: args.triggers,
1133
1203
  jsxFactory: args["jsx-factory"],
1134
1204
  jsxFragment: args["jsx-fragment"],
1135
1205
  tsconfig: args.tsconfig,
1136
1206
  routes: args.routes,
1137
1207
  assetPaths,
1138
- legacyEnv: isLegacyEnv(args, config),
1208
+ legacyEnv: isLegacyEnv(config),
1209
+ minify: args.minify,
1210
+ nodeCompat: args.nodeCompat,
1139
1211
  experimentalPublic: args["experimental-public"] !== undefined,
1212
+ outDir: args.outdir,
1213
+ dryRun: args.dryRun,
1140
1214
  });
1141
1215
  }
1142
1216
  );
@@ -1146,63 +1220,65 @@ export async function main(argv: string[]): Promise<void> {
1146
1220
  "tail [name]",
1147
1221
  "🦚 Starts a log tailing session for a published Worker.",
1148
1222
  (yargs) => {
1149
- return (
1150
- yargs
1151
- .positional("name", {
1152
- describe: "Name of the worker",
1153
- type: "string",
1154
- })
1155
- // TODO: auto-detect if this should be json or pretty based on atty
1156
- .option("format", {
1157
- default: "pretty",
1158
- choices: ["json", "pretty"],
1159
- describe: "The format of log entries",
1160
- })
1161
- .option("status", {
1162
- choices: ["ok", "error", "canceled"],
1163
- describe: "Filter by invocation status",
1164
- array: true,
1165
- })
1166
- .option("header", {
1167
- type: "string",
1168
- describe: "Filter by HTTP header",
1169
- })
1170
- .option("method", {
1171
- type: "string",
1172
- describe: "Filter by HTTP method",
1173
- array: true,
1174
- })
1175
- .option("sampling-rate", {
1176
- type: "number",
1177
- describe: "Adds a percentage of requests to log sampling rate",
1178
- })
1179
- .option("search", {
1180
- type: "string",
1181
- describe: "Filter by a text match in console.log messages",
1182
- })
1183
- .option("ip", {
1184
- type: "string",
1185
- describe:
1186
- 'Filter by the IP address the request originates from. Use "self" to filter for your own IP',
1187
- array: true,
1188
- })
1189
- .option("env", {
1190
- type: "string",
1191
- describe: "Perform on a specific environment",
1192
- alias: "e",
1193
- })
1194
- .option("debug", {
1195
- type: "boolean",
1196
- hidden: true,
1197
- default: false,
1198
- describe:
1199
- "If a log would have been filtered out, send it through anyway alongside the filter which would have blocked it.",
1200
- })
1201
- );
1223
+ return yargs
1224
+ .positional("name", {
1225
+ describe: "Name of the worker",
1226
+ type: "string",
1227
+ })
1228
+ .option("format", {
1229
+ default: process.stdout.isTTY ? "pretty" : "json",
1230
+ choices: ["json", "pretty"],
1231
+ describe: "The format of log entries",
1232
+ })
1233
+ .option("status", {
1234
+ choices: ["ok", "error", "canceled"],
1235
+ describe: "Filter by invocation status",
1236
+ array: true,
1237
+ })
1238
+ .option("header", {
1239
+ type: "string",
1240
+ requiresArg: true,
1241
+ describe: "Filter by HTTP header",
1242
+ })
1243
+ .option("method", {
1244
+ type: "string",
1245
+ requiresArg: true,
1246
+ describe: "Filter by HTTP method",
1247
+ array: true,
1248
+ })
1249
+ .option("sampling-rate", {
1250
+ type: "number",
1251
+ describe: "Adds a percentage of requests to log sampling rate",
1252
+ })
1253
+ .option("search", {
1254
+ type: "string",
1255
+ requiresArg: true,
1256
+ describe: "Filter by a text match in console.log messages",
1257
+ })
1258
+ .option("ip", {
1259
+ type: "string",
1260
+ requiresArg: true,
1261
+ describe:
1262
+ 'Filter by the IP address the request originates from. Use "self" to filter for your own IP',
1263
+ array: true,
1264
+ })
1265
+ .option("env", {
1266
+ type: "string",
1267
+ requiresArg: true,
1268
+ describe: "Perform on a specific environment",
1269
+ alias: "e",
1270
+ })
1271
+ .option("debug", {
1272
+ type: "boolean",
1273
+ hidden: true,
1274
+ default: false,
1275
+ describe:
1276
+ "If a log would have been filtered out, send it through anyway alongside the filter which would have blocked it.",
1277
+ });
1202
1278
  },
1203
1279
  async (args) => {
1204
1280
  if (args.format === "pretty") {
1205
- printWranglerBanner();
1281
+ await printWranglerBanner();
1206
1282
  }
1207
1283
  const config = readConfig(args.config as ConfigPath, args);
1208
1284
 
@@ -1215,7 +1291,7 @@ export async function main(argv: string[]): Promise<void> {
1215
1291
  const accountId = await requireAuth(config);
1216
1292
 
1217
1293
  const cliFilters: TailCLIFilters = {
1218
- status: args.status as ("ok" | "error" | "canceled")[],
1294
+ status: args.status as ("ok" | "error" | "canceled")[] | undefined,
1219
1295
  header: args.header,
1220
1296
  method: args.method,
1221
1297
  samplingRate: args["sampling-rate"],
@@ -1232,16 +1308,18 @@ export async function main(argv: string[]): Promise<void> {
1232
1308
  accountId,
1233
1309
  scriptName,
1234
1310
  filters,
1235
- !isLegacyEnv(args, config) ? args.env : undefined
1311
+ !isLegacyEnv(config) ? args.env : undefined
1236
1312
  );
1237
1313
 
1238
1314
  const scriptDisplayName = `${scriptName}${
1239
- args.env && !isLegacyEnv(args, config) ? ` (${args.env})` : ""
1315
+ args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
1240
1316
  }`;
1241
1317
 
1242
- console.log(
1243
- `successfully created tail, expires at ${expiration.toLocaleString()}`
1244
- );
1318
+ if (args.format === "pretty") {
1319
+ logger.log(
1320
+ `Successfully created tail, expires at ${expiration.toLocaleString()}`
1321
+ );
1322
+ }
1245
1323
 
1246
1324
  onExit(async () => {
1247
1325
  tail.terminate();
@@ -1268,7 +1346,9 @@ export async function main(argv: string[]): Promise<void> {
1268
1346
  }
1269
1347
  }
1270
1348
 
1271
- console.log(`Connected to ${scriptDisplayName}, waiting for logs...`);
1349
+ if (args.format === "pretty") {
1350
+ logger.log(`Connected to ${scriptDisplayName}, waiting for logs...`);
1351
+ }
1272
1352
 
1273
1353
  tail.on("close", async () => {
1274
1354
  tail.terminate();
@@ -1293,6 +1373,7 @@ export async function main(argv: string[]): Promise<void> {
1293
1373
  })
1294
1374
  .option("env", {
1295
1375
  type: "string",
1376
+ requiresArg: true,
1296
1377
  describe: "Perform on a specific environment",
1297
1378
  })
1298
1379
  .option("watch", {
@@ -1310,7 +1391,7 @@ export async function main(argv: string[]): Promise<void> {
1310
1391
  }
1311
1392
 
1312
1393
  // Delegate to `wrangler dev`
1313
- console.warn(
1394
+ logger.warn(
1314
1395
  "***************************************************\n" +
1315
1396
  "The `wrangler preview` command has been deprecated.\n" +
1316
1397
  "Attempting to run `wrangler dev` instead.\n" +
@@ -1329,8 +1410,10 @@ export async function main(argv: string[]): Promise<void> {
1329
1410
  rules={getRules(config)}
1330
1411
  env={args.env}
1331
1412
  zone={undefined}
1332
- legacyEnv={isLegacyEnv(args, config)}
1413
+ legacyEnv={isLegacyEnv(config)}
1333
1414
  build={config.build || {}}
1415
+ minify={undefined}
1416
+ nodeCompat={config.node_compat}
1334
1417
  initialMode={args.local ? "local" : "remote"}
1335
1418
  jsxFactory={config.jsx_factory}
1336
1419
  jsxFragment={config.jsx_fragment}
@@ -1343,14 +1426,8 @@ export async function main(argv: string[]): Promise<void> {
1343
1426
  port={config.dev?.port}
1344
1427
  ip={config.dev.ip}
1345
1428
  public={undefined}
1346
- compatibilityDate={
1347
- config.compatibility_date ||
1348
- new Date().toISOString().substring(0, 10)
1349
- }
1350
- compatibilityFlags={
1351
- (args["compatibility-flags"] as string[]) ||
1352
- config.compatibility_flags
1353
- }
1429
+ compatibilityDate={getDevCompatibilityDate(config)}
1430
+ compatibilityFlags={config.compatibility_flags}
1354
1431
  usageModel={config.usage_model}
1355
1432
  bindings={{
1356
1433
  kv_namespaces: config.kv_namespaces?.map(
@@ -1377,10 +1454,26 @@ export async function main(argv: string[]): Promise<void> {
1377
1454
  vars: config.vars,
1378
1455
  wasm_modules: config.wasm_modules,
1379
1456
  text_blobs: config.text_blobs,
1457
+ data_blobs: config.data_blobs,
1380
1458
  durable_objects: config.durable_objects,
1381
- r2_buckets: config.r2_buckets,
1459
+ r2_buckets: config.r2_buckets?.map(
1460
+ ({ binding, preview_bucket_name, bucket_name: _bucket_name }) => {
1461
+ // same idea as kv namespace preview id,
1462
+ // same copy-on-write TODO
1463
+ if (!preview_bucket_name) {
1464
+ throw new Error(
1465
+ `In development, you should use a separate r2 bucket than the one you'd use in production. Please create a new r2 bucket with "wrangler r2 bucket create <name>" and add its name as preview_bucket_name to the r2_buckets "${binding}" in your wrangler.toml`
1466
+ );
1467
+ }
1468
+ return {
1469
+ binding,
1470
+ bucket_name: preview_bucket_name,
1471
+ };
1472
+ }
1473
+ ),
1382
1474
  unsafe: config.unsafe?.bindings,
1383
1475
  }}
1476
+ crons={config.triggers.crons}
1384
1477
  inspectorPort={await getPort({ port: 9229 })}
1385
1478
  />
1386
1479
  );
@@ -1402,10 +1495,12 @@ export async function main(argv: string[]): Promise<void> {
1402
1495
  return yargs
1403
1496
  .option("env", {
1404
1497
  type: "string",
1498
+ requiresArg: true,
1405
1499
  describe: "Perform on a specific environment",
1406
1500
  })
1407
1501
  .option("zone", {
1408
1502
  type: "string",
1503
+ requiresArg: true,
1409
1504
  describe: "Zone id",
1410
1505
  })
1411
1506
  .positional("zone", {
@@ -1437,10 +1532,12 @@ export async function main(argv: string[]): Promise<void> {
1437
1532
  })
1438
1533
  .option("zone", {
1439
1534
  type: "string",
1535
+ requiresArg: true,
1440
1536
  describe: "zone id",
1441
1537
  })
1442
1538
  .option("env", {
1443
1539
  type: "string",
1540
+ requiresArg: true,
1444
1541
  describe: "Perform on a specific environment",
1445
1542
  });
1446
1543
  },
@@ -1497,16 +1594,18 @@ export async function main(argv: string[]): Promise<void> {
1497
1594
  .option("name", {
1498
1595
  describe: "Name of the worker",
1499
1596
  type: "string",
1597
+ requiresArg: true,
1500
1598
  })
1501
1599
  .option("env", {
1502
1600
  type: "string",
1601
+ requiresArg: true,
1503
1602
  describe:
1504
1603
  "Binds the secret to the Worker of the specific environment",
1505
1604
  alias: "e",
1506
1605
  });
1507
1606
  },
1508
1607
  async (args) => {
1509
- printWranglerBanner();
1608
+ await printWranglerBanner();
1510
1609
  const config = readConfig(args.config as ConfigPath, args);
1511
1610
 
1512
1611
  const scriptName = getLegacyScriptName(args, config);
@@ -1514,22 +1613,22 @@ export async function main(argv: string[]): Promise<void> {
1514
1613
  throw new Error("Missing script name");
1515
1614
  }
1516
1615
 
1517
- const isInteractive = process.stdin.isTTY;
1518
- const accountId = await requireAuth(config, isInteractive);
1616
+ const accountId = await requireAuth(config);
1519
1617
 
1618
+ const isInteractive = process.stdin.isTTY;
1520
1619
  const secretValue = isInteractive
1521
1620
  ? await prompt("Enter a secret value:", "password")
1522
1621
  : await readFromStdin();
1523
1622
 
1524
- console.log(
1623
+ logger.log(
1525
1624
  `🌀 Creating the secret for script ${scriptName} ${
1526
- args.env && !isLegacyEnv(args, config) ? `(${args.env})` : ""
1625
+ args.env && !isLegacyEnv(config) ? `(${args.env})` : ""
1527
1626
  }`
1528
1627
  );
1529
1628
 
1530
1629
  async function submitSecret() {
1531
1630
  const url =
1532
- !args.env || isLegacyEnv(args, config)
1631
+ !args.env || isLegacyEnv(config)
1533
1632
  ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
1534
1633
  : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1535
1634
 
@@ -1547,7 +1646,7 @@ export async function main(argv: string[]): Promise<void> {
1547
1646
  const createDraftWorker = async () => {
1548
1647
  // TODO: log a warning
1549
1648
  await fetchResult(
1550
- !args["legacy-env"] && args.env
1649
+ !isLegacyEnv(config) && args.env
1551
1650
  ? `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}`
1552
1651
  : `/accounts/${accountId}/workers/scripts/${scriptName}`,
1553
1652
  {
@@ -1566,6 +1665,7 @@ export async function main(argv: string[]): Promise<void> {
1566
1665
  r2_buckets: [],
1567
1666
  wasm_modules: {},
1568
1667
  text_blobs: {},
1668
+ data_blobs: {},
1569
1669
  unsafe: [],
1570
1670
  },
1571
1671
  modules: [],
@@ -1582,7 +1682,7 @@ export async function main(argv: string[]): Promise<void> {
1582
1682
  return (
1583
1683
  typeof e === "object" &&
1584
1684
  e !== null &&
1585
- (e as { code: 10007 }).code === 10007
1685
+ (e as { code: number }).code === 10007
1586
1686
  );
1587
1687
  }
1588
1688
 
@@ -1599,14 +1699,14 @@ export async function main(argv: string[]): Promise<void> {
1599
1699
  }
1600
1700
  }
1601
1701
 
1602
- console.log(`✨ Success! Uploaded secret ${args.key}`);
1702
+ logger.log(`✨ Success! Uploaded secret ${args.key}`);
1603
1703
  }
1604
1704
  )
1605
1705
  .command(
1606
1706
  "delete <key>",
1607
1707
  "Delete a secret variable from a script",
1608
- (yargs) => {
1609
- printWranglerBanner();
1708
+ async (yargs) => {
1709
+ await printWranglerBanner();
1610
1710
  return yargs
1611
1711
  .positional("key", {
1612
1712
  describe: "The variable name to be accessible in the script",
@@ -1615,9 +1715,11 @@ export async function main(argv: string[]): Promise<void> {
1615
1715
  .option("name", {
1616
1716
  describe: "Name of the worker",
1617
1717
  type: "string",
1718
+ requiresArg: true,
1618
1719
  })
1619
1720
  .option("env", {
1620
1721
  type: "string",
1722
+ requiresArg: true,
1621
1723
  describe:
1622
1724
  "Binds the secret to the Worker of the specific environment",
1623
1725
  alias: "e",
@@ -1638,23 +1740,23 @@ export async function main(argv: string[]): Promise<void> {
1638
1740
  `Are you sure you want to permanently delete the variable ${
1639
1741
  args.key
1640
1742
  } on the script ${scriptName}${
1641
- args.env && !isLegacyEnv(args, config) ? ` (${args.env})` : ""
1743
+ args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
1642
1744
  }?`
1643
1745
  )
1644
1746
  ) {
1645
- console.log(
1747
+ logger.log(
1646
1748
  `🌀 Deleting the secret ${args.key} on script ${scriptName}${
1647
- args.env && !isLegacyEnv(args, config) ? ` (${args.env})` : ""
1749
+ args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
1648
1750
  }`
1649
1751
  );
1650
1752
 
1651
1753
  const url =
1652
- !args.env || isLegacyEnv(args, config)
1754
+ !args.env || isLegacyEnv(config)
1653
1755
  ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
1654
1756
  : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1655
1757
 
1656
1758
  await fetchResult(`${url}/${args.key}`, { method: "DELETE" });
1657
- console.log(`✨ Success! Deleted secret ${args.key}`);
1759
+ logger.log(`✨ Success! Deleted secret ${args.key}`);
1658
1760
  }
1659
1761
  }
1660
1762
  )
@@ -1666,9 +1768,11 @@ export async function main(argv: string[]): Promise<void> {
1666
1768
  .option("name", {
1667
1769
  describe: "Name of the worker",
1668
1770
  type: "string",
1771
+ requiresArg: true,
1669
1772
  })
1670
1773
  .option("env", {
1671
1774
  type: "string",
1775
+ requiresArg: true,
1672
1776
  describe:
1673
1777
  "Binds the secret to the Worker of the specific environment.",
1674
1778
  alias: "e",
@@ -1685,11 +1789,11 @@ export async function main(argv: string[]): Promise<void> {
1685
1789
  const accountId = await requireAuth(config);
1686
1790
 
1687
1791
  const url =
1688
- !args.env || isLegacyEnv(args, config)
1792
+ !args.env || isLegacyEnv(config)
1689
1793
  ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
1690
1794
  : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1691
1795
 
1692
- console.log(JSON.stringify(await fetchResult(url), null, " "));
1796
+ logger.log(JSON.stringify(await fetchResult(url), null, " "));
1693
1797
  }
1694
1798
  );
1695
1799
  }
@@ -1715,6 +1819,7 @@ export async function main(argv: string[]): Promise<void> {
1715
1819
  })
1716
1820
  .option("env", {
1717
1821
  type: "string",
1822
+ requiresArg: true,
1718
1823
  describe: "Perform on a specific environment",
1719
1824
  alias: "e",
1720
1825
  })
@@ -1724,7 +1829,7 @@ export async function main(argv: string[]): Promise<void> {
1724
1829
  });
1725
1830
  },
1726
1831
  async (args) => {
1727
- printWranglerBanner();
1832
+ await printWranglerBanner();
1728
1833
 
1729
1834
  if (!isValidNamespaceBinding(args.namespace)) {
1730
1835
  throw new CommandLineArgsError(
@@ -1734,7 +1839,7 @@ export async function main(argv: string[]): Promise<void> {
1734
1839
 
1735
1840
  const config = readConfig(args.config as ConfigPath, args);
1736
1841
  if (!config.name) {
1737
- console.warn(
1842
+ logger.warn(
1738
1843
  "No configured name present, using `worker` as a prefix for the title"
1739
1844
  );
1740
1845
  }
@@ -1748,16 +1853,16 @@ export async function main(argv: string[]): Promise<void> {
1748
1853
 
1749
1854
  // TODO: generate a binding name stripping non alphanumeric chars
1750
1855
 
1751
- console.log(`🌀 Creating namespace with title "${title}"`);
1856
+ logger.log(`🌀 Creating namespace with title "${title}"`);
1752
1857
  const namespaceId = await createNamespace(accountId, title);
1753
1858
 
1754
- console.log("✨ Success!");
1859
+ logger.log("✨ Success!");
1755
1860
  const envString = args.env ? ` under [env.${args.env}]` : "";
1756
1861
  const previewString = args.preview ? "preview_" : "";
1757
- console.log(
1862
+ logger.log(
1758
1863
  `Add the following to your configuration file in your kv_namespaces array${envString}:`
1759
1864
  );
1760
- console.log(
1865
+ logger.log(
1761
1866
  `{ binding = "${args.namespace}", ${previewString}id = "${namespaceId}" }`
1762
1867
  );
1763
1868
 
@@ -1775,7 +1880,7 @@ export async function main(argv: string[]): Promise<void> {
1775
1880
 
1776
1881
  // TODO: we should show bindings if they exist for given ids
1777
1882
 
1778
- console.log(
1883
+ logger.log(
1779
1884
  JSON.stringify(await listNamespaces(accountId), null, " ")
1780
1885
  );
1781
1886
  }
@@ -1787,15 +1892,18 @@ export async function main(argv: string[]): Promise<void> {
1787
1892
  return yargs
1788
1893
  .option("binding", {
1789
1894
  type: "string",
1895
+ requiresArg: true,
1790
1896
  describe: "The name of the namespace to delete",
1791
1897
  })
1792
1898
  .option("namespace-id", {
1793
1899
  type: "string",
1900
+ requiresArg: true,
1794
1901
  describe: "The id of the namespace to delete",
1795
1902
  })
1796
1903
  .check(demandOneOfOption("binding", "namespace-id"))
1797
1904
  .option("env", {
1798
1905
  type: "string",
1906
+ requiresArg: true,
1799
1907
  describe: "Perform on a specific environment",
1800
1908
  alias: "e",
1801
1909
  })
@@ -1805,7 +1913,7 @@ export async function main(argv: string[]): Promise<void> {
1805
1913
  });
1806
1914
  },
1807
1915
  async (args) => {
1808
- printWranglerBanner();
1916
+ await printWranglerBanner();
1809
1917
  const config = readConfig(args.config as ConfigPath, args);
1810
1918
 
1811
1919
  let id;
@@ -1869,15 +1977,18 @@ export async function main(argv: string[]): Promise<void> {
1869
1977
  })
1870
1978
  .option("binding", {
1871
1979
  type: "string",
1980
+ requiresArg: true,
1872
1981
  describe: "The binding of the namespace to write to",
1873
1982
  })
1874
1983
  .option("namespace-id", {
1875
1984
  type: "string",
1985
+ requiresArg: true,
1876
1986
  describe: "The id of the namespace to write to",
1877
1987
  })
1878
1988
  .check(demandOneOfOption("binding", "namespace-id"))
1879
1989
  .option("env", {
1880
1990
  type: "string",
1991
+ requiresArg: true,
1881
1992
  describe: "Perform on a specific environment",
1882
1993
  alias: "e",
1883
1994
  })
@@ -1896,12 +2007,13 @@ export async function main(argv: string[]): Promise<void> {
1896
2007
  })
1897
2008
  .option("path", {
1898
2009
  type: "string",
2010
+ requiresArg: true,
1899
2011
  describe: "Read value from the file at a given path",
1900
2012
  })
1901
2013
  .check(demandOneOfOption("value", "path"));
1902
2014
  },
1903
2015
  async ({ key, ttl, expiration, ...args }) => {
1904
- printWranglerBanner();
2016
+ await printWranglerBanner();
1905
2017
  const config = readConfig(args.config as ConfigPath, args);
1906
2018
  const namespaceId = getNamespaceId(args, config);
1907
2019
  // One of `args.path` and `args.value` must be defined
@@ -1911,12 +2023,12 @@ export async function main(argv: string[]): Promise<void> {
1911
2023
  args.value!;
1912
2024
 
1913
2025
  if (args.path) {
1914
- console.log(
1915
- `writing the contents of ${args.path} to the key "${key}" on namespace ${namespaceId}`
2026
+ logger.log(
2027
+ `Writing the contents of ${args.path} to the key "${key}" on namespace ${namespaceId}.`
1916
2028
  );
1917
2029
  } else {
1918
- console.log(
1919
- `writing the value "${value}" to key "${key}" on namespace ${namespaceId}`
2030
+ logger.log(
2031
+ `Writing the value "${value}" to key "${key}" on namespace ${namespaceId}.`
1920
2032
  );
1921
2033
  }
1922
2034
 
@@ -1937,15 +2049,18 @@ export async function main(argv: string[]): Promise<void> {
1937
2049
  return yargs
1938
2050
  .option("binding", {
1939
2051
  type: "string",
2052
+ requiresArg: true,
1940
2053
  describe: "The name of the namespace to list",
1941
2054
  })
1942
2055
  .option("namespace-id", {
1943
2056
  type: "string",
2057
+ requiresArg: true,
1944
2058
  describe: "The id of the namespace to list",
1945
2059
  })
1946
2060
  .check(demandOneOfOption("binding", "namespace-id"))
1947
2061
  .option("env", {
1948
2062
  type: "string",
2063
+ requiresArg: true,
1949
2064
  describe: "Perform on a specific environment",
1950
2065
  alias: "e",
1951
2066
  })
@@ -1957,6 +2072,7 @@ export async function main(argv: string[]): Promise<void> {
1957
2072
  })
1958
2073
  .option("prefix", {
1959
2074
  type: "string",
2075
+ requiresArg: true,
1960
2076
  describe: "A prefix to filter listed keys",
1961
2077
  });
1962
2078
  },
@@ -1972,7 +2088,7 @@ export async function main(argv: string[]): Promise<void> {
1972
2088
  namespaceId,
1973
2089
  prefix
1974
2090
  );
1975
- console.log(JSON.stringify(results, undefined, 2));
2091
+ logger.log(JSON.stringify(results, undefined, 2));
1976
2092
  }
1977
2093
  )
1978
2094
  .command(
@@ -1987,15 +2103,18 @@ export async function main(argv: string[]): Promise<void> {
1987
2103
  })
1988
2104
  .option("binding", {
1989
2105
  type: "string",
2106
+ requiresArg: true,
1990
2107
  describe: "The name of the namespace to get from",
1991
2108
  })
1992
2109
  .option("namespace-id", {
1993
2110
  type: "string",
2111
+ requiresArg: true,
1994
2112
  describe: "The id of the namespace to get from",
1995
2113
  })
1996
2114
  .check(demandOneOfOption("binding", "namespace-id"))
1997
2115
  .option("env", {
1998
2116
  type: "string",
2117
+ requiresArg: true,
1999
2118
  describe: "Perform on a specific environment",
2000
2119
  alias: "e",
2001
2120
  })
@@ -2016,7 +2135,7 @@ export async function main(argv: string[]): Promise<void> {
2016
2135
 
2017
2136
  const accountId = await requireAuth(config);
2018
2137
 
2019
- console.log(await getKeyValue(accountId, namespaceId, key));
2138
+ logger.log(await getKeyValue(accountId, namespaceId, key));
2020
2139
  }
2021
2140
  )
2022
2141
  .command(
@@ -2031,15 +2150,18 @@ export async function main(argv: string[]): Promise<void> {
2031
2150
  })
2032
2151
  .option("binding", {
2033
2152
  type: "string",
2153
+ requiresArg: true,
2034
2154
  describe: "The name of the namespace to delete from",
2035
2155
  })
2036
2156
  .option("namespace-id", {
2037
2157
  type: "string",
2158
+ requiresArg: true,
2038
2159
  describe: "The id of the namespace to delete from",
2039
2160
  })
2040
2161
  .check(demandOneOfOption("binding", "namespace-id"))
2041
2162
  .option("env", {
2042
2163
  type: "string",
2164
+ requiresArg: true,
2043
2165
  describe: "Perform on a specific environment",
2044
2166
  alias: "e",
2045
2167
  })
@@ -2049,12 +2171,12 @@ export async function main(argv: string[]): Promise<void> {
2049
2171
  });
2050
2172
  },
2051
2173
  async ({ key, ...args }) => {
2052
- printWranglerBanner();
2174
+ await printWranglerBanner();
2053
2175
  const config = readConfig(args.config as ConfigPath, args);
2054
2176
  const namespaceId = getNamespaceId(args, config);
2055
2177
 
2056
- console.log(
2057
- `deleting the key "${key}" on namespace ${namespaceId}`
2178
+ logger.log(
2179
+ `Deleting the key "${key}" on namespace ${namespaceId}.`
2058
2180
  );
2059
2181
 
2060
2182
  const accountId = await requireAuth(config);
@@ -2087,15 +2209,18 @@ export async function main(argv: string[]): Promise<void> {
2087
2209
  })
2088
2210
  .option("binding", {
2089
2211
  type: "string",
2212
+ requiresArg: true,
2090
2213
  describe: "The name of the namespace to insert values into",
2091
2214
  })
2092
2215
  .option("namespace-id", {
2093
2216
  type: "string",
2217
+ requiresArg: true,
2094
2218
  describe: "The id of the namespace to insert values into",
2095
2219
  })
2096
2220
  .check(demandOneOfOption("binding", "namespace-id"))
2097
2221
  .option("env", {
2098
2222
  type: "string",
2223
+ requiresArg: true,
2099
2224
  describe: "Perform on a specific environment",
2100
2225
  alias: "e",
2101
2226
  })
@@ -2105,7 +2230,7 @@ export async function main(argv: string[]): Promise<void> {
2105
2230
  });
2106
2231
  },
2107
2232
  async ({ filename, ...args }) => {
2108
- printWranglerBanner();
2233
+ await printWranglerBanner();
2109
2234
  // The simplest implementation I could think of.
2110
2235
  // This could be made more efficient with a streaming parser/uploader
2111
2236
  // but we'll do that in the future if needed.
@@ -2147,7 +2272,7 @@ export async function main(argv: string[]): Promise<void> {
2147
2272
  }
2148
2273
  }
2149
2274
  if (warnings.length > 0) {
2150
- console.warn(
2275
+ logger.warn(
2151
2276
  `Unexpected key-value properties in "${filename}".\n` +
2152
2277
  warnings.join("\n")
2153
2278
  );
@@ -2174,11 +2299,11 @@ export async function main(argv: string[]): Promise<void> {
2174
2299
  namespaceId,
2175
2300
  content,
2176
2301
  (index, total) => {
2177
- console.log(`Uploaded ${index} of ${total}.`);
2302
+ logger.log(`Uploaded ${index} of ${total}.`);
2178
2303
  }
2179
2304
  );
2180
2305
 
2181
- console.log("Success!");
2306
+ logger.log("Success!");
2182
2307
  }
2183
2308
  )
2184
2309
  .command(
@@ -2193,15 +2318,18 @@ export async function main(argv: string[]): Promise<void> {
2193
2318
  })
2194
2319
  .option("binding", {
2195
2320
  type: "string",
2321
+ requiresArg: true,
2196
2322
  describe: "The name of the namespace to delete from",
2197
2323
  })
2198
2324
  .option("namespace-id", {
2199
2325
  type: "string",
2326
+ requiresArg: true,
2200
2327
  describe: "The id of the namespace to delete from",
2201
2328
  })
2202
2329
  .check(demandOneOfOption("binding", "namespace-id"))
2203
2330
  .option("env", {
2204
2331
  type: "string",
2332
+ requiresArg: true,
2205
2333
  describe: "Perform on a specific environment",
2206
2334
  alias: "e",
2207
2335
  })
@@ -2216,7 +2344,7 @@ export async function main(argv: string[]): Promise<void> {
2216
2344
  });
2217
2345
  },
2218
2346
  async ({ filename, ...args }) => {
2219
- printWranglerBanner();
2347
+ await printWranglerBanner();
2220
2348
  const config = readConfig(args.config as ConfigPath, args);
2221
2349
  const namespaceId = getNamespaceId(args, config);
2222
2350
 
@@ -2225,7 +2353,7 @@ export async function main(argv: string[]): Promise<void> {
2225
2353
  `Are you sure you want to delete all the keys read from "${filename}" from kv-namespace with id "${namespaceId}"?`
2226
2354
  );
2227
2355
  if (!result) {
2228
- console.log(`Not deleting keys read from "${filename}".`);
2356
+ logger.log(`Not deleting keys read from "${filename}".`);
2229
2357
  return;
2230
2358
  }
2231
2359
  }
@@ -2268,11 +2396,11 @@ export async function main(argv: string[]): Promise<void> {
2268
2396
  namespaceId,
2269
2397
  content,
2270
2398
  (index, total) => {
2271
- console.log(`Deleted ${index} of ${total}.`);
2399
+ logger.log(`Deleted ${index} of ${total}.`);
2272
2400
  }
2273
2401
  );
2274
2402
 
2275
- console.log("Success!");
2403
+ logger.log("Success!");
2276
2404
  }
2277
2405
  );
2278
2406
  }
@@ -2301,15 +2429,15 @@ export async function main(argv: string[]): Promise<void> {
2301
2429
  });
2302
2430
  },
2303
2431
  async (args) => {
2304
- printWranglerBanner();
2432
+ await printWranglerBanner();
2305
2433
 
2306
2434
  const config = readConfig(args.config as ConfigPath, args);
2307
2435
 
2308
2436
  const accountId = await requireAuth(config);
2309
2437
 
2310
- console.log(`Creating bucket ${args.name}.`);
2438
+ logger.log(`Creating bucket ${args.name}.`);
2311
2439
  await createR2Bucket(accountId, args.name);
2312
- console.log(`Created bucket ${args.name}.`);
2440
+ logger.log(`Created bucket ${args.name}.`);
2313
2441
  }
2314
2442
  );
2315
2443
 
@@ -2318,7 +2446,7 @@ export async function main(argv: string[]): Promise<void> {
2318
2446
 
2319
2447
  const accountId = await requireAuth(config);
2320
2448
 
2321
- console.log(JSON.stringify(await listR2Buckets(accountId), null, 2));
2449
+ logger.log(JSON.stringify(await listR2Buckets(accountId), null, 2));
2322
2450
  });
2323
2451
 
2324
2452
  r2BucketYargs.command(
@@ -2332,21 +2460,97 @@ export async function main(argv: string[]): Promise<void> {
2332
2460
  });
2333
2461
  },
2334
2462
  async (args) => {
2335
- printWranglerBanner();
2463
+ await printWranglerBanner();
2336
2464
 
2337
2465
  const config = readConfig(args.config as ConfigPath, args);
2338
2466
 
2339
2467
  const accountId = await requireAuth(config);
2340
2468
 
2341
- console.log(`Deleting bucket ${args.name}.`);
2469
+ logger.log(`Deleting bucket ${args.name}.`);
2342
2470
  await deleteR2Bucket(accountId, args.name);
2343
- console.log(`Deleted bucket ${args.name}.`);
2471
+ logger.log(`Deleted bucket ${args.name}.`);
2344
2472
  }
2345
2473
  );
2346
2474
  return r2BucketYargs;
2347
2475
  });
2348
2476
  });
2349
2477
 
2478
+ /**
2479
+ * User Group: login, logout, and whoami
2480
+ * TODO: group commands into User group similar to .group() for flags in yargs
2481
+ */
2482
+ // login
2483
+ wrangler.command(
2484
+ // this needs scopes as an option?
2485
+ "login",
2486
+ "🔓 Login to Cloudflare",
2487
+ (yargs) => {
2488
+ // TODO: This needs some copy editing
2489
+ // I mean, this entire app does, but this too.
2490
+ return yargs
2491
+ .option("scopes-list", {
2492
+ describe: "List all the available OAuth scopes with descriptions",
2493
+ })
2494
+ .option("scopes", {
2495
+ describe: "Pick the set of applicable OAuth scopes when logging in",
2496
+ array: true,
2497
+ type: "string",
2498
+ requiresArg: true,
2499
+ });
2500
+
2501
+ // TODO: scopes
2502
+ },
2503
+ async (args) => {
2504
+ await printWranglerBanner();
2505
+ if (args["scopes-list"]) {
2506
+ listScopes();
2507
+ return;
2508
+ }
2509
+ if (args.scopes) {
2510
+ if (args.scopes.length === 0) {
2511
+ // don't allow no scopes to be passed, that would be weird
2512
+ listScopes();
2513
+ return;
2514
+ }
2515
+ if (!validateScopeKeys(args.scopes)) {
2516
+ throw new CommandLineArgsError(
2517
+ `One of ${args.scopes} is not a valid authentication scope. Run "wrangler login --list-scopes" to see the valid scopes.`
2518
+ );
2519
+ }
2520
+ await login({ scopes: args.scopes });
2521
+ return;
2522
+ }
2523
+ await login();
2524
+
2525
+ // TODO: would be nice if it optionally saved login
2526
+ // credentials inside node_modules/.cache or something
2527
+ // this way you could have multiple users on a single machine
2528
+ }
2529
+ );
2530
+
2531
+ // logout
2532
+ wrangler.command(
2533
+ // this needs scopes as an option?
2534
+ "logout",
2535
+ "🚪 Logout from Cloudflare",
2536
+ () => {},
2537
+ async () => {
2538
+ await printWranglerBanner();
2539
+ await logout();
2540
+ }
2541
+ );
2542
+
2543
+ // whoami
2544
+ wrangler.command(
2545
+ "whoami",
2546
+ "🕵️ Retrieve your user info and test your auth config",
2547
+ () => {},
2548
+ async () => {
2549
+ await printWranglerBanner();
2550
+ await whoami();
2551
+ }
2552
+ );
2553
+
2350
2554
  wrangler
2351
2555
  .option("legacy-env", {
2352
2556
  type: "boolean",
@@ -2356,6 +2560,7 @@ export async function main(argv: string[]): Promise<void> {
2356
2560
  alias: "c",
2357
2561
  describe: "Path to .toml configuration file",
2358
2562
  type: "string",
2563
+ requiresArg: true,
2359
2564
  });
2360
2565
 
2361
2566
  wrangler.group(["config", "help", "version", "legacy-env"], "Flags:");
@@ -2366,20 +2571,19 @@ export async function main(argv: string[]): Promise<void> {
2366
2571
  try {
2367
2572
  await wrangler.parse();
2368
2573
  } catch (e) {
2574
+ logger.log(""); // Just adds a bit of space
2369
2575
  if (e instanceof CommandLineArgsError) {
2370
2576
  wrangler.showHelp("error");
2371
- console.error(""); // Just adds a bit of space
2372
- console.error(e.message);
2577
+ logger.log(""); // Add a bit of space.
2578
+ logger.error(e.message);
2373
2579
  } else if (e instanceof ParseError) {
2374
- console.error("");
2375
2580
  e.notes.push({
2376
2581
  text: "\nIf you think this is a bug, please open an issue at: https://github.com/cloudflare/wrangler2/issues/new",
2377
2582
  });
2378
- console.error(formatMessage(e));
2583
+ logger.error(formatMessage(e));
2379
2584
  } else {
2380
- console.error(e instanceof Error ? e.message : e);
2381
- console.error(""); // Just adds a bit of space
2382
- console.error(
2585
+ logger.error(e instanceof Error ? e.message : e);
2586
+ logger.log(
2383
2587
  `${fgGreenColor}%s${resetColor}`,
2384
2588
  "If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
2385
2589
  );
@@ -2387,3 +2591,25 @@ export async function main(argv: string[]): Promise<void> {
2387
2591
  throw e;
2388
2592
  }
2389
2593
  }
2594
+
2595
+ function getDevCompatibilityDate(
2596
+ config: Config,
2597
+ compatibilityDate = config.compatibility_date
2598
+ ) {
2599
+ const currentDate = new Date().toISOString().substring(0, 10);
2600
+ if (config.configPath !== undefined && compatibilityDate === undefined) {
2601
+ logger.warn(
2602
+ `No compatibility_date was specified. Using today's date: ${currentDate}.\n` +
2603
+ "Add one to your wrangler.toml file:\n" +
2604
+ "```\n" +
2605
+ `compatibility_date = "${currentDate}"\n` +
2606
+ "```\n" +
2607
+ "or pass it in your terminal:\n" +
2608
+ "```\n" +
2609
+ `--compatibility-date=${currentDate}\n` +
2610
+ "```\n" +
2611
+ "See https://developers.cloudflare.com/workers/platform/compatibility-dates for more information."
2612
+ );
2613
+ }
2614
+ return compatibilityDate ?? currentDate;
2615
+ }