superpush
Version:
A command-line interface for SuperPe CodePush - enabling over-the-air (OTA) updates for React Native applications.
799 lines (781 loc) • 30.6 kB
JavaScript
// src/index.ts
import fs2 from "fs";
import path4 from "path";
import { Command } from "commander";
// src/wrapper.ts
import chalk6 from "chalk";
// src/configs.ts
import path from "path";
import dotenv from "dotenv";
dotenv.config();
var CONFIG_DIR = ".superpush";
var CONFIG_DIR_PATH = path.join(process.cwd(), CONFIG_DIR);
var CONFIGS = {
PROJECT_DIR: path.basename(process.cwd()),
CONFIG_DIR,
CONFIG_DIR_PATH,
ANDROID: "android",
ANDROID_BUNDLE_OUTPUT: `${CONFIG_DIR}/android/index.android.bundle`,
ANDROID_ASSET_DESTINATION: `${CONFIG_DIR}/android`,
IOS: "ios",
IOS_BUNDLE_OUTPUT: `${CONFIG_DIR}/ios/main.jsbundle`,
IOS_ASSET_DESTINATION: `${CONFIG_DIR}/ios`,
API_BASE_URL: "https://superpush-api.superpe.in",
API_VERSION: "/api/v1"
};
// src/utils/keys.ts
import Conf from "conf";
var config = new Conf({ projectName: "superpush" });
function setKey(key, value) {
config.set(key, value);
}
function getKey(key) {
return config.get(key);
}
function deleteKey(key) {
config.delete(key);
}
// src/commands.ts
import fs from "fs";
import path3 from "path";
import chalk5 from "chalk";
import { rm } from "fs/promises";
// src/utils/prompt.ts
import chalk from "chalk";
import * as readline from "readline";
var createPrompt = () => {
return readline.createInterface({
input: process.stdin,
output: process.stdout
});
};
var prompt = (question) => {
const rl = createPrompt();
return new Promise((resolve) => {
rl.question(question, (answer) => {
rl.close();
resolve(answer.trim());
});
});
};
// src/utils/api.ts
var Api = class {
baseUrl;
defaultConfig;
defaultHeaders;
constructor(config2) {
this.baseUrl = config2.baseUrl.replace(/\/$/, "");
this.defaultHeaders = {
"Content-Type": "application/json",
...config2.headers
};
this.defaultConfig = {
method: "GET",
timeout: config2.timeout || 1e4,
retries: config2.retries || 3,
headers: this.defaultHeaders
};
}
async makeRequest(endpoint, params, config2) {
const finalConfig = { ...this.defaultConfig, ...config2 };
const url = `${this.baseUrl}${endpoint.startsWith("/") ? endpoint : "/" + endpoint}`;
let attempt = 0;
const maxRetries = finalConfig.retries || 3;
while (attempt <= maxRetries) {
try {
const controller = new AbortController();
const timeoutId = finalConfig.timeout && finalConfig.timeout > 0 ? setTimeout(() => controller.abort(), finalConfig.timeout) : null;
const requestOptions = {
method: finalConfig.method,
headers: finalConfig.headers,
signal: controller.signal
};
if (params && ["POST", "PUT", "PATCH"].includes(finalConfig.method || "GET")) {
if (params instanceof FormData) {
requestOptions.body = params;
const headers = { ...finalConfig.headers };
delete headers["Content-Type"];
requestOptions.headers = headers;
} else {
requestOptions.body = JSON.stringify(params);
}
}
if (params && finalConfig.method === "GET") {
const searchParams = new URLSearchParams(params);
const separator = url.includes("?") ? "&" : "?";
const finalUrl = `${url}${separator}${searchParams.toString()}`;
const response2 = await fetch(finalUrl, requestOptions);
if (timeoutId) clearTimeout(timeoutId);
return await this.handleResponse(response2);
}
const response = await fetch(url, requestOptions);
if (timeoutId) clearTimeout(timeoutId);
return await this.handleResponse(response);
} catch (error) {
attempt++;
if (attempt > maxRetries) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error occurred"
};
}
await this.delay(Math.pow(2, attempt) * 1e3);
}
}
return {
success: false,
error: "Max retries exceeded"
};
}
async handleResponse(response) {
try {
const contentType = response.headers.get("content-type");
const isJson = contentType?.includes("application/json");
if (!response.ok) {
const errorData = isJson ? await response.json() : await response.text();
return {
success: false,
error: typeof errorData === "string" ? errorData : typeof errorData === "object" && errorData !== null && "detail" in errorData && typeof errorData.detail === "string" ? errorData.detail : "Request failed"
};
}
const data = isJson ? await response.json() : await response.text();
return {
success: true,
data
};
} catch (error) {
return {
success: false,
error: "Failed to parse response"
};
}
}
delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async get(endpoint, params, config2) {
return this.makeRequest(endpoint, params, { ...config2, method: "GET" });
}
async post(endpoint, params, config2) {
return this.makeRequest(endpoint, params, { ...config2, method: "POST" });
}
async put(endpoint, params, config2) {
return this.makeRequest(endpoint, params, { ...config2, method: "PUT" });
}
async delete(endpoint, params, config2) {
return this.makeRequest(endpoint, params, { ...config2, method: "DELETE" });
}
async patch(endpoint, params, config2) {
return this.makeRequest(endpoint, params, { ...config2, method: "PATCH" });
}
setHeader(key, value) {
this.defaultHeaders[key] = value;
this.defaultConfig.headers = this.defaultHeaders;
}
removeHeader(key) {
delete this.defaultHeaders[key];
this.defaultConfig.headers = this.defaultHeaders;
}
};
var createApi = (config2) => {
return new Api(config2);
};
// src/utils/spinner.ts
import chalk2 from "chalk";
var Spinner = class {
frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
interval = null;
currentFrame = 0;
message;
constructor(message) {
this.message = message;
}
start() {
process.stdout.write("\x1B[?25l");
this.interval = setInterval(() => {
process.stdout.write("\r" + chalk2.cyan(this.frames[this.currentFrame]) + " " + this.message);
this.currentFrame = (this.currentFrame + 1) % this.frames.length;
}, 100);
}
stop(finalMessage) {
if (this.interval) {
clearInterval(this.interval);
this.interval = null;
}
process.stdout.write("\r" + " ".repeat(this.message.length + 2) + "\r");
process.stdout.write("\x1B[?25h");
if (finalMessage) {
console.log(finalMessage);
}
}
};
// src/utils/compress.ts
import { pipeline } from "stream/promises";
import { createBrotliCompress } from "zlib";
import { createWriteStream } from "fs";
import * as path2 from "path";
import { spawn } from "child_process";
var compressBundle = async (platformDir, bundleOutputCompressed) => {
return new Promise((resolve, reject) => {
const tar = spawn("tar", [
"--exclude=*/.DS_Store/*",
"--exclude=._*",
"--exclude=*/._*",
"-c",
"-C",
path2.dirname(platformDir),
path2.basename(platformDir)
]);
const writeStream = createWriteStream(bundleOutputCompressed);
const brotliCompress = createBrotliCompress();
let hasError = false;
tar.on("error", (error) => {
if (!hasError) {
hasError = true;
reject(error);
}
});
tar.stderr.on("data", (data) => {
if (!hasError) {
hasError = true;
reject(new Error(`tar error: ${data.toString()}`));
}
});
pipeline(tar.stdout, brotliCompress, writeStream).then(() => {
if (!hasError) {
resolve();
}
}).catch((error) => {
if (!hasError) {
hasError = true;
reject(error);
}
});
tar.on("close", (code) => {
if (!hasError && code !== 0) {
hasError = true;
reject(new Error(`tar process exited with code ${code}`));
}
});
});
};
// src/utils/exec.ts
import { exec, spawn as spawn2 } from "child_process";
var executeInteractive = (cmd, args = [], options = {}) => {
return new Promise((resolve, reject) => {
const child = spawn2(cmd, args, {
stdio: "inherit",
shell: true,
...options
});
child.on("close", (code) => {
resolve(code ?? 0);
});
child.on("error", (err) => {
reject(err);
});
});
};
// src/utils/interactive-history.ts
import chalk3 from "chalk";
var InteractiveHistory = class {
data;
currentReleaseIndex = 0;
currentPatchPage = 0;
patchesPerPage = 5;
constructor(data) {
this.data = data;
}
async start(appId, platform) {
console.log(chalk3.blue(`
\u{1F4F1} App ID: ${appId}`));
console.log(chalk3.blue(`\u{1F527} Platform: ${platform}
`));
if (!this.data.releases || this.data.releases.length === 0) {
console.log(chalk3.yellow("\u26A0\uFE0F No releases found for this platform.\n"));
return;
}
await this.showCurrentView(appId, platform);
}
async showCurrentView(appId, platform) {
console.clear();
console.log(chalk3.blue(`\u{1F4F1} App ID: ${appId} | \u{1F527} Platform: ${platform}`));
console.log(chalk3.bold.white("\u{1F4E6} Release History\n"));
const currentRelease = this.data.releases[this.currentReleaseIndex];
const isLatestRelease = this.currentReleaseIndex === 0;
const versionIcon = isLatestRelease ? "\u{1F680}" : "\u{1F4CB}";
console.log(chalk3.green(`${versionIcon} Version: ${currentRelease.version} (${this.currentReleaseIndex + 1}/${this.data.releases.length})`));
if (currentRelease.patches && currentRelease.patches.length > 0) {
console.log(chalk3.cyan(" \u{1F4C4} Patches:"));
const startIndex = this.currentPatchPage * this.patchesPerPage;
const endIndex = Math.min(startIndex + this.patchesPerPage, currentRelease.patches.length);
const visiblePatches = currentRelease.patches.slice(startIndex, endIndex);
visiblePatches.forEach((patch, index) => {
const patchIcon = patch.is_latest ? "\u25CF" : "\u25CB";
const patchColor = patch.is_latest ? chalk3.bold.green : chalk3.gray;
const latestLabel = patch.is_latest ? " (LATEST)" : "";
const messageText = patch.message ? ` - ${patch.message}` : "";
console.log(patchColor(` ${patchIcon} ${patch.version}${latestLabel}${chalk3.dim(messageText)}`));
});
if (currentRelease.patches.length > this.patchesPerPage) {
const totalPages = Math.ceil(currentRelease.patches.length / this.patchesPerPage);
console.log(chalk3.dim(`
Showing ${startIndex + 1}-${endIndex} of ${currentRelease.patches.length} patches (Page ${this.currentPatchPage + 1}/${totalPages})`));
}
} else {
console.log(chalk3.gray(" \u{1F4C4} No patches available"));
}
console.log("");
await this.showNavigationMenu(appId, platform);
}
async showNavigationMenu(appId, platform) {
const options = [];
const currentRelease = this.data.releases[this.currentReleaseIndex];
if (this.currentReleaseIndex > 0) {
options.push("p - Previous release");
}
if (this.currentReleaseIndex < this.data.releases.length - 1) {
options.push("n - Next release");
}
if (currentRelease.patches && currentRelease.patches.length > this.patchesPerPage) {
const totalPages = Math.ceil(currentRelease.patches.length / this.patchesPerPage);
if (this.currentPatchPage > 0) {
options.push("u - Previous patches");
}
if (this.currentPatchPage < totalPages - 1) {
options.push("d - Next patches");
}
}
options.push("q - Quit");
console.log(chalk3.cyan("Navigation: ") + chalk3.dim(options.join(" | ")));
const input = await prompt("\nEnter command: ");
await this.handleInput(appId, platform, input.toLowerCase().trim());
}
async handleInput(appId, platform, input) {
switch (input) {
case "p":
if (this.currentReleaseIndex > 0) {
this.currentReleaseIndex--;
this.currentPatchPage = 0;
await this.showCurrentView(appId, platform);
}
break;
case "n":
if (this.currentReleaseIndex < this.data.releases.length - 1) {
this.currentReleaseIndex++;
this.currentPatchPage = 0;
await this.showCurrentView(appId, platform);
}
break;
case "u":
if (this.currentPatchPage > 0) {
this.currentPatchPage--;
await this.showCurrentView(appId, platform);
}
break;
case "d":
const currentRelease = this.data.releases[this.currentReleaseIndex];
const totalPages = Math.ceil(currentRelease.patches.length / this.patchesPerPage);
if (this.currentPatchPage < totalPages - 1) {
this.currentPatchPage++;
await this.showCurrentView(appId, platform);
}
break;
case "q":
console.log(chalk3.green("\n\u{1F44B} Goodbye!\n"));
break;
default:
console.log(chalk3.red("\n\u274C Invalid command. Please try again."));
await new Promise((resolve) => setTimeout(resolve, 1e3));
await this.showCurrentView(appId, platform);
break;
}
}
};
// src/utils/checks.ts
import chalk4 from "chalk";
var isValidPlatform = (platform) => {
if (![CONFIGS.ANDROID, CONFIGS.IOS].includes(platform)) {
console.error(chalk4.red(`
\u274C Invalid platform. Use '${CONFIGS.ANDROID}' or '${CONFIGS.IOS}'.
`));
process.exit(1);
}
};
var isValidVersion = (release2) => {
if (!release2 || !/^\d+\.\d+\.\d+$/.test(release2)) {
console.error(chalk4.red("\n\n\u274C Invalid release version format. Use 'x.x.x'\n\n"));
process.exit(1);
}
};
var isValidPatch = (patch) => {
if (!patch || !/^\d+\.\d+\.\d+_v\d+$/.test(patch)) {
console.error(chalk4.red("\n\n\u274C Invalid patch format. Use 'x.x.x_vx'\n\n"));
process.exit(1);
}
};
// src/commands.ts
var api = createApi({
baseUrl: `${CONFIGS.API_BASE_URL}${CONFIGS.API_VERSION}`,
timeout: 5e3,
retries: 2
});
var secureHeaders = {
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${getKey("auth") ? getKey("auth").access_token : ""}`
}
};
var login = async () => {
const email = await prompt(chalk5.blue("\u{1F4F1} Enter your email: "));
const password = await prompt(chalk5.blue("\u{1F4F1} Enter your password: "));
const spinner = new Spinner(`Logging in as ${email}...`);
try {
const response = await api.post(
"/login",
{ email, password }
);
if (response.success && response.data) {
const authTokens = {
email,
access_token: response.data.access_token,
refresh_token: response.data.refresh_token,
expires_at: response.data.expires_at || new Date(Date.now() + 7 * 24 * 3600 * 1e3).toISOString()
// Default to 1 week if not provided
};
setKey("auth", authTokens);
spinner.stop();
console.log(chalk5.green(`
\u2705 Logged in successfully as ${email}
`));
} else {
spinner.stop();
throw new Error(response.error || "Login failed");
}
} catch (error) {
spinner.stop();
console.error(chalk5.red("\n\u274C Login failed:\n"), error.message || error, "\n");
process.exit(1);
}
};
var refreshToken = async () => {
const auth = getKey("auth");
if (auth && new Date(auth.expires_at) < /* @__PURE__ */ new Date()) {
const spinner = new Spinner(`Auth token has expired. Re-authenticating...`);
try {
spinner.start();
const response = await api.post(
"/refresh-token",
{ email: auth.email, refresh_token: auth.refresh_token }
);
if (response.success && response.data) {
const authTokens = {
email: auth.email,
access_token: response.data.access_token,
refresh_token: response.data.refresh_token,
expires_at: response.data.expires_at || new Date(Date.now() + 7 * 24 * 3600 * 1e3).toISOString()
// Default to 1 week if not provided
};
setKey("auth", authTokens);
spinner.stop();
console.log(chalk5.green("\u2705 Auth token refreshed successfully!"));
} else {
spinner.stop();
console.error(chalk5.red(`\u274C Failed to refresh auth token. Please login using '${chalk5.italic("superpush login")}'.`));
process.exit(1);
}
} catch (error) {
spinner.stop();
console.error(chalk5.red("\n\u274C Failed to refresh auth token:\n"), error.message || error, "\n");
process.exit(1);
}
} else if (!auth) {
console.error(chalk5.red(`
\u274C No SuperPush session found. Please login first using '${chalk5.italic("superpush login")}'.
`));
process.exit(1);
}
};
var upgrade = (version2) => {
if (!version2) {
version2 = "latest";
}
executeInteractive(`npm i -g superpush@${version2}`).then((output) => {
console.log(chalk5.green(`
\u2705 Upgraded superpush version to ${version2}
`));
}).catch((error) => {
console.error(
chalk5.red(`
\u274C Failed to upgrade superpush version to ${version2}:
`),
error,
"\n"
);
});
};
var init = async () => {
if (!fs.existsSync(CONFIGS.CONFIG_DIR_PATH)) {
fs.mkdirSync(CONFIGS.CONFIG_DIR_PATH);
}
if (getKey(`${CONFIGS.PROJECT_DIR}`)) {
const overwrite = await prompt(chalk5.yellow("\n\u26A0\uFE0F Configuration file already exists. Overwrite? (y/N): "));
if (!["y", "yes"].includes(overwrite.toLowerCase())) {
console.log(chalk5.red("\u274C Initialization cancelled.\n"));
return;
}
}
console.log(chalk5.blue("\n\n \u{1F680} Welcome to SuperPush CLI! Let's set up your app...\n"));
while (true) {
try {
const appName = await prompt(chalk5.blue("\u{1F4F1} Enter your app name: "));
if (!appName || appName.trim() === "") {
console.log(chalk5.red("\u274C App name cannot be empty. Please try again or press Ctrl+C to quit."));
continue;
}
const trimmedAppName = appName.trim();
console.log(chalk5.cyan(`\u{1F50D} Checking availability for "${trimmedAppName}"...`));
const response = await api.post(
"/init",
{ app_name: trimmedAppName },
secureHeaders
);
if (response.success && response.data) {
const config2 = {
app_name: trimmedAppName,
app_id: response.data.id
};
setKey(`${CONFIGS.PROJECT_DIR}`, config2);
console.log(chalk5.green(`\u2705 App "${trimmedAppName}" created successfully!`));
console.log(`\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510`);
console.log("\u2502", chalk5.yellow("\u26A0\uFE0F Run the below command to ignore the configuration file from version control."), "\u2502");
console.log("\u2502", chalk5.gray.italic(` echo "\\n# superpush CLI\\n${CONFIGS.CONFIG_DIR}" >> .gitignore `), "\u2502");
console.log(`\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518`);
return;
} else {
throw new Error(response.error || "Failed to create app");
}
} catch (error) {
const errorMessage = error.message || error;
if (errorMessage.includes("already taken") || errorMessage.includes("exists") || errorMessage.includes("unavailable")) {
console.log(chalk5.red("\u274C App name is already taken. Please try a different name."));
continue;
} else {
console.error(chalk5.red(`\u274C Failed to create app: ${errorMessage}`));
console.log(chalk5.yellow("Please try again or press Ctrl+C to quit."));
continue;
}
}
}
};
var buildBundle = (platform) => {
const bundleOutput = platform === CONFIGS.ANDROID ? CONFIGS.ANDROID_BUNDLE_OUTPUT : CONFIGS.IOS_BUNDLE_OUTPUT;
const assetsDest = platform === CONFIGS.ANDROID ? CONFIGS.ANDROID_ASSET_DESTINATION : CONFIGS.IOS_ASSET_DESTINATION;
executeInteractive(
`npx react-native bundle --platform ${platform} --dev false --entry-file index.js --bundle-output ${bundleOutput} --assets-dest ${assetsDest}`
).then((output) => {
console.log(chalk5.green(`\u2705 Build successful for ${platform}
`));
}).catch((error) => {
console.error(chalk5.red("\u274C Build failed:\n"), error, "\n");
});
};
var build = (platform) => {
if (platform) {
isValidPlatform(platform);
console.log(chalk5.blue(`
Building for ${platform}...`));
buildBundle(platform);
} else {
console.log(chalk5.blue(`
Building for both ${CONFIGS.ANDROID} and ${CONFIGS.IOS}...`));
buildBundle(CONFIGS.ANDROID);
buildBundle(CONFIGS.IOS);
}
};
var release = async (platform, release_version, options) => {
isValidPlatform(platform);
isValidVersion(release_version);
const superpush_config = getKey(`${CONFIGS.PROJECT_DIR}`);
const bundleOutput = platform === CONFIGS.ANDROID ? CONFIGS.ANDROID_BUNDLE_OUTPUT : CONFIGS.IOS_BUNDLE_OUTPUT;
const platformDir = `${CONFIGS.CONFIG_DIR}/${platform}`;
const bundleOutputCompressed = `${CONFIGS.CONFIG_DIR}/${platform}_bundle.tar.br`;
if (!fs.existsSync(bundleOutput)) {
console.error(chalk5.red(`
\u274C Bundle file not found. Please run 'superpush build <platform>'
`));
process.exit(1);
}
console.log("");
const compressSpinner = new Spinner(`Compressing ${platform} bundle...`);
try {
compressSpinner.start();
await compressBundle(platformDir, bundleOutputCompressed);
compressSpinner.stop();
console.log(chalk5.green(`\u2705 Bundle compressed successfully
`));
} catch (error) {
compressSpinner.stop();
console.error(chalk5.red("\n\u274C Failed to compress bundle:\n"), error.message || error, "\n");
process.exit(1);
}
if (!fs.existsSync(bundleOutputCompressed)) {
console.error(chalk5.red(`\u274C Compressed bundle file not found. Please check the build process
`));
process.exit(1);
}
const bundleFile = fs.readFileSync(bundleOutputCompressed);
const formData = new FormData();
formData.append("app_id", superpush_config.app_id);
formData.append("platform", platform);
formData.append("release_version", release_version);
const fileName = path3.basename(bundleOutputCompressed);
formData.append("bundle", new Blob([bundleFile]), fileName);
if (options.message) formData.append("release_notes", options.message);
if (options.force) formData.append("mandatory", options.force);
if (options.rollout) formData.append("rollout", options.rollout);
let releaseSpinner = new Spinner(`Releasing ${platform} bundle...`);
try {
releaseSpinner.start();
const response = await api.post(
"/release",
formData,
{ ...secureHeaders, timeout: 0, retries: 0 }
);
releaseSpinner.stop();
if (response.success && response.data) {
console.log(chalk5.green(`\u2705 Successfully released the ${platform} bundle
`));
} else {
throw new Error(response.error || "Failed to release bundle");
}
} catch (error) {
releaseSpinner.stop();
const errorMessage = error.message || error;
console.error(chalk5.red("\n\u274C Failed to release the bundle:\n"), errorMessage, "\n");
process.exit(1);
}
if (options.delete) {
await rm(platformDir, { recursive: true, force: true });
await rm(bundleOutputCompressed, { recursive: true, force: true });
console.log(chalk5.yellow(`\u{1F5D1}\uFE0F Deleted ${platform} build files
`));
}
};
var rollback = async (platform, version2, patch) => {
isValidPlatform(platform);
isValidVersion(version2);
patch = `${version2}_${patch}`;
isValidPatch(patch);
const superpush_config = getKey(`${CONFIGS.PROJECT_DIR}`);
try {
const response = await api.post(
"/rollback",
{ app_id: superpush_config.app_id, platform, release_version: version2, composite_version: patch },
secureHeaders
);
if (response.success && response.data) {
console.log(chalk5.green(`\u2705 ${response.data.message}`));
} else {
throw new Error(response.error || "Failed to fetch release history");
}
} catch (error) {
const errorMessage = error.message || error;
console.error(chalk5.red("\n\u274C Failed to rollback:\n"), errorMessage, "\n");
}
};
var history = async (platform) => {
isValidPlatform(platform);
const superpush_config = getKey(`${CONFIGS.PROJECT_DIR}`);
try {
const response = await api.get(
"/releases",
{ app_id: superpush_config.app_id, platform },
secureHeaders
);
if (response.success && response.data) {
const interactiveHistory = new InteractiveHistory(response.data);
await interactiveHistory.start(superpush_config.app_id, platform);
} else {
throw new Error(response.error || "Failed to fetch release history");
}
} catch (error) {
const errorMessage = error.message || error;
console.error(chalk5.red("\n\u274C Failed to fetch release history:\n"), errorMessage, "\n");
}
};
var disable = async (platform, version2, patch) => {
isValidPlatform(platform);
isValidVersion(version2);
patch = `${version2}_${patch}`;
isValidPatch(patch);
const superpush_config = getKey(`${CONFIGS.PROJECT_DIR}`);
try {
const response = await api.post(
"/disable",
{ app_id: superpush_config.app_id, platform, release_version: version2, composite_version: patch },
secureHeaders
);
if (response.success && response.data) {
console.log(chalk5.green(`\u2705 ${response.data.message}`));
} else {
throw new Error(response.error || "Failed to disable the patch");
}
} catch (error) {
const errorMessage = error.message || error;
console.error(chalk5.red("\n\u274C Failed to disable:\n"), errorMessage, "\n");
}
};
var logout = async () => {
const auth = getKey("auth");
if (!auth) {
console.error(chalk5.red(`
\u274C No SuperPush session found. Please login first using '${chalk5.italic("superpush login")}'.
`));
return;
}
const confirm = await prompt(chalk5.yellow(`
Are you sure you want to logout from SuperPush? (y/N): `));
if (!["y", "yes"].includes(confirm.toLowerCase())) {
console.log(chalk5.red("\u274C Logout cancelled.\n"));
return;
}
deleteKey("auth");
console.log(chalk5.green(`\u2705 Logged out successfully from SuperPush as ${auth.email}
`));
};
// src/wrapper.ts
var wrapper = async (cmd, callback) => {
if (!["upgrade", "login", "build", "logout"].includes(cmd)) {
await refreshToken();
}
if (!["upgrade", "login", "init", "build", "logout"].includes(cmd) && !getKey(`${CONFIGS.PROJECT_DIR}`)) {
console.error(chalk6.red(`Application not configured. Please run '${chalk6.italic("superpush init")}' to configure.
`));
process.exit(1);
}
await Promise.resolve(callback());
};
// src/index.ts
var packageJsonPath = path4.join(__dirname, "..", "package.json");
var { version } = JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8"));
var program = new Command();
program.name("superpush").description("A SuperPe CodePush CLI tool").version(version, "-v, --version", "Output the current version of SuperPe CodePush CLI");
program.command("login").description("Login to SuperPush").action(() => wrapper("login", () => login()));
program.command("upgrade").description("Upgrade to the latest version of superpush cli").argument("[version]", "Upgrade to a specify a version").action((version2) => wrapper("upgrade", () => upgrade(version2)));
program.command("init").description("Initialise the react application name").action(() => wrapper("init", async () => init()));
program.command("build").description("Build the bundle").argument("[platform]", "Platform to build for (e.g., android, ios). By default, it builds for both platforms.").action((platform) => wrapper("build", () => build(platform)));
program.command("release").description("Release the bundle").argument("<platform>", "Platform of the bundle").argument("<release_version>", "Release version to push (e.g., x.x.x)").option("-m, --message <message>", "Release message").option("-f, --force", "Mark the release as mandatory").option("-r, --rollout <rollout>", "Release rollout percentage", (value) => parseInt(value, 10)).option("-d, --delete", "Delete the bundle directory post release").action(
(platform, release_version, options) => wrapper("release", () => release(platform, release_version, options))
);
program.command("rollback").description("Rollback to specific version").argument("<platform>", "Platform to rollback (e.g., android, ios)").argument("<release>", "Rollback to specified version (e.g., x.x.x)").argument("<patch>", "Rollback to specified patch (e.g., vx)").action(
(platform, release2, patch) => wrapper("rollback", () => rollback(platform, release2, patch))
);
program.command("history").description("List the releases history").argument("<platform>", "Releases by platform (e.g., android, ios)").action((platform) => wrapper("history", () => history(platform)));
program.command("disable").description("Disable the release for a specific platform").argument("<platform>", "Platform to disable (e.g., android, ios)").argument("<release>", "Disable specified version (e.g., x.x.x)").argument("<patch>", "Disable specified patch (e.g., vx)").action(
(platform, release2, patch) => wrapper("disable", () => disable(platform, release2, patch))
);
program.command("logout").description("Logout from SuperPush").action(() => wrapper("logout", () => logout()));
program.parse();
//# sourceMappingURL=index.js.map