wrangler 2.0.5 → 2.0.8

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 (46) hide show
  1. package/README.md +1 -1
  2. package/bin/wrangler.js +16 -4
  3. package/package.json +6 -4
  4. package/pages/functions/buildPlugin.ts +13 -0
  5. package/pages/functions/buildWorker.ts +13 -0
  6. package/src/__tests__/configuration.test.ts +335 -86
  7. package/src/__tests__/dev.test.tsx +166 -15
  8. package/src/__tests__/helpers/mock-dialogs.ts +41 -1
  9. package/src/__tests__/index.test.ts +30 -16
  10. package/src/__tests__/init.test.ts +249 -131
  11. package/src/__tests__/kv.test.ts +101 -101
  12. package/src/__tests__/package-manager.test.ts +154 -7
  13. package/src/__tests__/pages.test.ts +369 -39
  14. package/src/__tests__/parse.test.ts +5 -1
  15. package/src/__tests__/publish.test.ts +556 -84
  16. package/src/__tests__/r2.test.ts +47 -24
  17. package/src/__tests__/secret.test.ts +39 -4
  18. package/src/abort.d.ts +3 -0
  19. package/src/bundle.ts +32 -1
  20. package/src/cfetch/index.ts +21 -4
  21. package/src/cfetch/internal.ts +14 -9
  22. package/src/config/environment.ts +40 -14
  23. package/src/config/index.ts +162 -0
  24. package/src/config/validation.ts +179 -64
  25. package/src/create-worker-preview.ts +17 -7
  26. package/src/create-worker-upload-form.ts +22 -8
  27. package/src/dev/dev.tsx +2 -4
  28. package/src/dev/local.tsx +6 -0
  29. package/src/dev/remote.tsx +15 -1
  30. package/src/dialogs.tsx +48 -0
  31. package/src/durable.ts +102 -0
  32. package/src/index.tsx +314 -144
  33. package/src/inspect.ts +39 -0
  34. package/src/kv.ts +77 -13
  35. package/src/open-in-browser.ts +5 -12
  36. package/src/package-manager.ts +50 -3
  37. package/src/pages.tsx +210 -65
  38. package/src/parse.ts +21 -4
  39. package/src/proxy.ts +38 -22
  40. package/src/publish.ts +227 -113
  41. package/src/sites.tsx +11 -9
  42. package/src/worker.ts +8 -0
  43. package/templates/new-worker-scheduled.js +17 -0
  44. package/templates/new-worker-scheduled.ts +32 -0
  45. package/templates/new-worker.ts +16 -1
  46. package/wrangler-dist/cli.js +35466 -22362
package/src/index.tsx CHANGED
@@ -11,14 +11,15 @@ import { render } from "ink";
11
11
  import React from "react";
12
12
  import onExit from "signal-exit";
13
13
  import supportsColor from "supports-color";
14
+ import { setGlobalDispatcher, ProxyAgent } from "undici";
14
15
  import makeCLI from "yargs";
15
16
  import { version as wranglerVersion } from "../package.json";
16
17
  import { fetchResult } from "./cfetch";
17
- import { findWranglerToml, readConfig } from "./config";
18
+ import { findWranglerToml, printBindings, readConfig } from "./config";
18
19
  import { createWorkerUploadForm } from "./create-worker-upload-form";
19
20
  import Dev from "./dev/dev";
20
21
  import { getVarsForDev } from "./dev/dev-vars";
21
- import { confirm, prompt } from "./dialogs";
22
+ import { confirm, prompt, select } from "./dialogs";
22
23
  import { getEntry } from "./entry";
23
24
  import { DeprecationError } from "./errors";
