apiblaze 0.1.5 → 0.1.7

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 (2) hide show
  1. package/dist/index.js +154 -33
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -87,6 +87,8 @@ var init_auth = __esm({
87
87
  // src/lib/api.ts
88
88
  var api_exports = {};
89
89
  __export(api_exports, {
90
+ checkProxyName: () => checkProxyName,
91
+ createProxy: () => createProxy,
90
92
  deleteDevTunnel: () => deleteDevTunnel,
91
93
  getLocalhostTargets: () => getLocalhostTargets,
92
94
  getProjects: () => getProjects,
@@ -119,7 +121,12 @@ async function apiFetch(path3, options = {}) {
119
121
  return res.json();
120
122
  }
121
123
  async function getTeams() {
122
- return apiFetch("/api/cli/teams");
124
+ const res = await apiFetch("/api/cli/teams");
125
+ const raw = Array.isArray(res) ? res : res?.teams ?? res?.data ?? [];
126
+ return raw.map((t) => {
127
+ const teamId = t.teamId ?? t.team_id;
128
+ return { teamId: teamId ?? "", name: t.name ?? teamId ?? "" };
129
+ }).filter((t) => t.teamId);
123
130
  }
124
131
  async function getLocalhostTargets(teamId) {
125
132
  return apiFetch(`/api/cli/localhost-targets?team_id=${encodeURIComponent(teamId)}`);
@@ -127,6 +134,15 @@ async function getLocalhostTargets(teamId) {
127
134
  async function getProjects(teamId) {
128
135
  return apiFetch(`/api/cli/projects?team_id=${encodeURIComponent(teamId)}`);
129
136
  }
137
+ async function checkProxyName(name) {
138
+ return apiFetch(`/api/cli/check-name?name=${encodeURIComponent(name)}`);
139
+ }
140
+ async function createProxy(payload) {
141
+ return apiFetch("/api/cli/create-proxy", {
142
+ method: "POST",
143
+ body: JSON.stringify(payload)
144
+ });
145
+ }
130
146
  async function putDevTunnel(payload) {
131
147
  return apiFetch("/api/cli/dev-tunnel", {
132
148
  method: "PUT",
@@ -148,7 +164,7 @@ var init_api = __esm({
148
164
 
149
165
  // src/index.ts
150
166
  var import_commander = require("commander");
151
- var import_chalk5 = __toESM(require("chalk"));
167
+ var import_chalk6 = __toESM(require("chalk"));
152
168
  init_types();
153
169
 
154
170
  // src/commands/login.ts
@@ -226,7 +242,7 @@ async function runLogin() {
226
242
  });
227
243
  spinner.succeed(import_chalk.default.green("Authorized!"));
228
244
  if (githubHandle) {
229
- console.log(`${import_chalk.default.cyan("\u2192")} Logged in as ${import_chalk.default.bold("@" + githubHandle)}${apiblazeUserId ? import_chalk.default.dim(` (${apiblazeUserId})`) : ""}`);
245
+ console.log(`${import_chalk.default.cyan("\u2192")} Logged in as ${import_chalk.default.bold("@" + githubHandle)}`);
230
246
  }
231
247
  let teamId = defaultTeamId ?? void 0;
232
248
  let teamName;
@@ -602,36 +618,32 @@ async function runProjects() {
602
618
  process.exit(1);
603
619
  }
604
620
  if (creds.githubHandle) {
605
- console.log(
606
- `${import_chalk4.default.cyan("\u2192")} Logged in as ${import_chalk4.default.bold("@" + creds.githubHandle)}` + (creds.apiblazeUserId ? import_chalk4.default.dim(` (${creds.apiblazeUserId})`) : "")
607
- );
621
+ console.log(`${import_chalk4.default.cyan("\u2192")} Logged in as ${import_chalk4.default.bold("@" + creds.githubHandle)}`);
608
622
  }
609
- let teamId;
610
- let teamName;
611
- const teams = await getTeams().catch(() => []);
612
- if (teams.length === 0) {
613
- teamId = creds.teamId;
614
- teamName = creds.teamName;
615
- } else if (teams.length === 1) {
616
- teamId = teams[0].teamId;
617
- teamName = teams[0].name;
618
- } else {
619
- const { default: inquirer2 } = await import("inquirer");
620
- const { chosen } = await inquirer2.prompt([{
621
- type: "list",
622
- name: "chosen",
623
- message: "Which team?",
624
- default: creds.teamId,
625
- choices: teams.map((t) => ({ name: t.name, value: t.teamId }))
626
- }]);
627
- teamId = chosen;
628
- teamName = teams.find((t) => t.teamId === chosen)?.name;
623
+ let teamId = creds.teamId;
624
+ let teamName = creds.teamName;
625
+ if (!teamId) {
626
+ const teams = await getTeams().catch(() => []);
627
+ if (teams.length === 1) {
628
+ teamId = teams[0].teamId;
629
+ teamName = teams[0].name;
630
+ } else if (teams.length > 1) {
631
+ const { default: inquirer2 } = await import("inquirer");
632
+ const { chosen } = await inquirer2.prompt([{
633
+ type: "list",
634
+ name: "chosen",
635
+ message: "Which team do you want to use?",
636
+ choices: teams.map((t) => ({ name: t.name, value: t.teamId }))
637
+ }]);
638
+ teamId = chosen;
639
+ teamName = teams.find((t) => t.teamId === chosen)?.name;
640
+ }
629
641
  }
630
642
  if (!teamId) {
631
643
  console.error(import_chalk4.default.red("No team found. Run `apiblaze login` to set up your team."));
632
644
  process.exit(1);
633
645
  }
634
- console.log(`${import_chalk4.default.cyan("\u2192")} Team: ${import_chalk4.default.bold(teamName ?? teamId)} ${import_chalk4.default.dim(teamId)}
646
+ console.log(`${import_chalk4.default.cyan("\u2192")} Team: ${import_chalk4.default.bold(teamName ?? teamId)}
635
647
  `);
636
648
  const spinner = (0, import_ora4.default)("Fetching projects...").start();
637
649
  let projects;
@@ -646,12 +658,113 @@ async function runProjects() {
646
658
  console.log(import_chalk4.default.yellow("No projects found for this team."));
647
659
  return;
648
660
  }
661
+ const width = Math.max(...projects.map((p) => p.projectName.length));
649
662
  for (const p of projects) {
650
- console.log(`${import_chalk4.default.bold(p.projectName)} ${import_chalk4.default.dim(p.projectId)}`);
651
- console.log(` ${import_chalk4.default.dim("version:")} ${p.apiVersion} ${import_chalk4.default.dim("team:")} ${p.teamId}`);
663
+ console.log(` ${import_chalk4.default.bold(p.projectName.padEnd(width))} ${import_chalk4.default.dim("v" + p.apiVersion)}`);
652
664
  }
653
665
  console.log(import_chalk4.default.dim(`
654
- ${projects.length} project(s).`));
666
+ ${projects.length} project${projects.length === 1 ? "" : "s"}`));
667
+ }
668
+
669
+ // src/commands/create.ts
670
+ var import_chalk5 = __toESM(require("chalk"));
671
+ var import_ora5 = __toESM(require("ora"));
672
+ init_auth();
673
+ init_api();
674
+ function normalizeName(raw) {
675
+ return (raw || "").toLowerCase().replace(/[^a-z0-9]/g, "");
676
+ }
677
+ function isHttpUrl(s) {
678
+ try {
679
+ const u = new URL(s.trim());
680
+ return u.protocol === "http:" || u.protocol === "https:";
681
+ } catch {
682
+ return false;
683
+ }
684
+ }
685
+ async function runCreate() {
686
+ const creds = loadCredentials();
687
+ if (!creds) {
688
+ console.error(import_chalk5.default.red("Not logged in. Run `apiblaze login` first."));
689
+ process.exit(1);
690
+ }
691
+ console.log(import_chalk5.default.bold("\nCreate an API proxy\n"));
692
+ const { default: inquirer2 } = await import("inquirer");
693
+ let name = "";
694
+ for (; ; ) {
695
+ const { rawName } = await inquirer2.prompt([{
696
+ type: "input",
697
+ name: "rawName",
698
+ message: "Proxy name (your API will live at <name>.apiblaze.com):",
699
+ transformer: (v) => normalizeName(v)
700
+ }]);
701
+ name = normalizeName(rawName);
702
+ if (name.length < 3) {
703
+ console.log(import_chalk5.default.yellow(" Name must be at least 3 characters (letters and digits only).\n"));
704
+ continue;
705
+ }
706
+ const spinner2 = (0, import_ora5.default)("Checking availability...").start();
707
+ try {
708
+ const check = await checkProxyName(name);
709
+ spinner2.stop();
710
+ if (!check.canUseProjectName || !check.canUseApiVersion) {
711
+ const why = check.message ? ` \u2014 ${check.message}` : "";
712
+ console.log(import_chalk5.default.yellow(` "${name}" is not available${why}. Try another.
713
+ `));
714
+ continue;
715
+ }
716
+ } catch {
717
+ spinner2.stop();
718
+ console.log(import_chalk5.default.dim(" (could not verify availability; continuing)"));
719
+ }
720
+ console.log(`${import_chalk5.default.cyan("\u2192")} Your API will live at ${import_chalk5.default.bold(`https://${name}.apiblaze.com`)}
721
+ `);
722
+ break;
723
+ }
724
+ let targetUrl = "";
725
+ for (; ; ) {
726
+ const { url } = await inquirer2.prompt([{
727
+ type: "input",
728
+ name: "url",
729
+ message: "Target URL to forward requests to (e.g. https://httpbin.org):"
730
+ }]);
731
+ if (!isHttpUrl(url)) {
732
+ console.log(import_chalk5.default.yellow(" Enter a valid http(s) URL.\n"));
733
+ continue;
734
+ }
735
+ targetUrl = url.trim();
736
+ break;
737
+ }
738
+ console.log(`${import_chalk5.default.cyan("\u2192")} Auth: ${import_chalk5.default.bold("API key")} \u2014 consumers send an X-API-Key header
739
+ `);
740
+ const spinner = (0, import_ora5.default)("Creating proxy (tenant, keys, dev portal)...").start();
741
+ let result;
742
+ try {
743
+ result = await createProxy({ name, target_url: targetUrl, auth_type: "api_key" });
744
+ spinner.succeed(import_chalk5.default.green("Proxy created!"));
745
+ } catch (err) {
746
+ spinner.fail("Failed to create proxy.");
747
+ throw err;
748
+ }
749
+ const version = result.api_version || "1.0.0";
750
+ const keys = result.api_keys ?? {};
751
+ const adminKey = keys.prod ?? Object.values(keys)[0];
752
+ console.log();
753
+ console.log(` ${import_chalk5.default.dim("Proxy URL: ")} ${import_chalk5.default.bold(`https://${name}.apiblaze.com/${version}/prod`)}`);
754
+ if (result.devPortal) {
755
+ console.log(` ${import_chalk5.default.dim("Dev portal:")} ${import_chalk5.default.bold(result.devPortal)}`);
756
+ }
757
+ if (adminKey) {
758
+ console.log();
759
+ console.log(` ${import_chalk5.default.dim("Consumer admin API key (prod):")}`);
760
+ console.log(` ${import_chalk5.default.bold.green(adminKey)}`);
761
+ console.log(import_chalk5.default.dim("\n Save this now \u2014 send it as the X-API-Key header. It may not be shown again."));
762
+ const otherEnvs = Object.keys(keys).filter((e) => e !== "prod");
763
+ if (otherEnvs.length) {
764
+ console.log(import_chalk5.default.dim(` (Separate keys were also created for: ${otherEnvs.join(", ")}.)`));
765
+ }
766
+ }
767
+ console.log();
655
768
  }
656
769
 
657
770
  // src/index.ts
@@ -665,6 +778,14 @@ program.command("login").description("Authenticate with APIblaze").action(async
665
778
  process.exit(1);
666
779
  }
667
780
  });
781
+ program.command("create").description("Create a new API proxy").action(async () => {
782
+ try {
783
+ await runCreate();
784
+ } catch (err) {
785
+ printError(err);
786
+ process.exit(1);
787
+ }
788
+ });
668
789
  program.command("projects").description("List the projects in your team").action(async () => {
669
790
  try {
670
791
  await runProjects();
@@ -683,13 +804,13 @@ program.command("dev").description("Start a dev tunnel for your localhost projec
683
804
  });
684
805
  function printError(err) {
685
806
  if (err instanceof ApiError) {
686
- console.error(import_chalk5.default.red(`
807
+ console.error(import_chalk6.default.red(`
687
808
  API error (${err.status}): ${err.message}`));
688
809
  } else if (err instanceof Error) {
689
- console.error(import_chalk5.default.red(`
810
+ console.error(import_chalk6.default.red(`
690
811
  Error: ${err.message}`));
691
812
  } else {
692
- console.error(import_chalk5.default.red("\nUnknown error"));
813
+ console.error(import_chalk6.default.red("\nUnknown error"));
693
814
  }
694
815
  }
695
816
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apiblaze",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Dev tunnel CLI for APIblaze — route localhost projects through Cloudflare tunnels",
5
5
  "keywords": [
6
6
  "apiblaze",