wrangler 0.0.0-e29a241 → 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 (88) hide show
  1. package/README.md +25 -14
  2. package/bin/wrangler.js +13 -1
  3. package/miniflare-dist/index.mjs +4828 -21
  4. package/package.json +29 -24
  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 +692 -321
  14. package/src/__tests__/dev.test.tsx +284 -18
  15. package/src/__tests__/guess-worker-format.test.ts +28 -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 +27 -769
  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 +133 -61
  29. package/src/__tests__/logger.test.ts +115 -0
  30. package/src/__tests__/pages.test.ts +311 -26
  31. package/src/__tests__/publish.test.ts +2789 -618
  32. package/src/__tests__/r2.test.ts +26 -10
  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 +146 -20
  36. package/src/__tests__/user.test.ts +131 -0
  37. package/src/__tests__/whoami.test.tsx +14 -9
  38. package/src/bundle.ts +30 -3
  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 +13 -53
  43. package/src/config/environment.ts +88 -4
  44. package/src/config/index.ts +11 -4
  45. package/src/config/validation-helpers.ts +96 -7
  46. package/src/config/validation.ts +407 -79
  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 +132 -78
  52. package/src/dev/local.tsx +115 -29
  53. package/src/dev/remote.tsx +23 -13
  54. package/src/dev/use-esbuild.ts +23 -5
  55. package/src/dialogs.tsx +16 -6
  56. package/src/entry.ts +83 -24
  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 +619 -377
  61. package/src/inspect.ts +17 -11
  62. package/src/intl-polyfill.d.ts +139 -0
  63. package/src/logger.ts +66 -0
  64. package/src/miniflare-cli/index.ts +4 -0
  65. package/src/miniflare-cli/request-context.ts +40 -0
  66. package/src/module-collection.ts +7 -7
  67. package/src/open-in-browser.ts +5 -4
  68. package/src/package-manager.ts +9 -8
  69. package/src/pages.tsx +883 -62
  70. package/src/parse.ts +1 -1
  71. package/src/proxy.ts +25 -11
  72. package/src/publish.ts +263 -141
  73. package/src/sites.tsx +27 -14
  74. package/src/tail/index.ts +2 -2
  75. package/src/tail/printing.ts +13 -8
  76. package/src/update-check.ts +19 -0
  77. package/src/user.tsx +116 -41
  78. package/src/whoami.tsx +2 -1
  79. package/src/worker.ts +15 -6
  80. package/templates/gitignore +171 -0
  81. package/templates/static-asset-facade.js +1 -1
  82. package/wrangler-dist/cli.js +21167 -50229
  83. package/miniflare-dist/index.mjs.map +0 -7
  84. package/src/__tests__/dev2.test.tsx +0 -154
  85. package/src/__tests__/logout.test.ts +0 -52
  86. package/src/__tests__/sentry.test.ts +0 -146
  87. package/src/reporting.ts +0 -131
  88. 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
- import { pages } from "./pages";
36
- import { formatMessage, ParseError, parseJSON, readFileSync } from "./parse";
39
+ import { pages, pagesBetaWarning } from "./pages";
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,45 +110,39 @@ function printWranglerBanner() {
99
110
  );
100
111
  }
101
112
 
102
- function isLegacyEnv(args: unknown, config: Config): boolean {
103
- return (
104
- (args as { "legacy-env": boolean | undefined })["legacy-env"] ??
105
- config.legacy_env
106
- );
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
116
+ return config.legacy_env;
107
117
  }
108
118
 
