@valbuild/cli 0.85.1 → 0.86.1

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.
@@ -486,19 +486,18 @@ function findSimilar(key, targets) {
486
486
  })).sort((a, b) => a.distance - b.distance);
487
487
  }
488
488
 
489
- async function files({
490
- root,
491
- managedDir
489
+ async function listUnusedFiles({
490
+ root
492
491
  }) {
493
- const printFilesUsedByVal = !managedDir;
492
+ const managedDir = "public/val";
494
493
  const projectRoot = root ? path__default["default"].resolve(root) : process.cwd();
495
494
  const service = await server.createService(projectRoot, {});
496
495
  const valFiles = await fastGlob.glob("**/*.val.{js,ts}", {
497
496
  ignore: ["node_modules/**"],
498
497
  cwd: projectRoot
499
498
  });
500
- const absoluteFilesPathUsedByVal = [];
501
- async function printOrGetFileRefs(file) {
499
+ const filesUsedByVal = [];
500
+ async function pushFilesUsedByVal(file) {
502
501
  const moduleId = `/${file}`; // TODO: check if this always works? (Windows?)
503
502
  const valModule = await service.get(moduleId, "", {
504
503
  validate: true,
@@ -515,11 +514,7 @@ async function files({
515
514
  const value = error.value;
516
515
  if (isFileRef(value)) {
517
516
  const absoluteFilePathUsedByVal = path__default["default"].join(projectRoot, ...value[core.FILE_REF_PROP].split("/"));
518
- if (printFilesUsedByVal) {
519
- console.log(absoluteFilePathUsedByVal);
520
- } else {
521
- absoluteFilesPathUsedByVal.push(absoluteFilePathUsedByVal);
522
- }
517
+ filesUsedByVal.push(absoluteFilePathUsedByVal);
523
518
  }
524
519
  }
525
520
  }
@@ -527,19 +522,17 @@ async function files({
527
522
  }
528
523
  }
529
524
  for (const file of valFiles) {
530
- await printOrGetFileRefs(file);
525
+ await pushFilesUsedByVal(file);
531
526
  }
532
- if (managedDir) {
533
- const managedRoot = path__default["default"].isAbsolute(managedDir) ? managedDir : path__default["default"].join(projectRoot, managedDir);
534
- const allFilesInManagedDir = await fastGlob.glob("**/*", {
535
- ignore: ["node_modules/**"],
536
- cwd: managedRoot
537
- });
538
- for (const file of allFilesInManagedDir) {
539
- const absoluteFilePath = path__default["default"].join(managedRoot, file);
540
- if (!absoluteFilesPathUsedByVal.includes(absoluteFilePath)) {
541
- console.log(path__default["default"].join(managedRoot, file));
542
- }
527
+ const managedRoot = path__default["default"].join(projectRoot, managedDir);
528
+ const allFilesInManagedDir = await fastGlob.glob("**/*", {
529
+ ignore: ["node_modules/**"],
530
+ cwd: managedRoot
531
+ });
532
+ for (const file of allFilesInManagedDir) {
533
+ const absoluteFilePath = path__default["default"].join(managedRoot, file);
534
+ if (!filesUsedByVal.includes(absoluteFilePath)) {
535
+ console.log(path__default["default"].join(managedRoot, file));
543
536
  }
544
537
  }
545
538
  service.dispose();
@@ -581,7 +574,94 @@ const getVersions = () => {
581
574
  };
582
575
  };
583
576
 
584
- const host = process.env.VAL_BUILD_URL || "https://app.val.build";
577
+ const host$1 = process.env.VAL_BUILD_URL || "https://admin.val.build";
578
+ async function connect(options) {
579
+ const {
580
+ root
581
+ } = options;
582
+ const projectRoot = root ? path__default["default"].resolve(root) : process.cwd();
583
+ const maybeProject = await tryGetProject(projectRoot);
584
+ const maybeGitRemote = await tryGetGitRemote(projectRoot);
585
+ const maybeGitOwnerAndRepo = maybeGitRemote !== null ? getGitHubOwnerAndRepo(maybeGitRemote) : null;
586
+ const params = new URLSearchParams();
587
+ params.set("step", "create-project");
588
+ if (maybeProject) {
589
+ params.set("org", maybeProject.orgName);
590
+ params.set("project", maybeProject.projectName);
591
+ }
592
+ if (maybeGitOwnerAndRepo) {
593
+ params.set("github_repo", [maybeGitOwnerAndRepo.owner, maybeGitOwnerAndRepo.repo].join("/"));
594
+ }
595
+ const url = `${host$1}/connect?${params.toString()}`;
596
+ console.log(picocolors__default["default"].dim(`\nFollow the instructions in your browser to complete the setup:\n${picocolors__default["default"].cyan(url)}\n`));
597
+ }
598
+ async function tryGetProject(projectRoot) {
599
+ const valConfigFile = (await evalValConfigFile(projectRoot, "val.config.ts")) || (await evalValConfigFile(projectRoot, "val.config.js"));
600
+ if (valConfigFile && valConfigFile.project) {
601
+ const parts = valConfigFile.project.split("/");
602
+ if (parts.length === 2) {
603
+ return {
604
+ orgName: parts[0],
605
+ projectName: parts[1]
606
+ };
607
+ } else {
608
+ console.error(picocolors__default["default"].red(`Invalid project format in val.config file: "${valConfigFile.project}". Expected format "orgName/projectName".`));
609
+ process.exit(1);
610
+ }
611
+ }
612
+ return null;
613
+ }
614
+ function getGitHubOwnerAndRepo(gitRemote) {
615
+ const sshMatch = gitRemote.match(/git@github\.com:(.+?)\/(.+?)(\.git)?$/);
616
+ if (sshMatch) {
617
+ return {
618
+ owner: sshMatch[1],
619
+ repo: sshMatch[2]
620
+ };
621
+ }
622
+ const httpsMatch = gitRemote.match(/https:\/\/github\.com\/(.+?)\/(.+?)(\.git)?$/);
623
+ if (httpsMatch) {
624
+ return {
625
+ owner: httpsMatch[1],
626
+ repo: httpsMatch[2]
627
+ };
628
+ }
629
+ return null;
630
+ }
631
+ async function tryGetGitRemote(root) {
632
+ try {
633
+ const gitConfig = tryGetGitConfig(root);
634
+ if (!gitConfig) {
635
+ return null;
636
+ }
637
+ const remoteMatch = gitConfig.match(/\[remote "origin"\][\s\S]*?url = (.+)/);
638
+ if (remoteMatch && remoteMatch[1]) {
639
+ return remoteMatch[1].trim();
640
+ }
641
+ return null;
642
+ } catch (error) {
643
+ console.error(picocolors__default["default"].red("Failed to read .git/config file."), error);
644
+ return null;
645
+ }
646
+ }
647
+ function tryGetGitConfig(root) {
648
+ let currentDir = root;
649
+ let lastDir = null;
650
+ while (currentDir !== lastDir) {
651
+ const gitConfigPath = path__default["default"].join(currentDir, ".git", "config");
652
+ if (fs__default$1["default"].existsSync(gitConfigPath)) {
653
+ return fs__default$1["default"].readFileSync(gitConfigPath, "utf-8");
654
+ }
655
+ lastDir = currentDir;
656
+ currentDir = path__default["default"].dirname(currentDir);
657
+ if (lastDir === currentDir) {
658
+ break;
659
+ }
660
+ }
661
+ return null;
662
+ }
663
+
664
+ const host = process.env.VAL_BUILD_URL || "https://admin.val.build";
585
665
  async function login(options) {
586
666
  try {
587
667
  var _response$headers$get;
@@ -598,7 +678,7 @@ async function login(options) {
598
678
  let url;
599
679
  if (!((_response$headers$get = response.headers.get("content-type")) !== null && _response$headers$get !== void 0 && _response$headers$get.includes("application/json"))) {
600
680
  const text = await response.text();
601
- console.error(picocolors__default["default"].red("Unexpected failure while trying to login (content type was not JSON). Server response:"), text || "<empty>");
681
+ console.error(picocolors__default["default"].red("Unexpected failure while trying to login (content type was not JSON). "), text ? `Server response: ${text} (status: ${response.status})` : `Status: ${response.status}`);
602
682
  process.exit(1);
603
683
  }
604
684
  const json = await response.json();
@@ -630,7 +710,9 @@ async function pollForConfirmation(token) {
630
710
  const start = Date.now();
631
711
  while (Date.now() - start < MAX_DURATION) {
632
712
  await new Promise(resolve => setTimeout(resolve, 1000));
633
- const response = await fetch(`${host}/api/login?token=${token}&consume=true`);
713
+ const response = await fetch(`${host}/api/login?token=${token}&consume=true`, {
714
+ method: "POST"
715
+ });
634
716
  if (response.status === 500) {
635
717
  console.error(picocolors__default["default"].red("An error occurred on the server."));
636
718
  process.exit(1);
@@ -638,7 +720,7 @@ async function pollForConfirmation(token) {
638
720
  if (response.status === 200) {
639
721
  const json = await response.json();
640
722
  if (json) {
641
- if (typeof json.profile.username === "string" && typeof json.pat === "string") {
723
+ if (typeof json.profile.email === "string" && typeof json.pat === "string") {
642
724
  return json;
643
725
  } else {
644
726
  console.error(picocolors__default["default"].red("Unexpected response from the server."));
@@ -655,7 +737,7 @@ function saveToken(result, filePath) {
655
737
  recursive: true
656
738
  });
657
739
  fs__default$1["default"].writeFileSync(filePath, JSON.stringify(result, null, 2));
658
- console.log(picocolors__default["default"].green(`Token for ${picocolors__default["default"].cyan(result.profile.username)} saved to ${picocolors__default["default"].cyan(filePath)}`));
740
+ console.log(picocolors__default["default"].green(`Token for ${picocolors__default["default"].cyan(result.profile.email)} saved to ${picocolors__default["default"].cyan(filePath)}`));
659
741
  }
660
742
 
661
743
  async function main() {
@@ -673,7 +755,8 @@ async function main() {
673
755
  Commands:
674
756
  validate
675
757
  login
676
- list-files
758
+ files
759
+ connect
677
760
  versions
678
761
 
679
762
  Command: validate
@@ -684,21 +767,21 @@ async function main() {
684
767
 
685
768
 
686
769
  Command: login
687
- Description: login to app.val.build and generate a Personal Access Token
770
+ Description: login to admin.val.build and generate a Personal Access Token
688
771
  Options:
689
772
  --root [root], -r [root] Set project root directory (default process.cwd())
690
773
 
691
774
 
692
- Command: files
693
- Description: EXPERIMENTAL.
694
- Perform file operations in Val.
695
- By default it lists files (images, ...) currently in use by Val.
775
+ Command: connect
776
+ Description: connect your local project to a Val Build project at admin.val.build
777
+ Options:
778
+ --root [root], -r [root] Set project root directory (default process.cwd())
696
779
 
697
- If a managed directory is specified,
698
- it will list all files in the managed directory that ARE NOT currently used by Val.
780
+ Command: list-unused-files
781
+ Description: EXPERIMENTAL.
782
+ List files that are in public/val but not in use by any Val module.
699
783
  This is useful for cleaning up unused files.
700
784
  Options:
701
- --managedDir [dir] If set, list files found in directory that are not managed by Val
702
785
  --root [root], -r [root] Set project root directory (default process.cwd())
703
786
  `, {
704
787
  flags: {
@@ -731,13 +814,12 @@ async function main() {
731
814
  }
732
815
  const [command] = input;
733
816
  switch (command) {
734
- case "files":
817
+ case "list-unused-files":
735
818
  if (flags.fix || flags.noEslint) {
736
- return error(`Command "files" does not support --fix or --noEslint flags`);
819
+ return error(`Command "list-unused-files" does not support --fix or --noEslint flags`);
737
820
  }
738
- return files({
739
- root: flags.root,
740
- managedDir: flags.managedDir
821
+ return listUnusedFiles({
822
+ root: flags.root
741
823
  });
742
824
  case "versions":
743
825
  return versions();
@@ -745,6 +827,13 @@ async function main() {
745
827
  return login({
746
828
  root: flags.root
747
829
  });
830
+ case "connect":
831
+ if (flags.fix || flags.noEslint) {
832
+ return error(`Command "connect" does not support --fix or --noEslint flags`);
833
+ }
834
+ return connect({
835
+ root: flags.root
836
+ });
748
837
  case "validate":
749
838
  case "idate":
750
839
  if (flags.managedDir) {
@@ -486,19 +486,18 @@ function findSimilar(key, targets) {
486
486
  })).sort((a, b) => a.distance - b.distance);
487
487
  }
488
488
 
489
- async function files({
490
- root,
491
- managedDir
489
+ async function listUnusedFiles({
490
+ root
492
491
  }) {
493
- const printFilesUsedByVal = !managedDir;
492
+ const managedDir = "public/val";
494
493
  const projectRoot = root ? path__default["default"].resolve(root) : process.cwd();
495
494
  const service = await server.createService(projectRoot, {});
496
495
  const valFiles = await fastGlob.glob("**/*.val.{js,ts}", {
497
496
  ignore: ["node_modules/**"],
498
497
  cwd: projectRoot
499
498
  });
500
- const absoluteFilesPathUsedByVal = [];
501
- async function printOrGetFileRefs(file) {
499
+ const filesUsedByVal = [];
500
+ async function pushFilesUsedByVal(file) {
502
501
  const moduleId = `/${file}`; // TODO: check if this always works? (Windows?)
503
502
  const valModule = await service.get(moduleId, "", {
504
503
  validate: true,
@@ -515,11 +514,7 @@ async function files({
515
514
  const value = error.value;
516
515
  if (isFileRef(value)) {
517
516
  const absoluteFilePathUsedByVal = path__default["default"].join(projectRoot, ...value[core.FILE_REF_PROP].split("/"));
518
- if (printFilesUsedByVal) {
519
- console.log(absoluteFilePathUsedByVal);
520
- } else {
521
- absoluteFilesPathUsedByVal.push(absoluteFilePathUsedByVal);
522
- }
517
+ filesUsedByVal.push(absoluteFilePathUsedByVal);
523
518
  }
524
519
  }
525
520
  }
@@ -527,19 +522,17 @@ async function files({
527
522
  }
528
523
  }
529
524
  for (const file of valFiles) {
530
- await printOrGetFileRefs(file);
525
+ await pushFilesUsedByVal(file);
531
526
  }
532
- if (managedDir) {
533
- const managedRoot = path__default["default"].isAbsolute(managedDir) ? managedDir : path__default["default"].join(projectRoot, managedDir);
534
- const allFilesInManagedDir = await fastGlob.glob("**/*", {
535
- ignore: ["node_modules/**"],
536
- cwd: managedRoot
537
- });
538
- for (const file of allFilesInManagedDir) {
539
- const absoluteFilePath = path__default["default"].join(managedRoot, file);
540
- if (!absoluteFilesPathUsedByVal.includes(absoluteFilePath)) {
541
- console.log(path__default["default"].join(managedRoot, file));
542
- }
527
+ const managedRoot = path__default["default"].join(projectRoot, managedDir);
528
+ const allFilesInManagedDir = await fastGlob.glob("**/*", {
529
+ ignore: ["node_modules/**"],
530
+ cwd: managedRoot
531
+ });
532
+ for (const file of allFilesInManagedDir) {
533
+ const absoluteFilePath = path__default["default"].join(managedRoot, file);
534
+ if (!filesUsedByVal.includes(absoluteFilePath)) {
535
+ console.log(path__default["default"].join(managedRoot, file));
543
536
  }
544
537
  }
545
538
  service.dispose();
@@ -581,7 +574,94 @@ const getVersions = () => {
581
574
  };
582
575
  };
583
576
 
584
- const host = process.env.VAL_BUILD_URL || "https://app.val.build";
577
+ const host$1 = process.env.VAL_BUILD_URL || "https://admin.val.build";
578
+ async function connect(options) {
579
+ const {
580
+ root
581
+ } = options;
582
+ const projectRoot = root ? path__default["default"].resolve(root) : process.cwd();
583
+ const maybeProject = await tryGetProject(projectRoot);
584
+ const maybeGitRemote = await tryGetGitRemote(projectRoot);
585
+ const maybeGitOwnerAndRepo = maybeGitRemote !== null ? getGitHubOwnerAndRepo(maybeGitRemote) : null;
586
+ const params = new URLSearchParams();
587
+ params.set("step", "create-project");
588
+ if (maybeProject) {
589
+ params.set("org", maybeProject.orgName);
590
+ params.set("project", maybeProject.projectName);
591
+ }
592
+ if (maybeGitOwnerAndRepo) {
593
+ params.set("github_repo", [maybeGitOwnerAndRepo.owner, maybeGitOwnerAndRepo.repo].join("/"));
594
+ }
595
+ const url = `${host$1}/connect?${params.toString()}`;
596
+ console.log(picocolors__default["default"].dim(`\nFollow the instructions in your browser to complete the setup:\n${picocolors__default["default"].cyan(url)}\n`));
597
+ }
598
+ async function tryGetProject(projectRoot) {
599
+ const valConfigFile = (await evalValConfigFile(projectRoot, "val.config.ts")) || (await evalValConfigFile(projectRoot, "val.config.js"));
600
+ if (valConfigFile && valConfigFile.project) {
601
+ const parts = valConfigFile.project.split("/");
602
+ if (parts.length === 2) {
603
+ return {
604
+ orgName: parts[0],
605
+ projectName: parts[1]
606
+ };
607
+ } else {
608
+ console.error(picocolors__default["default"].red(`Invalid project format in val.config file: "${valConfigFile.project}". Expected format "orgName/projectName".`));
609
+ process.exit(1);
610
+ }
611
+ }
612
+ return null;
613
+ }
614
+ function getGitHubOwnerAndRepo(gitRemote) {
615
+ const sshMatch = gitRemote.match(/git@github\.com:(.+?)\/(.+?)(\.git)?$/);
616
+ if (sshMatch) {
617
+ return {
618
+ owner: sshMatch[1],
619
+ repo: sshMatch[2]
620
+ };
621
+ }
622
+ const httpsMatch = gitRemote.match(/https:\/\/github\.com\/(.+?)\/(.+?)(\.git)?$/);
623
+ if (httpsMatch) {
624
+ return {
625
+ owner: httpsMatch[1],
626
+ repo: httpsMatch[2]
627
+ };
628
+ }
629
+ return null;
630
+ }
631
+ async function tryGetGitRemote(root) {
632
+ try {
633
+ const gitConfig = tryGetGitConfig(root);
634
+ if (!gitConfig) {
635
+ return null;
636
+ }
637
+ const remoteMatch = gitConfig.match(/\[remote "origin"\][\s\S]*?url = (.+)/);
638
+ if (remoteMatch && remoteMatch[1]) {
639
+ return remoteMatch[1].trim();
640
+ }
641
+ return null;
642
+ } catch (error) {
643
+ console.error(picocolors__default["default"].red("Failed to read .git/config file."), error);
644
+ return null;
645
+ }
646
+ }
647
+ function tryGetGitConfig(root) {
648
+ let currentDir = root;
649
+ let lastDir = null;
650
+ while (currentDir !== lastDir) {
651
+ const gitConfigPath = path__default["default"].join(currentDir, ".git", "config");
652
+ if (fs__default$1["default"].existsSync(gitConfigPath)) {
653
+ return fs__default$1["default"].readFileSync(gitConfigPath, "utf-8");
654
+ }
655
+ lastDir = currentDir;
656
+ currentDir = path__default["default"].dirname(currentDir);
657
+ if (lastDir === currentDir) {
658
+ break;
659
+ }
660
+ }
661
+ return null;
662
+ }
663
+
664
+ const host = process.env.VAL_BUILD_URL || "https://admin.val.build";
585
665
  async function login(options) {
586
666
  try {
587
667
  var _response$headers$get;
@@ -598,7 +678,7 @@ async function login(options) {
598
678
  let url;
599
679
  if (!((_response$headers$get = response.headers.get("content-type")) !== null && _response$headers$get !== void 0 && _response$headers$get.includes("application/json"))) {
600
680
  const text = await response.text();
601
- console.error(picocolors__default["default"].red("Unexpected failure while trying to login (content type was not JSON). Server response:"), text || "<empty>");
681
+ console.error(picocolors__default["default"].red("Unexpected failure while trying to login (content type was not JSON). "), text ? `Server response: ${text} (status: ${response.status})` : `Status: ${response.status}`);
602
682
  process.exit(1);
603
683
  }
604
684
  const json = await response.json();
@@ -630,7 +710,9 @@ async function pollForConfirmation(token) {
630
710
  const start = Date.now();
631
711
  while (Date.now() - start < MAX_DURATION) {
632
712
  await new Promise(resolve => setTimeout(resolve, 1000));
633
- const response = await fetch(`${host}/api/login?token=${token}&consume=true`);
713
+ const response = await fetch(`${host}/api/login?token=${token}&consume=true`, {
714
+ method: "POST"
715
+ });
634
716
  if (response.status === 500) {
635
717
  console.error(picocolors__default["default"].red("An error occurred on the server."));
636
718
  process.exit(1);
@@ -638,7 +720,7 @@ async function pollForConfirmation(token) {
638
720
  if (response.status === 200) {
639
721
  const json = await response.json();
640
722
  if (json) {
641
- if (typeof json.profile.username === "string" && typeof json.pat === "string") {
723
+ if (typeof json.profile.email === "string" && typeof json.pat === "string") {
642
724
  return json;
643
725
  } else {
644
726
  console.error(picocolors__default["default"].red("Unexpected response from the server."));
@@ -655,7 +737,7 @@ function saveToken(result, filePath) {
655
737
  recursive: true
656
738
  });
657
739
  fs__default$1["default"].writeFileSync(filePath, JSON.stringify(result, null, 2));
658
- console.log(picocolors__default["default"].green(`Token for ${picocolors__default["default"].cyan(result.profile.username)} saved to ${picocolors__default["default"].cyan(filePath)}`));
740
+ console.log(picocolors__default["default"].green(`Token for ${picocolors__default["default"].cyan(result.profile.email)} saved to ${picocolors__default["default"].cyan(filePath)}`));
659
741
  }
660
742
 
661
743
  async function main() {
@@ -673,7 +755,8 @@ async function main() {
673
755
  Commands:
674
756
  validate
675
757
  login
676
- list-files
758
+ files
759
+ connect
677
760
  versions
678
761
 
679
762
  Command: validate
@@ -684,21 +767,21 @@ async function main() {
684
767
 
685
768
 
686
769
  Command: login
687
- Description: login to app.val.build and generate a Personal Access Token
770
+ Description: login to admin.val.build and generate a Personal Access Token
688
771
  Options:
689
772
  --root [root], -r [root] Set project root directory (default process.cwd())
690
773
 
691
774
 
692
- Command: files
693
- Description: EXPERIMENTAL.
694
- Perform file operations in Val.
695
- By default it lists files (images, ...) currently in use by Val.
775
+ Command: connect
776
+ Description: connect your local project to a Val Build project at admin.val.build
777
+ Options:
778
+ --root [root], -r [root] Set project root directory (default process.cwd())
696
779
 
697
- If a managed directory is specified,
698
- it will list all files in the managed directory that ARE NOT currently used by Val.
780
+ Command: list-unused-files
781
+ Description: EXPERIMENTAL.
782
+ List files that are in public/val but not in use by any Val module.
699
783
  This is useful for cleaning up unused files.
700
784
  Options:
701
- --managedDir [dir] If set, list files found in directory that are not managed by Val
702
785
  --root [root], -r [root] Set project root directory (default process.cwd())
703
786
  `, {
704
787
  flags: {
@@ -731,13 +814,12 @@ async function main() {
731
814
  }
732
815
  const [command] = input;
733
816
  switch (command) {
734
- case "files":
817
+ case "list-unused-files":
735
818
  if (flags.fix || flags.noEslint) {
736
- return error(`Command "files" does not support --fix or --noEslint flags`);
819
+ return error(`Command "list-unused-files" does not support --fix or --noEslint flags`);
737
820
  }
738
- return files({
739
- root: flags.root,
740
- managedDir: flags.managedDir
821
+ return listUnusedFiles({
822
+ root: flags.root
741
823
  });
742
824
  case "versions":
743
825
  return versions();
@@ -745,6 +827,13 @@ async function main() {
745
827
  return login({
746
828
  root: flags.root
747
829
  });
830
+ case "connect":
831
+ if (flags.fix || flags.noEslint) {
832
+ return error(`Command "connect" does not support --fix or --noEslint flags`);
833
+ }
834
+ return connect({
835
+ root: flags.root
836
+ });
748
837
  case "validate":
749
838
  case "idate":
750
839
  if (flags.managedDir) {
@@ -454,19 +454,18 @@ function findSimilar(key, targets) {
454
454
  })).sort((a, b) => a.distance - b.distance);
455
455
  }
456
456
 
457
- async function files({
458
- root,
459
- managedDir
457
+ async function listUnusedFiles({
458
+ root
460
459
  }) {
461
- const printFilesUsedByVal = !managedDir;
460
+ const managedDir = "public/val";
462
461
  const projectRoot = root ? path.resolve(root) : process.cwd();
463
462
  const service = await createService(projectRoot, {});
464
463
  const valFiles = await glob("**/*.val.{js,ts}", {
465
464
  ignore: ["node_modules/**"],
466
465
  cwd: projectRoot
467
466
  });
468
- const absoluteFilesPathUsedByVal = [];
469
- async function printOrGetFileRefs(file) {
467
+ const filesUsedByVal = [];
468
+ async function pushFilesUsedByVal(file) {
470
469
  const moduleId = `/${file}`; // TODO: check if this always works? (Windows?)
471
470
  const valModule = await service.get(moduleId, "", {
472
471
  validate: true,
@@ -483,11 +482,7 @@ async function files({
483
482
  const value = error.value;
484
483
  if (isFileRef(value)) {
485
484
  const absoluteFilePathUsedByVal = path.join(projectRoot, ...value[FILE_REF_PROP].split("/"));
486
- if (printFilesUsedByVal) {
487
- console.log(absoluteFilePathUsedByVal);
488
- } else {
489
- absoluteFilesPathUsedByVal.push(absoluteFilePathUsedByVal);
490
- }
485
+ filesUsedByVal.push(absoluteFilePathUsedByVal);
491
486
  }
492
487
  }
493
488
  }
@@ -495,19 +490,17 @@ async function files({
495
490
  }
496
491
  }
497
492
  for (const file of valFiles) {
498
- await printOrGetFileRefs(file);
493
+ await pushFilesUsedByVal(file);
499
494
  }
500
- if (managedDir) {
501
- const managedRoot = path.isAbsolute(managedDir) ? managedDir : path.join(projectRoot, managedDir);
502
- const allFilesInManagedDir = await glob("**/*", {
503
- ignore: ["node_modules/**"],
504
- cwd: managedRoot
505
- });
506
- for (const file of allFilesInManagedDir) {
507
- const absoluteFilePath = path.join(managedRoot, file);
508
- if (!absoluteFilesPathUsedByVal.includes(absoluteFilePath)) {
509
- console.log(path.join(managedRoot, file));
510
- }
495
+ const managedRoot = path.join(projectRoot, managedDir);
496
+ const allFilesInManagedDir = await glob("**/*", {
497
+ ignore: ["node_modules/**"],
498
+ cwd: managedRoot
499
+ });
500
+ for (const file of allFilesInManagedDir) {
501
+ const absoluteFilePath = path.join(managedRoot, file);
502
+ if (!filesUsedByVal.includes(absoluteFilePath)) {
503
+ console.log(path.join(managedRoot, file));
511
504
  }
512
505
  }
513
506
  service.dispose();
@@ -549,7 +542,94 @@ const getVersions = () => {
549
542
  };
550
543
  };
551
544
 
552
- const host = process.env.VAL_BUILD_URL || "https://app.val.build";
545
+ const host$1 = process.env.VAL_BUILD_URL || "https://admin.val.build";
546
+ async function connect(options) {
547
+ const {
548
+ root
549
+ } = options;
550
+ const projectRoot = root ? path.resolve(root) : process.cwd();
551
+ const maybeProject = await tryGetProject(projectRoot);
552
+ const maybeGitRemote = await tryGetGitRemote(projectRoot);
553
+ const maybeGitOwnerAndRepo = maybeGitRemote !== null ? getGitHubOwnerAndRepo(maybeGitRemote) : null;
554
+ const params = new URLSearchParams();
555
+ params.set("step", "create-project");
556
+ if (maybeProject) {
557
+ params.set("org", maybeProject.orgName);
558
+ params.set("project", maybeProject.projectName);
559
+ }
560
+ if (maybeGitOwnerAndRepo) {
561
+ params.set("github_repo", [maybeGitOwnerAndRepo.owner, maybeGitOwnerAndRepo.repo].join("/"));
562
+ }
563
+ const url = `${host$1}/connect?${params.toString()}`;
564
+ console.log(picocolors.dim(`\nFollow the instructions in your browser to complete the setup:\n${picocolors.cyan(url)}\n`));
565
+ }
566
+ async function tryGetProject(projectRoot) {
567
+ const valConfigFile = (await evalValConfigFile(projectRoot, "val.config.ts")) || (await evalValConfigFile(projectRoot, "val.config.js"));
568
+ if (valConfigFile && valConfigFile.project) {
569
+ const parts = valConfigFile.project.split("/");
570
+ if (parts.length === 2) {
571
+ return {
572
+ orgName: parts[0],
573
+ projectName: parts[1]
574
+ };
575
+ } else {
576
+ console.error(picocolors.red(`Invalid project format in val.config file: "${valConfigFile.project}". Expected format "orgName/projectName".`));
577
+ process.exit(1);
578
+ }
579
+ }
580
+ return null;
581
+ }
582
+ function getGitHubOwnerAndRepo(gitRemote) {
583
+ const sshMatch = gitRemote.match(/git@github\.com:(.+?)\/(.+?)(\.git)?$/);
584
+ if (sshMatch) {
585
+ return {
586
+ owner: sshMatch[1],
587
+ repo: sshMatch[2]
588
+ };
589
+ }
590
+ const httpsMatch = gitRemote.match(/https:\/\/github\.com\/(.+?)\/(.+?)(\.git)?$/);
591
+ if (httpsMatch) {
592
+ return {
593
+ owner: httpsMatch[1],
594
+ repo: httpsMatch[2]
595
+ };
596
+ }
597
+ return null;
598
+ }
599
+ async function tryGetGitRemote(root) {
600
+ try {
601
+ const gitConfig = tryGetGitConfig(root);
602
+ if (!gitConfig) {
603
+ return null;
604
+ }
605
+ const remoteMatch = gitConfig.match(/\[remote "origin"\][\s\S]*?url = (.+)/);
606
+ if (remoteMatch && remoteMatch[1]) {
607
+ return remoteMatch[1].trim();
608
+ }
609
+ return null;
610
+ } catch (error) {
611
+ console.error(picocolors.red("Failed to read .git/config file."), error);
612
+ return null;
613
+ }
614
+ }
615
+ function tryGetGitConfig(root) {
616
+ let currentDir = root;
617
+ let lastDir = null;
618
+ while (currentDir !== lastDir) {
619
+ const gitConfigPath = path.join(currentDir, ".git", "config");
620
+ if (fs$1.existsSync(gitConfigPath)) {
621
+ return fs$1.readFileSync(gitConfigPath, "utf-8");
622
+ }
623
+ lastDir = currentDir;
624
+ currentDir = path.dirname(currentDir);
625
+ if (lastDir === currentDir) {
626
+ break;
627
+ }
628
+ }
629
+ return null;
630
+ }
631
+
632
+ const host = process.env.VAL_BUILD_URL || "https://admin.val.build";
553
633
  async function login(options) {
554
634
  try {
555
635
  var _response$headers$get;
@@ -566,7 +646,7 @@ async function login(options) {
566
646
  let url;
567
647
  if (!((_response$headers$get = response.headers.get("content-type")) !== null && _response$headers$get !== void 0 && _response$headers$get.includes("application/json"))) {
568
648
  const text = await response.text();
569
- console.error(picocolors.red("Unexpected failure while trying to login (content type was not JSON). Server response:"), text || "<empty>");
649
+ console.error(picocolors.red("Unexpected failure while trying to login (content type was not JSON). "), text ? `Server response: ${text} (status: ${response.status})` : `Status: ${response.status}`);
570
650
  process.exit(1);
571
651
  }
572
652
  const json = await response.json();
@@ -598,7 +678,9 @@ async function pollForConfirmation(token) {
598
678
  const start = Date.now();
599
679
  while (Date.now() - start < MAX_DURATION) {
600
680
  await new Promise(resolve => setTimeout(resolve, 1000));
601
- const response = await fetch(`${host}/api/login?token=${token}&consume=true`);
681
+ const response = await fetch(`${host}/api/login?token=${token}&consume=true`, {
682
+ method: "POST"
683
+ });
602
684
  if (response.status === 500) {
603
685
  console.error(picocolors.red("An error occurred on the server."));
604
686
  process.exit(1);
@@ -606,7 +688,7 @@ async function pollForConfirmation(token) {
606
688
  if (response.status === 200) {
607
689
  const json = await response.json();
608
690
  if (json) {
609
- if (typeof json.profile.username === "string" && typeof json.pat === "string") {
691
+ if (typeof json.profile.email === "string" && typeof json.pat === "string") {
610
692
  return json;
611
693
  } else {
612
694
  console.error(picocolors.red("Unexpected response from the server."));
@@ -623,7 +705,7 @@ function saveToken(result, filePath) {
623
705
  recursive: true
624
706
  });
625
707
  fs$1.writeFileSync(filePath, JSON.stringify(result, null, 2));
626
- console.log(picocolors.green(`Token for ${picocolors.cyan(result.profile.username)} saved to ${picocolors.cyan(filePath)}`));
708
+ console.log(picocolors.green(`Token for ${picocolors.cyan(result.profile.email)} saved to ${picocolors.cyan(filePath)}`));
627
709
  }
628
710
 
629
711
  async function main() {
@@ -641,7 +723,8 @@ async function main() {
641
723
  Commands:
642
724
  validate
643
725
  login
644
- list-files
726
+ files
727
+ connect
645
728
  versions
646
729
 
647
730
  Command: validate
@@ -652,21 +735,21 @@ async function main() {
652
735
 
653
736
 
654
737
  Command: login
655
- Description: login to app.val.build and generate a Personal Access Token
738
+ Description: login to admin.val.build and generate a Personal Access Token
656
739
  Options:
657
740
  --root [root], -r [root] Set project root directory (default process.cwd())
658
741
 
659
742
 
660
- Command: files
661
- Description: EXPERIMENTAL.
662
- Perform file operations in Val.
663
- By default it lists files (images, ...) currently in use by Val.
743
+ Command: connect
744
+ Description: connect your local project to a Val Build project at admin.val.build
745
+ Options:
746
+ --root [root], -r [root] Set project root directory (default process.cwd())
664
747
 
665
- If a managed directory is specified,
666
- it will list all files in the managed directory that ARE NOT currently used by Val.
748
+ Command: list-unused-files
749
+ Description: EXPERIMENTAL.
750
+ List files that are in public/val but not in use by any Val module.
667
751
  This is useful for cleaning up unused files.
668
752
  Options:
669
- --managedDir [dir] If set, list files found in directory that are not managed by Val
670
753
  --root [root], -r [root] Set project root directory (default process.cwd())
671
754
  `, {
672
755
  flags: {
@@ -699,13 +782,12 @@ async function main() {
699
782
  }
700
783
  const [command] = input;
701
784
  switch (command) {
702
- case "files":
785
+ case "list-unused-files":
703
786
  if (flags.fix || flags.noEslint) {
704
- return error(`Command "files" does not support --fix or --noEslint flags`);
787
+ return error(`Command "list-unused-files" does not support --fix or --noEslint flags`);
705
788
  }
706
- return files({
707
- root: flags.root,
708
- managedDir: flags.managedDir
789
+ return listUnusedFiles({
790
+ root: flags.root
709
791
  });
710
792
  case "versions":
711
793
  return versions();
@@ -713,6 +795,13 @@ async function main() {
713
795
  return login({
714
796
  root: flags.root
715
797
  });
798
+ case "connect":
799
+ if (flags.fix || flags.noEslint) {
800
+ return error(`Command "connect" does not support --fix or --noEslint flags`);
801
+ }
802
+ return connect({
803
+ root: flags.root
804
+ });
716
805
  case "validate":
717
806
  case "idate":
718
807
  if (flags.managedDir) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@valbuild/cli",
3
3
  "private": false,
4
- "version": "0.85.1",
4
+ "version": "0.86.1",
5
5
  "description": "Val CLI tools",
6
6
  "bin": {
7
7
  "val": "./bin.js"
@@ -20,9 +20,9 @@
20
20
  "dependencies": {
21
21
  "@inquirer/confirm": "^2.0.15",
22
22
  "@inquirer/prompts": "^3.0.2",
23
- "@valbuild/core": "~0.85.1",
24
- "@valbuild/eslint-plugin": "~0.85.1",
25
- "@valbuild/server": "~0.85.1",
23
+ "@valbuild/core": "~0.86.1",
24
+ "@valbuild/eslint-plugin": "~0.86.1",
25
+ "@valbuild/server": "~0.86.1",
26
26
  "chalk": "^4.1.2",
27
27
  "cors": "^2.8.5",
28
28
  "express": "^4.18.2",
package/src/cli.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import meow from "meow";
2
2
  import { error } from "./logger";
3
3
  import { validate } from "./validate";
4
- import { files as files } from "./files";
4
+ import { listUnusedFiles as listUnusedFiles } from "./listUnusedFiles";
5
5
  import { getVersions } from "./getVersions";
6
+ import { connect } from "./connect";
6
7
  import chalk from "chalk";
7
8
  import { login } from "./login";
8
9
 
@@ -18,7 +19,8 @@ async function main(): Promise<void> {
18
19
  Commands:
19
20
  validate
20
21
  login
21
- list-files
22
+ files
23
+ connect
22
24
  versions
23
25
 
24
26
  Command: validate
@@ -29,21 +31,21 @@ async function main(): Promise<void> {
29
31
 
30
32
 
31
33
  Command: login
32
- Description: login to app.val.build and generate a Personal Access Token
34
+ Description: login to admin.val.build and generate a Personal Access Token
33
35
  Options:
34
36
  --root [root], -r [root] Set project root directory (default process.cwd())
35
37
 
36
38
 
37
- Command: files
38
- Description: EXPERIMENTAL.
39
- Perform file operations in Val.
40
- By default it lists files (images, ...) currently in use by Val.
39
+ Command: connect
40
+ Description: connect your local project to a Val Build project at admin.val.build
41
+ Options:
42
+ --root [root], -r [root] Set project root directory (default process.cwd())
41
43
 
42
- If a managed directory is specified,
43
- it will list all files in the managed directory that ARE NOT currently used by Val.
44
+ Command: list-unused-files
45
+ Description: EXPERIMENTAL.
46
+ List files that are in public/val but not in use by any Val module.
44
47
  This is useful for cleaning up unused files.
45
48
  Options:
46
- --managedDir [dir] If set, list files found in directory that are not managed by Val
47
49
  --root [root], -r [root] Set project root directory (default process.cwd())
48
50
  `,
49
51
  {
@@ -81,15 +83,14 @@ async function main(): Promise<void> {
81
83
 
82
84
  const [command] = input;
83
85
  switch (command) {
84
- case "files":
86
+ case "list-unused-files":
85
87
  if (flags.fix || flags.noEslint) {
86
88
  return error(
87
- `Command "files" does not support --fix or --noEslint flags`,
89
+ `Command "list-unused-files" does not support --fix or --noEslint flags`,
88
90
  );
89
91
  }
90
- return files({
92
+ return listUnusedFiles({
91
93
  root: flags.root,
92
- managedDir: flags.managedDir,
93
94
  });
94
95
  case "versions":
95
96
  return versions();
@@ -97,6 +98,15 @@ async function main(): Promise<void> {
97
98
  return login({
98
99
  root: flags.root,
99
100
  });
101
+ case "connect":
102
+ if (flags.fix || flags.noEslint) {
103
+ return error(
104
+ `Command "connect" does not support --fix or --noEslint flags`,
105
+ );
106
+ }
107
+ return connect({
108
+ root: flags.root,
109
+ });
100
110
  case "validate":
101
111
  case "idate":
102
112
  if (flags.managedDir) {
package/src/connect.ts ADDED
@@ -0,0 +1,115 @@
1
+ import pc from "picocolors";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { evalValConfigFile } from "./utils/evalValConfigFile";
5
+
6
+ const host = process.env.VAL_BUILD_URL || "https://admin.val.build";
7
+
8
+ export async function connect(options: { root?: string }) {
9
+ const { root } = options;
10
+ const projectRoot = root ? path.resolve(root) : process.cwd();
11
+
12
+ const maybeProject = await tryGetProject(projectRoot);
13
+ const maybeGitRemote = await tryGetGitRemote(projectRoot);
14
+ const maybeGitOwnerAndRepo =
15
+ maybeGitRemote !== null ? getGitHubOwnerAndRepo(maybeGitRemote) : null;
16
+
17
+ const params = new URLSearchParams();
18
+ params.set("step", "create-project");
19
+ if (maybeProject) {
20
+ params.set("org", maybeProject.orgName);
21
+ params.set("project", maybeProject.projectName);
22
+ }
23
+ if (maybeGitOwnerAndRepo) {
24
+ params.set(
25
+ "github_repo",
26
+ [maybeGitOwnerAndRepo.owner, maybeGitOwnerAndRepo.repo].join("/"),
27
+ );
28
+ }
29
+ const url = `${host}/connect?${params.toString()}`;
30
+
31
+ console.log(
32
+ pc.dim(
33
+ `\nFollow the instructions in your browser to complete the setup:\n${pc.cyan(url)}\n`,
34
+ ),
35
+ );
36
+ }
37
+
38
+ async function tryGetProject(projectRoot: string): Promise<{
39
+ orgName: string;
40
+ projectName: string;
41
+ } | null> {
42
+ const valConfigFile =
43
+ (await evalValConfigFile(projectRoot, "val.config.ts")) ||
44
+ (await evalValConfigFile(projectRoot, "val.config.js"));
45
+
46
+ if (valConfigFile && valConfigFile.project) {
47
+ const parts = valConfigFile.project.split("/");
48
+ if (parts.length === 2) {
49
+ return {
50
+ orgName: parts[0],
51
+ projectName: parts[1],
52
+ };
53
+ } else {
54
+ console.error(
55
+ pc.red(
56
+ `Invalid project format in val.config file: "${valConfigFile.project}". Expected format "orgName/projectName".`,
57
+ ),
58
+ );
59
+ process.exit(1);
60
+ }
61
+ }
62
+ return null;
63
+ }
64
+
65
+ function getGitHubOwnerAndRepo(
66
+ gitRemote: string,
67
+ ): { owner: string; repo: string } | null {
68
+ const sshMatch = gitRemote.match(/git@github\.com:(.+?)\/(.+?)(\.git)?$/);
69
+ if (sshMatch) {
70
+ return { owner: sshMatch[1], repo: sshMatch[2] };
71
+ }
72
+ const httpsMatch = gitRemote.match(
73
+ /https:\/\/github\.com\/(.+?)\/(.+?)(\.git)?$/,
74
+ );
75
+ if (httpsMatch) {
76
+ return { owner: httpsMatch[1], repo: httpsMatch[2] };
77
+ }
78
+ return null;
79
+ }
80
+
81
+ async function tryGetGitRemote(root: string): Promise<string | null> {
82
+ try {
83
+ const gitConfig = tryGetGitConfig(root);
84
+ if (!gitConfig) {
85
+ return null;
86
+ }
87
+ const remoteMatch = gitConfig.match(
88
+ /\[remote "origin"\][\s\S]*?url = (.+)/,
89
+ );
90
+ if (remoteMatch && remoteMatch[1]) {
91
+ return remoteMatch[1].trim();
92
+ }
93
+ return null;
94
+ } catch (error) {
95
+ console.error(pc.red("Failed to read .git/config file."), error);
96
+ return null;
97
+ }
98
+ }
99
+
100
+ function tryGetGitConfig(root: string): string | null {
101
+ let currentDir = root;
102
+ let lastDir = null;
103
+ while (currentDir !== lastDir) {
104
+ const gitConfigPath = path.join(currentDir, ".git", "config");
105
+ if (fs.existsSync(gitConfigPath)) {
106
+ return fs.readFileSync(gitConfigPath, "utf-8");
107
+ }
108
+ lastDir = currentDir;
109
+ currentDir = path.dirname(currentDir);
110
+ if (lastDir === currentDir) {
111
+ break;
112
+ }
113
+ }
114
+ return null;
115
+ }
@@ -9,14 +9,8 @@ import { createService } from "@valbuild/server";
9
9
  import { glob } from "fast-glob";
10
10
  import path from "path";
11
11
 
12
- export async function files({
13
- root,
14
- managedDir,
15
- }: {
16
- root?: string;
17
- managedDir?: string;
18
- }) {
19
- const printFilesUsedByVal = !managedDir;
12
+ export async function listUnusedFiles({ root }: { root?: string }) {
13
+ const managedDir = "public/val";
20
14
  const projectRoot = root ? path.resolve(root) : process.cwd();
21
15
 
22
16
  const service = await createService(projectRoot, {});
@@ -26,8 +20,8 @@ export async function files({
26
20
  cwd: projectRoot,
27
21
  });
28
22
 
29
- const absoluteFilesPathUsedByVal: string[] = [];
30
- async function printOrGetFileRefs(file: string) {
23
+ const filesUsedByVal: string[] = [];
24
+ async function pushFilesUsedByVal(file: string) {
31
25
  const moduleId = `/${file}` as ModuleFilePath; // TODO: check if this always works? (Windows?)
32
26
  const valModule = await service.get(moduleId, "" as ModulePath, {
33
27
  validate: true,
@@ -47,11 +41,7 @@ export async function files({
47
41
  projectRoot,
48
42
  ...value[FILE_REF_PROP].split("/"),
49
43
  );
50
- if (printFilesUsedByVal) {
51
- console.log(absoluteFilePathUsedByVal);
52
- } else {
53
- absoluteFilesPathUsedByVal.push(absoluteFilePathUsedByVal);
54
- }
44
+ filesUsedByVal.push(absoluteFilePathUsedByVal);
55
45
  }
56
46
  }
57
47
  }
@@ -59,22 +49,18 @@ export async function files({
59
49
  }
60
50
  }
61
51
  for (const file of valFiles) {
62
- await printOrGetFileRefs(file);
52
+ await pushFilesUsedByVal(file);
63
53
  }
64
54
 
65
- if (managedDir) {
66
- const managedRoot = path.isAbsolute(managedDir)
67
- ? managedDir
68
- : path.join(projectRoot, managedDir);
69
- const allFilesInManagedDir = await glob("**/*", {
70
- ignore: ["node_modules/**"],
71
- cwd: managedRoot,
72
- });
73
- for (const file of allFilesInManagedDir) {
74
- const absoluteFilePath = path.join(managedRoot, file);
75
- if (!absoluteFilesPathUsedByVal.includes(absoluteFilePath)) {
76
- console.log(path.join(managedRoot, file));
77
- }
55
+ const managedRoot = path.join(projectRoot, managedDir);
56
+ const allFilesInManagedDir = await glob("**/*", {
57
+ ignore: ["node_modules/**"],
58
+ cwd: managedRoot,
59
+ });
60
+ for (const file of allFilesInManagedDir) {
61
+ const absoluteFilePath = path.join(managedRoot, file);
62
+ if (!filesUsedByVal.includes(absoluteFilePath)) {
63
+ console.log(path.join(managedRoot, file));
78
64
  }
79
65
  }
80
66
 
package/src/login.ts CHANGED
@@ -3,7 +3,7 @@ import fs from "fs";
3
3
  import path from "path";
4
4
  import { getPersonalAccessTokenPath } from "@valbuild/server";
5
5
 
6
- const host = process.env.VAL_BUILD_URL || "https://app.val.build";
6
+ const host = process.env.VAL_BUILD_URL || "https://admin.val.build";
7
7
 
8
8
  export async function login(options: { root?: string }) {
9
9
  try {
@@ -22,9 +22,11 @@ export async function login(options: { root?: string }) {
22
22
  const text = await response.text();
23
23
  console.error(
24
24
  pc.red(
25
- "Unexpected failure while trying to login (content type was not JSON). Server response:",
25
+ "Unexpected failure while trying to login (content type was not JSON). ",
26
26
  ),
27
- text || "<empty>",
27
+ text
28
+ ? `Server response: ${text} (status: ${response.status})`
29
+ : `Status: ${response.status}`,
28
30
  );
29
31
  process.exit(1);
30
32
  }
@@ -61,7 +63,7 @@ export async function login(options: { root?: string }) {
61
63
 
62
64
  const MAX_DURATION = 5 * 60 * 1000; // 5 minutes
63
65
  async function pollForConfirmation(token: string): Promise<{
64
- profile: { username: string };
66
+ profile: { email: string };
65
67
  pat: string;
66
68
  }> {
67
69
  const start = Date.now();
@@ -69,6 +71,7 @@ async function pollForConfirmation(token: string): Promise<{
69
71
  await new Promise((resolve) => setTimeout(resolve, 1000));
70
72
  const response = await fetch(
71
73
  `${host}/api/login?token=${token}&consume=true`,
74
+ { method: "POST" },
72
75
  );
73
76
  if (response.status === 500) {
74
77
  console.error(pc.red("An error occurred on the server."));
@@ -78,7 +81,7 @@ async function pollForConfirmation(token: string): Promise<{
78
81
  const json = await response.json();
79
82
  if (json) {
80
83
  if (
81
- typeof json.profile.username === "string" &&
84
+ typeof json.profile.email === "string" &&
82
85
  typeof json.pat === "string"
83
86
  ) {
84
87
  return json;
@@ -95,7 +98,7 @@ async function pollForConfirmation(token: string): Promise<{
95
98
 
96
99
  function saveToken(
97
100
  result: {
98
- profile: { username: string };
101
+ profile: { email: string };
99
102
  pat: string;
100
103
  },
101
104
  filePath: string,
@@ -105,7 +108,7 @@ function saveToken(
105
108
  console.log(
106
109
  pc.green(
107
110
  `Token for ${pc.cyan(
108
- result.profile.username,
111
+ result.profile.email,
109
112
  )} saved to ${pc.cyan(filePath)}`,
110
113
  ),
111
114
  );