superpush
Version:
A command-line interface for SuperPe CodePush - enabling over-the-air (OTA) updates for React Native applications.
799 lines (766 loc) • 32.9 kB
JavaScript
; function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2;
// src/index.ts
var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs);
var _path = require('path'); var path2 = _interopRequireWildcard(_path);
var _commander = require('commander');
// src/wrapper.ts
var _chalk = require('chalk'); var _chalk2 = _interopRequireDefault(_chalk);
// src/configs.ts
var _dotenv = require('dotenv'); var _dotenv2 = _interopRequireDefault(_dotenv);
_dotenv2.default.config();
var CONFIG_DIR = ".superpush";
var CONFIG_DIR_PATH = path2.default.join(process.cwd(), CONFIG_DIR);
var CONFIGS = {
PROJECT_DIR: path2.default.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
var _conf = require('conf'); var _conf2 = _interopRequireDefault(_conf);
var config = new (0, _conf2.default)({ 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
var _promises = require('fs/promises');
// src/utils/prompt.ts
var _readline = require('readline'); var readline = _interopRequireWildcard(_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 {
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 = _optionalChain([contentType, 'optionalAccess', _ => _.includes, 'call', _2 => _2("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
var Spinner = (_class = class {
__init() {this.frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"]}
__init2() {this.interval = null}
__init3() {this.currentFrame = 0}
constructor(message) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);
this.message = message;
}
start() {
process.stdout.write("\x1B[?25l");
this.interval = setInterval(() => {
process.stdout.write("\r" + _chalk2.default.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);
}
}
}, _class);
// src/utils/compress.ts
var _promises3 = require('stream/promises');
var _zlib = require('zlib');
var _child_process = require('child_process');
var compressBundle = async (platformDir, bundleOutputCompressed) => {
return new Promise((resolve, reject) => {
const tar = _child_process.spawn.call(void 0, "tar", [
"--exclude=*/.DS_Store/*",
"--exclude=._*",
"--exclude=*/._*",
"-c",
"-C",
path2.dirname(platformDir),
path2.basename(platformDir)
]);
const writeStream = _fs.createWriteStream.call(void 0, bundleOutputCompressed);
const brotliCompress = _zlib.createBrotliCompress.call(void 0, );
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()}`));
}
});
_promises3.pipeline.call(void 0, 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
var executeInteractive = (cmd, args = [], options = {}) => {
return new Promise((resolve, reject) => {
const child = _child_process.spawn.call(void 0, cmd, args, {
stdio: "inherit",
shell: true,
...options
});
child.on("close", (code) => {
resolve(_nullishCoalesce(code, () => ( 0)));
});
child.on("error", (err) => {
reject(err);
});
});
};
// src/utils/interactive-history.ts
var InteractiveHistory = (_class2 = class {
__init4() {this.currentReleaseIndex = 0}
__init5() {this.currentPatchPage = 0}
__init6() {this.patchesPerPage = 5}
constructor(data) {;_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);
this.data = data;
}
async start(appId, platform) {
console.log(_chalk2.default.blue(`
\u{1F4F1} App ID: ${appId}`));
console.log(_chalk2.default.blue(`\u{1F527} Platform: ${platform}
`));
if (!this.data.releases || this.data.releases.length === 0) {
console.log(_chalk2.default.yellow("\u26A0\uFE0F No releases found for this platform.\n"));
return;
}
await this.showCurrentView(appId, platform);
}
async showCurrentView(appId, platform) {
console.clear();
console.log(_chalk2.default.blue(`\u{1F4F1} App ID: ${appId} | \u{1F527} Platform: ${platform}`));
console.log(_chalk2.default.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(_chalk2.default.green(`${versionIcon} Version: ${currentRelease.version} (${this.currentReleaseIndex + 1}/${this.data.releases.length})`));
if (currentRelease.patches && currentRelease.patches.length > 0) {
console.log(_chalk2.default.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 ? _chalk2.default.bold.green : _chalk2.default.gray;
const latestLabel = patch.is_latest ? " (LATEST)" : "";
const messageText = patch.message ? ` - ${patch.message}` : "";
console.log(patchColor(` ${patchIcon} ${patch.version}${latestLabel}${_chalk2.default.dim(messageText)}`));
});
if (currentRelease.patches.length > this.patchesPerPage) {
const totalPages = Math.ceil(currentRelease.patches.length / this.patchesPerPage);
console.log(_chalk2.default.dim(`
Showing ${startIndex + 1}-${endIndex} of ${currentRelease.patches.length} patches (Page ${this.currentPatchPage + 1}/${totalPages})`));
}
} else {
console.log(_chalk2.default.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(_chalk2.default.cyan("Navigation: ") + _chalk2.default.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(_chalk2.default.green("\n\u{1F44B} Goodbye!\n"));
break;
default:
console.log(_chalk2.default.red("\n\u274C Invalid command. Please try again."));
await new Promise((resolve) => setTimeout(resolve, 1e3));
await this.showCurrentView(appId, platform);
break;
}
}
}, _class2);
// src/utils/checks.ts
var isValidPlatform = (platform) => {
if (![CONFIGS.ANDROID, CONFIGS.IOS].includes(platform)) {
console.error(_chalk2.default.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(_chalk2.default.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(_chalk2.default.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(_chalk2.default.blue("\u{1F4F1} Enter your email: "));
const password = await prompt(_chalk2.default.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(_chalk2.default.green(`
\u2705 Logged in successfully as ${email}
`));
} else {
spinner.stop();
throw new Error(response.error || "Login failed");
}
} catch (error) {
spinner.stop();
console.error(_chalk2.default.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(_chalk2.default.green("\u2705 Auth token refreshed successfully!"));
} else {
spinner.stop();
console.error(_chalk2.default.red(`\u274C Failed to refresh auth token. Please login using '${_chalk2.default.italic("superpush login")}'.`));
process.exit(1);
}
} catch (error) {
spinner.stop();
console.error(_chalk2.default.red("\n\u274C Failed to refresh auth token:\n"), error.message || error, "\n");
process.exit(1);
}
} else if (!auth) {
console.error(_chalk2.default.red(`
\u274C No SuperPush session found. Please login first using '${_chalk2.default.italic("superpush login")}'.
`));
process.exit(1);
}
};
var upgrade = (version2) => {
if (!version2) {
version2 = "latest";
}
executeInteractive(`npm i -g superpush@${version2}`).then((output) => {
console.log(_chalk2.default.green(`
\u2705 Upgraded superpush version to ${version2}
`));
}).catch((error) => {
console.error(
_chalk2.default.red(`
\u274C Failed to upgrade superpush version to ${version2}:
`),
error,
"\n"
);
});
};
var init = async () => {
if (!_fs2.default.existsSync(CONFIGS.CONFIG_DIR_PATH)) {
_fs2.default.mkdirSync(CONFIGS.CONFIG_DIR_PATH);
}
if (getKey(`${CONFIGS.PROJECT_DIR}`)) {
const overwrite = await prompt(_chalk2.default.yellow("\n\u26A0\uFE0F Configuration file already exists. Overwrite? (y/N): "));
if (!["y", "yes"].includes(overwrite.toLowerCase())) {
console.log(_chalk2.default.red("\u274C Initialization cancelled.\n"));
return;
}
}
console.log(_chalk2.default.blue("\n\n \u{1F680} Welcome to SuperPush CLI! Let's set up your app...\n"));
while (true) {
try {
const appName = await prompt(_chalk2.default.blue("\u{1F4F1} Enter your app name: "));
if (!appName || appName.trim() === "") {
console.log(_chalk2.default.red("\u274C App name cannot be empty. Please try again or press Ctrl+C to quit."));
continue;
}
const trimmedAppName = appName.trim();
console.log(_chalk2.default.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(_chalk2.default.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", _chalk2.default.yellow("\u26A0\uFE0F Run the below command to ignore the configuration file from version control."), "\u2502");
console.log("\u2502", _chalk2.default.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(_chalk2.default.red("\u274C App name is already taken. Please try a different name."));
continue;
} else {
console.error(_chalk2.default.red(`\u274C Failed to create app: ${errorMessage}`));
console.log(_chalk2.default.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(_chalk2.default.green(`\u2705 Build successful for ${platform}
`));
}).catch((error) => {
console.error(_chalk2.default.red("\u274C Build failed:\n"), error, "\n");
});
};
var build = (platform) => {
if (platform) {
isValidPlatform(platform);
console.log(_chalk2.default.blue(`
Building for ${platform}...`));
buildBundle(platform);
} else {
console.log(_chalk2.default.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 (!_fs2.default.existsSync(bundleOutput)) {
console.error(_chalk2.default.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(_chalk2.default.green(`\u2705 Bundle compressed successfully
`));
} catch (error) {
compressSpinner.stop();
console.error(_chalk2.default.red("\n\u274C Failed to compress bundle:\n"), error.message || error, "\n");
process.exit(1);
}
if (!_fs2.default.existsSync(bundleOutputCompressed)) {
console.error(_chalk2.default.red(`\u274C Compressed bundle file not found. Please check the build process
`));
process.exit(1);
}
const bundleFile = _fs2.default.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 = path2.default.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(_chalk2.default.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(_chalk2.default.red("\n\u274C Failed to release the bundle:\n"), errorMessage, "\n");
process.exit(1);
}
if (options.delete) {
await _promises.rm.call(void 0, platformDir, { recursive: true, force: true });
await _promises.rm.call(void 0, bundleOutputCompressed, { recursive: true, force: true });
console.log(_chalk2.default.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(_chalk2.default.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(_chalk2.default.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(_chalk2.default.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(_chalk2.default.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(_chalk2.default.red("\n\u274C Failed to disable:\n"), errorMessage, "\n");
}
};
var logout = async () => {
const auth = getKey("auth");
if (!auth) {
console.error(_chalk2.default.red(`
\u274C No SuperPush session found. Please login first using '${_chalk2.default.italic("superpush login")}'.
`));
return;
}
const confirm = await prompt(_chalk2.default.yellow(`
Are you sure you want to logout from SuperPush? (y/N): `));
if (!["y", "yes"].includes(confirm.toLowerCase())) {
console.log(_chalk2.default.red("\u274C Logout cancelled.\n"));
return;
}
deleteKey("auth");
console.log(_chalk2.default.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(_chalk2.default.red(`Application not configured. Please run '${_chalk2.default.italic("superpush init")}' to configure.
`));
process.exit(1);
}
await Promise.resolve(callback());
};
// src/index.ts
var packageJsonPath = path2.default.join(__dirname, "..", "package.json");
var { version } = JSON.parse(_fs2.default.readFileSync(packageJsonPath, "utf-8"));
var program = new (0, _commander.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.cjs.map