@supernovaio/cli
Version:
Supernova.io Command Line Interface
198 lines (196 loc) ⢠7.94 kB
JavaScript
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3729578b-db64-5313-a569-42baf788f2a2")}catch(e){}}();
import { Command } from "@oclif/core";
import * as Sentry from "@sentry/node";
import inquirer from "inquirer";
import open from "open";
import pkceChallenge from "pkce-challenge";
import { AuthService, VaultService } from "../services/index.js";
import { getApiClient } from "../utils/api-client.js";
import { SupernovaConfigService } from "../utils/config.service.js";
import { getTargetEnv } from "./environment.js";
import { NotAuthorizedError } from "./not-authorized.error.js";
const hasAccess = (role) => role && ["Admin", "Contributor", "Creator", "Owner"].includes(role);
export class BaseCommand extends Command {
env = getTargetEnv();
configService = SupernovaConfigService.getInstance();
_apiClient;
async apiClient() {
if (this._apiClient)
return this._apiClient;
try {
const apiClient = await getApiClient(this.env);
const { user: me } = await apiClient.users.getMe();
Sentry.setUser({ id: me.id });
this._apiClient = apiClient;
return apiClient;
}
catch (error) {
if (error instanceof NotAuthorizedError && (await this.promptLoginIfInteractive())) {
const apiClient = await getApiClient(this.env);
const { user: me } = await apiClient.users.getMe();
Sentry.setUser({ id: me.id });
this._apiClient = apiClient;
return apiClient;
}
throw error;
}
}
async ensureAuthenticated() {
await this.apiClient();
}
async performLogin() {
const authService = new AuthService();
const vaultService = new VaultService();
const { code_challenge: codeChallenge, code_verifier: codeVerifier } = await pkceChallenge();
this.log("\nš Authentication Process\n");
const { authorizeUrl, readKey } = await authService.getAuthUrlFromServer(this.env, codeChallenge);
this.log("Opening browser for authentication...");
this.log("\nIf browser does not open automatically, copy and paste this URL into it:");
this.log(`${authorizeUrl}\n`);
try {
await open(authorizeUrl);
}
catch (error) {
Sentry.captureException(error);
}
const tokens = await authService.getTokensFromServer(this.env, codeVerifier, readKey);
if (tokens) {
this.log("Login successful! Saving your session on this machine...\n");
await vaultService.storeTokensToVault(tokens, this.env);
this.log("All done, you are now logged in.\n");
return true;
}
this.log("Login timed out.");
return false;
}
async openBrowser(url) {
try {
await open(url);
}
catch {
this.log("Unable to open your browser automatically. Copy and paste this URL into it:");
this.log(`\u001B[4m\u001B[34m${url}\u001B[0m\n`);
}
}
async promptLoginIfInteractive() {
if (!process.stdin.isTTY || process.env.SUPERNOVA_TOKEN) {
return false;
}
const { confirm } = await inquirer.prompt([
{
default: true,
message: "Authentication is required. Open browser to continue login?",
name: "confirm",
type: "confirm",
},
]);
if (!confirm)
return false;
return this.performLogin();
}
async init() {
await super.init();
if (this.env !== "production") {
this.log(`Using ${this.env} environment`);
}
}
async promptBrandId(designSystemId, versionId) {
const client = await this.apiClient();
const brandsEndpoint = client.designSystems.versions.brands;
const { brands } = await brandsEndpoint.list(designSystemId, versionId ?? "head");
const options = brands.map(b => ({ name: b.meta.name, value: b.persistentId }));
if (options.length === 1)
return options[0].value;
return this.prompt("Select a brand:", options);
}
async fetchAccessibleDesignSystems() {
const { designSystems: client } = await this.apiClient();
const { designSystems, workspaces } = await client.listUserDesignSystems();
const workspaceById = new Map(workspaces.map(ws => [ws.id, ws]));
if (designSystems.length === 0) {
this.error("You don't have any design system.");
}
const accessible = designSystems.filter(ds => hasAccess(ds.role));
if (accessible.length === 0) {
this.error("You don't have access to any design system.");
}
return { accessible, workspaceById };
}
async promptDesignSystemFromList(accessible, workspaceById) {
const dsNameCount = accessible.reduce((acc, { meta: { name } }) => {
acc.set(name, (acc.get(name) ?? 0) + 1);
return acc;
}, new Map());
const choices = accessible
.slice()
.sort((a, b) => a.meta.name.localeCompare(b.meta.name))
.map(({ id, meta: { name }, workspaceId }) => ({
name: (dsNameCount.get(name) ?? 0) > 1 ? `${name} (${workspaceById.get(workspaceId)?.profile?.name})` : name,
value: id,
}));
return this.searchPrompt("Select a design system:", choices);
}
async promptDesignSystemId() {
const { accessible, workspaceById } = await this.fetchAccessibleDesignSystems();
if (accessible.length === 1) {
return accessible[0].id;
}
return this.promptDesignSystemFromList(accessible, workspaceById);
}
async getVersionId(designSystemId, versionId) {
if (versionId)
return versionId;
const { versions } = (await this.apiClient()).designSystems;
const verIds = (await versions.list(designSystemId)).designSystemVersions.map(({ id, meta: { name } }) => ({
id,
name,
}));
if (verIds.length === 1) {
return verIds[0].id;
}
return this.prompt("Select a version:", verIds.map(wrk => ({
name: wrk.name,
value: wrk.id,
})));
}
async getWorkspaceId(workspaceId) {
if (workspaceId)
return workspaceId;
const { workspaces } = await this.apiClient();
const wrks = (await workspaces.list()).membership.map(({ workspace: { id, profile: { name }, }, }) => ({ id, name }));
if (wrks.length === 1) {
return wrks[0].id;
}
return this.prompt("Select a workspace:", wrks.map(wrk => ({
name: wrk.name,
value: wrk.id,
})));
}
async prompt(message, choices) {
const choice = await inquirer.prompt([
{
choices,
message,
name: "selected",
type: "list",
},
]);
return choice.selected;
}
async searchPrompt(message, choices) {
const choice = await inquirer.prompt({
type: "search",
name: "selected",
message,
async source(term) {
if (!term)
return choices;
const lower = term.toLowerCase();
return choices.filter(c => c.name.toLowerCase().includes(lower));
},
});
return choice.selected;
}
}
//# sourceMappingURL=base-command.js.map
//# debugId=3729578b-db64-5313-a569-42baf788f2a2