109
119
  function getScriptName(
110
120
  args: { name: string | undefined; env: string | undefined },
111
121
  config: Config
112
122
  ): string | undefined {
113
- const shortScriptName = args.name ?? config.name;
114
- if (!shortScriptName) {
115
- return;
123
+ if (args.name && isLegacyEnv(config) && args.env) {
124
+ throw new CommandLineArgsError(
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:" +
126
+ `
127
+ [env.${args.env}]
128
+ name = "${args.name}"
129
+ `
130
+ );
116
131
  }
117
132
 
118
- return isLegacyEnv(args, config)
119
- ? `${shortScriptName}${args.env ? `-${args.env}` : ""}`
120
- : shortScriptName;
133
+ return args.name ?? config.name;
121
134
  }
122
135
 
123
136
  /**
124
- * Ensure that a user is logged in, and a valid account_id is available.
137
+ * Alternative to the getScriptName() because special Legacy cases allowed "name", and "env" together in Wrangler1
125
138
  */
126
- async function requireAuth(
127
- config: Config,
128
- isInteractive = true
129
- ): Promise<string> {
130
- const loggedIn = await loginOrRefreshIfRequired(isInteractive);
131
- if (!loggedIn) {
132
- // didn't login, let's just quit
133
- throw new Error("Did not login, quitting...");
134
- }
135
- const accountId = config.account_id || (await getAccountId(isInteractive));
136
- if (!accountId) {
137
- throw new Error("No account id found, quitting...");
138
- }
139
-
140
- return accountId;
139
+ function getLegacyScriptName(
140
+ args: { name: string | undefined; env: string | undefined },
141
+ config: Config
142
+ ) {
143
+ return args.name && args.env && isLegacyEnv(config)
144
+ ? `${args.name}-${args.env}`
145
+ : args.name ?? config.name;
141
146
  }
142
147
 
143
148
  /**
@@ -199,21 +204,18 @@ function demandOneOfOption(...options: string[]) {
199
204
  }
200
205
 
201
206
  class CommandLineArgsError extends Error {}
202
- class DeprecationError extends Error {
203
- constructor(message: string) {
204
- super(`DEPRECATION WARNING:\n${message}`);
205
- }
206
- }
207
207
 
208
208
  export async function main(argv: string[]): Promise<void> {
209
209
  const wrangler = makeCLI(argv)
210
+ .strict()
210
211
  // We handle errors ourselves in a try-catch around `yargs.parse`.
211
212
  // If you want the "help info" to be displayed then throw an instance of `CommandLineArgsError`.
212
213
  // Otherwise we just log the error that was thrown without any "help info".
213
214
  .showHelpOnFail(false)
214
215
  .fail((msg, error) => {
215
- if (!error) {
216
- // 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.
217
219
  error = new CommandLineArgsError(msg);
218
220
  }
219
221
  throw error;
@@ -263,14 +265,19 @@ export async function main(argv: string[]): Promise<void> {
263
265
  default: "worker",
264
266
  })
265
267
  .positional("template", {
266
- describe: "a link to a GitHub template",
268
+ describe: "The URL of a GitHub template",
267
269
  default: "https://github.com/cloudflare/worker-template",
268
270
  });
269
271
  },
270
- () => {
272
+ (generateArgs) => {
271
273
  // "👯 [DEPRECATED]. Scaffold a Cloudflare Workers project from a public GitHub repository.",
272
274
  throw new DeprecationError(
273
- "`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."
274
281
  );
275
282
  }
276
283
  );
@@ -285,6 +292,18 @@ export async function main(argv: string[]): Promise<void> {
285
292
  describe: "The name of your worker",
286
293
  type: "string",
287
294
  })
295
+ .option("type", {
296
+ describe: "The type of worker to create",
297
+ type: "string",
298
+ choices: ["rust", "javascript", "webpack"],
299
+ hidden: true,
300
+ deprecated: true,
301
+ })
302
+ .option("site", {
303
+ hidden: true,
304
+ type: "boolean",
305
+ deprecated: true,
306
+ })
288
307
  .option("yes", {
289
308
  describe: 'Answer "yes" to any prompts for new projects',
290
309
  type: "boolean",
@@ -292,8 +311,8 @@ export async function main(argv: string[]): Promise<void> {
292
311
  });
293
312
  },
294
313
  async (args) => {
295
- printWranglerBanner();
296
- if ("type" in args) {
314
+ await printWranglerBanner();
315
+ if (args.type) {
297
316
  let message = "The --type option is no longer supported.";
298
317
  if (args.type === "webpack") {
299
318
  message +=
@@ -303,9 +322,30 @@ export async function main(argv: string[]): Promise<void> {
303
322
  throw new CommandLineArgsError(message);
304
323
  }
305
324
 
306
- // 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
+ }
307
343
 
308
- 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, "-");
309
349
 
310
350
  const packageManager = await getPackageManager(creationDirectory);
311
351
 
@@ -317,10 +357,9 @@ export async function main(argv: string[]): Promise<void> {
317
357
  "./wrangler.toml"
318
358
  );
319
359
  let justCreatedWranglerToml = false;
320
- const workerName =
321
- args.name || path.basename(path.resolve(process.cwd()));
360
+
322
361
  if (fs.existsSync(wranglerTomlDestination)) {
323
- console.warn(`${wranglerTomlDestination} file already exists!`);
362
+ logger.warn(`${wranglerTomlDestination} file already exists!`);
324
363
  const shouldContinue = await confirm(
325
364
  "Do you want to continue initializing this project?"
326
365
  );
@@ -330,6 +369,7 @@ export async function main(argv: string[]): Promise<void> {
330
369
  } else {
331
370
  await mkdir(creationDirectory, { recursive: true });
332
371
  const compatibilityDate = new Date().toISOString().substring(0, 10);
372
+
333
373
  try {
334
374
  await writeFile(
335
375
  wranglerTomlDestination,
@@ -338,7 +378,8 @@ export async function main(argv: string[]): Promise<void> {
338
378
  compatibility_date: compatibilityDate,
339
379
  }) + "\n"
340
380
  );
341
- console.log(`✨ Successfully created wrangler.toml`);
381
+
382
+ logger.log(`✨ Successfully created wrangler.toml`);
342
383
  justCreatedWranglerToml = true;
343
384
  } catch (err) {
344
385
  throw new Error(
@@ -347,9 +388,27 @@ export async function main(argv: string[]): Promise<void> {
347
388
  }
348
389
  }
349
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
+
350
409
  let pathToPackageJson = await findUp("package.json");
351
410
  let shouldCreatePackageJson = false;
352
- const yesFlag = args.yes ?? false;
411
+
353
412
  if (!pathToPackageJson) {
354
413
  // If no package.json exists, ask to create one
355
414
  shouldCreatePackageJson =
@@ -374,8 +433,9 @@ export async function main(argv: string[]): Promise<void> {
374
433
  " "
375
434
  ) + "\n"
376
435
  );
436
+
377
437
  await packageManager.install();
378
- console.log(`✨ Created package.json`);
438
+ logger.log(`✨ Created package.json`);
379
439
  pathToPackageJson = path.join(creationDirectory, "package.json");
380
440
  } else {
381
441
  return;
@@ -400,7 +460,7 @@ export async function main(argv: string[]): Promise<void> {
400
460
  ));
401
461
  if (shouldInstall) {
402
462
  await packageManager.addDevDeps(`wrangler@${wranglerVersion}`);
403
- console.log(`✨ Installed wrangler`);
463
+ logger.log(`✨ Installed wrangler`);
404
464
  }
405
465
  }
406
466
  }
@@ -421,7 +481,7 @@ export async function main(argv: string[]): Promise<void> {
421
481
  "typescript"
422
482
  );
423
483
 
424
- console.log(
484
+ logger.log(
425
485
  `✨ Created tsconfig.json, installed @cloudflare/workers-types into devDependencies`
426
486
  );
427
487
  pathToTSConfig = path.join(creationDirectory, "tsconfig.json");
@@ -449,7 +509,7 @@ export async function main(argv: string[]): Promise<void> {
449
509
  // it could be complicated in existing projects
450
510
  // and we don't want to break them. Instead, we simply
451
511
  // tell the user that they need to update their tsconfig.json
452
- console.log(
512
+ logger.log(
453
513
  `✨ Installed @cloudflare/workers-types.\nPlease add "@cloudflare/workers-types" to compilerOptions.types in your tsconfig.json`
454
514
  );
455
515
  }
@@ -473,7 +533,7 @@ export async function main(argv: string[]): Promise<void> {
473
533
  ) {
474
534
  if (isCreatingWranglerToml) {
475
535
  // rewrite wrangler.toml with main = "path/to/script"
476
- const parsedWranglerToml = TOML.parse(
536
+ const parsedWranglerToml = parseTOML(
477
537
  readFileSync(wranglerTomlDestination)
478
538
  );
479
539
  fs.writeFileSync(
@@ -508,21 +568,21 @@ export async function main(argv: string[]): Promise<void> {
508
568
  2
509
569
  ) + "\n"
510
570
  );
511
- console.log(
571
+ logger.log(
512
572
  `\nTo start developing your Worker, run \`${
513
573
  isNamedWorker ? `cd ${args.name} && ` : ""
514
574
  }npm start\``
515
575
  );
516
- console.log(
576
+ logger.log(
517
577
  `To publish your Worker to the Internet, run \`npm run publish\``
518
578
  );
519
579
  } else {
520
- console.log(
580
+ logger.log(
521
581
  `\nTo start developing your Worker, run \`npx wrangler dev\`${
522
582
  isCreatingWranglerToml ? "" : ` ${scriptPath}`
523
583
  }`
524
584
  );
525
- console.log(
585
+ logger.log(
526
586
  `To publish your Worker to the Internet, run \`npx wrangler publish\`${
527
587
  isCreatingWranglerToml ? "" : ` ${scriptPath}`
528
588
  }`
@@ -549,7 +609,7 @@ export async function main(argv: string[]): Promise<void> {
549
609
  readFileSync(path.join(__dirname, "../templates/new-worker.ts"))
550
610
  );
551
611
 
552
- console.log(`✨ Created src/index.ts`);
612
+ logger.log(`✨ Created src/index.ts`);
553
613
 
554
614
  await writePackageJsonScriptsAndUpdateWranglerToml(
555
615
  shouldWritePackageJsonScripts,
@@ -573,7 +633,7 @@ export async function main(argv: string[]): Promise<void> {
573
633
  readFileSync(path.join(__dirname, "../templates/new-worker.js"))
574
634
  );
575
635
 
576
- console.log(`✨ Created src/index.js`);
636
+ logger.log(`✨ Created src/index.js`);
577
637
 
578
638
  await writePackageJsonScriptsAndUpdateWranglerToml(
579
639
  shouldWritePackageJsonScripts,
@@ -604,79 +664,6 @@ export async function main(argv: string[]): Promise<void> {
604
664
  }
605
665
  );
606
666
 
607
- // login
608
- wrangler.command(
609
- // this needs scopes as an option?
610
- "login",
611
- false, // we don't need to show this in the menu
612
- // "🔓 Login to Cloudflare",
613
- (yargs) => {
614
- // TODO: This needs some copy editing
615
- // I mean, this entire app does, but this too.
616
- return yargs
617
- .option("scopes-list", {
618
- describe: "List all the available OAuth scopes with descriptions",
619
- })
620
- .option("scopes", {
621
- describe: "Pick the set of applicable OAuth scopes when logging in",
622
- array: true,
623
- type: "string",
624
- });
625
-
626
- // TODO: scopes
627
- },
628
- async (args) => {
629
- printWranglerBanner();
630
- if (args["scopes-list"]) {
631
- listScopes();
632
- return;
633
- }
634
- if (args.scopes) {
635
- if (args.scopes.length === 0) {
636
- // don't allow no scopes to be passed, that would be weird
637
- listScopes();
638
- return;
639
- }
640
- if (!validateScopeKeys(args.scopes)) {
641
- throw new CommandLineArgsError(
642
- `One of ${args.scopes} is not a valid authentication scope. Run "wrangler login --list-scopes" to see the valid scopes.`
643
- );
644
- }
645
- await login({ scopes: args.scopes });
646
- return;
647
- }
648
- await login();
649
-
650
- // TODO: would be nice if it optionally saved login
651
- // credentials inside node_modules/.cache or something
652
- // this way you could have multiple users on a single machine
653
- }
654
- );
655
-
656
- // logout
657
- wrangler.command(
658
- // this needs scopes as an option?
659
- "logout",
660
- false, // we don't need to show this in the menu
661
- // "🚪 Logout from Cloudflare",
662
- () => {},
663
- async () => {
664
- printWranglerBanner();
665
- await logout();
666
- }
667
- );
668
-
669
- // whoami
670
- wrangler.command(
671
- "whoami",
672
- "🕵️ Retrieve your user info and test your auth config",
673
- () => {},
674
- async () => {
675
- printWranglerBanner();
676
- await whoami();
677
- }
678
- );
679
-
680
667
  // config
681
668
  wrangler.command(
682
669
  "config",
@@ -703,24 +690,30 @@ export async function main(argv: string[]): Promise<void> {
703
690
  .option("name", {
704
691
  describe: "Name of the worker",
705
692
  type: "string",
693
+ requiresArg: true,
706
694
  })
707
695
  .option("format", {
708
696
  choices: ["modules", "service-worker"] as const,
709
697
  describe: "Choose an entry type",
698
+ deprecated: true,
710
699
  })
711
700
  .option("env", {
712
701
  describe: "Perform on a specific environment",
713
702
  type: "string",
703
+ requiresArg: true,
714
704
  alias: "e",
715
705
  })
716
706
  .option("compatibility-date", {
717
707
  describe: "Date to use for compatibility checks",
718
708
  type: "string",
709
+ requiresArg: true,
719
710
  })
720
711
  .option("compatibility-flags", {
721
712
  describe: "Flags to use for compatibility checks",
722
- type: "array",
723
713
  alias: "compatibility-flag",
714
+ type: "string",
715
+ requiresArg: true,
716
+ array: true,
724
717
  })
725
718
  .option("latest", {
726
719
  describe: "Use the latest version of the worker runtime",
@@ -730,6 +723,7 @@ export async function main(argv: string[]): Promise<void> {
730
723
  .option("ip", {
731
724
  describe: "IP address to listen on, defaults to `localhost`",
732
725
  type: "string",
726
+ requiresArg: true,
733
727
  })
734
728
  .option("port", {
735
729
  describe: "Port to listen on",
@@ -742,10 +736,13 @@ export async function main(argv: string[]): Promise<void> {
742
736
  .option("routes", {
743
737
  describe: "Routes to upload",
744
738
  alias: "route",
745
- type: "array",
739
+ type: "string",
740
+ requiresArg: true,
741
+ array: true,
746
742
  })
747
743
  .option("host", {
748
744
  type: "string",
745
+ requiresArg: true,
749
746
  describe:
750
747
  "Host to forward requests to, defaults to the zone of project",
751
748
  })
@@ -756,21 +753,25 @@ export async function main(argv: string[]): Promise<void> {
756
753
  .option("experimental-public", {
757
754
  describe: "Static assets to be served",
758
755
  type: "string",
756
+ requiresArg: true,
759
757
  })
760
758
  .option("site", {
761
759
  describe: "Root folder of static assets for Workers Sites",
762
760
  type: "string",
761
+ requiresArg: true,
763
762
  })
764
763
  .option("site-include", {
765
764
  describe:
766
765
  "Array of .gitignore-style patterns that match file or directory names from the sites directory. Only matched items will be uploaded.",
767
766
  type: "string",
767
+ requiresArg: true,
768
768
  array: true,
769
769
  })
770
770
  .option("site-exclude", {
771
771
  describe:
772
772
  "Array of .gitignore-style patterns that match file or directory names from the sites directory. Matched items will not be uploaded.",
773
773
  type: "string",
774
+ requiresArg: true,
774
775
  array: true,
775
776
  })
776
777
  .option("upstream-protocol", {
@@ -781,10 +782,17 @@ export async function main(argv: string[]): Promise<void> {
781
782
  .option("jsx-factory", {
782
783
  describe: "The function that is called for each JSX element",
783
784
  type: "string",
785
+ requiresArg: true,
784
786
  })
785
787
  .option("jsx-fragment", {
786
788
  describe: "The function that is called for each JSX fragment",
787
789
  type: "string",
790
+ requiresArg: true,
791
+ })
792
+ .option("tsconfig", {
793
+ describe: "Path to a custom tsconfig.json file",
794
+ type: "string",
795
+ requiresArg: true,
788
796
  })
789
797
  .option("local", {
790
798
  alias: "l",
@@ -792,36 +800,54 @@ export async function main(argv: string[]): Promise<void> {
792
800
  type: "boolean",
793
801
  default: false, // I bet this will a point of contention. We'll revisit it.
794
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
+ })
795
811
  .option("experimental-enable-local-persistence", {
796
812
  describe: "Enable persistence for this session (only for local mode)",
797
813
  type: "boolean",
814
+ })
815
+ .option("inspect", {
816
+ describe: "Enable dev tools",
817
+ type: "boolean",
818
+ deprecated: true,
798
819
  });
799
820
  },
800
821
  async (args) => {
801
- printWranglerBanner();
822
+ await printWranglerBanner();
802
823
  const configPath =
803
824
  (args.config as ConfigPath) ||
804
825
  (args.script && findWranglerToml(path.dirname(args.script)));
805
- const config = readConfig(configPath, args.env);
826
+ const config = readConfig(configPath, args);
806
827
  const entry = await getEntry(args, config, "dev");
807
828
 
829
+ if (args.inspect) {
830
+ logger.warn(
831
+ "Passing --inspect is unnecessary, now you can always connect to devtools."
832
+ );
833
+ }
834
+
808
835
  if (args["experimental-public"]) {
809
- console.warn(
810
- "🚨 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."
811
838
  );
812
839
  }
813
840
 
814
841
  if (args.public) {
815
842
  throw new Error(
816
- "🚨 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."
817
844
  );
818
845
  }
819
846
 
820
847
  const upstreamProtocol =
821
- (args["upstream-protocol"] as "http" | "https") ||
822
- config.dev.upstream_protocol;
848
+ args["upstream-protocol"] || config.dev.upstream_protocol;
823
849
  if (upstreamProtocol === "http") {
824
- console.warn(
850
+ logger.warn(
825
851
  "Setting upstream-protocol to http is not currently implemented.\n" +
826
852
  "If this is required in your project, please add your use case to the following issue:\n" +
827
853
  "https://github.com/cloudflare/wrangler2/issues/583."
@@ -858,13 +884,6 @@ export async function main(argv: string[]): Promise<void> {
858
884
  return zones[0]?.id;
859
885
  }
860
886
 
861
- const hostLike =
862
- args.host ||
863
- config.dev.host ||
864
- (args.routes && args.routes[0]) ||
865
- config.route ||
866
- (config.routes && config.routes[0]);
867
-
868
887
  // When we're given a host (in one of the above ways), we do 2 things:
869
888
  // - We try to extract a host from it
870
889
  // - We try to get a zone id from the host
@@ -875,27 +894,57 @@ export async function main(argv: string[]): Promise<void> {
875
894
  // get a zone id for it, by lopping off subdomains until we get a hit
876
895
  // from the API. That's it!
877
896
 
878
- const host = typeof hostLike === "string" ? getHost(hostLike) : undefined;
897
+ let zone: { host: string; id: string } | undefined;
898
+
899
+ if (!args.local) {
900
+ const hostLike =
901
+ args.host ||
902
+ config.dev.host ||
903
+ (args.routes && args.routes[0]) ||
904
+ config.route ||
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
+
912
+ const host =
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
+
921
+ const hostPieces =
922
+ typeof host === "string" ? host.split(".") : undefined;
923
+
924
+ while (!zoneId && hostPieces && hostPieces.length > 1) {
925
+ zoneId = await getZoneId(hostPieces.join("."));
926
+ hostPieces.shift();
927
+ }
879
928
 
880
- let zoneId: string | undefined;
881
- const hostPieces = typeof host === "string" ? host.split(".") : undefined;
929
+ if (host && !zoneId) {
930
+ throw new Error(`Could not find zone for ${hostLike}`);
931
+ }
882
932
 
883
- while (hostPieces && hostPieces.length > 1) {
884
- zoneId = await getZoneId(hostPieces.join("."));
885
- if (zoneId) break;
886
- hostPieces.shift();
887
- }
888
- if (host && !zoneId) {
889
- throw new Error(`Could not find zone for ${hostLike}`);
933
+ zone =
934
+ typeof zoneId === "string" && typeof host === "string"
935
+ ? {
936
+ host,
937
+ id: zoneId,
938
+ }
939
+ : undefined;
890
940
  }
891
941
 
892
- const zone =
893
- typeof zoneId === "string" && typeof host === "string"
894
- ? {
895
- host,
896
- id: zoneId,
897
- }
898
- : undefined;
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
+ }
899
948
 
900
949
  const { waitUntilExit } = render(
901
950
  <Dev
@@ -904,18 +953,16 @@ export async function main(argv: string[]): Promise<void> {
904
953
  env={args.env}
905
954
  zone={zone}
906
955
  rules={getRules(config)}
907
- legacyEnv={isLegacyEnv(args, config)}
908
- buildCommand={config.build || {}}
956
+ legacyEnv={isLegacyEnv(config)}
957
+ minify={args.minify ?? config.minify}
958
+ nodeCompat={nodeCompat}
959
+ build={config.build || {}}
909
960
  initialMode={args.local ? "local" : "remote"}
910
961
  jsxFactory={args["jsx-factory"] || config.jsx_factory}
911
962
  jsxFragment={args["jsx-fragment"] || config.jsx_fragment}
963
+ tsconfig={args.tsconfig ?? config.tsconfig}
912
964
  upstreamProtocol={upstreamProtocol}
913
- localProtocol={
914
- // The typings are not quite clever enough to handle element accesses, only property accesses,
915
- // so we need to cast here.
916
- (args["local-protocol"] as "http" | "https") ||
917
- config.dev.local_protocol
918
- }
965
+ localProtocol={args["local-protocol"] || config.dev.local_protocol}
919
966
  enableLocalPersistence={
920
967
  args["experimental-enable-local-persistence"] || false
921
968
  }
@@ -934,14 +981,12 @@ export async function main(argv: string[]): Promise<void> {
934
981
  args["inspector-port"] ?? (await getPort({ port: 9229 }))
935
982
  }
936
983
  public={args["experimental-public"]}
937
- compatibilityDate={
938
- args["compatibility-date"] ||
939
- config.compatibility_date ||
940
- new Date().toISOString().substring(0, 10)
941
- }
984
+ compatibilityDate={getDevCompatibilityDate(
985
+ config,
986
+ args["compatibility-date"]
987
+ )}
942
988
  compatibilityFlags={
943
- (args["compatibility-flags"] as string[]) ||
944
- config.compatibility_flags
989
+ args["compatibility-flags"] || config.compatibility_flags
945
990
  }
946
991
  usageModel={config.usage_model}
947
992
  bindings={{
@@ -966,13 +1011,29 @@ export async function main(argv: string[]): Promise<void> {
966
1011
  };
967
1012
  }
968
1013
  ),
969
- vars: config.vars,
1014
+ vars: getVarsForDev(config),
970
1015
  wasm_modules: config.wasm_modules,
971
1016
  text_blobs: config.text_blobs,
1017
+ data_blobs: config.data_blobs,
972
1018
  durable_objects: config.durable_objects,
973
- 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
+ ),
974
1034
  unsafe: config.unsafe?.bindings,
975
1035
  }}
1036
+ crons={config.triggers.crons}
976
1037
  />
977
1038
  );
978
1039
  await waitUntilExit();
@@ -987,29 +1048,41 @@ export async function main(argv: string[]): Promise<void> {
987
1048
  return yargs
988
1049
  .option("env", {
989
1050
  type: "string",
1051
+ requiresArg: true,
990
1052
  describe: "Perform on a specific environment",
991
1053
  alias: "e",
992
1054
  })
993
1055
  .positional("script", {
994
1056
  describe: "The path to an entry point for your worker",
995
1057
  type: "string",
1058
+ requiresArg: true,
996
1059
  })
997
1060
  .option("name", {
998
1061
  describe: "Name of the worker",
999
1062
  type: "string",
1063
+ requiresArg: true,
1064
+ })
1065
+ .option("outdir", {
1066
+ describe: "Output directory for the bundled worker",
1067
+ type: "string",
1068
+ requiresArg: true,
1000
1069
  })
1001
1070
  .option("format", {
1002
1071
  choices: ["modules", "service-worker"] as const,
1003
1072
  describe: "Choose an entry type",
1073
+ deprecated: true,
1004
1074
  })
1005
1075
  .option("compatibility-date", {
1006
1076
  describe: "Date to use for compatibility checks",
1007
1077
  type: "string",
1078
+ requiresArg: true,
1008
1079
  })
1009
1080
  .option("compatibility-flags", {
1010
1081
  describe: "Flags to use for compatibility checks",
1011
- type: "array",
1012
1082
  alias: "compatibility-flag",
1083
+ type: "string",
1084
+ requiresArg: true,
1085
+ array: true,
1013
1086
  })
1014
1087
  .option("latest", {
1015
1088
  describe: "Use the latest version of the worker runtime",
@@ -1019,64 +1092,91 @@ export async function main(argv: string[]): Promise<void> {
1019
1092
  .option("experimental-public", {
1020
1093
  describe: "Static assets to be served",
1021
1094
  type: "string",
1095
+ requiresArg: true,
1022
1096
  })
1023
1097
  .option("site", {
1024
1098
  describe: "Root folder of static assets for Workers Sites",
1025
1099
  type: "string",
1100
+ requiresArg: true,
1026
1101
  })
1027
1102
  .option("site-include", {
1028
1103
  describe:
1029
1104
  "Array of .gitignore-style patterns that match file or directory names from the sites directory. Only matched items will be uploaded.",
1030
1105
  type: "string",
1106
+ requiresArg: true,
1031
1107
  array: true,
1032
1108
  })
1033
1109
  .option("site-exclude", {
1034
1110
  describe:
1035
1111
  "Array of .gitignore-style patterns that match file or directory names from the sites directory. Matched items will not be uploaded.",
1036
1112
  type: "string",
1113
+ requiresArg: true,
1037
1114
  array: true,
1038
1115
  })
1039
1116
  .option("triggers", {
1040
1117
  describe: "cron schedules to attach",
1041
1118
  alias: ["schedule", "schedules"],
1042
- type: "array",
1119
+ type: "string",
1120
+ requiresArg: true,
1121
+ array: true,
1043
1122
  })
1044
1123
  .option("routes", {
1045
1124
  describe: "Routes to upload",
1046
1125
  alias: "route",
1047
- type: "array",
1126
+ type: "string",
1127
+ requiresArg: true,
1128
+ array: true,
1048
1129
  })
1049
1130
  .option("jsx-factory", {
1050
1131
  describe: "The function that is called for each JSX element",
1051
1132
  type: "string",
1133
+ requiresArg: true,
1052
1134
  })
1053
1135
  .option("jsx-fragment", {
1054
1136
  describe: "The function that is called for each JSX fragment",
1055
1137
  type: "string",
1138
+ requiresArg: true,
1139
+ })
1140
+ .option("tsconfig", {
1141
+ describe: "Path to a custom tsconfig.json file",
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",
1056
1156
  });
1057
1157
  },
1058
1158
  async (args) => {
1059
- printWranglerBanner();
1159
+ await printWranglerBanner();
1060
1160
  if (args["experimental-public"]) {
1061
- console.warn(
1062
- "🚨 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."
1063
1163
  );
1064
1164
  }
1065
1165
  if (args.public) {
1066
1166
  throw new Error(
1067
- "🚨 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."
1068
1168
  );
1069
1169
  }
1070
1170
 
1071
1171
  const configPath =
1072
1172
  (args.config as ConfigPath) ||
1073
1173
  (args.script && findWranglerToml(path.dirname(args.script)));
1074
- const config = readConfig(configPath, args.env);
1174
+ const config = readConfig(configPath, args);
1075
1175
  const entry = await getEntry(args, config, "publish");
1076
1176
 
1077
1177
  if (args.latest) {
1078
- console.warn(
1079
- "⚠️ 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"
1080
1180
  );
1081
1181
  }
1082
1182
 
@@ -1098,14 +1198,19 @@ export async function main(argv: string[]): Promise<void> {
1098
1198
  compatibilityDate: args.latest
1099
1199
  ? new Date().toISOString().substring(0, 10)
1100
1200
  : args["compatibility-date"],
1101
- compatibilityFlags: args["compatibility-flags"] as string[],
1201
+ compatibilityFlags: args["compatibility-flags"],
1102
1202
  triggers: args.triggers,
1103
1203
  jsxFactory: args["jsx-factory"],
1104
1204
  jsxFragment: args["jsx-fragment"],
1205
+ tsconfig: args.tsconfig,
1105
1206
  routes: args.routes,
1106
1207
  assetPaths,
1107
- legacyEnv: isLegacyEnv(args, config),
1208
+ legacyEnv: isLegacyEnv(config),
1209
+ minify: args.minify,
1210
+ nodeCompat: args.nodeCompat,
1108
1211
  experimentalPublic: args["experimental-public"] !== undefined,
1212
+ outDir: args.outdir,
1213
+ dryRun: args.dryRun,
1109
1214
  });
1110
1215
  }
1111
1216
  );
@@ -1115,67 +1220,69 @@ export async function main(argv: string[]): Promise<void> {
1115
1220
  "tail [name]",
1116
1221
  "🦚 Starts a log tailing session for a published Worker.",
1117
1222
  (yargs) => {
1118
- return (
1119
- yargs
1120
- .positional("name", {
1121
- describe: "Name of the worker",
1122
- type: "string",
1123
- })
1124
- // TODO: auto-detect if this should be json or pretty based on atty
1125
- .option("format", {
1126
- default: "json",
1127
- choices: ["json", "pretty"],
1128
- describe: "The format of log entries",
1129
- })
1130
- .option("status", {
1131
- choices: ["ok", "error", "canceled"],
1132
- describe: "Filter by invocation status",
1133
- array: true,
1134
- })
1135
- .option("header", {
1136
- type: "string",
1137
- describe: "Filter by HTTP header",
1138
- })
1139
- .option("method", {
1140
- type: "string",
1141
- describe: "Filter by HTTP method",
1142
- array: true,
1143
- })
1144
- .option("sampling-rate", {
1145
- type: "number",
1146
- describe: "Adds a percentage of requests to log sampling rate",
1147
- })
1148
- .option("search", {
1149
- type: "string",
1150
- describe: "Filter by a text match in console.log messages",
1151
- })
1152
- .option("ip", {
1153
- type: "string",
1154
- describe:
1155
- 'Filter by the IP address the request originates from. Use "self" to filter for your own IP',
1156
- array: true,
1157
- })
1158
- .option("env", {
1159
- type: "string",
1160
- describe: "Perform on a specific environment",
1161
- alias: "e",
1162
- })
1163
- .option("debug", {
1164
- type: "boolean",
1165
- hidden: true,
1166
- default: false,
1167
- describe:
1168
- "If a log would have been filtered out, send it through anyway alongside the filter which would have blocked it.",
1169
- })
1170
- );
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
+ });
1171
1278
  },
1172
1279
  async (args) => {
1173
1280
  if (args.format === "pretty") {
1174
- printWranglerBanner();
1281
+ await printWranglerBanner();
1175
1282
  }
1176
- const config = readConfig(args.config as ConfigPath, args.env);
1283
+ const config = readConfig(args.config as ConfigPath, args);
1177
1284
 
1178
- const scriptName = getScriptName(args, config);
1285
+ const scriptName = getLegacyScriptName(args, config);
1179
1286
 
1180
1287
  if (!scriptName) {
1181
1288
  throw new Error("Missing script name");
@@ -1184,7 +1291,7 @@ export async function main(argv: string[]): Promise<void> {
1184
1291
  const accountId = await requireAuth(config);
1185
1292
 
1186
1293
  const cliFilters: TailCLIFilters = {
1187
- status: args.status as ("ok" | "error" | "canceled")[],
1294
+ status: args.status as ("ok" | "error" | "canceled")[] | undefined,
1188
1295
  header: args.header,
1189
1296
  method: args.method,
1190
1297
  samplingRate: args["sampling-rate"],
@@ -1201,16 +1308,18 @@ export async function main(argv: string[]): Promise<void> {
1201
1308
  accountId,
1202
1309
  scriptName,
1203
1310
  filters,
1204
- !isLegacyEnv(args, config) ? args.env : undefined
1311
+ !isLegacyEnv(config) ? args.env : undefined
1205
1312
  );
1206
1313
 
1207
1314
  const scriptDisplayName = `${scriptName}${
1208
- args.env && !isLegacyEnv(args, config) ? ` (${args.env})` : ""
1315
+ args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
1209
1316
  }`;
1210
1317
 
1211
- console.log(
1212
- `successfully created tail, expires at ${expiration.toLocaleString()}`
1213
- );
1318
+ if (args.format === "pretty") {
1319
+ logger.log(
1320
+ `Successfully created tail, expires at ${expiration.toLocaleString()}`
1321
+ );
1322
+ }
1214
1323
 
1215
1324
  onExit(async () => {
1216
1325
  tail.terminate();
@@ -1237,7 +1346,9 @@ export async function main(argv: string[]): Promise<void> {
1237
1346
  }
1238
1347
  }
1239
1348
 
1240
- console.log(`Connected to ${scriptDisplayName}, waiting for logs...`);
1349
+ if (args.format === "pretty") {
1350
+ logger.log(`Connected to ${scriptDisplayName}, waiting for logs...`);
1351
+ }
1241
1352
 
1242
1353
  tail.on("close", async () => {
1243
1354
  tail.terminate();
@@ -1262,6 +1373,7 @@ export async function main(argv: string[]): Promise<void> {
1262
1373
  })
1263
1374
  .option("env", {
1264
1375
  type: "string",
1376
+ requiresArg: true,
1265
1377
  describe: "Perform on a specific environment",
1266
1378
  })
1267
1379
  .option("watch", {
@@ -1279,14 +1391,14 @@ export async function main(argv: string[]): Promise<void> {
1279
1391
  }
1280
1392
 
1281
1393
  // Delegate to `wrangler dev`
1282
- console.warn(
1394
+ logger.warn(
1283
1395
  "***************************************************\n" +
1284
1396
  "The `wrangler preview` command has been deprecated.\n" +
1285
1397
  "Attempting to run `wrangler dev` instead.\n" +
1286
1398
  "***************************************************\n"
1287
1399
  );
1288
1400
 
1289
- const config = readConfig(args.config as ConfigPath, args.env);
1401
+ const config = readConfig(args.config as ConfigPath, args);
1290
1402
  const entry = await getEntry({}, config, "dev");
1291
1403
 
1292
1404
  const accountId = await requireAuth(config);
@@ -1298,11 +1410,14 @@ export async function main(argv: string[]): Promise<void> {
1298
1410
  rules={getRules(config)}
1299
1411
  env={args.env}
1300
1412
  zone={undefined}
1301
- legacyEnv={isLegacyEnv(args, config)}
1302
- buildCommand={config.build || {}}
1413
+ legacyEnv={isLegacyEnv(config)}
1414
+ build={config.build || {}}
1415
+ minify={undefined}
1416
+ nodeCompat={config.node_compat}
1303
1417
  initialMode={args.local ? "local" : "remote"}
1304
1418
  jsxFactory={config.jsx_factory}
1305
1419
  jsxFragment={config.jsx_fragment}
1420
+ tsconfig={config.tsconfig}
1306
1421
  upstreamProtocol={config.dev.upstream_protocol}
1307
1422
  localProtocol={config.dev.local_protocol}
1308
1423
  enableLocalPersistence={false}
@@ -1311,14 +1426,8 @@ export async function main(argv: string[]): Promise<void> {
1311
1426
  port={config.dev?.port}
1312
1427
  ip={config.dev.ip}
1313
1428
  public={undefined}
1314
- compatibilityDate={
1315
- config.compatibility_date ||
1316
- new Date().toISOString().substring(0, 10)
1317
- }
1318
- compatibilityFlags={
1319
- (args["compatibility-flags"] as string[]) ||
1320
- config.compatibility_flags
1321
- }
1429
+ compatibilityDate={getDevCompatibilityDate(config)}
1430
+ compatibilityFlags={config.compatibility_flags}
1322
1431
  usageModel={config.usage_model}
1323
1432
  bindings={{
1324
1433
  kv_namespaces: config.kv_namespaces?.map(
@@ -1345,10 +1454,26 @@ export async function main(argv: string[]): Promise<void> {
1345
1454
  vars: config.vars,
1346
1455
  wasm_modules: config.wasm_modules,
1347
1456
  text_blobs: config.text_blobs,
1457
+ data_blobs: config.data_blobs,
1348
1458
  durable_objects: config.durable_objects,
1349
- 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
+ ),
1350
1474
  unsafe: config.unsafe?.bindings,
1351
1475
  }}
1476
+ crons={config.triggers.crons}
1352
1477
  inspectorPort={await getPort({ port: 9229 })}
1353
1478
  />
1354
1479
  );
@@ -1370,10 +1495,12 @@ export async function main(argv: string[]): Promise<void> {
1370
1495
  return yargs
1371
1496
  .option("env", {
1372
1497
  type: "string",
1498
+ requiresArg: true,
1373
1499
  describe: "Perform on a specific environment",
1374
1500
  })
1375
1501
  .option("zone", {
1376
1502
  type: "string",
1503
+ requiresArg: true,
1377
1504
  describe: "Zone id",
1378
1505
  })
1379
1506
  .positional("zone", {
@@ -1395,7 +1522,7 @@ export async function main(argv: string[]): Promise<void> {
1395
1522
  }
1396
1523
  )
1397
1524
  .command(
1398
- "delete",
1525
+ "delete [id]",
1399
1526
  "Delete a route associated with a zone",
1400
1527
  (yargs) => {
1401
1528
  return yargs
@@ -1405,10 +1532,12 @@ export async function main(argv: string[]): Promise<void> {
1405
1532
  })
1406
1533
  .option("zone", {
1407
1534
  type: "string",
1535
+ requiresArg: true,
1408
1536
  describe: "zone id",
1409
1537
  })
1410
1538
  .option("env", {
1411
1539
  type: "string",
1540
+ requiresArg: true,
1412
1541
  describe: "Perform on a specific environment",
1413
1542
  });
1414
1543
  },
@@ -1465,39 +1594,41 @@ export async function main(argv: string[]): Promise<void> {
1465
1594
  .option("name", {
1466
1595
  describe: "Name of the worker",
1467
1596
  type: "string",
1597
+ requiresArg: true,
1468
1598
  })
1469
1599
  .option("env", {
1470
1600
  type: "string",
1601
+ requiresArg: true,
1471
1602
  describe:
1472
1603
  "Binds the secret to the Worker of the specific environment",
1473
1604
  alias: "e",
1474
1605
  });
1475
1606
  },
1476
1607
  async (args) => {
1477
- printWranglerBanner();
1478
- const config = readConfig(args.config as ConfigPath, args.env);
1608
+ await printWranglerBanner();
1609
+ const config = readConfig(args.config as ConfigPath, args);
1479
1610
 
1480
- const scriptName = getScriptName(args, config);
1611
+ const scriptName = getLegacyScriptName(args, config);
1481
1612
  if (!scriptName) {
1482
1613
  throw new Error("Missing script name");
1483
1614
  }
1484
1615
 
1485
- const isInteractive = process.stdin.isTTY;
1486
- const accountId = await requireAuth(config, isInteractive);
1616
+ const accountId = await requireAuth(config);
1487
1617
 
1618
+ const isInteractive = process.stdin.isTTY;
1488
1619
  const secretValue = isInteractive
1489
1620
  ? await prompt("Enter a secret value:", "password")
1490
1621
  : await readFromStdin();
1491
1622
 
1492
- console.log(
1623
+ logger.log(
1493
1624
  `🌀 Creating the secret for script ${scriptName} ${
1494
- args.env && !isLegacyEnv(args, config) ? `(${args.env})` : ""
1625
+ args.env && !isLegacyEnv(config) ? `(${args.env})` : ""
1495
1626
  }`
1496
1627
  );
1497
1628
 
1498
1629
  async function submitSecret() {
1499
1630
  const url =
1500
- !args.env || isLegacyEnv(args, config)
1631
+ !args.env || isLegacyEnv(config)
1501
1632
  ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
1502
1633
  : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1503
1634
 
@@ -1515,7 +1646,7 @@ export async function main(argv: string[]): Promise<void> {
1515
1646
  const createDraftWorker = async () => {
1516
1647
  // TODO: log a warning
1517
1648
  await fetchResult(
1518
- !args["legacy-env"] && args.env
1649
+ !isLegacyEnv(config) && args.env
1519
1650
  ? `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}`
1520
1651
  : `/accounts/${accountId}/workers/scripts/${scriptName}`,
1521
1652
  {
@@ -1534,6 +1665,7 @@ export async function main(argv: string[]): Promise<void> {
1534
1665
  r2_buckets: [],
1535
1666
  wasm_modules: {},
1536
1667
  text_blobs: {},
1668
+ data_blobs: {},
1537
1669
  unsafe: [],
1538
1670
  },
1539
1671
  modules: [],
@@ -1550,7 +1682,7 @@ export async function main(argv: string[]): Promise<void> {
1550
1682
  return (
1551
1683
  typeof e === "object" &&
1552
1684
  e !== null &&
1553
- (e as { code: 10007 }).code === 10007
1685
+ (e as { code: number }).code === 10007
1554
1686
  );
1555
1687
  }
1556
1688
 
@@ -1567,14 +1699,14 @@ export async function main(argv: string[]): Promise<void> {
1567
1699
  }
1568
1700
  }
1569
1701
 
1570
- console.log(`✨ Success! Uploaded secret ${args.key}`);
1702
+ logger.log(`✨ Success! Uploaded secret ${args.key}`);
1571
1703
  }
1572
1704
  )
1573
1705
  .command(
1574
1706
  "delete <key>",
1575
1707
  "Delete a secret variable from a script",
1576
- (yargs) => {
1577
- printWranglerBanner();
1708
+ async (yargs) => {
1709
+ await printWranglerBanner();
1578
1710
  return yargs
1579
1711
  .positional("key", {
1580
1712
  describe: "The variable name to be accessible in the script",
@@ -1583,18 +1715,20 @@ export async function main(argv: string[]): Promise<void> {
1583
1715
  .option("name", {
1584
1716
  describe: "Name of the worker",
1585
1717
  type: "string",
1718
+ requiresArg: true,
1586
1719
  })
1587
1720
  .option("env", {
1588
1721
  type: "string",
1722
+ requiresArg: true,
1589
1723
  describe:
1590
1724
  "Binds the secret to the Worker of the specific environment",
1591
1725
  alias: "e",
1592
1726
  });
1593
1727
  },
1594
1728
  async (args) => {
1595
- const config = readConfig(args.config as ConfigPath, args.env);
1729
+ const config = readConfig(args.config as ConfigPath, args);
1596
1730
 
1597
- const scriptName = getScriptName(args, config);
1731
+ const scriptName = getLegacyScriptName(args, config);
1598
1732
  if (!scriptName) {
1599
1733
  throw new Error("Missing script name");
1600
1734
  }
@@ -1606,23 +1740,23 @@ export async function main(argv: string[]): Promise<void> {
1606
1740
  `Are you sure you want to permanently delete the variable ${
1607
1741
  args.key
1608
1742
  } on the script ${scriptName}${
1609
- args.env && !isLegacyEnv(args, config) ? ` (${args.env})` : ""
1743
+ args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
1610
1744
  }?`
1611
1745
  )
1612
1746
  ) {
1613
- console.log(
1747
+ logger.log(
1614
1748
  `🌀 Deleting the secret ${args.key} on script ${scriptName}${
1615
- args.env && !isLegacyEnv(args, config) ? ` (${args.env})` : ""
1749
+ args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
1616
1750
  }`
1617
1751
  );
1618
1752
 
1619
1753
  const url =
1620
- !args.env || isLegacyEnv(args, config)
1754
+ !args.env || isLegacyEnv(config)
1621
1755
  ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
1622
1756
  : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1623
1757
 
1624
1758
  await fetchResult(`${url}/${args.key}`, { method: "DELETE" });
1625
- console.log(`✨ Success! Deleted secret ${args.key}`);
1759
+ logger.log(`✨ Success! Deleted secret ${args.key}`);
1626
1760
  }
1627
1761
  }
1628
1762
  )
@@ -1634,18 +1768,20 @@ export async function main(argv: string[]): Promise<void> {
1634
1768
  .option("name", {
1635
1769
  describe: "Name of the worker",
1636
1770
  type: "string",
1771
+ requiresArg: true,
1637
1772
  })
1638
1773
  .option("env", {
1639
1774
  type: "string",
1775
+ requiresArg: true,
1640
1776
  describe:
1641
1777
  "Binds the secret to the Worker of the specific environment.",
1642
1778
  alias: "e",
1643
1779
  });
1644
1780
  },
1645
1781
  async (args) => {
1646
- const config = readConfig(args.config as ConfigPath, args.env);
1782
+ const config = readConfig(args.config as ConfigPath, args);
1647
1783
 
1648
- const scriptName = getScriptName(args, config);
1784
+ const scriptName = getLegacyScriptName(args, config);
1649
1785
  if (!scriptName) {
1650
1786
  throw new Error("Missing script name");
1651
1787
  }
@@ -1653,11 +1789,11 @@ export async function main(argv: string[]): Promise<void> {
1653
1789
  const accountId = await requireAuth(config);
1654
1790
 
1655
1791
  const url =
1656
- !args.env || isLegacyEnv(args, config)
1792
+ !args.env || isLegacyEnv(config)
1657
1793
  ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
1658
1794
  : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1659
1795
 
1660
- console.log(JSON.stringify(await fetchResult(url), null, " "));
1796
+ logger.log(JSON.stringify(await fetchResult(url), null, " "));
1661
1797
  }
1662
1798
  );
1663
1799
  }
@@ -1683,6 +1819,7 @@ export async function main(argv: string[]): Promise<void> {
1683
1819
  })
1684
1820
  .option("env", {
1685
1821
  type: "string",
1822
+ requiresArg: true,
1686
1823
  describe: "Perform on a specific environment",
1687
1824
  alias: "e",
1688
1825
  })
@@ -1692,13 +1829,7 @@ export async function main(argv: string[]): Promise<void> {
1692
1829
  });
1693
1830
  },
1694
1831
  async (args) => {
1695
- printWranglerBanner();
1696
- if (args._.length > 2) {
1697
- const extraArgs = args._.slice(2).join(" ");
1698
- throw new CommandLineArgsError(
1699
- `Unexpected additional positional arguments "${extraArgs}".`
1700
- );
1701
- }
1832
+ await printWranglerBanner();
1702
1833
 
1703
1834
  if (!isValidNamespaceBinding(args.namespace)) {
1704
1835
  throw new CommandLineArgsError(
@@ -1706,9 +1837,9 @@ export async function main(argv: string[]): Promise<void> {
1706
1837
  );
1707
1838
  }
1708
1839
 
1709
- const config = readConfig(args.config as ConfigPath, args.env);
1840
+ const config = readConfig(args.config as ConfigPath, args);
1710
1841
  if (!config.name) {
1711
- console.warn(
1842
+ logger.warn(
1712
1843
  "No configured name present, using `worker` as a prefix for the title"
1713
1844
  );
1714
1845
  }
@@ -1722,16 +1853,16 @@ export async function main(argv: string[]): Promise<void> {
1722
1853
 
1723
1854
  // TODO: generate a binding name stripping non alphanumeric chars
1724
1855
 
1725
- console.log(`🌀 Creating namespace with title "${title}"`);
1856
+ logger.log(`🌀 Creating namespace with title "${title}"`);
1726
1857
  const namespaceId = await createNamespace(accountId, title);
1727
1858
 
1728
- console.log("✨ Success!");
1859
+ logger.log("✨ Success!");
1729
1860
  const envString = args.env ? ` under [env.${args.env}]` : "";
1730
1861
  const previewString = args.preview ? "preview_" : "";
1731
- console.log(
1862
+ logger.log(
1732
1863
  `Add the following to your configuration file in your kv_namespaces array${envString}:`
1733
1864
  );
1734
- console.log(
1865
+ logger.log(
1735
1866
  `{ binding = "${args.namespace}", ${previewString}id = "${namespaceId}" }`
1736
1867
  );
1737
1868
 
@@ -1743,13 +1874,13 @@ export async function main(argv: string[]): Promise<void> {
1743
1874
  "Outputs a list of all KV namespaces associated with your account id.",
1744
1875
  {},
1745
1876
  async (args) => {
1746
- const config = readConfig(args.config as ConfigPath, undefined);
1877
+ const config = readConfig(args.config as ConfigPath, args);
1747
1878
 
1748
1879
  const accountId = await requireAuth(config);
1749
1880
 
1750
1881
  // TODO: we should show bindings if they exist for given ids
1751
1882
 
1752
- console.log(
1883
+ logger.log(
1753
1884
  JSON.stringify(await listNamespaces(accountId), null, " ")
1754
1885
  );
1755
1886
  }
@@ -1761,15 +1892,18 @@ export async function main(argv: string[]): Promise<void> {
1761
1892
  return yargs
1762
1893
  .option("binding", {
1763
1894
  type: "string",
1895
+ requiresArg: true,
1764
1896
  describe: "The name of the namespace to delete",
1765
1897
  })
1766
1898
  .option("namespace-id", {
1767
1899
  type: "string",
1900
+ requiresArg: true,
1768
1901
  describe: "The id of the namespace to delete",
1769
1902
  })
1770
1903
  .check(demandOneOfOption("binding", "namespace-id"))
1771
1904
  .option("env", {
1772
1905
  type: "string",
1906
+ requiresArg: true,
1773
1907
  describe: "Perform on a specific environment",
1774
1908
  alias: "e",
1775
1909
  })
@@ -1779,8 +1913,8 @@ export async function main(argv: string[]): Promise<void> {
1779
1913
  });
1780
1914
  },
1781
1915
  async (args) => {
1782
- printWranglerBanner();
1783
- const config = readConfig(args.config as ConfigPath, args.env);
1916
+ await printWranglerBanner();
1917
+ const config = readConfig(args.config as ConfigPath, args);
1784
1918
 
1785
1919
  let id;
1786
1920
  try {
@@ -1843,15 +1977,18 @@ export async function main(argv: string[]): Promise<void> {
1843
1977
  })
1844
1978
  .option("binding", {
1845
1979
  type: "string",
1980
+ requiresArg: true,
1846
1981
  describe: "The binding of the namespace to write to",
1847
1982
  })
1848
1983
  .option("namespace-id", {
1849
1984
  type: "string",
1985
+ requiresArg: true,
1850
1986
  describe: "The id of the namespace to write to",
1851
1987
  })
1852
1988
  .check(demandOneOfOption("binding", "namespace-id"))
1853
1989
  .option("env", {
1854
1990
  type: "string",
1991
+ requiresArg: true,
1855
1992
  describe: "Perform on a specific environment",
1856
1993
  alias: "e",
1857
1994
  })
@@ -1870,13 +2007,14 @@ export async function main(argv: string[]): Promise<void> {
1870
2007
  })
1871
2008
  .option("path", {
1872
2009
  type: "string",
2010
+ requiresArg: true,
1873
2011
  describe: "Read value from the file at a given path",
1874
2012
  })
1875
2013
  .check(demandOneOfOption("value", "path"));
1876
2014
  },
1877
2015
  async ({ key, ttl, expiration, ...args }) => {
1878
- printWranglerBanner();
1879
- const config = readConfig(args.config as ConfigPath, args.env);
2016
+ await printWranglerBanner();
2017
+ const config = readConfig(args.config as ConfigPath, args);
1880
2018
  const namespaceId = getNamespaceId(args, config);
1881
2019
  // One of `args.path` and `args.value` must be defined
1882
2020
  const value = args.path
@@ -1885,12 +2023,12 @@ export async function main(argv: string[]): Promise<void> {
1885
2023
  args.value!;
1886
2024
 
1887
2025
  if (args.path) {
1888
- console.log(
1889
- `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}.`
1890
2028
  );
1891
2029
  } else {
1892
- console.log(
1893
- `writing the value "${value}" to key "${key}" on namespace ${namespaceId}`
2030
+ logger.log(
2031
+ `Writing the value "${value}" to key "${key}" on namespace ${namespaceId}.`
1894
2032
  );
1895
2033
  }
1896
2034
 
@@ -1911,15 +2049,18 @@ export async function main(argv: string[]): Promise<void> {
1911
2049
  return yargs
1912
2050
  .option("binding", {
1913
2051
  type: "string",
2052
+ requiresArg: true,
1914
2053
  describe: "The name of the namespace to list",
1915
2054
  })
1916
2055
  .option("namespace-id", {
1917
2056
  type: "string",
2057
+ requiresArg: true,
1918
2058
  describe: "The id of the namespace to list",
1919
2059
  })
1920
2060
  .check(demandOneOfOption("binding", "namespace-id"))
1921
2061
  .option("env", {
1922
2062
  type: "string",
2063
+ requiresArg: true,
1923
2064
  describe: "Perform on a specific environment",
1924
2065
  alias: "e",
1925
2066
  })
@@ -1931,12 +2072,13 @@ export async function main(argv: string[]): Promise<void> {
1931
2072
  })
1932
2073
  .option("prefix", {
1933
2074
  type: "string",
2075
+ requiresArg: true,
1934
2076
  describe: "A prefix to filter listed keys",
1935
2077
  });
1936
2078
  },
1937
2079
  async ({ prefix, ...args }) => {
1938
2080
  // TODO: support for limit+cursor (pagination)
1939
- const config = readConfig(args.config as ConfigPath, args.env);
2081
+ const config = readConfig(args.config as ConfigPath, args);
1940
2082
  const namespaceId = getNamespaceId(args, config);
1941
2083
 
1942
2084
  const accountId = await requireAuth(config);
@@ -1946,7 +2088,7 @@ export async function main(argv: string[]): Promise<void> {
1946
2088
  namespaceId,
1947
2089
  prefix
1948
2090
  );
1949
- console.log(JSON.stringify(results, undefined, 2));
2091
+ logger.log(JSON.stringify(results, undefined, 2));
1950
2092
  }
1951
2093
  )
1952
2094
  .command(
@@ -1961,15 +2103,18 @@ export async function main(argv: string[]): Promise<void> {
1961
2103
  })
1962
2104
  .option("binding", {
1963
2105
  type: "string",
2106
+ requiresArg: true,
1964
2107
  describe: "The name of the namespace to get from",
1965
2108
  })
1966
2109
  .option("namespace-id", {
1967
2110
  type: "string",
2111
+ requiresArg: true,
1968
2112
  describe: "The id of the namespace to get from",
1969
2113
  })
1970
2114
  .check(demandOneOfOption("binding", "namespace-id"))
1971
2115
  .option("env", {
1972
2116
  type: "string",
2117
+ requiresArg: true,
1973
2118
  describe: "Perform on a specific environment",
1974
2119
  alias: "e",
1975
2120
  })
@@ -1985,12 +2130,12 @@ export async function main(argv: string[]): Promise<void> {
1985
2130
  });
1986
2131
  },
1987
2132
  async ({ key, ...args }) => {
1988
- const config = readConfig(args.config as ConfigPath, args.env);
2133
+ const config = readConfig(args.config as ConfigPath, args);
1989
2134
  const namespaceId = getNamespaceId(args, config);
1990
2135
 
1991
2136
  const accountId = await requireAuth(config);
1992
2137
 
1993
- console.log(await getKeyValue(accountId, namespaceId, key));
2138
+ logger.log(await getKeyValue(accountId, namespaceId, key));
1994
2139
  }
1995
2140
  )
1996
2141
  .command(
@@ -2005,15 +2150,18 @@ export async function main(argv: string[]): Promise<void> {
2005
2150
  })
2006
2151
  .option("binding", {
2007
2152
  type: "string",
2153
+ requiresArg: true,
2008
2154
  describe: "The name of the namespace to delete from",
2009
2155
  })
2010
2156
  .option("namespace-id", {
2011
2157
  type: "string",
2158
+ requiresArg: true,
2012
2159
  describe: "The id of the namespace to delete from",
2013
2160
  })
2014
2161
  .check(demandOneOfOption("binding", "namespace-id"))
2015
2162
  .option("env", {
2016
2163
  type: "string",
2164
+ requiresArg: true,
2017
2165
  describe: "Perform on a specific environment",
2018
2166
  alias: "e",
2019
2167
  })
@@ -2023,12 +2171,12 @@ export async function main(argv: string[]): Promise<void> {
2023
2171
  });
2024
2172
  },
2025
2173
  async ({ key, ...args }) => {
2026
- printWranglerBanner();
2027
- const config = readConfig(args.config as ConfigPath, args.env);
2174
+ await printWranglerBanner();
2175
+ const config = readConfig(args.config as ConfigPath, args);
2028
2176
  const namespaceId = getNamespaceId(args, config);
2029
2177
 
2030
- console.log(
2031
- `deleting the key "${key}" on namespace ${namespaceId}`
2178
+ logger.log(
2179
+ `Deleting the key "${key}" on namespace ${namespaceId}.`
2032
2180
  );
2033
2181
 
2034
2182
  const accountId = await requireAuth(config);
@@ -2061,15 +2209,18 @@ export async function main(argv: string[]): Promise<void> {
2061
2209
  })
2062
2210
  .option("binding", {
2063
2211
  type: "string",
2212
+ requiresArg: true,
2064
2213
  describe: "The name of the namespace to insert values into",
2065
2214
  })
2066
2215
  .option("namespace-id", {
2067
2216
  type: "string",
2217
+ requiresArg: true,
2068
2218
  describe: "The id of the namespace to insert values into",
2069
2219
  })
2070
2220
  .check(demandOneOfOption("binding", "namespace-id"))
2071
2221
  .option("env", {
2072
2222
  type: "string",
2223
+ requiresArg: true,
2073
2224
  describe: "Perform on a specific environment",
2074
2225
  alias: "e",
2075
2226
  })
@@ -2079,12 +2230,12 @@ export async function main(argv: string[]): Promise<void> {
2079
2230
  });
2080
2231
  },
2081
2232
  async ({ filename, ...args }) => {
2082
- printWranglerBanner();
2233
+ await printWranglerBanner();
2083
2234
  // The simplest implementation I could think of.
2084
2235
  // This could be made more efficient with a streaming parser/uploader
2085
2236
  // but we'll do that in the future if needed.
2086
2237
 
2087
- const config = readConfig(args.config as ConfigPath, args.env);
2238
+ const config = readConfig(args.config as ConfigPath, args);
2088
2239
  const namespaceId = getNamespaceId(args, config);
2089
2240
  const content = parseJSON(readFileSync(filename), filename);
2090
2241
 
@@ -2121,7 +2272,7 @@ export async function main(argv: string[]): Promise<void> {
2121
2272
  }
2122
2273
  }
2123
2274
  if (warnings.length > 0) {
2124
- console.warn(
2275
+ logger.warn(
2125
2276
  `Unexpected key-value properties in "${filename}".\n` +
2126
2277
  warnings.join("\n")
2127
2278
  );
@@ -2148,11 +2299,11 @@ export async function main(argv: string[]): Promise<void> {
2148
2299
  namespaceId,
2149
2300
  content,
2150
2301
  (index, total) => {
2151
- console.log(`Uploaded ${index} of ${total}.`);
2302
+ logger.log(`Uploaded ${index} of ${total}.`);
2152
2303
  }
2153
2304
  );
2154
2305
 
2155
- console.log("Success!");
2306
+ logger.log("Success!");
2156
2307
  }
2157
2308
  )
2158
2309
  .command(
@@ -2167,15 +2318,18 @@ export async function main(argv: string[]): Promise<void> {
2167
2318
  })
2168
2319
  .option("binding", {
2169
2320
  type: "string",
2321
+ requiresArg: true,
2170
2322
  describe: "The name of the namespace to delete from",
2171
2323
  })
2172
2324
  .option("namespace-id", {
2173
2325
  type: "string",
2326
+ requiresArg: true,
2174
2327
  describe: "The id of the namespace to delete from",
2175
2328
  })
2176
2329
  .check(demandOneOfOption("binding", "namespace-id"))
2177
2330
  .option("env", {
2178
2331
  type: "string",
2332
+ requiresArg: true,
2179
2333
  describe: "Perform on a specific environment",
2180
2334
  alias: "e",
2181
2335
  })
@@ -2190,8 +2344,8 @@ export async function main(argv: string[]): Promise<void> {
2190
2344
  });
2191
2345
  },
2192
2346
  async ({ filename, ...args }) => {
2193
- printWranglerBanner();
2194
- const config = readConfig(args.config as ConfigPath, args.env);
2347
+ await printWranglerBanner();
2348
+ const config = readConfig(args.config as ConfigPath, args);
2195
2349
  const namespaceId = getNamespaceId(args, config);
2196
2350
 
2197
2351
  if (!args.force) {
@@ -2199,7 +2353,7 @@ export async function main(argv: string[]): Promise<void> {
2199
2353
  `Are you sure you want to delete all the keys read from "${filename}" from kv-namespace with id "${namespaceId}"?`
2200
2354
  );
2201
2355
  if (!result) {
2202
- console.log(`Not deleting keys read from "${filename}".`);
2356
+ logger.log(`Not deleting keys read from "${filename}".`);
2203
2357
  return;
2204
2358
  }
2205
2359
  }
@@ -2242,18 +2396,22 @@ export async function main(argv: string[]): Promise<void> {
2242
2396
  namespaceId,
2243
2397
  content,
2244
2398
  (index, total) => {
2245
- console.log(`Deleted ${index} of ${total}.`);
2399
+ logger.log(`Deleted ${index} of ${total}.`);
2246
2400
  }
2247
2401
  );
2248
2402
 
2249
- console.log("Success!");
2403
+ logger.log("Success!");
2250
2404
  }
2251
2405
  );
2252
2406
  }
2253
2407
  );
2254
2408
 
2255
- wrangler.command("pages", "⚡️ Configure Cloudflare Pages", (pagesYargs) =>
2256
- pages(pagesYargs.command(subHelp))
2409
+ wrangler.command(
2410
+ "pages",
2411
+ "⚡️ Configure Cloudflare Pages",
2412
+ async (pagesYargs) => {
2413
+ await pages(pagesYargs.command(subHelp).epilogue(pagesBetaWarning));
2414
+ }
2257
2415
  );
2258
2416
 
2259
2417
  wrangler.command("r2", "📦 Interact with an R2 store", (r2Yargs) => {
@@ -2271,31 +2429,24 @@ export async function main(argv: string[]): Promise<void> {
2271
2429
  });
2272
2430
  },
2273
2431
  async (args) => {
2274
- printWranglerBanner();
2275
- // We expect three values in `_`: `r2`, `bucket`, `create`.
2276
- if (args._.length > 3) {
2277
- const extraArgs = args._.slice(3).join(" ");
2278
- throw new CommandLineArgsError(
2279
- `Unexpected additional positional arguments "${extraArgs}".`
2280
- );
2281
- }
2432
+ await printWranglerBanner();
2282
2433
 
2283
- const config = readConfig(args.config as ConfigPath, undefined);
2434
+ const config = readConfig(args.config as ConfigPath, args);
2284
2435
 
2285
2436
  const accountId = await requireAuth(config);
2286
2437
 
2287
- console.log(`Creating bucket ${args.name}.`);
2438
+ logger.log(`Creating bucket ${args.name}.`);
2288
2439
  await createR2Bucket(accountId, args.name);
2289
- console.log(`Created bucket ${args.name}.`);
2440
+ logger.log(`Created bucket ${args.name}.`);
2290
2441
  }
2291
2442
  );
2292
2443
 
2293
2444
  r2BucketYargs.command("list", "List R2 buckets", {}, async (args) => {
2294
- const config = readConfig(args.config as ConfigPath, undefined);
2445
+ const config = readConfig(args.config as ConfigPath, args);
2295
2446
 
2296
2447
  const accountId = await requireAuth(config);
2297
2448
 
2298
- console.log(JSON.stringify(await listR2Buckets(accountId), null, 2));
2449
+ logger.log(JSON.stringify(await listR2Buckets(accountId), null, 2));
2299
2450
  });
2300
2451
 
2301
2452
  r2BucketYargs.command(
@@ -2309,28 +2460,97 @@ export async function main(argv: string[]): Promise<void> {
2309
2460
  });
2310
2461
  },
2311
2462
  async (args) => {
2312
- printWranglerBanner();
2313
- // We expect three values in `_`: `r2`, `bucket`, `delete`.
2314
- if (args._.length > 3) {
2315
- const extraArgs = args._.slice(3).join(" ");
2316
- throw new CommandLineArgsError(
2317
- `Unexpected additional positional arguments "${extraArgs}".`
2318
- );
2319
- }
2463
+ await printWranglerBanner();
2320
2464
 
2321
- const config = readConfig(args.config as ConfigPath, undefined);
2465
+ const config = readConfig(args.config as ConfigPath, args);
2322
2466
 
2323
2467
  const accountId = await requireAuth(config);
2324
2468
 
2325
- console.log(`Deleting bucket ${args.name}.`);
2469
+ logger.log(`Deleting bucket ${args.name}.`);
2326
2470
  await deleteR2Bucket(accountId, args.name);
2327
- console.log(`Deleted bucket ${args.name}.`);
2471
+ logger.log(`Deleted bucket ${args.name}.`);
2328
2472
  }
2329
2473
  );
2330
2474
  return r2BucketYargs;
2331
2475
  });
2332
2476
  });
2333
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
+
2334
2554
  wrangler
2335
2555
  .option("legacy-env", {
2336
2556
  type: "boolean",
@@ -2340,6 +2560,7 @@ export async function main(argv: string[]): Promise<void> {
2340
2560
  alias: "c",
2341
2561
  describe: "Path to .toml configuration file",
2342
2562
  type: "string",
2563
+ requiresArg: true,
2343
2564
  });
2344
2565
 
2345
2566
  wrangler.group(["config", "help", "version", "legacy-env"], "Flags:");
@@ -2350,20 +2571,19 @@ export async function main(argv: string[]): Promise<void> {
2350
2571
  try {
2351
2572
  await wrangler.parse();
2352
2573
  } catch (e) {
2574
+ logger.log(""); // Just adds a bit of space
2353
2575
  if (e instanceof CommandLineArgsError) {
2354
2576
  wrangler.showHelp("error");
2355
- console.error(""); // Just adds a bit of space
2356
- console.error(e.message);
2577
+ logger.log(""); // Add a bit of space.
2578
+ logger.error(e.message);
2357
2579
  } else if (e instanceof ParseError) {
2358
- console.error("");
2359
2580
  e.notes.push({
2360
2581
  text: "\nIf you think this is a bug, please open an issue at: https://github.com/cloudflare/wrangler2/issues/new",
2361
2582
  });
2362
- console.error(formatMessage(e));
2583
+ logger.error(formatMessage(e));
2363
2584
  } else {
2364
- console.error(e instanceof Error ? e.message : e);
2365
- console.error(""); // Just adds a bit of space
2366
- console.error(
2585
+ logger.error(e instanceof Error ? e.message : e);
2586
+ logger.log(
2367
2587
  `${fgGreenColor}%s${resetColor}`,
2368
2588
  "If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
2369
2589
  );
@@ -2371,3 +2591,25 @@ export async function main(argv: string[]): Promise<void> {
2371
2591
  throw e;
2372
2592
  }
2373
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
+ }