swiftpatch-cli 1.1.3 → 1.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -57,16 +57,26 @@ __export(api_exports, {
57
57
  api: () => api
58
58
  });
59
59
  import axios from "axios";
60
- function warnIfInsecure(url) {
60
+ import { readFileSync } from "fs";
61
+ import { dirname, join } from "path";
62
+ import { fileURLToPath as fileURLToPath2 } from "url";
63
+ function rejectIfInsecure(url) {
61
64
  if (url.startsWith("http://") && !url.includes("localhost") && !url.includes("127.0.0.1")) {
62
- console.warn(
63
- "\x1B[33m\u26A0 Warning: API URL uses HTTP instead of HTTPS. This is insecure for production use.\x1B[0m"
64
- );
65
+ if (process.env.SWIFTPATCH_ALLOW_INSECURE === "1") {
66
+ console.warn(
67
+ "\x1B[33m\u26A0 Warning: API URL uses HTTP instead of HTTPS. Credentials will be sent in plaintext.\x1B[0m"
68
+ );
69
+ } else {
70
+ throw new Error(
71
+ "API URL uses HTTP instead of HTTPS. This is insecure and credentials would be sent in plaintext.\nSet SWIFTPATCH_ALLOW_INSECURE=1 to bypass this check (not recommended for production)."
72
+ );
73
+ }
65
74
  }
66
75
  }
67
- function normalizeIds(obj) {
76
+ function normalizeIds(obj, depth = 0) {
77
+ if (depth > 20) return obj;
68
78
  if (obj === null || obj === void 0) return obj;
69
- if (Array.isArray(obj)) return obj.map(normalizeIds);
79
+ if (Array.isArray(obj)) return obj.map((item) => normalizeIds(item, depth + 1));
70
80
  if (typeof obj === "object") {
71
81
  const result = {};
72
82
  for (const [key, value] of Object.entries(obj)) {
@@ -75,14 +85,14 @@ function normalizeIds(obj) {
75
85
  } else if (key === "__v") {
76
86
  continue;
77
87
  } else {
78
- result[key] = normalizeIds(value);
88
+ result[key] = normalizeIds(value, depth + 1);
79
89
  }
80
90
  }
81
91
  return result;
82
92
  }
83
93
  return obj;
84
94
  }
85
- var API_BASE_URL, ApiClient, api;
95
+ var API_BASE_URL, __dirname2, CLI_VERSION, ApiClient, api;
86
96
  var init_api = __esm({
87
97
  "src/lib/api.ts"() {
88
98
  "use strict";
@@ -90,17 +100,24 @@ var init_api = __esm({
90
100
  init_auth();
91
101
  init_config();
92
102
  API_BASE_URL = process.env.SWIFTPATCH_API_URL || "https://api.blendapp.ae/api/v1";
103
+ __dirname2 = dirname(fileURLToPath2(import.meta.url));
104
+ CLI_VERSION = "1.0.0";
105
+ try {
106
+ const pkg = JSON.parse(readFileSync(join(__dirname2, "../package.json"), "utf-8"));
107
+ CLI_VERSION = pkg.version;
108
+ } catch {
109
+ }
93
110
  ApiClient = class {
94
111
  client;
95
112
  constructor() {
96
113
  const baseURL = config.get("apiUrl") || API_BASE_URL;
97
- warnIfInsecure(baseURL);
114
+ rejectIfInsecure(baseURL);
98
115
  this.client = axios.create({
99
116
  baseURL,
100
117
  timeout: 3e4,
101
118
  headers: {
102
119
  "Content-Type": "application/json",
103
- "User-Agent": "swiftpatch-cli/1.0.0"
120
+ "User-Agent": `swiftpatch-cli/${CLI_VERSION}`
104
121
  }
105
122
  });
106
123
  this.client.interceptors.request.use((reqConfig) => {
@@ -476,8 +493,8 @@ init_esm_shims();
476
493
  import { Command as Command47 } from "commander";
477
494
  import chalk45 from "chalk";
478
495
  import { readFileSync as readFileSync3 } from "fs";
479
- import { fileURLToPath as fileURLToPath2 } from "url";
480
- import { dirname as dirname2, join as join2 } from "path";
496
+ import { fileURLToPath as fileURLToPath3 } from "url";
497
+ import { dirname as dirname3, join as join3 } from "path";
481
498
 
482
499
  // src/commands/login.ts
483
500
  init_esm_shims();
@@ -520,7 +537,7 @@ var logger = {
520
537
  };
521
538
 
522
539
  // src/commands/login.ts
523
- var loginCommand = new Command("login").description("Authenticate with SwiftPatch").option("-k, --api-key <key>", "Login with API key (for CI/CD)").option("-e, --email <email>", "Login with email and password").option("-p, --password <password>", "Password (used with --email)").option("-i, --interactive", "Force interactive login").action(async (options) => {
540
+ var loginCommand = new Command("login").description("Authenticate with SwiftPatch").option("-k, --api-key <key>", "Login with API key (for CI/CD)").option("-e, --email <email>", "Login with email and password").option("-i, --interactive", "Force interactive login").action(async (options) => {
524
541
  const existingToken = auth.getToken();
525
542
  if (existingToken && !options.interactive && !options.apiKey && !options.email) {
526
543
  const user = await api.getCurrentUser().catch(() => null);
@@ -556,7 +573,7 @@ var loginCommand = new Command("login").description("Authenticate with SwiftPatc
556
573
  return;
557
574
  }
558
575
  if (options.email) {
559
- await emailLogin(options.email, options.password);
576
+ await emailLogin(options.email);
560
577
  return;
561
578
  }
562
579
  const { method } = await inquirer.prompt([
@@ -579,19 +596,17 @@ var loginCommand = new Command("login").description("Authenticate with SwiftPatc
579
596
  await apiKeyLogin();
580
597
  }
581
598
  });
582
- async function emailLogin(email, password) {
583
- if (!password) {
584
- const answers = await inquirer.prompt([
585
- {
586
- type: "password",
587
- name: "password",
588
- message: "Password:",
589
- mask: "*",
590
- validate: (input) => input ? true : "Password is required"
591
- }
592
- ]);
593
- password = answers.password;
594
- }
599
+ async function emailLogin(email) {
600
+ const answers = await inquirer.prompt([
601
+ {
602
+ type: "password",
603
+ name: "password",
604
+ message: "Password:",
605
+ mask: "*",
606
+ validate: (input) => input ? true : "Password is required"
607
+ }
608
+ ]);
609
+ const password = answers.password;
595
610
  const spinner = ora("Logging in...").start();
596
611
  try {
597
612
  const result = await api.loginWithCredentials(email, password);
@@ -615,26 +630,19 @@ async function emailLogin(email, password) {
615
630
  }
616
631
  }
617
632
  async function emailLoginInteractive() {
618
- const { email, password } = await inquirer.prompt([
633
+ const { email } = await inquirer.prompt([
619
634
  {
620
635
  type: "input",
621
636
  name: "email",
622
637
  message: "Email:",
623
638
  validate: (input) => {
624
639
  if (!input) return "Email is required";
625
- if (!input.includes("@")) return "Invalid email format";
640
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input)) return "Invalid email format";
626
641
  return true;
627
642
  }
628
- },
629
- {
630
- type: "password",
631
- name: "password",
632
- message: "Password:",
633
- mask: "*",
634
- validate: (input) => input ? true : "Password is required"
635
643
  }
636
644
  ]);
637
- await emailLogin(email, password);
645
+ await emailLogin(email);
638
646
  }
639
647
  async function browserLogin() {
640
648
  const spinner = ora("Opening browser...").start();
@@ -1150,7 +1158,6 @@ import { Command as Command10 } from "commander";
1150
1158
  import chalk12 from "chalk";
1151
1159
  import inquirer6 from "inquirer";
1152
1160
  import ora7 from "ora";
1153
- import path4 from "path";
1154
1161
  import fs6 from "fs-extra";
1155
1162
 
1156
1163
  // src/lib/bundler.ts
@@ -1168,8 +1175,7 @@ var bundler = {
1168
1175
  minify = true,
1169
1176
  sourcemap = false
1170
1177
  } = options;
1171
- const tempDir = path2.join(os2.tmpdir(), `swiftpatch-bundle-${Date.now()}`);
1172
- await fs.ensureDir(tempDir);
1178
+ const tempDir = await fs.mkdtemp(path2.join(os2.tmpdir(), "swiftpatch-bundle-"));
1173
1179
  const bundlePath = path2.join(tempDir, `index.${platform}.bundle`);
1174
1180
  const sourcemapPath = sourcemap ? path2.join(tempDir, `index.${platform}.bundle.map`) : void 0;
1175
1181
  const args = [
@@ -1206,8 +1212,7 @@ var bundler = {
1206
1212
  function runCommand(command, args) {
1207
1213
  return new Promise((resolve, reject) => {
1208
1214
  const child = spawn(command, args, {
1209
- stdio: ["inherit", "pipe", "pipe"],
1210
- shell: true
1215
+ stdio: ["inherit", "pipe", "pipe"]
1211
1216
  });
1212
1217
  let stderr = "";
1213
1218
  child.stdout?.on("data", () => {
@@ -1232,9 +1237,13 @@ function runCommand(command, args) {
1232
1237
  init_esm_shims();
1233
1238
  import axios2 from "axios";
1234
1239
  import fs2 from "fs-extra";
1240
+ var MAX_UPLOAD_SIZE = 500 * 1024 * 1024;
1235
1241
  var uploader = {
1236
1242
  async uploadBundle(uploadUrl, bundlePath, onProgress) {
1237
1243
  const fileSize = (await fs2.stat(bundlePath)).size;
1244
+ if (fileSize > MAX_UPLOAD_SIZE) {
1245
+ throw new Error(`Bundle size (${(fileSize / 1024 / 1024).toFixed(1)}MB) exceeds maximum upload size of ${MAX_UPLOAD_SIZE / 1024 / 1024}MB`);
1246
+ }
1238
1247
  const fileStream = fs2.createReadStream(bundlePath);
1239
1248
  let uploadedBytes = 0;
1240
1249
  fileStream.on("data", (chunk) => {
@@ -1256,6 +1265,9 @@ var uploader = {
1256
1265
  */
1257
1266
  async uploadZip(uploadUrl, zipPath, onProgress) {
1258
1267
  const fileSize = (await fs2.stat(zipPath)).size;
1268
+ if (fileSize > MAX_UPLOAD_SIZE) {
1269
+ throw new Error(`ZIP size (${(fileSize / 1024 / 1024).toFixed(1)}MB) exceeds maximum upload size of ${MAX_UPLOAD_SIZE / 1024 / 1024}MB`);
1270
+ }
1259
1271
  const fileStream = fs2.createReadStream(zipPath);
1260
1272
  let uploadedBytes = 0;
1261
1273
  fileStream.on("data", (chunk) => {
@@ -1296,7 +1308,6 @@ function sleep2(ms) {
1296
1308
  init_esm_shims();
1297
1309
  import crypto2 from "crypto";
1298
1310
  import fs3 from "fs-extra";
1299
- import { readFileSync } from "fs";
1300
1311
  async function hashBundle(bundlePath) {
1301
1312
  return new Promise((resolve, reject) => {
1302
1313
  const hash = crypto2.createHash("sha256");
@@ -1306,14 +1317,6 @@ async function hashBundle(bundlePath) {
1306
1317
  stream.on("error", reject);
1307
1318
  });
1308
1319
  }
1309
- function calculateZipHash(filePath) {
1310
- try {
1311
- const fileBuffer = readFileSync(filePath);
1312
- return crypto2.createHash("sha256").update(fileBuffer).digest("hex");
1313
- } catch {
1314
- return null;
1315
- }
1316
- }
1317
1320
  async function hashFile(filePath) {
1318
1321
  return new Promise((resolve, reject) => {
1319
1322
  const hash = crypto2.createHash("sha256");
@@ -1677,14 +1680,13 @@ var releaseCommand = new Command10("release").description("Bundle and publish a
1677
1680
  });
1678
1681
  async function detectAppFromConfig() {
1679
1682
  const configPaths = [
1680
- "./swiftpatch.config.js",
1681
1683
  "./swiftpatch.config.json",
1682
1684
  "./.swiftpatchrc"
1683
1685
  ];
1684
1686
  for (const configPath of configPaths) {
1685
1687
  if (await fs6.pathExists(configPath)) {
1686
1688
  try {
1687
- const config2 = configPath.endsWith(".js") ? (await import(path4.resolve(configPath))).default : await fs6.readJson(configPath);
1689
+ const config2 = await fs6.readJson(configPath);
1688
1690
  return config2.appId || config2.app;
1689
1691
  } catch {
1690
1692
  continue;
@@ -2794,7 +2796,7 @@ init_esm_shims();
2794
2796
  import { Command as Command34 } from "commander";
2795
2797
  import chalk32 from "chalk";
2796
2798
  import ora27 from "ora";
2797
- import path5 from "path";
2799
+ import path4 from "path";
2798
2800
  import fs7 from "fs-extra";
2799
2801
 
2800
2802
  // src/lib/claude.ts
@@ -2954,7 +2956,7 @@ async function collectDiagnostics(projectRoot) {
2954
2956
  gradleDeps: [],
2955
2957
  warnings: []
2956
2958
  };
2957
- const pkgPath = path5.join(projectRoot, "package.json");
2959
+ const pkgPath = path4.join(projectRoot, "package.json");
2958
2960
  if (await fs7.pathExists(pkgPath)) {
2959
2961
  try {
2960
2962
  diag.packageJson = await fs7.readJson(pkgPath);
@@ -2985,13 +2987,13 @@ async function collectDiagnostics(projectRoot) {
2985
2987
  }
2986
2988
  const assetDirs = ["assets", "src/assets", "app/assets", "src/images"];
2987
2989
  for (const dir of assetDirs) {
2988
- const fullDir = path5.join(projectRoot, dir);
2990
+ const fullDir = path4.join(projectRoot, dir);
2989
2991
  if (await fs7.pathExists(fullDir)) {
2990
2992
  try {
2991
2993
  const files = await fs7.readdir(fullDir);
2992
2994
  for (const file of files.slice(0, 50)) {
2993
2995
  try {
2994
- const stat = await fs7.stat(path5.join(fullDir, file));
2996
+ const stat = await fs7.stat(path4.join(fullDir, file));
2995
2997
  if (stat.isFile() && stat.size > 100 * 1024) {
2996
2998
  diag.assets.push({ name: `${dir}/${file}`, sizeKB: Math.round(stat.size / 1024) });
2997
2999
  }
@@ -3002,7 +3004,7 @@ async function collectDiagnostics(projectRoot) {
3002
3004
  }
3003
3005
  }
3004
3006
  }
3005
- const gradlePath = path5.join(projectRoot, "android/app/build.gradle");
3007
+ const gradlePath = path4.join(projectRoot, "android/app/build.gradle");
3006
3008
  if (await fs7.pathExists(gradlePath)) {
3007
3009
  try {
3008
3010
  const gradle = await fs7.readFile(gradlePath, "utf-8");
@@ -3014,7 +3016,7 @@ async function collectDiagnostics(projectRoot) {
3014
3016
  } catch {
3015
3017
  }
3016
3018
  }
3017
- const gradlePropsPath = path5.join(projectRoot, "android/gradle.properties");
3019
+ const gradlePropsPath = path4.join(projectRoot, "android/gradle.properties");
3018
3020
  if (await fs7.pathExists(gradlePropsPath)) {
3019
3021
  try {
3020
3022
  const props = await fs7.readFile(gradlePropsPath, "utf-8");
@@ -3025,9 +3027,9 @@ async function collectDiagnostics(projectRoot) {
3025
3027
  }
3026
3028
  }
3027
3029
  const signingPaths = [
3028
- path5.join(projectRoot, "swiftpatch-private.pem"),
3029
- path5.join(projectRoot, ".swiftpatch/private.pem"),
3030
- path5.join(projectRoot, "keys/swiftpatch-private.pem")
3030
+ path4.join(projectRoot, "swiftpatch-private.pem"),
3031
+ path4.join(projectRoot, ".swiftpatch/private.pem"),
3032
+ path4.join(projectRoot, "keys/swiftpatch-private.pem")
3031
3033
  ];
3032
3034
  for (const p of signingPaths) {
3033
3035
  if (await fs7.pathExists(p)) {
@@ -3035,7 +3037,7 @@ async function collectDiagnostics(projectRoot) {
3035
3037
  break;
3036
3038
  }
3037
3039
  }
3038
- const podfileLock = path5.join(projectRoot, "ios/Podfile.lock");
3040
+ const podfileLock = path4.join(projectRoot, "ios/Podfile.lock");
3039
3041
  if (await fs7.pathExists(podfileLock)) {
3040
3042
  try {
3041
3043
  const stat = await fs7.stat(podfileLock);
@@ -3046,8 +3048,8 @@ async function collectDiagnostics(projectRoot) {
3046
3048
  return diag;
3047
3049
  }
3048
3050
  var aiDoctorCommand = new Command34("doctor").description("AI-powered pre-publish diagnostics for your React Native project").option("-d, --dir <path>", "Project root directory", process.cwd()).option("-y, --yes", "Skip consent prompt (for CI/CD)").option("--json", "Output raw diagnostics as JSON").action(async (options) => {
3049
- const projectRoot = path5.resolve(options.dir);
3050
- if (!await fs7.pathExists(path5.join(projectRoot, "package.json"))) {
3051
+ const projectRoot = path4.resolve(options.dir);
3052
+ if (!await fs7.pathExists(path4.join(projectRoot, "package.json"))) {
3051
3053
  logger.error("No package.json found. Run this from your React Native project root.");
3052
3054
  process.exit(1);
3053
3055
  }
@@ -3809,83 +3811,65 @@ init_api();
3809
3811
  import { Command as Command39 } from "commander";
3810
3812
  import chalk37 from "chalk";
3811
3813
  import ora31 from "ora";
3812
- import path9 from "path";
3813
- import fs11 from "fs-extra";
3814
+ import path7 from "path";
3815
+ import fs10 from "fs-extra";
3814
3816
  import os3 from "os";
3815
3817
 
3816
- // src/lib/archive.ts
3817
- init_esm_shims();
3818
- import archiver from "archiver";
3819
- import fs8 from "fs";
3820
- import path6 from "path";
3821
- function createZip(inputPath, outputPath) {
3822
- return new Promise((resolve, reject) => {
3823
- const zipPath = path6.join(outputPath, "build.zip");
3824
- const output = fs8.createWriteStream(zipPath);
3825
- const archive = archiver("zip", { zlib: { level: 9 } });
3826
- output.on("close", () => resolve());
3827
- archive.on("error", (err) => reject(err));
3828
- archive.pipe(output);
3829
- archive.directory(inputPath, "build");
3830
- archive.finalize();
3831
- });
3832
- }
3833
-
3834
3818
  // src/lib/artifacts.ts
3835
3819
  init_esm_shims();
3836
- import fs9 from "fs-extra";
3837
- import path7 from "path";
3820
+ import fs8 from "fs-extra";
3821
+ import path5 from "path";
3838
3822
  async function keepArtifacts(contentRootPath, platform, variant) {
3839
- const artifactsDir = path7.join(process.cwd(), "swiftpatch-artifacts", platform, variant);
3840
- await fs9.ensureDir(artifactsDir);
3841
- const bundlesDir = path7.join(contentRootPath, "bundles");
3842
- const sourcemapsDir = path7.join(contentRootPath, "sourcemaps");
3843
- if (await fs9.pathExists(bundlesDir)) {
3844
- await copyDirRecursive(bundlesDir, path7.join(artifactsDir, "bundles"));
3823
+ const artifactsDir = path5.join(process.cwd(), "swiftpatch-artifacts", platform, variant);
3824
+ await fs8.ensureDir(artifactsDir);
3825
+ const bundlesDir = path5.join(contentRootPath, "bundles");
3826
+ const sourcemapsDir = path5.join(contentRootPath, "sourcemaps");
3827
+ if (await fs8.pathExists(bundlesDir)) {
3828
+ await copyDirRecursive(bundlesDir, path5.join(artifactsDir, "bundles"));
3845
3829
  }
3846
- if (await fs9.pathExists(sourcemapsDir)) {
3847
- await copyDirRecursive(sourcemapsDir, path7.join(artifactsDir, "sourcemaps"));
3830
+ if (await fs8.pathExists(sourcemapsDir)) {
3831
+ await copyDirRecursive(sourcemapsDir, path5.join(artifactsDir, "sourcemaps"));
3848
3832
  }
3849
3833
  }
3850
3834
  async function copyDirRecursive(srcDir, destDir) {
3851
- await fs9.ensureDir(destDir);
3852
- const entries = await fs9.readdir(srcDir);
3835
+ await fs8.ensureDir(destDir);
3836
+ const entries = await fs8.readdir(srcDir);
3853
3837
  for (const entry of entries) {
3854
- const srcPath = path7.join(srcDir, entry);
3855
- const destPath = path7.join(destDir, entry);
3856
- const stat = await fs9.stat(srcPath);
3838
+ const srcPath = path5.join(srcDir, entry);
3839
+ const destPath = path5.join(destDir, entry);
3840
+ const stat = await fs8.stat(srcPath);
3857
3841
  if (stat.isDirectory()) {
3858
3842
  await copyDirRecursive(srcPath, destPath);
3859
3843
  } else {
3860
- await fs9.copyFile(srcPath, destPath);
3844
+ await fs8.copyFile(srcPath, destPath);
3861
3845
  }
3862
3846
  }
3863
3847
  }
3864
3848
 
3865
3849
  // src/lib/react-native-utils.ts
3866
3850
  init_esm_shims();
3867
- import * as path8 from "path";
3868
- import * as fs10 from "fs";
3851
+ import * as path6 from "path";
3852
+ import * as fs9 from "fs";
3869
3853
  import * as childProcess from "child_process";
3870
3854
  import { coerce, compare } from "semver";
3871
3855
  function findUpwardReactNativePackageJson(startDir = process.cwd()) {
3872
3856
  let current = startDir;
3873
- while (current !== path8.parse(current).root) {
3874
- const candidate = path8.join(current, "node_modules", "react-native", "package.json");
3875
- if (fs10.existsSync(candidate)) return candidate;
3876
- current = path8.dirname(current);
3857
+ while (current !== path6.parse(current).root) {
3858
+ const candidate = path6.join(current, "node_modules", "react-native", "package.json");
3859
+ if (fs9.existsSync(candidate)) return candidate;
3860
+ current = path6.dirname(current);
3877
3861
  }
3878
3862
  return null;
3879
3863
  }
3880
3864
  function getReactNativeVersion() {
3881
3865
  const rnPackageJsonPath = findUpwardReactNativePackageJson();
3882
3866
  if (!rnPackageJsonPath) return null;
3883
- const rnPackageJson = JSON.parse(fs10.readFileSync(rnPackageJsonPath, "utf-8"));
3867
+ const rnPackageJson = JSON.parse(fs9.readFileSync(rnPackageJsonPath, "utf-8"));
3884
3868
  return rnPackageJson.version;
3885
3869
  }
3886
- function directoryExistsSync(dirname3) {
3870
+ function directoryExistsSync(dirname4) {
3887
3871
  try {
3888
- return fs10.statSync(dirname3).isDirectory();
3872
+ return fs9.statSync(dirname4).isDirectory();
3889
3873
  } catch (err) {
3890
3874
  if (err.code !== "ENOENT") throw err;
3891
3875
  }
@@ -3893,14 +3877,14 @@ function directoryExistsSync(dirname3) {
3893
3877
  }
3894
3878
  function getReactNativePackagePath() {
3895
3879
  const rnPackageJsonPath = findUpwardReactNativePackageJson();
3896
- if (rnPackageJsonPath) return path8.dirname(rnPackageJsonPath);
3880
+ if (rnPackageJsonPath) return path6.dirname(rnPackageJsonPath);
3897
3881
  const result = childProcess.spawnSync("node", [
3898
3882
  "--print",
3899
3883
  "require.resolve('react-native/package.json')"
3900
3884
  ]);
3901
- const packagePath = path8.dirname(result.stdout.toString().trim());
3885
+ const packagePath = path6.dirname(result.stdout.toString().trim());
3902
3886
  if (result.status === 0 && directoryExistsSync(packagePath)) return packagePath;
3903
- return path8.join("node_modules", "react-native");
3887
+ return path6.join("node_modules", "react-native");
3904
3888
  }
3905
3889
  function resolvePackageDirFromCwd(packageName) {
3906
3890
  const result = childProcess.spawnSync("node", [
@@ -3910,7 +3894,7 @@ function resolvePackageDirFromCwd(packageName) {
3910
3894
  if (result.status !== 0) return null;
3911
3895
  const resolved = result.stdout.toString().trim();
3912
3896
  if (!resolved) return null;
3913
- const packageDir = path8.dirname(resolved);
3897
+ const packageDir = path6.dirname(resolved);
3914
3898
  return directoryExistsSync(packageDir) ? packageDir : null;
3915
3899
  }
3916
3900
  function isValidPlatform(platform) {
@@ -3918,18 +3902,18 @@ function isValidPlatform(platform) {
3918
3902
  }
3919
3903
  function fileExists(filePath) {
3920
3904
  try {
3921
- return fs10.statSync(filePath).isFile();
3905
+ return fs9.statSync(filePath).isFile();
3922
3906
  } catch {
3923
3907
  return false;
3924
3908
  }
3925
3909
  }
3926
3910
  function getCliPath() {
3927
- return path8.join(getReactNativePackagePath(), "cli.js");
3911
+ return path6.join(getReactNativePackagePath(), "cli.js");
3928
3912
  }
3929
3913
  async function runReactNativeBundleCommand(bundleName, entryFile, outputFolder, platform, sourcemap, devMode) {
3930
- fs10.mkdirSync(path8.join(outputFolder, "bundles"), { recursive: true });
3914
+ fs9.mkdirSync(path6.join(outputFolder, "bundles"), { recursive: true });
3931
3915
  if (sourcemap) {
3932
- fs10.mkdirSync(path8.join(outputFolder, "sourcemaps"), { recursive: true });
3916
+ fs9.mkdirSync(path6.join(outputFolder, "sourcemaps"), { recursive: true });
3933
3917
  }
3934
3918
  const reactNativeBundleArgs = [
3935
3919
  getCliPath(),
@@ -3937,14 +3921,14 @@ async function runReactNativeBundleCommand(bundleName, entryFile, outputFolder,
3937
3921
  "--dev",
3938
3922
  devMode,
3939
3923
  "--assets-dest",
3940
- path8.join(outputFolder, "bundles"),
3924
+ path6.join(outputFolder, "bundles"),
3941
3925
  "--bundle-output",
3942
- path8.join(outputFolder, "bundles", bundleName),
3926
+ path6.join(outputFolder, "bundles", bundleName),
3943
3927
  "--entry-file",
3944
3928
  entryFile,
3945
3929
  "--platform",
3946
3930
  platform,
3947
- ...sourcemap ? ["--sourcemap-output", path8.join(outputFolder, "sourcemaps", bundleName + ".map")] : []
3931
+ ...sourcemap ? ["--sourcemap-output", path6.join(outputFolder, "sourcemaps", bundleName + ".map")] : []
3948
3932
  ];
3949
3933
  logger.info('Running "react-native bundle" command');
3950
3934
  const reactNativeBundleProcess = childProcess.spawn("node", reactNativeBundleArgs);
@@ -3970,17 +3954,27 @@ async function runHermesEmitBinaryCommand(bundleName, outputFolder, hermesLogs =
3970
3954
  ...sourcemap ? ["--output-source-map"] : [],
3971
3955
  "--emit-binary",
3972
3956
  "--out",
3973
- path8.join(outputFolder, bundleName + ".hbc"),
3974
- path8.join(outputFolder, "bundles", bundleName)
3957
+ path6.join(outputFolder, bundleName + ".hbc"),
3958
+ path6.join(outputFolder, "bundles", bundleName)
3975
3959
  ];
3976
3960
  logger.info("Converting JS bundle to Hermes bytecode");
3977
3961
  const hermesCommand = await getHermesCommand();
3978
- logger.info(`Hermesc path: ${hermescPath || hermesCommand}`);
3979
- const hermesProcess = childProcess.spawn(hermescPath || hermesCommand, hermesArgs);
3962
+ const resolvedHermesPath = hermescPath || hermesCommand;
3963
+ if (hermescPath) {
3964
+ const binaryName = path6.basename(hermescPath).replace(/\.exe$/, "");
3965
+ if (!["hermesc", "hermes"].includes(binaryName)) {
3966
+ throw new Error(
3967
+ `Invalid hermesc path: binary must be named "hermesc" or "hermes", got "${path6.basename(hermescPath)}"`
3968
+ );
3969
+ }
3970
+ }
3971
+ logger.info(`Hermesc path: ${resolvedHermesPath}`);
3972
+ const hermesProcess = childProcess.spawn(resolvedHermesPath, hermesArgs);
3980
3973
  let logFile = null;
3974
+ const logFilePath = path6.join(outputFolder, "hermes-output.log");
3981
3975
  let isWarned = false;
3982
3976
  if (hermesLogs) {
3983
- logFile = fs10.createWriteStream("output.log", { flags: "a" });
3977
+ logFile = fs9.createWriteStream(logFilePath, { flags: "a" });
3984
3978
  }
3985
3979
  return new Promise((resolve, reject) => {
3986
3980
  hermesProcess.stdout.on("data", (data) => {
@@ -3996,21 +3990,21 @@ async function runHermesEmitBinaryCommand(bundleName, outputFolder, hermesLogs =
3996
3990
  });
3997
3991
  hermesProcess.on("close", (exitCode, signal) => {
3998
3992
  if (hermesLogs && logFile) {
3999
- logger.success("Done writing logs in output.log file.");
3993
+ logger.success(`Done writing logs to ${logFilePath}`);
4000
3994
  logFile.end();
4001
3995
  }
4002
3996
  if (exitCode !== 0) {
4003
3997
  reject(new Error(`"hermes" command failed (exitCode=${exitCode}, signal=${signal}).`));
4004
3998
  return;
4005
3999
  }
4006
- const source = path8.join(outputFolder, bundleName + ".hbc");
4007
- const destination = path8.join(outputFolder, "bundles", bundleName);
4008
- fs10.copyFile(source, destination, (err) => {
4000
+ const source = path6.join(outputFolder, bundleName + ".hbc");
4001
+ const destination = path6.join(outputFolder, "bundles", bundleName);
4002
+ fs9.copyFile(source, destination, (err) => {
4009
4003
  if (err) {
4010
4004
  reject(new Error(`Copying Hermes output failed: ${err.message}`));
4011
4005
  return;
4012
4006
  }
4013
- fs10.unlink(source, (unlinkErr) => {
4007
+ fs9.unlink(source, (unlinkErr) => {
4014
4008
  if (unlinkErr) {
4015
4009
  reject(unlinkErr);
4016
4010
  return;
@@ -4041,7 +4035,7 @@ function getHermesOSExe() {
4041
4035
  return process.platform === "win32" ? hermesExecutableName + ".exe" : hermesExecutableName;
4042
4036
  }
4043
4037
  async function getHermesCommand() {
4044
- const bundledHermesEngine = path8.join(
4038
+ const bundledHermesEngine = path6.join(
4045
4039
  getReactNativePackagePath(),
4046
4040
  "sdks",
4047
4041
  "hermesc",
@@ -4051,23 +4045,23 @@ async function getHermesCommand() {
4051
4045
  if (fileExists(bundledHermesEngine)) return bundledHermesEngine;
4052
4046
  const hermesEnginePackageDir = resolvePackageDirFromCwd("hermes-engine");
4053
4047
  if (hermesEnginePackageDir) {
4054
- const hermesEngine2 = path8.join(hermesEnginePackageDir, getHermesOSBin(), getHermesOSExe());
4048
+ const hermesEngine2 = path6.join(hermesEnginePackageDir, getHermesOSBin(), getHermesOSExe());
4055
4049
  if (fileExists(hermesEngine2)) return hermesEngine2;
4056
4050
  }
4057
- const hermesEngine = path8.join("node_modules", "hermes-engine", getHermesOSBin(), getHermesOSExe());
4051
+ const hermesEngine = path6.join("node_modules", "hermes-engine", getHermesOSBin(), getHermesOSExe());
4058
4052
  if (fileExists(hermesEngine)) return hermesEngine;
4059
4053
  const hermesCompilerPackageDir = resolvePackageDirFromCwd("hermes-compiler");
4060
4054
  if (hermesCompilerPackageDir) {
4061
- const hermesCompiler2 = path8.join(hermesCompilerPackageDir, "hermesc", getHermesOSBin(), getHermesOSExe());
4055
+ const hermesCompiler2 = path6.join(hermesCompilerPackageDir, "hermesc", getHermesOSBin(), getHermesOSExe());
4062
4056
  if (fileExists(hermesCompiler2)) return hermesCompiler2;
4063
4057
  }
4064
- const hermesCompiler = path8.join("node_modules", "hermes-compiler", "hermesc", getHermesOSBin(), getHermesOSExe());
4058
+ const hermesCompiler = path6.join("node_modules", "hermes-compiler", "hermesc", getHermesOSBin(), getHermesOSExe());
4065
4059
  if (fileExists(hermesCompiler)) return hermesCompiler;
4066
4060
  const hermesVmPackageDir = resolvePackageDirFromCwd("hermesvm");
4067
4061
  if (hermesVmPackageDir) {
4068
- return path8.join(hermesVmPackageDir, getHermesOSBin(), "hermes");
4062
+ return path6.join(hermesVmPackageDir, getHermesOSBin(), "hermes");
4069
4063
  }
4070
- return path8.join("node_modules", "hermesvm", getHermesOSBin(), "hermes");
4064
+ return path6.join("node_modules", "hermesvm", getHermesOSBin(), "hermes");
4071
4065
  }
4072
4066
 
4073
4067
  // src/commands/publish-bundle.ts
@@ -4116,8 +4110,7 @@ var publishBundleCommand = new Command39("publish-bundle").description("Bundle,
4116
4110
  const uploadPath = appSlug || resolvedAppId || "";
4117
4111
  const entryFile = options.entryFile || "index.js";
4118
4112
  const bundleName = platform === "ios" ? "main.jsbundle" : "index.android.bundle";
4119
- const contentRootPath = path9.join(os3.tmpdir(), `swiftpatch-publish-${Date.now()}`);
4120
- await fs11.ensureDir(contentRootPath);
4113
+ const contentRootPath = await fs10.mkdtemp(path7.join(os3.tmpdir(), "swiftpatch-publish-"));
4121
4114
  try {
4122
4115
  const spinner = ora31("Bundling JavaScript...").start();
4123
4116
  try {
@@ -4159,7 +4152,7 @@ var publishBundleCommand = new Command39("publish-bundle").description("Bundle,
4159
4152
  if (options.privateKey) {
4160
4153
  const signSpinner = ora31("Signing bundle...").start();
4161
4154
  try {
4162
- const bundlesDir2 = path9.join(contentRootPath, "bundles");
4155
+ const bundlesDir2 = path7.join(contentRootPath, "bundles");
4163
4156
  await signBundleDirectory(bundlesDir2, options.privateKey);
4164
4157
  signSpinner.succeed("Bundle signed with JWT RS256");
4165
4158
  } catch (error) {
@@ -4167,26 +4160,19 @@ var publishBundleCommand = new Command39("publish-bundle").description("Bundle,
4167
4160
  process.exit(1);
4168
4161
  }
4169
4162
  }
4170
- const zipSpinner = ora31("Creating ZIP archive...").start();
4171
- const bundlesDir = path9.join(contentRootPath, "bundles");
4172
- await createZip(bundlesDir, contentRootPath);
4173
- const zipPath = path9.join(contentRootPath, "build.zip");
4174
- if (!await fs11.pathExists(zipPath)) {
4175
- zipSpinner.fail("ZIP creation failed: build.zip not found");
4176
- process.exit(1);
4177
- }
4178
- const zipSize = (await fs11.stat(zipPath)).size;
4179
- zipSpinner.succeed(`ZIP archive created: ${chalk37.gray(formatBytes4(zipSize))}`);
4180
- const hashSpinner = ora31("Computing ZIP hash...").start();
4181
- const hash = calculateZipHash(zipPath);
4163
+ const bundlesDir = path7.join(contentRootPath, "bundles");
4164
+ const rawBundlePath = path7.join(bundlesDir, bundleName);
4165
+ const hashSpinner = ora31("Computing bundle hash...").start();
4166
+ const hash = await hashBundle(rawBundlePath);
4182
4167
  if (!hash) {
4183
- hashSpinner.fail("Failed to compute ZIP hash");
4168
+ hashSpinner.fail("Failed to compute bundle hash");
4184
4169
  process.exit(1);
4185
4170
  }
4186
- hashSpinner.succeed(`ZIP hash: ${chalk37.gray(hash.slice(0, 16))}...`);
4171
+ const bundleSize = (await fs10.stat(rawBundlePath)).size;
4172
+ hashSpinner.succeed(`Bundle hash: ${chalk37.gray(hash.slice(0, 16))}... (${formatBytes4(bundleSize)})`);
4187
4173
  let bundleSignature;
4188
4174
  if (options.privateKey) {
4189
- const bundlesDir2 = path9.join(contentRootPath, "bundles");
4175
+ const bundlesDir2 = path7.join(contentRootPath, "bundles");
4190
4176
  const sig = await readBundleSignature(bundlesDir2);
4191
4177
  if (sig) bundleSignature = sig;
4192
4178
  }
@@ -4204,7 +4190,7 @@ var publishBundleCommand = new Command39("publish-bundle").description("Bundle,
4204
4190
  );
4205
4191
  urlSpinner.succeed("Upload URL received");
4206
4192
  const uploadSpinner = ora31("Uploading bundle...").start();
4207
- await uploader.uploadZip(url, zipPath, (percent) => {
4193
+ await uploader.uploadBundle(url, rawBundlePath, (percent) => {
4208
4194
  uploadSpinner.text = `Uploading bundle... ${percent}%`;
4209
4195
  });
4210
4196
  uploadSpinner.succeed("Bundle uploaded");
@@ -4233,7 +4219,8 @@ var publishBundleCommand = new Command39("publish-bundle").description("Bundle,
4233
4219
  process.exit(1);
4234
4220
  }
4235
4221
  } finally {
4236
- await fs11.remove(contentRootPath).catch(() => {
4222
+ await fs10.remove(contentRootPath).catch(() => {
4223
+ logger.warning(`Failed to clean up temp directory: ${contentRootPath}`);
4237
4224
  });
4238
4225
  }
4239
4226
  });
@@ -4359,12 +4346,12 @@ init_esm_shims();
4359
4346
  import { Command as Command42 } from "commander";
4360
4347
  import chalk40 from "chalk";
4361
4348
  import ora34 from "ora";
4362
- import path10 from "path";
4349
+ import path8 from "path";
4363
4350
  var generateKeyPairCommand = new Command42("generate-key-pair").description("Generate RSA 2048-bit key pair for bundle signing").option("-o, --output <dir>", "Output directory for key files", ".").action(async (options) => {
4364
4351
  console.log("");
4365
4352
  logger.info(chalk40.bold("SwiftPatch Generate Key Pair"));
4366
4353
  console.log("");
4367
- const outputDir = path10.resolve(options.output || ".");
4354
+ const outputDir = path8.resolve(options.output || ".");
4368
4355
  const spinner = ora34("Generating RSA 2048-bit key pair...").start();
4369
4356
  try {
4370
4357
  const { publicKeyPath, privateKeyPath } = await generateKeyPair(outputDir);
@@ -4397,8 +4384,9 @@ import { Command as Command43 } from "commander";
4397
4384
  import chalk41 from "chalk";
4398
4385
  import inquirer21 from "inquirer";
4399
4386
  import ora35 from "ora";
4400
- import fs12 from "fs-extra";
4401
- import path11 from "path";
4387
+ import fs11 from "fs-extra";
4388
+ import path9 from "path";
4389
+ import os4 from "os";
4402
4390
  var deployCommand = new Command43("deploy").description("Bundle, publish, and release in one step").option("-p, --platform <platform>", "Target platform (ios or android)").option("-a, --app <app-id>", "App ID or slug").option("-o, --org <org-id>", "Organization ID").option("--app-version <version>", "Target app version (semver)").option("-n, --release-note <note>", "Release notes").option("-m, --mandatory", "Mark as mandatory update").option("--paused", "Create release in paused state").option("--hermes", "Compile to Hermes bytecode").option("--ci-token <token>", "CI token for authentication").option("-e, --entry-file <path>", "Entry file (default: index.js)").option("--dev", "Dev bundle").option("--sourcemap", "Generate sourcemaps").option("-k, --private-key <path>", "Private key path for signing").option("-y, --yes", "Skip confirmations").action(async (options) => {
4403
4391
  const ciToken = options.ciToken || process.env.SWIFTPATCH_CI_TOKEN;
4404
4392
  if (!ciToken) {
@@ -4407,8 +4395,8 @@ var deployCommand = new Command43("deploy").description("Bundle, publish, and re
4407
4395
  const orgId = ciToken ? void 0 : await resolveOrgId(options.org);
4408
4396
  let platform = options.platform;
4409
4397
  if (!platform) {
4410
- const hasIos = await fs12.pathExists("./ios");
4411
- const hasAndroid = await fs12.pathExists("./android");
4398
+ const hasIos = await fs11.pathExists("./ios");
4399
+ const hasAndroid = await fs11.pathExists("./android");
4412
4400
  if (hasIos && !hasAndroid) {
4413
4401
  platform = "ios";
4414
4402
  } else if (hasAndroid && !hasIos) {
@@ -4428,7 +4416,7 @@ var deployCommand = new Command43("deploy").description("Bundle, publish, and re
4428
4416
  let appVersion = options.appVersion;
4429
4417
  if (!appVersion) {
4430
4418
  try {
4431
- const pkg = await fs12.readJson("./package.json");
4419
+ const pkg = await fs11.readJson("./package.json");
4432
4420
  appVersion = pkg.version;
4433
4421
  } catch {
4434
4422
  }
@@ -4469,94 +4457,96 @@ var deployCommand = new Command43("deploy").description("Bundle, publish, and re
4469
4457
  console.log(chalk41.bold(" Deploy"));
4470
4458
  console.log(chalk41.gray(` Platform: ${platform} \xB7 Version: ${appVersion}`));
4471
4459
  console.log("");
4472
- const bundleSpinner = ora35("Bundling JavaScript...").start();
4460
+ const tmpRoot = await fs11.mkdtemp(path9.join(os4.tmpdir(), "swiftpatch-deploy-"));
4473
4461
  try {
4474
- const entryFile = options.entryFile || "index.js";
4475
- const outputDir = path11.join(".swiftpatch-tmp", platform);
4476
- await fs12.ensureDir(outputDir);
4462
+ const bundleSpinner = ora35("Bundling JavaScript...").start();
4463
+ try {
4464
+ const entryFile = options.entryFile || "index.js";
4465
+ const outputDir2 = path9.join(tmpRoot, platform);
4466
+ await fs11.ensureDir(outputDir2);
4467
+ const bundleName2 = platform === "ios" ? "main.jsbundle" : "index.android.bundle";
4468
+ await runReactNativeBundleCommand(
4469
+ bundleName2,
4470
+ entryFile,
4471
+ outputDir2,
4472
+ platform,
4473
+ options.sourcemap || false,
4474
+ options.dev || false
4475
+ );
4476
+ if (options.hermes) {
4477
+ bundleSpinner.text = "Compiling to Hermes bytecode...";
4478
+ }
4479
+ bundleSpinner.succeed("Bundle created");
4480
+ } catch (error) {
4481
+ bundleSpinner.fail(`Bundle failed: ${error.message}`);
4482
+ process.exit(1);
4483
+ }
4484
+ const hashSpinner = ora35("Computing bundle hash...").start();
4477
4485
  const bundleName = platform === "ios" ? "main.jsbundle" : "index.android.bundle";
4478
- await runReactNativeBundleCommand(
4479
- bundleName,
4480
- entryFile,
4481
- outputDir,
4482
- platform,
4483
- options.sourcemap || false,
4484
- options.dev || false
4485
- );
4486
- if (options.hermes) {
4487
- bundleSpinner.text = "Compiling to Hermes bytecode...";
4486
+ const outputDir = path9.join(tmpRoot, platform);
4487
+ const rawBundlePath = path9.join(outputDir, bundleName);
4488
+ let bundleHash;
4489
+ try {
4490
+ bundleHash = await hashBundle(rawBundlePath);
4491
+ if (!bundleHash) throw new Error("Failed to compute bundle hash");
4492
+ hashSpinner.succeed(`Bundle hashed (${bundleHash.slice(0, 12)}...)`);
4493
+ } catch (error) {
4494
+ hashSpinner.fail(`Hash failed: ${error.message}`);
4495
+ process.exit(1);
4488
4496
  }
4489
- bundleSpinner.succeed("Bundle created");
4490
- } catch (error) {
4491
- bundleSpinner.fail(`Bundle failed: ${error.message}`);
4492
- process.exit(1);
4493
- }
4494
- const archiveSpinner = ora35("Creating archive...").start();
4495
- let zipPath;
4496
- let bundleHash;
4497
- try {
4498
- const outputDir = path11.join(".swiftpatch-tmp", platform);
4499
- const zipOutputDir = path11.join(".swiftpatch-tmp", "zip");
4500
- await fs12.ensureDir(zipOutputDir);
4501
- await createZip(outputDir, zipOutputDir);
4502
- zipPath = path11.join(zipOutputDir, "build.zip");
4503
- bundleHash = calculateZipHash(zipPath);
4504
- if (!bundleHash) throw new Error("Failed to compute bundle hash");
4505
- archiveSpinner.succeed(`Archive created (hash: ${bundleHash.slice(0, 12)}...)`);
4506
- } catch (error) {
4507
- archiveSpinner.fail(`Archive failed: ${error.message}`);
4508
- process.exit(1);
4509
- }
4510
- const uploadSpinner = ora35("Uploading bundle...").start();
4511
- try {
4512
- const { url } = await api.generateSignedUrl(
4513
- { hash: bundleHash, uploadPath, platform, releaseNote },
4514
- ciToken
4515
- );
4516
- await uploader.uploadZip(url, zipPath, (progress) => {
4517
- uploadSpinner.text = `Uploading bundle... ${progress}%`;
4518
- });
4519
- uploadSpinner.succeed("Bundle uploaded");
4520
- } catch (error) {
4521
- uploadSpinner.fail(`Upload failed: ${error.message}`);
4522
- process.exit(1);
4523
- }
4524
- if (!ciToken) {
4525
- console.log("");
4526
- logger.warning("Deploy requires a CI token to create the release.");
4527
- console.log(chalk41.gray(" Bundle was uploaded. Promote it manually:"));
4528
- console.log(chalk41.cyan(` $ swiftpatch release-bundle --hash ${bundleHash} --app-version ${appVersion} --ci-token <token>`));
4529
- console.log("");
4530
- } else {
4531
- const releaseSpinner = ora35("Creating release...").start();
4497
+ const uploadSpinner = ora35("Uploading bundle...").start();
4532
4498
  try {
4533
- await api.promoteBundle(
4534
- {
4535
- projectId: uploadPath,
4536
- hash: bundleHash,
4537
- appVersion,
4538
- releaseNote,
4539
- isMandatory: options.mandatory || false,
4540
- isPaused: options.paused || false
4541
- },
4499
+ const { url } = await api.generateSignedUrl(
4500
+ { hash: bundleHash, uploadPath, platform, releaseNote },
4542
4501
  ciToken
4543
4502
  );
4544
- releaseSpinner.succeed("Release created!");
4545
- console.log("");
4546
- console.log(chalk41.bold(" Deploy Complete"));
4547
- console.log(` Hash: ${chalk41.gray(bundleHash)}`);
4548
- console.log(` Version: ${chalk41.cyan(appVersion)}`);
4549
- console.log(` Platform: ${platform}`);
4550
- console.log(` Mandatory: ${options.mandatory ? chalk41.yellow("Yes") : "No"}`);
4551
- console.log(` Status: ${options.paused ? chalk41.yellow("Paused") : chalk41.green("Released")}`);
4552
- console.log("");
4503
+ await uploader.uploadBundle(url, rawBundlePath, (progress) => {
4504
+ uploadSpinner.text = `Uploading bundle... ${progress}%`;
4505
+ });
4506
+ uploadSpinner.succeed("Bundle uploaded");
4553
4507
  } catch (error) {
4554
- releaseSpinner.fail(`Release failed: ${error.message}`);
4508
+ uploadSpinner.fail(`Upload failed: ${error.message}`);
4555
4509
  process.exit(1);
4556
4510
  }
4511
+ if (!ciToken) {
4512
+ console.log("");
4513
+ logger.warning("Deploy requires a CI token to create the release.");
4514
+ console.log(chalk41.gray(" Bundle was uploaded. Promote it manually:"));
4515
+ console.log(chalk41.cyan(` $ swiftpatch release-bundle --hash ${bundleHash} --app-version ${appVersion} --ci-token <token>`));
4516
+ console.log("");
4517
+ } else {
4518
+ const releaseSpinner = ora35("Creating release...").start();
4519
+ try {
4520
+ await api.promoteBundle(
4521
+ {
4522
+ projectId: uploadPath,
4523
+ hash: bundleHash,
4524
+ appVersion,
4525
+ releaseNote,
4526
+ isMandatory: options.mandatory || false,
4527
+ isPaused: options.paused || false
4528
+ },
4529
+ ciToken
4530
+ );
4531
+ releaseSpinner.succeed("Release created!");
4532
+ console.log("");
4533
+ console.log(chalk41.bold(" Deploy Complete"));
4534
+ console.log(` Hash: ${chalk41.gray(bundleHash)}`);
4535
+ console.log(` Version: ${chalk41.cyan(appVersion)}`);
4536
+ console.log(` Platform: ${platform}`);
4537
+ console.log(` Mandatory: ${options.mandatory ? chalk41.yellow("Yes") : "No"}`);
4538
+ console.log(` Status: ${options.paused ? chalk41.yellow("Paused") : chalk41.green("Released")}`);
4539
+ console.log("");
4540
+ } catch (error) {
4541
+ releaseSpinner.fail(`Release failed: ${error.message}`);
4542
+ process.exit(1);
4543
+ }
4544
+ }
4545
+ } finally {
4546
+ await fs11.remove(tmpRoot).catch((err) => {
4547
+ logger.warning(`Failed to clean up temp directory: ${tmpRoot}`);
4548
+ });
4557
4549
  }
4558
- await fs12.remove(".swiftpatch-tmp").catch(() => {
4559
- });
4560
4550
  });
4561
4551
 
4562
4552
  // src/commands/status.ts
@@ -4653,18 +4643,18 @@ import { Command as Command45 } from "commander";
4653
4643
  import chalk43 from "chalk";
4654
4644
  import inquirer23 from "inquirer";
4655
4645
  import ora37 from "ora";
4656
- import fs13 from "fs-extra";
4657
- import path12 from "path";
4646
+ import fs12 from "fs-extra";
4647
+ import path10 from "path";
4658
4648
  var initCommand = new Command45("init").description("Initialize SwiftPatch in your React Native project").option("-y, --yes", "Accept defaults").action(async (options) => {
4659
4649
  console.log("");
4660
4650
  console.log(chalk43.bold(" SwiftPatch Setup"));
4661
4651
  console.log("");
4662
- const hasPackageJson = await fs13.pathExists("./package.json");
4652
+ const hasPackageJson = await fs12.pathExists("./package.json");
4663
4653
  if (!hasPackageJson) {
4664
4654
  logger.error("No package.json found. Run this command in your React Native project root.");
4665
4655
  process.exit(1);
4666
4656
  }
4667
- const pkg = await fs13.readJson("./package.json");
4657
+ const pkg = await fs12.readJson("./package.json");
4668
4658
  const hasRN = pkg.dependencies?.["react-native"] || pkg.devDependencies?.["react-native"];
4669
4659
  if (!hasRN) {
4670
4660
  logger.warning("react-native not found in dependencies. Is this a React Native project?");
@@ -4693,8 +4683,8 @@ var initCommand = new Command45("init").description("Initialize SwiftPatch in yo
4693
4683
  }
4694
4684
  logger.success("Authenticated");
4695
4685
  const orgId = await resolveOrgId();
4696
- const hasIos = await fs13.pathExists("./ios");
4697
- const hasAndroid = await fs13.pathExists("./android");
4686
+ const hasIos = await fs12.pathExists("./ios");
4687
+ const hasAndroid = await fs12.pathExists("./android");
4698
4688
  const platforms = [];
4699
4689
  if (hasIos) platforms.push("ios");
4700
4690
  if (hasAndroid) platforms.push("android");
@@ -4745,13 +4735,30 @@ var initCommand = new Command45("init").description("Initialize SwiftPatch in yo
4745
4735
  platform: defaultPlatform || (selectedApp.platform === "BOTH" ? void 0 : selectedApp.platform.toLowerCase()),
4746
4736
  deploymentKey: selectedApp.deploymentKey
4747
4737
  };
4748
- const rcPath = path12.resolve(".swiftpatchrc");
4749
- await fs13.writeJson(rcPath, rcConfig, { spaces: 2 });
4738
+ const rcPath = path10.resolve(".swiftpatchrc");
4739
+ await fs12.writeJson(rcPath, rcConfig, { spaces: 2 });
4750
4740
  logger.success("Created .swiftpatchrc");
4751
4741
  config.set("defaultOrg", orgId);
4752
4742
  config.set("defaultApp", selectedApp.id);
4753
4743
  if (defaultPlatform) config.set("defaultPlatform", defaultPlatform);
4754
4744
  logger.success("Saved defaults to CLI config");
4745
+ const gitignorePath = path10.resolve(".gitignore");
4746
+ const swiftpatchIgnores = [
4747
+ "# SwiftPatch",
4748
+ ".swiftpatch-tmp/",
4749
+ "swiftpatch-artifacts/",
4750
+ "swiftpatch-private.pem"
4751
+ ];
4752
+ try {
4753
+ let gitignore = await fs12.pathExists(gitignorePath) ? await fs12.readFile(gitignorePath, "utf-8") : "";
4754
+ const missing = swiftpatchIgnores.filter((line) => !gitignore.includes(line));
4755
+ if (missing.length > 0) {
4756
+ const addition = (gitignore.endsWith("\n") || gitignore === "" ? "" : "\n") + missing.join("\n") + "\n";
4757
+ await fs12.appendFile(gitignorePath, addition);
4758
+ logger.success("Updated .gitignore with SwiftPatch entries");
4759
+ }
4760
+ } catch {
4761
+ }
4755
4762
  console.log("");
4756
4763
  console.log(chalk43.bold(" Setup Complete"));
4757
4764
  console.log("");
@@ -4772,7 +4779,7 @@ async function createApp(orgId, pkg, platforms) {
4772
4779
  type: "input",
4773
4780
  name: "name",
4774
4781
  message: "App name:",
4775
- default: pkg.name || path12.basename(process.cwd())
4782
+ default: pkg.name || path10.basename(process.cwd())
4776
4783
  }
4777
4784
  ]);
4778
4785
  let platform;
@@ -4808,7 +4815,7 @@ init_auth();
4808
4815
  init_config();
4809
4816
  import { Command as Command46 } from "commander";
4810
4817
  import chalk44 from "chalk";
4811
- import fs14 from "fs-extra";
4818
+ import fs13 from "fs-extra";
4812
4819
  var doctorCommand = new Command46("doctor").description("Diagnose your SwiftPatch setup").action(async () => {
4813
4820
  console.log("");
4814
4821
  console.log(chalk44.bold(" SwiftPatch Doctor"));
@@ -4822,9 +4829,9 @@ var doctorCommand = new Command46("doctor").description("Diagnose your SwiftPatc
4822
4829
  logger.error(`Node.js ${nodeVersion} \u2014 requires >= 18.0.0`);
4823
4830
  issues++;
4824
4831
  }
4825
- const hasPackageJson = await fs14.pathExists("./package.json");
4832
+ const hasPackageJson = await fs13.pathExists("./package.json");
4826
4833
  if (hasPackageJson) {
4827
- const pkg = await fs14.readJson("./package.json");
4834
+ const pkg = await fs13.readJson("./package.json");
4828
4835
  const rnVersion = pkg.dependencies?.["react-native"] || pkg.devDependencies?.["react-native"];
4829
4836
  if (rnVersion) {
4830
4837
  logger.success(`React Native ${rnVersion}`);
@@ -4843,8 +4850,8 @@ var doctorCommand = new Command46("doctor").description("Diagnose your SwiftPatc
4843
4850
  } else {
4844
4851
  logger.warning("No package.json found (not in a project directory?)");
4845
4852
  }
4846
- const hasIos = await fs14.pathExists("./ios");
4847
- const hasAndroid = await fs14.pathExists("./android");
4853
+ const hasIos = await fs13.pathExists("./ios");
4854
+ const hasAndroid = await fs13.pathExists("./android");
4848
4855
  if (hasIos || hasAndroid) {
4849
4856
  const platforms = [hasIos && "iOS", hasAndroid && "Android"].filter(Boolean).join(", ");
4850
4857
  logger.success(`Platforms: ${platforms}`);
@@ -4888,10 +4895,10 @@ var doctorCommand = new Command46("doctor").description("Diagnose your SwiftPatc
4888
4895
  } else {
4889
4896
  logger.info("No default app set");
4890
4897
  }
4891
- const hasRc = await fs14.pathExists("./.swiftpatchrc");
4898
+ const hasRc = await fs13.pathExists("./.swiftpatchrc");
4892
4899
  if (hasRc) {
4893
4900
  try {
4894
- const rc = await fs14.readJson("./.swiftpatchrc");
4901
+ const rc = await fs13.readJson("./.swiftpatchrc");
4895
4902
  logger.success(`.swiftpatchrc found (app: ${rc.app || rc.appId || "unknown"})`);
4896
4903
  } catch {
4897
4904
  logger.warning(".swiftpatchrc exists but is invalid JSON");
@@ -4902,7 +4909,7 @@ var doctorCommand = new Command46("doctor").description("Diagnose your SwiftPatc
4902
4909
  }
4903
4910
  if (hasPackageJson) {
4904
4911
  try {
4905
- const pkg = await fs14.readJson("./package.json");
4912
+ const pkg = await fs13.readJson("./package.json");
4906
4913
  const rnVersion = pkg.dependencies?.["react-native"];
4907
4914
  if (rnVersion) {
4908
4915
  const cleanVersion = rnVersion.replace(/[\^~>=<]/g, "");
@@ -4926,10 +4933,10 @@ var doctorCommand = new Command46("doctor").description("Diagnose your SwiftPatc
4926
4933
  });
4927
4934
 
4928
4935
  // src/cli.ts
4929
- var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
4936
+ var __dirname3 = dirname3(fileURLToPath3(import.meta.url));
4930
4937
  var version = "1.0.0";
4931
4938
  try {
4932
- const pkg = JSON.parse(readFileSync3(join2(__dirname2, "../package.json"), "utf-8"));
4939
+ const pkg = JSON.parse(readFileSync3(join3(__dirname3, "../package.json"), "utf-8"));
4933
4940
  version = pkg.version;
4934
4941
  } catch {
4935
4942
  }
@@ -5002,13 +5009,13 @@ init_esm_shims();
5002
5009
  function checkForUpdates() {
5003
5010
  try {
5004
5011
  import("update-notifier").then(({ default: updateNotifier }) => {
5005
- import("fs-extra").then(({ default: fs15 }) => {
5006
- import("url").then(({ fileURLToPath: fileURLToPath3 }) => {
5007
- import("path").then(({ dirname: dirname3, join: join3 }) => {
5012
+ import("fs-extra").then(({ default: fs14 }) => {
5013
+ import("url").then(({ fileURLToPath: fileURLToPath4 }) => {
5014
+ import("path").then(({ dirname: dirname4, join: join4 }) => {
5008
5015
  try {
5009
- const __dirname3 = dirname3(fileURLToPath3(import.meta.url));
5016
+ const __dirname4 = dirname4(fileURLToPath4(import.meta.url));
5010
5017
  const pkg = JSON.parse(
5011
- fs15.readFileSync(join3(__dirname3, "../../package.json"), "utf-8")
5018
+ fs14.readFileSync(join4(__dirname4, "../../package.json"), "utf-8")
5012
5019
  );
5013
5020
  const notifier = updateNotifier({ pkg, updateCheckInterval: 1e3 * 60 * 60 * 24 });
5014
5021
  notifier.notify();