UNPKG

@supernovaio/cli

Version:

Supernova.io Command Line Interface

198 lines (196 loc) • 7.94 kB
!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