24
25
  import {
@@ -43,6 +44,7 @@ import {
43
44
  formatMessage,
44
45
  ParseError,
45
46
  parseJSON,
47
+ parsePackageJSON,
46
48
  parseTOML,
47
49
  readFileSync,
48
50
  } from "./parse";
@@ -77,6 +79,17 @@ const resetColor = "\x1b[0m";
77
79
  const fgGreenColor = "\x1b[32m";
78
80
  const DEFAULT_LOCAL_PORT = 8787;
79
81
 
82
+ const proxy =
83
+ process.env.https_proxy ||
84
+ process.env.HTTPS_PROXY ||
85
+ process.env.http_proxy ||
86
+ process.env.HTTP_PROXY ||
87
+ undefined;
88
+
89
+ if (proxy) {
90
+ setGlobalDispatcher(new ProxyAgent(proxy));
91
+ }
92
+
80
93
  function getRules(config: Config): Config["rules"] {
81
94
  const rules = config.rules ?? config.build?.upload?.rules ?? [];
82
95
 
@@ -206,9 +219,17 @@ function demandOneOfOption(...options: string[]) {
206
219
  };
207
220
  }
208
221
 
222
+ /**
223
+ * Remove trailing white space from inputs.
224
+ * Matching Wrangler legacy behavior with handling inputs
225
+ */
226
+ function trimTrailingWhitespace(str: string) {
227
+ return str.trimEnd();
228
+ }
229
+
209
230
  class CommandLineArgsError extends Error {}
210
231
 
211
- export async function main(argv: string[]): Promise<void> {
232
+ function createCLIParser(argv: string[]) {
212
233
  const wrangler = makeCLI(argv)
213
234
  .strict()
214
235
  // We handle errors ourselves in a try-catch around `yargs.parse`.
@@ -325,6 +346,9 @@ export async function main(argv: string[]): Promise<void> {
325
346
  throw new CommandLineArgsError(message);
326
347
  }
327
348
 
349
+ const devDepsToInstall: string[] = [];
350
+ const instructions: string[] = [];
351
+ let shouldRunPackageManagerInstall = false;
328
352
  const creationDirectory = path.resolve(process.cwd(), args.name ?? "");
329
353
 
330
354
  if (args.site) {
@@ -413,19 +437,16 @@ export async function main(argv: string[]): Promise<void> {
413
437
  try {
414
438
  isGitInstalled = (await execa("git", ["--version"])).exitCode === 0;
415
439
  } catch (err) {
416
- if ((err as { code: string | undefined }).code !== "ENOENT") {
417
- // only throw if the error is not because git is not installed
418
- throw err;
419
- } else {
420
- isGitInstalled = false;
421
- }
440
+ isGitInstalled = false;
422
441
  }
423
442
  if (!isInsideGitProject && isGitInstalled) {
424
443
  const shouldInitGit =
425
444
  yesFlag ||
426
445
  (await confirm("Would you like to use git to manage this Worker?"));
427
446
  if (shouldInitGit) {
428
- await execa("git", ["init"], { cwd: creationDirectory });
447
+ await execa("git", ["init", "--initial-branch=main"], {
448
+ cwd: creationDirectory,
449
+ });
429
450
  await writeFile(
430
451
  path.join(creationDirectory, ".gitignore"),
431
452
  readFileSync(path.join(__dirname, "../templates/gitignore"))
@@ -471,7 +492,7 @@ export async function main(argv: string[]): Promise<void> {
471
492
  ) + "\n"
472
493
  );
473
494
 
474
- await packageManager.install();
495
+ shouldRunPackageManagerInstall = true;
475
496
  pathToPackageJson = path.join(creationDirectory, "package.json");
476
497
  logger.log(
477
498
  `✨ Created ${path.relative(process.cwd(), pathToPackageJson)}`
@@ -482,7 +503,7 @@ export async function main(argv: string[]): Promise<void> {
482
503
  } else {
483
504
  // If package.json exists and wrangler isn't installed,
484
505
  // then ask to add wrangler to devDependencies
485
- const packageJson = parseJSON(
506
+ const packageJson = parsePackageJSON(
486
507
  readFileSync(pathToPackageJson),
487
508
  pathToPackageJson
488
509
  );
@@ -501,8 +522,7 @@ export async function main(argv: string[]): Promise<void> {
501
522
  )}?`
502
523
  ));
503
524
  if (shouldInstall) {
504
- await packageManager.addDevDeps(`wrangler@${wranglerVersion}`);
505
- logger.log(`✨ Installed wrangler`);
525
+ devDepsToInstall.push(`wrangler@${wranglerVersion}`);
506
526
  }
507
527
  }
508
528
  }
@@ -520,23 +540,18 @@ export async function main(argv: string[]): Promise<void> {
520
540
  path.join(creationDirectory, "./tsconfig.json"),
521
541
  readFileSync(path.join(__dirname, "../templates/tsconfig.json"))
522
542
  );
523
- await packageManager.addDevDeps(
524
- "@cloudflare/workers-types",
525
- "typescript"
526
- );
543
+ devDepsToInstall.push("@cloudflare/workers-types");
544
+ devDepsToInstall.push("typescript");
527
545
  pathToTSConfig = path.join(creationDirectory, "tsconfig.json");
528
546
  logger.log(
529
- `✨ Created ${path.relative(
530
- process.cwd(),
531
- pathToTSConfig
532
- )}, installed @cloudflare/workers-types into devDependencies`
547
+ `✨ Created ${path.relative(process.cwd(), pathToTSConfig)}`
533
548
  );
534
549
  }
535
550
  } else {
536
551
  isTypescriptProject = true;
537
552
  // If there's a tsconfig, check if @cloudflare/workers-types
538
553
  // is already installed, and offer to install it if not
539
- const packageJson = parseJSON(
554
+ const packageJson = parsePackageJSON(
540
555
  readFileSync(pathToPackageJson),
541
556
  pathToPackageJson
542
557
  );
@@ -550,13 +565,13 @@ export async function main(argv: string[]): Promise<void> {
550
565
  "Would you like to install the type definitions for Workers into your package.json?"
551
566
  );
552
567
  if (shouldInstall) {
553
- await packageManager.addDevDeps("@cloudflare/workers-types");
568
+ devDepsToInstall.push("@cloudflare/workers-types");
554
569
  // We don't update the tsconfig.json because
555
570
  // it could be complicated in existing projects
556
571
  // and we don't want to break them. Instead, we simply
557
572
  // tell the user that they need to update their tsconfig.json
558
- logger.log(
559
- `✨ Installed @cloudflare/workers-types.\nPlease add "@cloudflare/workers-types" to compilerOptions.types in ${path.relative(
573
+ instructions.push(
574
+ `🚨 Please add "@cloudflare/workers-types" to compilerOptions.types in ${path.relative(
560
575
  process.cwd(),
561
576
  pathToTSConfig
562
577
  )}`
@@ -565,7 +580,7 @@ export async function main(argv: string[]): Promise<void> {
565
580
  }
566
581
  }
567
582
 
568
- const packageJsonContent = parseJSON(
583
+ const packageJsonContent = parsePackageJSON(
569
584
  readFileSync(pathToPackageJson),
570
585
  pathToPackageJson
571
586
  );
@@ -574,25 +589,60 @@ export async function main(argv: string[]): Promise<void> {
574
589
  !packageJsonContent.scripts?.publish &&
575
590
  shouldCreatePackageJson;
576
591
 
592
+ /*
593
+ * Passes the array of accumulated devDeps to install through to
594
+ * the package manager. Also generates a human-readable list
595
+ * of packages it installed.
596
+ * If there are no devDeps to install, optionally runs
597
+ * the package manager's install command.
598
+ */
599
+ async function installPackages(
600
+ shouldRunInstall: boolean,
601
+ depsToInstall: string[]
602
+ ) {
603
+ //lets install the devDeps they asked for
604
+ //and run their package manager's install command if needed
605
+ if (depsToInstall.length > 0) {
606
+ const formatter = new Intl.ListFormat("en", {
607
+ style: "long",
608
+ type: "conjunction",
609
+ });
610
+ await packageManager.addDevDeps(...depsToInstall);
611
+ const versionlessPackages = depsToInstall.map((dep) =>
612
+ dep === `wrangler@${wranglerVersion}` ? "wrangler" : dep
613
+ );
614
+
615
+ logger.log(
616
+ `✨ Installed ${formatter.format(
617
+ versionlessPackages
618
+ )} into devDependencies`
619
+ );
620
+ } else {
621
+ if (shouldRunInstall) {
622
+ await packageManager.install();
623
+ }
624
+ }
625
+ }
626
+
577
627
  async function writePackageJsonScriptsAndUpdateWranglerToml(
578
628
  isWritingScripts: boolean,
579
629
  isCreatingWranglerToml: boolean,
580
630
  packagePath: string,
581
- scriptPath: string
631
+ scriptPath: string,
632
+ extraToml: TOML.JsonMap
582
633
  ) {
583
634
  if (isCreatingWranglerToml) {
584
- // rewrite wrangler.toml with main = "path/to/script"
635
+ // rewrite wrangler.toml with main = "path/to/script" and any additional config specified in `extraToml`
585
636
  const parsedWranglerToml = parseTOML(
586
637
  readFileSync(wranglerTomlDestination)
587
638
  );
588
- fs.writeFileSync(
589
- wranglerTomlDestination,
590
- TOML.stringify({
591
- name: parsedWranglerToml.name,
592
- main: scriptPath,
593
- compatibility_date: parsedWranglerToml.compatibility_date,
594
- })
595
- );
639
+ const newToml = {
640
+ name: parsedWranglerToml.name,
641
+ main: scriptPath,
642
+ compatibility_date: parsedWranglerToml.compatibility_date,
643
+ ...extraToml,
644
+ };
645
+ fs.writeFileSync(wranglerTomlDestination, TOML.stringify(newToml));
596
646
  }
597
647
  const isNamedWorker =
598
648
  isCreatingWranglerToml && path.dirname(packagePath) !== process.cwd();
@@ -608,7 +658,7 @@ export async function main(argv: string[]): Promise<void> {
608
658
  start: isCreatingWranglerToml
609
659
  ? `wrangler dev`
610
660
  : `wrangler dev ${scriptPath}`,
611
- publish: isCreatingWranglerToml
661
+ deploy: isCreatingWranglerToml
612
662
  ? `wrangler publish`
613
663
  : `wrangler publish ${scriptPath}`,
614
664
  },
@@ -617,21 +667,21 @@ export async function main(argv: string[]): Promise<void> {
617
667
  2
618
668
  ) + "\n"
619
669
  );
620
- logger.log(
670
+ instructions.push(
621
671
  `\nTo start developing your Worker, run \`${
622
672
  isNamedWorker ? `cd ${args.name} && ` : ""
623
673
  }npm start\``
624
674
  );
625
- logger.log(
626
- `To publish your Worker to the Internet, run \`npm run publish\``
675
+ instructions.push(
676
+ `To publish your Worker to the Internet, run \`npm run deploy\``
627
677
  );
628
678
  } else {
629
- logger.log(
679
+ instructions.push(
630
680
  `\nTo start developing your Worker, run \`npx wrangler dev\`${
631
681
  isCreatingWranglerToml ? "" : ` ${scriptPath}`
632
682
  }`
633
683
  );
634
- logger.log(
684
+ instructions.push(
635
685
  `To publish your Worker to the Internet, run \`npx wrangler publish\`${
636
686
  isCreatingWranglerToml ? "" : ` ${scriptPath}`
637
687
  }`
@@ -639,24 +689,75 @@ export async function main(argv: string[]): Promise<void> {
639
689
  }
640
690
  }
641
691
 
692
+ async function getNewWorkerType(newWorkerFilename: string) {
693
+ return select(
694
+ `Would you like to create a Worker at ${newWorkerFilename}?`,
695
+ [
696
+ {
697
+ value: "none",
698
+ label: "None",
699
+ },
700
+ {
701
+ value: "fetch",
702
+ label: "Fetch handler",
703
+ },
704
+ {
705
+ value: "scheduled",
706
+ label: "Scheduled handler",
707
+ },
708
+ ],
709
+ 1
710
+ ) as Promise<"none" | "fetch" | "scheduled">;
711
+ }
712
+
713
+ function getNewWorkerTemplate(
714
+ lang: "js" | "ts",
715
+ workerType: "fetch" | "scheduled"
716
+ ) {
717
+ const templates = {
718
+ "js-fetch": "new-worker.js",
719
+ "js-scheduled": "new-worker-scheduled.js",
720
+ "ts-fetch": "new-worker.ts",
721
+ "ts-scheduled": "new-worker-scheduled.ts",
722
+ };
723
+
724
+ return templates[`${lang}-${workerType}`];
725
+ }
726
+
727
+ function getNewWorkerToml(
728
+ workerType: "fetch" | "scheduled"
729
+ ): TOML.JsonMap {
730
+ if (workerType === "scheduled") {
731
+ return {
732
+ triggers: {
733
+ crons: ["1 * * * *"],
734
+ },
735
+ };
736
+ }
737
+
738
+ return {};
739
+ }
740
+
642
741
  if (isTypescriptProject) {
643
742
  if (!fs.existsSync(path.join(creationDirectory, "./src/index.ts"))) {
644
- const shouldCreateSource =
645
- yesFlag ||
646
- (await confirm(
647
- `Would you like to create a Worker at ${path.relative(
648
- process.cwd(),
649
- path.join(creationDirectory, "./src/index.ts")
650
- )}?`
651
- ));
743
+ const newWorkerFilename = path.relative(
744
+ process.cwd(),
745
+ path.join(creationDirectory, "./src/index.ts")
746
+ );
747
+
748
+ const newWorkerType = yesFlag
749
+ ? "fetch"
750
+ : await getNewWorkerType(newWorkerFilename);
751
+
752
+ if (newWorkerType !== "none") {
753
+ const template = getNewWorkerTemplate("ts", newWorkerType);
652
754
 
653
- if (shouldCreateSource) {
654
755
  await mkdir(path.join(creationDirectory, "./src"), {
655
756
  recursive: true,
656
757
  });
657
758
  await writeFile(
658
759
  path.join(creationDirectory, "./src/index.ts"),
659
- readFileSync(path.join(__dirname, "../templates/new-worker.ts"))
760
+ readFileSync(path.join(__dirname, `../templates/${template}`))
660
761
  );
661
762
 
662
763
  logger.log(
@@ -670,28 +771,31 @@ export async function main(argv: string[]): Promise<void> {
670
771
  shouldWritePackageJsonScripts,
671
772
  justCreatedWranglerToml,
672
773
  pathToPackageJson,
673
- "src/index.ts"
774
+ "src/index.ts",
775
+ getNewWorkerToml(newWorkerType)
674
776
  );
675
777
  }
676
778
  }
677
779
  } else {
678
780
  if (!fs.existsSync(path.join(creationDirectory, "./src/index.js"))) {
679
- const shouldCreateSource =
680
- yesFlag ||
681
- (await confirm(
682
- `Would you like to create a Worker at ${path.relative(
683
- process.cwd(),
684
- path.join(creationDirectory, "./src/index.js")
685
- )}?`
686
- ));
781
+ const newWorkerFilename = path.relative(
782
+ process.cwd(),
783
+ path.join(creationDirectory, "./src/index.js")
784
+ );
785
+
786
+ const newWorkerType = yesFlag
787
+ ? "fetch"
788
+ : await getNewWorkerType(newWorkerFilename);
789
+
790
+ if (newWorkerType !== "none") {
791
+ const template = getNewWorkerTemplate("js", newWorkerType);
687
792
 
688
- if (shouldCreateSource) {
689
793
  await mkdir(path.join(creationDirectory, "./src"), {
690
794
  recursive: true,
691
795
  });
692
796
  await writeFile(
693
797
  path.join(creationDirectory, "./src/index.js"),
694
- readFileSync(path.join(__dirname, "../templates/new-worker.js"))
798
+ readFileSync(path.join(__dirname, `../templates/${template}`))
695
799
  );
696
800
 
697
801
  logger.log(
@@ -705,11 +809,27 @@ export async function main(argv: string[]): Promise<void> {
705
809
  shouldWritePackageJsonScripts,
706
810
  justCreatedWranglerToml,
707
811
  pathToPackageJson,
708
- "src/index.js"
812
+ "src/index.js",
813
+ getNewWorkerToml(newWorkerType)
709
814
  );
710
815
  }
711
816
  }
712
817
  }
818
+ // install packages as the final step of init
819
+ try {
820
+ await installPackages(shouldRunPackageManagerInstall, devDepsToInstall);
821
+ } catch (e) {
822
+ // fetching packages could fail due to loss of internet, etc
823
+ // we should let folks know we failed to fetch, but their
824
+ // workers project is still ready to go
825
+ logger.error(e instanceof Error ? e.message : e);
826
+ instructions.push(
827
+ "\n🚨 wrangler was unable to fetch your npm packages, but your project is ready to go"
828
+ );
829
+ }
830
+
831
+ // let users know what to do now
832
+ instructions.forEach((instruction) => logger.log(instruction));
713
833
  }
714
834
  );
715
835
 
@@ -720,13 +840,34 @@ export async function main(argv: string[]): Promise<void> {
720
840
  (yargs) => {
721
841
  return yargs.option("env", {
722
842
  describe: "Perform on a specific environment",
843
+ type: "string",
723
844
  });
724
845
  },
725
- () => {
846
+ async (buildArgs) => {
726
847
  // "[DEPRECATED] 🦀 Build your project (if applicable)",
727
- throw new DeprecationError(
728
- "`wrangler build` has been deprecated, please refer to https://developers.cloudflare.com/workers/wrangler/migration/deprecations/#build for alternatives"
848
+
849
+ const envFlag = buildArgs.env ? ` --env=${buildArgs.env}` : "";
850
+ logger.log(
851
+ formatMessage({
852
+ kind: "warning",
853
+ text: "Deprecation: `wrangler build` has been deprecated.",
854
+ notes: [
855
+ {
856
+ text: "Please refer to https://developers.cloudflare.com/workers/wrangler/migration/deprecations/#build for more information.",
857
+ },
858
+ {
859
+ text: `Attempting to run \`wrangler publish --dry-run --outdir=dist${envFlag}\` for you instead:`,
860
+ },
861
+ ],
862
+ })
729
863
  );
864
+
865
+ await createCLIParser([
866
+ "publish",
867
+ "--dry-run",
868
+ "--outdir=dist",
869
+ ...(buildArgs.env ? ["--env", buildArgs.env] : []),
870
+ ]).parse();
730
871
  }
731
872
  );
732
873
 
@@ -897,6 +1038,19 @@ export async function main(argv: string[]): Promise<void> {
897
1038
  const config = readConfig(configPath, args);
898
1039
  const entry = await getEntry(args, config, "dev");
899
1040
 
1041
+ if (config.services && config.services.length > 0) {
1042
+ logger.warn(
1043
+ `This worker is bound to live services: ${config.services
1044
+ .map(
1045
+ (service) =>
1046
+ `${service.binding} (${service.service}${
1047
+ service.environment ? `@${service.environment}` : ""
1048
+ })`
1049
+ )
1050
+ .join(", ")}`
1051
+ );
1052
+ }
1053
+
900
1054
  if (args.inspect) {
901
1055
  logger.warn(
902
1056
  "Passing --inspect is unnecessary, now you can always connect to devtools."
@@ -934,6 +1088,9 @@ export async function main(argv: string[]): Promise<void> {
934
1088
  * try to extract a host from it
935
1089
  */
936
1090
  function getHost(urlLike: string): string | undefined {
1091
+ // strip leading * / *.
1092
+ urlLike = urlLike.replace(/^\*(\.)?/g, "");
1093
+
937
1094
  if (
938
1095
  !(urlLike.startsWith("http://") || urlLike.startsWith("https://"))
939
1096
  ) {
@@ -1017,6 +1174,70 @@ export async function main(argv: string[]): Promise<void> {
1017
1174
  );
1018
1175
  }
1019
1176
 
1177
+ const bindings = {
1178
+ kv_namespaces: config.kv_namespaces?.map(
1179
+ ({ binding, preview_id, id: _id }) => {
1180
+ // In `dev`, we make folks use a separate kv namespace called
1181
+ // `preview_id` instead of `id` so that they don't
1182
+ // break production data. So here we check that a `preview_id`
1183
+ // has actually been configured.
1184
+ // This whole block of code will be obsoleted in the future
1185
+ // when we have copy-on-write for previews on edge workers.
1186
+ if (!preview_id) {
1187
+ // TODO: This error has to be a _lot_ better, ideally just asking
1188
+ // to create a preview namespace for the user automatically
1189
+ throw new Error(
1190
+ `In development, you should use a separate kv namespace than the one you'd use in production. Please create a new kv namespace with "wrangler kv:namespace create <name> --preview" and add its id as preview_id to the kv_namespace "${binding}" in your wrangler.toml`
1191
+ ); // Ugh, I really don't like this message very much
1192
+ }
1193
+ return {
1194
+ binding,
1195
+ id: preview_id,
1196
+ };
1197
+ }
1198
+ ),
1199
+ // Use a copy of combinedVars since we're modifying it later
1200
+ vars: getVarsForDev(config),
1201
+ wasm_modules: config.wasm_modules,
1202
+ text_blobs: config.text_blobs,
1203
+ data_blobs: config.data_blobs,
1204
+ durable_objects: config.durable_objects,
1205
+ r2_buckets: config.r2_buckets?.map(
1206
+ ({ binding, preview_bucket_name, bucket_name: _bucket_name }) => {
1207
+ // same idea as kv namespace preview id,
1208
+ // same copy-on-write TODO
1209
+ if (!preview_bucket_name) {
1210
+ throw new Error(
1211
+ `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`
1212
+ );
1213
+ }
1214
+ return {
1215
+ binding,
1216
+ bucket_name: preview_bucket_name,
1217
+ };
1218
+ }
1219
+ ),
1220
+ services: config.services,
1221
+ unsafe: config.unsafe?.bindings,
1222
+ };
1223
+
1224
+ // mask anything that was overridden in .dev.vars
1225
+ // so that we don't log potential secrets into the terminal
1226
+ const maskedVars = { ...bindings.vars };
1227
+ for (const key of Object.keys(maskedVars)) {
1228
+ if (maskedVars[key] !== config.vars[key]) {
1229
+ // This means it was overridden in .dev.vars
1230
+ // so let's mask it
1231
+ maskedVars[key] = "(hidden)";
1232
+ }
1233
+ }
1234
+
1235
+ // now log all available bindings into the terminal
1236
+ printBindings({
1237
+ ...bindings,
1238
+ vars: maskedVars,
1239
+ });
1240
+
1020
1241
  const { waitUntilExit } = render(
1021
1242
  <Dev
1022
1243
  name={getScriptName(args, config)}
@@ -1062,50 +1283,7 @@ export async function main(argv: string[]): Promise<void> {
1062
1283
  args["compatibility-flags"] || config.compatibility_flags
1063
1284
  }
1064
1285
  usageModel={config.usage_model}
1065
- bindings={{
1066
- kv_namespaces: config.kv_namespaces?.map(
1067
- ({ binding, preview_id, id: _id }) => {
1068
- // In `dev`, we make folks use a separate kv namespace called
1069
- // `preview_id` instead of `id` so that they don't
1070
- // break production data. So here we check that a `preview_id`
1071
- // has actually been configured.
1072
- // This whole block of code will be obsoleted in the future
1073
- // when we have copy-on-write for previews on edge workers.
1074
- if (!preview_id) {
1075
- // TODO: This error has to be a _lot_ better, ideally just asking
1076
- // to create a preview namespace for the user automatically
1077
- throw new Error(
1078
- `In development, you should use a separate kv namespace than the one you'd use in production. Please create a new kv namespace with "wrangler kv:namespace create <name> --preview" and add its id as preview_id to the kv_namespace "${binding}" in your wrangler.toml`
1079
- ); // Ugh, I really don't like this message very much
1080
- }
1081
- return {
1082
- binding,
1083
- id: preview_id,
1084
- };
1085
- }
1086
- ),
1087
- vars: getVarsForDev(config),
1088
- wasm_modules: config.wasm_modules,
1089
- text_blobs: config.text_blobs,
1090
- data_blobs: config.data_blobs,
1091
- durable_objects: config.durable_objects,
1092
- r2_buckets: config.r2_buckets?.map(
1093
- ({ binding, preview_bucket_name, bucket_name: _bucket_name }) => {
1094
- // same idea as kv namespace preview id,
1095
- // same copy-on-write TODO
1096
- if (!preview_bucket_name) {
1097
- throw new Error(
1098
- `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`
1099
- );
1100
- }
1101
- return {
1102
- binding,
1103
- bucket_name: preview_bucket_name,
1104
- };
1105
- }
1106
- ),
1107
- unsafe: config.unsafe?.bindings,
1108
- }}
1286
+ bindings={bindings}
1109
1287
  crons={config.triggers.crons}
1110
1288
  />
1111
1289
  );
@@ -1258,7 +1436,7 @@ export async function main(argv: string[]): Promise<void> {
1258
1436
  );
1259
1437
  }
1260
1438
 
1261
- const accountId = await requireAuth(config);
1439
+ const accountId = args.dryRun ? undefined : await requireAuth(config);
1262
1440
 
1263
1441
  const assetPaths = getAssetPaths(
1264
1442
  config,
@@ -1266,6 +1444,7 @@ export async function main(argv: string[]): Promise<void> {
1266
1444
  args.siteInclude,
1267
1445
  args.siteExclude
1268
1446
  );
1447
+
1269
1448
  await publish({
1270
1449
  config,
1271
1450
  accountId,
@@ -1556,6 +1735,7 @@ export async function main(argv: string[]): Promise<void> {
1556
1735
  };
1557
1736
  }
1558
1737
  ),
1738
+ services: config.services,
1559
1739
  unsafe: config.unsafe?.bindings,
1560
1740
  }}
1561
1741
  crons={config.triggers.crons}
@@ -1706,9 +1886,11 @@ export async function main(argv: string[]): Promise<void> {
1706
1886
  const accountId = await requireAuth(config);
1707
1887
 
1708
1888
  const isInteractive = process.stdin.isTTY;
1709
- const secretValue = isInteractive
1710
- ? await prompt("Enter a secret value:", "password")
1711
- : await readFromStdin();
1889
+ const secretValue = trimTrailingWhitespace(
1890
+ isInteractive
1891
+ ? await prompt("Enter a secret value:", "password")
1892
+ : await readFromStdin()
1893
+ );
1712
1894
 
1713
1895
  logger.log(
1714
1896
  `🌀 Creating the secret for script ${scriptName} ${
@@ -1753,6 +1935,7 @@ export async function main(argv: string[]): Promise<void> {
1753
1935
  vars: {},
1754
1936
  durable_objects: { bindings: [] },
1755
1937
  r2_buckets: [],
1938
+ services: [],
1756
1939
  wasm_modules: {},
1757
1940
  text_blobs: {},
1758
1941
  data_blobs: {},
@@ -2334,13 +2517,7 @@ export async function main(argv: string[]): Promise<void> {
2334
2517
  const warnings: string[] = [];
2335
2518
  for (let i = 0; i < content.length; i++) {
2336
2519
  const keyValue = content[i];
2337
- if (typeof keyValue !== "object") {
2338
- errors.push(
2339
- `The item at index ${i} is type: "${typeof keyValue}" - ${JSON.stringify(
2340
- keyValue
2341
- )}`
2342
- );
2343
- } else if (!isKVKeyValue(keyValue)) {
2520
+ if (!isKVKeyValue(keyValue)) {
2344
2521
  errors.push(
2345
2522
  `The item at index ${i} is ${JSON.stringify(keyValue)}`
2346
2523
  );
@@ -2378,14 +2555,7 @@ export async function main(argv: string[]): Promise<void> {
2378
2555
  }
2379
2556
 
2380
2557
  const accountId = await requireAuth(config);
2381
- await putKVBulkKeyValue(
2382
- accountId,
2383
- namespaceId,
2384
- content,
2385
- (index, total) => {
2386
- logger.log(`Uploaded ${index} of ${total}.`);
2387
- }
2388
- );
2558
+ await putKVBulkKeyValue(accountId, namespaceId, content);
2389
2559
 
2390
2560
  logger.log("Success!");
2391
2561
  }
@@ -2475,14 +2645,7 @@ export async function main(argv: string[]): Promise<void> {
2475
2645
 
2476
2646
  const accountId = await requireAuth(config);
2477
2647
 
2478
- await deleteKVBulkKeyValue(
2479
- accountId,
2480
- namespaceId,
2481
- content,
2482
- (index, total) => {
2483
- logger.log(`Deleted ${index} of ${total}.`);
2484
- }
2485
- );
2648
+ await deleteKVBulkKeyValue(accountId, namespaceId, content);
2486
2649
 
2487
2650
  logger.log("Success!");
2488
2651
  }
@@ -2647,24 +2810,31 @@ export async function main(argv: string[]): Promise<void> {
2647
2810
  wrangler.version(wranglerVersion).alias("v", "version");
2648
2811
  wrangler.exitProcess(false);
2649
2812
 
2813
+ return wrangler;
2814
+ }
2815
+
2816
+ export async function main(argv: string[]): Promise<void> {
2817
+ const wrangler = createCLIParser(argv);
2650
2818
  try {
2651
2819
  await wrangler.parse();
2652
2820
  } catch (e) {
2653
2821
  logger.log(""); // Just adds a bit of space
2654
2822
  if (e instanceof CommandLineArgsError) {
2655
- wrangler.showHelp("error");
2656
- logger.log(""); // Add a bit of space.
2657
2823
  logger.error(e.message);
2824
+ // We are not able to ask the `wrangler` CLI parser to show help for a subcommand programmatically.
2825
+ // The workaround is to re-run the parsing with an additional `--help` flag, which will result in the correct help message being displayed.
2826
+ // The `wrangler` object is "frozen"; we cannot reuse that with different args, so we must create a new CLI parser to generate the help message.
2827
+ await createCLIParser([...argv, "--help"]).parse();
2658
2828
  } else if (e instanceof ParseError) {
2659
2829
  e.notes.push({
2660
- text: "\nIf you think this is a bug, please open an issue at: https://github.com/cloudflare/wrangler2/issues/new",
2830
+ text: "\nIf you think this is a bug, please open an issue at: https://github.com/cloudflare/wrangler2/issues/new/choose",
2661
2831
  });
2662
- logger.error(formatMessage(e));
2832
+ logger.log(formatMessage(e));
2663
2833
  } else {
2664
2834
  logger.error(e instanceof Error ? e.message : e);
2665
2835
  logger.log(
2666
2836
  `${fgGreenColor}%s${resetColor}`,
2667
- "If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
2837
+ "If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
2668
2838
  );
2669
2839
  }
2670
2840
  throw e;