@omlet/cli
Version:
Omlet (https://omlet.dev) is a component analytics tool that uses a CLI to scan your codebase to detect components and their usage. Get real usage insights from customizable charts to measure adoption across all projects and identify opportunities to impr
194 lines (193 loc) • 7.39 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAuthenticatedUser = exports.getToken = exports.login = exports.AuthenticationError = void 0;
const clr = __importStar(require("colorette"));
const fs_1 = require("fs");
const http_1 = __importDefault(require("http"));
const inquirer_1 = __importDefault(require("inquirer"));
const open_1 = __importDefault(require("open"));
const os_1 = __importDefault(require("os"));
const server_destroy_1 = __importDefault(require("server-destroy"));
const upath_1 = __importDefault(require("upath"));
const url_1 = __importDefault(require("url"));
const apiClient_1 = require("./apiClient");
const config_1 = require("./config");
const spinner_1 = require("./spinner");
const tracking_1 = require("./tracking");
const LOGIN_ENDPOINT = "/login";
const CLI_LOGIN_SUCCESS_ENDPOINT = "/login/cli-success";
const TOKEN_FILE_NAME = ".omletrc";
const JWT_REGEX = /^[\w-]+\.[\w-]+\.[\w-]+$/;
const TOKEN_FILE_PATH = upath_1.default.join(os_1.default.homedir(), TOKEN_FILE_NAME);
function writeTokenFile(tokens) {
return fs_1.promises.writeFile(TOKEN_FILE_PATH, JSON.stringify(tokens));
}
async function readTokenFile() {
try {
const content = await fs_1.promises.readFile(TOKEN_FILE_PATH, "utf8");
return JSON.parse(content);
}
catch (e) {
const code = e.code;
if (code === "ENOENT") {
return {};
}
throw e;
}
}
function getLoginURL(redirect) {
const loginUrl = new url_1.default.URL(LOGIN_ENDPOINT, config_1.BASE_URL);
loginUrl.searchParams.append("redirect", redirect);
loginUrl.searchParams.set("cli", "true");
return loginUrl.toString();
}
function getSuccessURL() {
return new url_1.default.URL(CLI_LOGIN_SUCCESS_ENDPOINT, config_1.BASE_URL).toString();
}
function fetchToken(port) {
const loginURL = getLoginURL(`http://127.0.0.1:${port}/omlet-callback`);
return new Promise((resolve, reject) => {
console.log(clr.dim(`In case a new browser tab doesn't open, you can follow this link: ${loginURL}`));
const spinner = (0, spinner_1.createSpinner)("Waiting for token…");
spinner.start();
function resolveToken(token) {
server.destroy();
spinner.succeed("Token acquired");
resolve(token);
}
const server = http_1.default
.createServer(async (request, response) => {
if (!request.url) {
reject(new Error("Malformed request"));
return;
}
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
try {
if (request.method === "OPTIONS") {
response.writeHead(204);
response.end();
return;
}
else if (request.method === "GET" && request.url.indexOf("/omlet-callback") > -1) {
const requestUrl = new url_1.default.URL(request.url, `http://${request.headers.host}`);
const token = requestUrl.searchParams.get("token");
if (token) {
response.setHeader("Location", getSuccessURL());
response.writeHead(302);
response.end();
resolveToken(token);
return;
}
}
response.writeHead(400);
response.end("Invalid request");
}
catch (e) {
reject(e);
}
})
.listen(port, () => {
(0, open_1.default)(loginURL, { wait: false }).then(cp => cp.unref());
});
(0, server_destroy_1.default)(server);
});
}
async function getTokenFromPrompt() {
const loginURL = getLoginURL(getSuccessURL());
console.log(clr.dim(`In case a new browser tab doesn't open, you can follow this link: ${loginURL}`));
const cp = await (0, open_1.default)(loginURL, { wait: false });
cp.unref();
const { token } = await inquirer_1.default.prompt([{
type: "text",
message: "Paste your token here",
prefix: "",
name: "token",
validate: (value) => (JWT_REGEX.test(value) ? true : "Invalid token"),
}]);
return token;
}
async function resetToken() {
const tokens = await readTokenFile();
if (tokens[config_1.BASE_URL]) {
delete tokens[config_1.BASE_URL];
await writeTokenFile(tokens);
}
}
class AuthenticationError extends Error {
constructor(reason) {
super("Authentication failed");
this.name = this.constructor.name;
this.reason = reason;
}
}
exports.AuthenticationError = AuthenticationError;
async function login(port, isRemote) {
try {
const tokens = await readTokenFile();
const token = isRemote ? await getTokenFromPrompt() : await fetchToken(port);
tokens[config_1.BASE_URL] = token;
await writeTokenFile(tokens);
const user = await getAuthenticatedUser();
if (!user) {
throw new AuthenticationError(new Error("User not found"));
}
(0, tracking_1.trackLogin)();
return token;
}
catch (error) {
throw new AuthenticationError(error);
}
}
exports.login = login;
async function getToken() {
if (config_1.ENV_TOKEN) {
return config_1.ENV_TOKEN;
}
const tokens = await readTokenFile();
return tokens[config_1.BASE_URL];
}
exports.getToken = getToken;
async function getAuthenticatedUser() {
const token = await getToken();
if (token) {
try {
return await (0, apiClient_1.getMe)();
}
catch (err) {
const error = err;
const isAuthFailure = (error instanceof apiClient_1.LoginRequiredError ||
(error instanceof apiClient_1.RequestFailedError && error.statusCode < 500 && error.statusCode > 300));
if (isAuthFailure) {
await resetToken();
return null;
}
throw error;
}
}
return null;
}
exports.getAuthenticatedUser = getAuthenticatedUser;