UNPKG

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
#!/usr/bin/env node // 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