react-native-stallion 2.2.0 → 2.3.0-alpha.1

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.
Files changed (34) hide show
  1. package/android/src/main/java/com/stallion/events/StallionEventConstants.java +2 -1
  2. package/android/src/main/java/com/stallion/events/StallionEventManager.java +7 -0
  3. package/android/src/main/java/com/stallion/networkmanager/StallionSyncHandler.java +36 -4
  4. package/android/src/main/java/com/stallion/storage/StallionConfig.java +27 -0
  5. package/android/src/main/java/com/stallion/storage/StallionConfigConstants.java +2 -0
  6. package/android/src/main/java/com/stallion/utils/StallionSignatureVerification.java +129 -0
  7. package/ios/main/Stallion.swift +1 -0
  8. package/ios/main/StallionConfig.h +4 -1
  9. package/ios/main/StallionConfig.m +9 -2
  10. package/ios/main/StallionConfigConstants.h +5 -1
  11. package/ios/main/StallionConfigConstants.m +5 -1
  12. package/ios/main/StallionConstants.swift +1 -0
  13. package/ios/main/StallionEventHandler.h +1 -1
  14. package/ios/main/StallionEventHandler.m +9 -3
  15. package/ios/main/StallionExceptionHandler.h +1 -1
  16. package/ios/main/StallionExceptionHandler.m +7 -2
  17. package/ios/main/StallionFileManager.h +1 -1
  18. package/ios/main/StallionFileManager.m +1 -1
  19. package/ios/main/StallionMeta.h +1 -1
  20. package/ios/main/StallionMeta.m +1 -1
  21. package/ios/main/StallionMetaConstants.h +1 -1
  22. package/ios/main/StallionMetaConstants.m +1 -1
  23. package/ios/main/StallionSignatureVerification.swift +135 -0
  24. package/ios/main/StallionSlotManager.h +1 -1
  25. package/ios/main/StallionSlotManager.m +1 -1
  26. package/ios/main/StallionStageManager.swift +1 -1
  27. package/ios/main/StallionStateManager.h +1 -1
  28. package/ios/main/StallionStateManager.m +1 -1
  29. package/ios/main/StallionSyncHandler.swift +52 -20
  30. package/package.json +1 -1
  31. package/src/main/utils/StallionNativeUtils.js +4 -1
  32. package/src/main/utils/StallionNativeUtils.js.map +1 -1
  33. package/types/main/utils/StallionNativeUtils.d.ts +1 -1
  34. package/types/main/utils/StallionNativeUtils.d.ts.map +1 -1
@@ -13,7 +13,8 @@ public class StallionEventConstants {
13
13
  EXCEPTION_PROD,
14
14
  AUTO_ROLLED_BACK_PROD,
15
15
  CORRUPTED_FILE_ERROR,
16
- FILE_MOUNTING_ERROR
16
+ FILE_MOUNTING_ERROR,
17
+ SIGNATURE_VERIFICATION_FAILED
17
18
  }
18
19
 
19
20
  public enum NativeStageEventTypes {
@@ -18,6 +18,7 @@ public class StallionEventManager {
18
18
  public static final String STALLION_NATIVE_EVENT_NAME = "STALLION_NATIVE_EVENT";
19
19
  private static final String EVENTS_KEY = "stored_events";
20
20
  private static final int MAX_BATCH_COUNT_SIZE = 9;
21
+ private static final int MAX_EVENT_STORAGE_LIMIT = 20;
21
22
 
22
23
  private static StallionEventManager instance;
23
24
  private final StallionStateManager stallionStateManager;
@@ -105,6 +106,12 @@ public class StallionEventManager {
105
106
  try {
106
107
  String eventsString = stallionStateManager.getString(EVENTS_KEY, "{}");
107
108
  JSONObject eventsObject = new JSONObject(eventsString);
109
+
110
+ // Flush all if limit reached
111
+ if (eventsObject.length() >= MAX_EVENT_STORAGE_LIMIT) {
112
+ eventsObject = new JSONObject(); // reset
113
+ }
114
+
108
115
  eventsObject.put(uniqueId, eventPayload.toString());
109
116
  stallionStateManager.setString(EVENTS_KEY, eventsObject.toString());
110
117
  } catch (JSONException e) {
@@ -5,10 +5,14 @@ import com.stallion.storage.StallionConfigConstants;
5
5
  import com.stallion.storage.StallionMetaConstants;
6
6
  import com.stallion.storage.StallionStateManager;
7
7
  import com.stallion.storage.StallionConfig;
8
+ import com.stallion.utils.StallionFileManager;
9
+ import com.stallion.utils.StallionSignatureVerification;
8
10
  import com.stallion.utils.StallionSlotManager;
9
11
  import com.stallion.events.StallionEventConstants.NativeProdEventTypes;
10
12
 
11
13
  import org.json.JSONObject;
14
+
15
+ import java.io.File;
12
16
  import java.util.concurrent.atomic.AtomicBoolean;
13
17
 
14
18
  public class StallionSyncHandler {
@@ -85,11 +89,13 @@ public class StallionSyncHandler {
85
89
 
86
90
  StallionStateManager stateManager = StallionStateManager.getInstance();
87
91
  String lastRolledBackHash = stateManager.stallionMeta.getLastRolledBackHash();
92
+ String lastUnverifiedHash = stateManager.getStallionConfig().getLastUnverifiedHash();
88
93
 
89
94
  if (
90
- !newReleaseHash.isEmpty()
91
- && !newReleaseUrl.isEmpty()
92
- && !newReleaseHash.equals(lastRolledBackHash)
95
+ !newReleaseHash.isEmpty()
96
+ && !newReleaseUrl.isEmpty()
97
+ && !newReleaseHash.equals(lastRolledBackHash)
98
+ && !newReleaseHash.equals(lastUnverifiedHash)
93
99
  ) {
94
100
  if(stateManager.getIsMounted()) {
95
101
  downloadNewRelease(newReleaseHash, newReleaseUrl);
@@ -112,6 +118,7 @@ public class StallionSyncHandler {
112
118
  + StallionConfigConstants.TEMP_FOLDER_SLOT;
113
119
  String projectId = config.getProjectId();
114
120
  String downloadUrl = newReleaseUrl + "?projectId=" + projectId;
121
+ String publicSigningKey = config.getPublicSigningKey();
115
122
 
116
123
  long alreadyDownloaded = StallionDownloadCacheManager.getDownloadCache(config, downloadUrl, downloadPath);
117
124
 
@@ -131,6 +138,22 @@ public class StallionSyncHandler {
131
138
  @Override
132
139
  public void onSuccess(String successPayload) {
133
140
  isDownloadInProgress.set(false);
141
+ StallionDownloadCacheManager.deleteDownloadCache(downloadPath);
142
+
143
+ if(publicSigningKey != null && !publicSigningKey.isEmpty()) {
144
+ if(
145
+ !StallionSignatureVerification.verifyReleaseSignature(
146
+ downloadPath + StallionConfigConstants.UNZIP_FOLDER_NAME,
147
+ publicSigningKey)
148
+ ) {
149
+ // discard the downloaded release
150
+ config.setLastUnverifiedHash(newReleaseHash);
151
+ emitSignatureVerificationFailed(newReleaseHash);
152
+ StallionFileManager.deleteFileOrFolderSilently(new File(downloadPath));
153
+ return;
154
+ }
155
+ }
156
+
134
157
  stateManager.stallionMeta.setCurrentProdSlot(StallionMetaConstants.SlotStates.NEW_SLOT);
135
158
  stateManager.stallionMeta.setProdTempHash(newReleaseHash);
136
159
  String currentProdNewHash = stateManager.stallionMeta.getProdNewHash();
@@ -138,7 +161,6 @@ public class StallionSyncHandler {
138
161
  StallionSlotManager.stabilizeProd();
139
162
  }
140
163
  stateManager.syncStallionMeta();
141
- StallionDownloadCacheManager.deleteDownloadCache(downloadPath);
142
164
  emitDownloadSuccess(newReleaseHash);
143
165
  }
144
166
 
@@ -198,4 +220,14 @@ public class StallionSyncHandler {
198
220
  successPayload
199
221
  );
200
222
  }
223
+ private static void emitSignatureVerificationFailed(String releaseHash) {
224
+ JSONObject successPayload = new JSONObject();
225
+ try {
226
+ successPayload.put("releaseHash", releaseHash);
227
+ } catch (Exception ignored) { }
228
+ StallionEventManager.getInstance().sendEvent(
229
+ NativeProdEventTypes.SIGNATURE_VERIFICATION_FAILED.toString(),
230
+ successPayload
231
+ );
232
+ }
201
233
  }
@@ -20,6 +20,9 @@ public class StallionConfig {
20
20
  private final SharedPreferences sharedPreferences;
21
21
  private final String filesDirectory;
22
22
  private String lastDownloadingUrl;
23
+ private String lastUnverifiedHash;
24
+ private final String publicSigningKey;
25
+
23
26
 
24
27
  public StallionConfig(Context context, SharedPreferences sharedPreferences) {
25
28
  this.sharedPreferences = sharedPreferences;
@@ -32,6 +35,7 @@ public class StallionConfig {
32
35
  parentPackageName
33
36
  );
34
37
  this.projectId = stallionProjectIdRes != 0 ?context.getString(stallionProjectIdRes) : "";
38
+
35
39
  int stallionAppTokenRes = res.getIdentifier(
36
40
  StallionConfigConstants.STALLION_APP_TOKEN_IDENTIFIER,
37
41
  "string",
@@ -39,6 +43,13 @@ public class StallionConfig {
39
43
  );
40
44
  this.appToken = stallionAppTokenRes != 0 ? context.getString(stallionAppTokenRes) : "";
41
45
 
46
+ int stallionPublicKeyRes = res.getIdentifier(
47
+ StallionConfigConstants.STALLION_PUBLIC_SIGNING_KEY_IDENTIFIER,
48
+ "string",
49
+ parentPackageName
50
+ );
51
+ this.publicSigningKey = stallionPublicKeyRes != 0 ? context.getString(stallionPublicKeyRes) : "";
52
+
42
53
  // get or generate UID
43
54
  String cachedUniqueId = sharedPreferences.getString(
44
55
  StallionConfigConstants.UNIQUE_ID_IDENTIFIER,
@@ -65,6 +76,7 @@ public class StallionConfig {
65
76
  this.appVersion = fetchAppVersion(context);
66
77
  this.filesDirectory = context.getFilesDir().getAbsolutePath();
67
78
  this.lastDownloadingUrl = sharedPreferences.getString(StallionConfigConstants.LAST_DOWNLOADING_URL_IDENTIFIER, "");
79
+ this.lastUnverifiedHash = sharedPreferences.getString(StallionConfigConstants.LAST_UNVERIFIED_HASH, "");
68
80
  }
69
81
 
70
82
  public String getLastDownloadingUrl() {
@@ -78,6 +90,17 @@ public class StallionConfig {
78
90
  editor.apply();
79
91
  }
80
92
 
93
+ public String getLastUnverifiedHash() {
94
+ return this.lastUnverifiedHash;
95
+ }
96
+
97
+ public void setLastUnverifiedHash(String newUnverifiedHash) {
98
+ this.lastUnverifiedHash = newUnverifiedHash;
99
+ SharedPreferences.Editor editor = sharedPreferences.edit();
100
+ editor.putString(StallionConfigConstants.LAST_UNVERIFIED_HASH, newUnverifiedHash);
101
+ editor.apply();
102
+ }
103
+
81
104
  private String fetchAppVersion(Context context) {
82
105
  try {
83
106
  PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
@@ -118,6 +141,10 @@ public class StallionConfig {
118
141
 
119
142
  public String getFilesDirectory() { return this.filesDirectory; }
120
143
 
144
+ public String getPublicSigningKey() {
145
+ return this.publicSigningKey;
146
+ }
147
+
121
148
  public JSONObject toJSON() {
122
149
  JSONObject configJson = new JSONObject();
123
150
  try {
@@ -7,9 +7,11 @@ public class StallionConfigConstants {
7
7
 
8
8
  public static final String STALLION_PROJECT_ID_IDENTIFIER = "StallionProjectId";
9
9
  public static final String STALLION_APP_TOKEN_IDENTIFIER = "StallionAppToken";
10
+ public static final String STALLION_PUBLIC_SIGNING_KEY_IDENTIFIER = "StallionPublicSigningKey";
10
11
  public static final String UNIQUE_ID_IDENTIFIER = "stallionDeviceId";
11
12
  public static final String API_KEY_IDENTIFIER = "x-sdk-access-token";
12
13
  public static final String LAST_DOWNLOADING_URL_IDENTIFIER = "StallionLastDownloadingUrl";
14
+ public static final String LAST_UNVERIFIED_HASH = "LastUnverifiedHash";
13
15
 
14
16
  public static final String PROD_DIRECTORY = "/StallionProd";
15
17
  public static final String STAGE_DIRECTORY = "/StallionStage";
@@ -0,0 +1,129 @@
1
+ package com.stallion.utils;
2
+
3
+ import java.io.ByteArrayInputStream;
4
+ import java.io.File;
5
+ import java.io.FileInputStream;
6
+ import java.io.IOException;
7
+ import java.io.InputStream;
8
+ import java.nio.charset.StandardCharsets;
9
+ import java.security.DigestInputStream;
10
+ import java.security.KeyFactory;
11
+ import java.security.MessageDigest;
12
+ import java.security.PublicKey;
13
+ import java.security.Signature;
14
+ import java.security.spec.X509EncodedKeySpec;
15
+ import java.util.ArrayList;
16
+ import java.util.Collections;
17
+ import java.util.Scanner;
18
+
19
+ import org.json.JSONArray;
20
+ import org.json.JSONObject;
21
+
22
+ public class StallionSignatureVerification {
23
+
24
+ public static final String SIGNATURE_FILE_NAME = ".stallionsigned";
25
+
26
+ public static boolean verifyReleaseSignature(String downloadedBundlePath, String publicKeyPem) {
27
+ try {
28
+ File folderPath = new File(downloadedBundlePath);
29
+ File signatureFile = new File(folderPath, SIGNATURE_FILE_NAME);
30
+ if (!signatureFile.exists()) return false;
31
+
32
+ String jwt = readFileLegacy(signatureFile);
33
+ String[] jwtParts = jwt.split("\\.");
34
+ if (jwtParts.length != 3) return false;
35
+
36
+ String header = jwtParts[0];
37
+ String payload = jwtParts[1];
38
+ String signature = jwtParts[2];
39
+
40
+ byte[] signatureBytes = base64UrlDecode(signature);
41
+ byte[] signedContent = (header + "." + payload).getBytes(StandardCharsets.UTF_8);
42
+
43
+ String cleanedPem = publicKeyPem
44
+ .replace("-----BEGIN PUBLIC KEY-----", "")
45
+ .replace("-----END PUBLIC KEY-----", "")
46
+ .replaceAll("\\s", "");
47
+
48
+ byte[] decodedKey = android.util.Base64.decode(cleanedPem, android.util.Base64.DEFAULT);
49
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
50
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
51
+ PublicKey pubKey = keyFactory.generatePublic(keySpec);
52
+
53
+ Signature sig = Signature.getInstance("SHA256withRSA");
54
+ sig.initVerify(pubKey);
55
+ sig.update(signedContent);
56
+ if (!sig.verify(signatureBytes)) return false;
57
+
58
+ String jsonPayload = new String(base64UrlDecode(payload), StandardCharsets.UTF_8);
59
+ JSONObject payloadObj = new JSONObject(jsonPayload);
60
+ String expectedHash = payloadObj.optString("packageHash", "");
61
+
62
+ String actualHash = computeFolderHash(folderPath);
63
+ return expectedHash.equals(actualHash);
64
+
65
+ } catch (Exception e) {
66
+ e.printStackTrace();
67
+ return false;
68
+ }
69
+ }
70
+
71
+ private static String computeFolderHash(File folder) throws Exception {
72
+ ArrayList<String> manifest = new ArrayList<>();
73
+ addFolderContentsToManifest(folder, "", manifest);
74
+ Collections.sort(manifest);
75
+ JSONArray jsonArray = new JSONArray();
76
+ for (String entry : manifest) {
77
+ jsonArray.put(entry);
78
+ }
79
+ String jsonString = jsonArray.toString().replace("\\/", "/");
80
+ return computeHash(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)));
81
+ }
82
+
83
+ private static void addFolderContentsToManifest(File folder, String pathPrefix, ArrayList<String> manifest) throws Exception {
84
+ File[] files = folder.listFiles();
85
+ for (File file : files) {
86
+ String fileName = file.getName();
87
+ String relativePath = pathPrefix.isEmpty() ? fileName : pathPrefix + "/" + fileName;
88
+ if (isIgnored(relativePath)) continue;
89
+ if (file.isDirectory()) {
90
+ addFolderContentsToManifest(file, relativePath, manifest);
91
+ } else {
92
+ manifest.add(relativePath + ":" + computeHash(new FileInputStream(file)));
93
+ }
94
+ }
95
+ }
96
+
97
+ private static boolean isIgnored(String path) {
98
+ return path.equals(".DS_Store") || path.equals(SIGNATURE_FILE_NAME) || path.startsWith("__MACOSX/");
99
+ }
100
+
101
+ private static String computeHash(InputStream stream) throws Exception {
102
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
103
+ DigestInputStream dis = new DigestInputStream(stream, digest);
104
+ byte[] buffer = new byte[8192];
105
+ while (dis.read(buffer) != -1);
106
+ dis.close();
107
+ byte[] hash = digest.digest();
108
+ return String.format("%064x", new java.math.BigInteger(1, hash));
109
+ }
110
+
111
+ private static String readFileLegacy(File file) throws IOException {
112
+ Scanner scanner = new Scanner(file, "UTF-8");
113
+ StringBuilder sb = new StringBuilder();
114
+ while (scanner.hasNextLine()) {
115
+ sb.append(scanner.nextLine());
116
+ }
117
+ scanner.close();
118
+ return sb.toString();
119
+ }
120
+
121
+ private static byte[] base64UrlDecode(String input) {
122
+ String converted = input.replace('-', '+').replace('_', '/');
123
+ switch (converted.length() % 4) {
124
+ case 2: converted += "=="; break;
125
+ case 3: converted += "="; break;
126
+ }
127
+ return android.util.Base64.decode(converted, android.util.Base64.NO_WRAP);
128
+ }
129
+ }
@@ -149,6 +149,7 @@ class Stallion: RCTEventEmitter {
149
149
 
150
150
  @objc func restart() {
151
151
  DispatchQueue.main.async {
152
+ self.stallionStateManager.isMounted = false
152
153
  RCTTriggerReloadCommandListeners("Stallion: Restart")
153
154
  }
154
155
  }
@@ -2,7 +2,7 @@
2
2
  // StallionConfig.h
3
3
  // DoubleConversion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 28/01/25.
5
+ // Created by Thor963 on 28/01/25.
6
6
  //
7
7
 
8
8
  #import <Foundation/Foundation.h>
@@ -15,10 +15,13 @@
15
15
  @property (nonatomic, copy) NSString *sdkToken;
16
16
  @property (nonatomic, copy, readonly) NSString *appVersion;
17
17
  @property (nonatomic, copy, readonly) NSString *filesDirectory;
18
+ @property (nonatomic, copy, readonly) NSString *publicSigningKey;
19
+ @property (nonatomic, copy) NSString *lastUnverifiedHash;
18
20
 
19
21
  - (instancetype)initWithDefaults:(NSUserDefaults *)defaults;
20
22
 
21
23
  - (void)updateSdkToken:(NSString *)newSdkToken;
24
+ - (void)updateLastUnverifiedHash:(NSString *)newUnverifiedHash;
22
25
  - (NSDictionary *)toDictionary;
23
26
 
24
27
  @end
@@ -2,7 +2,7 @@
2
2
  // StallionConfig.m
3
3
  // DoubleConversion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 28/01/25.
5
+ // Created by Thor963 on 28/01/25.
6
6
  //
7
7
 
8
8
  #import "StallionConfig.h"
@@ -17,8 +17,9 @@
17
17
  _appToken = [[NSBundle mainBundle] objectForInfoDictionaryKey:STALLION_APP_TOKEN_IDENTIFIER] ?: @"";
18
18
  _sdkToken = [defaults stringForKey:API_KEY_IDENTIFIER] ?: @"";
19
19
  _appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:STALLION_APP_VERSION_IDENTIFIER] ?: @"";
20
-
21
20
  _filesDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject ?: @"";
21
+ _publicSigningKey = [[NSBundle mainBundle] objectForInfoDictionaryKey:STALLION_PUBLIC_SIGNING_KEY_IDENTIFIER] ?: @"";
22
+ _lastUnverifiedHash = [defaults stringForKey:LAST_UNVERIFIED_KEY_IDENTIFIER] ?: @"";
22
23
 
23
24
  NSString *cachedUid = [defaults stringForKey:UNIQUE_ID_IDENTIFIER];
24
25
  if (cachedUid && ![cachedUid isEqualToString:@""]) {
@@ -44,6 +45,12 @@
44
45
  [[NSUserDefaults standardUserDefaults] synchronize];
45
46
  }
46
47
 
48
+ - (void)updateLastUnverifiedHash:(NSString *)newUnverifiedHash {
49
+ _lastUnverifiedHash = newUnverifiedHash ?: @"";
50
+ [[NSUserDefaults standardUserDefaults] setObject:_lastUnverifiedHash forKey:LAST_UNVERIFIED_KEY_IDENTIFIER];
51
+ [[NSUserDefaults standardUserDefaults] synchronize];
52
+ }
53
+
47
54
  - (NSDictionary *)toDictionary {
48
55
  @try {
49
56
  return @{
@@ -2,7 +2,7 @@
2
2
  // StallionConfigConstants.h
3
3
  // DoubleConversion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 28/01/25.
5
+ // Created by Thor963 on 28/01/25.
6
6
  //
7
7
 
8
8
  #import <Foundation/Foundation.h>
@@ -29,4 +29,8 @@ extern NSString *const UNZIP_FOLDER_NAME;
29
29
 
30
30
  extern NSString *const STALLION_META_IDENTIFIER;
31
31
 
32
+ extern NSString *const STALLION_PUBLIC_SIGNING_KEY_IDENTIFIER;
33
+
34
+ extern NSString *const LAST_UNVERIFIED_KEY_IDENTIFIER;
35
+
32
36
  @end
@@ -2,7 +2,7 @@
2
2
  // StallionConfigConstants.m
3
3
  // DoubleConversion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 28/01/25.
5
+ // Created by Thor963 on 28/01/25.
6
6
  //
7
7
 
8
8
  #import "StallionConfigConstants.h"
@@ -29,4 +29,8 @@ NSString *const ANDROID_BUNDLE_FILE_NAME = @"/index.android.bundle";
29
29
  NSString *const DEFAULT_JS_BUNDLE_LOCATION_BASE = @"assets:/";
30
30
  NSString *const UNZIP_FOLDER_NAME = @"/build";
31
31
 
32
+ NSString *const STALLION_PUBLIC_SIGNING_KEY_IDENTIFIER = @"StallionPublicSigningKey";
33
+
34
+ NSString *const LAST_UNVERIFIED_KEY_IDENTIFIER = @"LastUnverifiedHash";
35
+
32
36
  @end
@@ -78,6 +78,7 @@ class StallionConstants {
78
78
  static let INSTALLED_PROD = "INSTALLED_PROD"
79
79
  static let STABILIZED_PROD = "STABILIZED_PROD"
80
80
  static let EXCEPTION_PROD = "EXCEPTION_PROD"
81
+ static let SIGNATURE_VERIFICATION_FAILED = "SIGNATURE_VERIFICATION_FAILED"
81
82
  }
82
83
  public struct NativeEventTypesStage {
83
84
  static let DOWNLOAD_STARTED_STAGE = "DOWNLOAD_STARTED_STAGE"
@@ -2,7 +2,7 @@
2
2
  // StallionEventHandler.h
3
3
  // DoubleConversion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 28/01/25.
5
+ // Created by Thor963 on 28/01/25.
6
6
  //
7
7
 
8
8
  #import <Foundation/Foundation.h>
@@ -2,7 +2,7 @@
2
2
  // StallionEventHandler.m
3
3
  // DoubleConversion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 28/01/25.
5
+ // Created by Thor963 on 28/01/25.
6
6
  //
7
7
 
8
8
  #import "StallionEventHandler.h"
@@ -15,6 +15,7 @@
15
15
  static NSString *const STALLION_NATIVE_EVENT_NAME = @"STALLION_NATIVE_EVENT";
16
16
  static NSString *const EVENTS_KEY = @"stored_events";
17
17
  static NSInteger const MAX_BATCH_COUNT_SIZE = 9;
18
+ static NSInteger const MAX_EVENT_STORAGE_LIMIT = 20;
18
19
 
19
20
  @implementation StallionEventHandler
20
21
 
@@ -30,11 +31,11 @@ static NSInteger const MAX_BATCH_COUNT_SIZE = 9;
30
31
  // Emit event to React Native and store locally
31
32
  - (void)cacheEvent:(NSString *)eventName eventPayload:(NSDictionary *)eventPayload {
32
33
  StallionStateManager *stallionStateManager = [StallionStateManager sharedInstance];
33
-
34
+
34
35
  NSString *projectId = stallionStateManager.stallionConfig.projectId ?: @"";
35
36
  NSString *appVersion = stallionStateManager.stallionConfig.appVersion ?: @"";
36
37
  NSString *uid = stallionStateManager.stallionConfig.uid ?: @"";
37
-
38
+
38
39
  NSMutableDictionary *mutablePayload = [eventPayload mutableCopy];
39
40
  NSString *uniqueId = [[NSUUID UUID] UUIDString];
40
41
  mutablePayload[@"eventId"] = uniqueId;
@@ -62,6 +63,11 @@ static NSInteger const MAX_BATCH_COUNT_SIZE = 9;
62
63
  eventsObject = [NSMutableDictionary dictionary];
63
64
  }
64
65
 
66
+ // Enforce max size
67
+ if (eventsObject.count >= MAX_EVENT_STORAGE_LIMIT) {
68
+ eventsObject = [NSMutableDictionary dictionary];
69
+ }
70
+
65
71
  eventsObject[uniqueId] = eventPayload;
66
72
 
67
73
  NSData *updatedData = [NSJSONSerialization dataWithJSONObject:eventsObject options:0 error:nil];
@@ -2,7 +2,7 @@
2
2
  // StallionExceptionHandler.h
3
3
  // react-native-stallion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 29/01/25.
5
+ // Created by Thor963 on 29/01/25.
6
6
  //
7
7
 
8
8
  #import <Foundation/Foundation.h>
@@ -2,7 +2,7 @@
2
2
  // StallionExceptionHandler.m
3
3
  // react-native-stallion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 29/01/25.
5
+ // Created by Thor963 on 29/01/25.
6
6
  //
7
7
 
8
8
  #import "StallionExceptionHandler.h"
@@ -51,7 +51,10 @@ void handleException(NSException *exception) {
51
51
  }
52
52
 
53
53
  } else if (meta.switchState == SwitchStateStage) {
54
- [StallionSlotManager rollbackStage];
54
+
55
+ if(isAutoRollback) {
56
+ [StallionSlotManager rollbackStage];
57
+ }
55
58
 
56
59
  [[StallionEventHandler sharedInstance] cacheEvent:StallionObjConstants.exception_stage_event
57
60
  eventPayload:@{
@@ -60,6 +63,7 @@ void handleException(NSException *exception) {
60
63
  StallionObjConstants.is_auto_rollback_key: isAutoRollback ? @"true" : @"false"
61
64
  }];
62
65
 
66
+ if(!exceptionAlertDismissed) {
63
67
  UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Stallion Exception Handler"
64
68
  message:[NSString stringWithFormat:@"%@\n%@",
65
69
  @"A crash occurred in the app. Build was rolled back. Check crash report below. Continue crash to invoke other exception handlers. \n \n",
@@ -82,6 +86,7 @@ void handleException(NSException *exception) {
82
86
  while (exceptionAlertDismissed == FALSE) {
83
87
  [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
84
88
  }
89
+ }
85
90
  }
86
91
 
87
92
  // Call default exception handler if available
@@ -2,7 +2,7 @@
2
2
  // StallionFileManager.h
3
3
  // react-native-stallion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 29/01/25.
5
+ // Created by Thor963 on 29/01/25.
6
6
  //
7
7
 
8
8
  #import <Foundation/Foundation.h>
@@ -2,7 +2,7 @@
2
2
  // StallionFileManager.m
3
3
  // react-native-stallion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 29/01/25.
5
+ // Created by Thor963 on 29/01/25.
6
6
  //
7
7
  #import "StallionFileManager.h"
8
8
 
@@ -2,7 +2,7 @@
2
2
  // StallionMeta.h
3
3
  // DoubleConversion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 28/01/25.
5
+ // Created by Thor963 on 28/01/25.
6
6
  //
7
7
 
8
8
  #import <Foundation/Foundation.h>
@@ -2,7 +2,7 @@
2
2
  // StallionMeta.m
3
3
  // DoubleConversion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 28/01/25.
5
+ // Created by Thor963 on 28/01/25.
6
6
  //
7
7
 
8
8
  #import "StallionMeta.h"
@@ -2,7 +2,7 @@
2
2
  // StallionMetaConstants.h
3
3
  // DoubleConversion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 28/01/25.
5
+ // Created by Thor963 on 28/01/25.
6
6
  //
7
7
 
8
8
  #import <Foundation/Foundation.h>
@@ -2,7 +2,7 @@
2
2
  // StallionMetaConstants.m
3
3
  // DoubleConversion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 28/01/25.
5
+ // Created by Thor963 on 28/01/25.
6
6
  //
7
7
 
8
8
  #import "StallionMetaConstants.h"
@@ -0,0 +1,135 @@
1
+ import Foundation
2
+ import CommonCrypto
3
+ import Security
4
+
5
+ class StallionSignatureVerification {
6
+
7
+ static let signatureFileName = ".stallionsigned"
8
+
9
+ static func verifyReleaseSignature(downloadedBundlePath: String, publicKeyPem: String) -> Bool {
10
+ do {
11
+ let signatureFilePath = (downloadedBundlePath as NSString).appendingPathComponent(signatureFileName)
12
+ guard FileManager.default.fileExists(atPath: signatureFilePath) else { return false }
13
+
14
+ let jwt = try String(contentsOfFile: signatureFilePath, encoding: .utf8)
15
+ let parts = jwt.components(separatedBy: ".")
16
+ guard parts.count == 3 else { return false }
17
+ let header = parts[0]
18
+ let payload = parts[1]
19
+ let signatureB64 = parts[2]
20
+
21
+ guard let signatureData = base64UrlDecode(signatureB64),
22
+ let signedData = (header + "." + payload).data(using: .utf8) else { return false }
23
+
24
+ guard let pubKey = try? convertPemToPublicKey(pemString: publicKeyPem) else { return false }
25
+
26
+ var error: Unmanaged<CFError>?
27
+ let verified = SecKeyVerifySignature(
28
+ pubKey,
29
+ .rsaSignatureMessagePKCS1v15SHA256,
30
+ signedData as CFData,
31
+ signatureData as CFData,
32
+ &error
33
+ )
34
+
35
+ guard verified else { return false }
36
+
37
+ guard let payloadData = base64UrlDecode(payload),
38
+ let payloadJson = try JSONSerialization.jsonObject(with: payloadData) as? [String: Any],
39
+ let expectedHash = payloadJson["packageHash"] as? String else { return false }
40
+
41
+ let actualHash = try computeCodePushStyleHash(folderPath: downloadedBundlePath)
42
+ return expectedHash == actualHash
43
+
44
+ } catch {
45
+ print("Signature verification error: \(error)")
46
+ return false
47
+ }
48
+ }
49
+
50
+ private static func computeCodePushStyleHash(folderPath: String) throws -> String {
51
+ var manifest: [String] = []
52
+ try addContentsOfFolder(to: &manifest, folderPath: folderPath, pathPrefix: "")
53
+ let sortedManifest = manifest.sorted()
54
+ let jsonData = try JSONSerialization.data(withJSONObject: sortedManifest, options: [])
55
+ var jsonString = String(data: jsonData, encoding: .utf8) ?? ""
56
+ jsonString = jsonString.replacingOccurrences(of: "\\/", with: "/")
57
+ return sha256Hex(jsonString)
58
+ }
59
+
60
+ private static func addContentsOfFolder(to manifest: inout [String], folderPath: String, pathPrefix: String) throws {
61
+ let items = try FileManager.default.contentsOfDirectory(atPath: folderPath)
62
+ for item in items {
63
+ let fullPath = (folderPath as NSString).appendingPathComponent(item)
64
+ let relativePath = pathPrefix.isEmpty ? item : (pathPrefix as NSString).appendingPathComponent(item)
65
+
66
+ if isIgnored(relativePath) { continue }
67
+
68
+ var isDir: ObjCBool = false
69
+ FileManager.default.fileExists(atPath: fullPath, isDirectory: &isDir)
70
+
71
+ if isDir.boolValue {
72
+ try addContentsOfFolder(to: &manifest, folderPath: fullPath, pathPrefix: relativePath)
73
+ } else {
74
+ let data = try Data(contentsOf: URL(fileURLWithPath: fullPath))
75
+ let hash = sha256Hex(data)
76
+ manifest.append("\(relativePath):\(hash)")
77
+ }
78
+ }
79
+ }
80
+
81
+ private static func isIgnored(_ path: String) -> Bool {
82
+ return path.hasPrefix("__MACOSX/") ||
83
+ path == ".DS_Store" ||
84
+ path.hasSuffix("/.DS_Store") ||
85
+ path == signatureFileName ||
86
+ path.hasSuffix("/\(signatureFileName)")
87
+ }
88
+
89
+ private static func sha256Hex(_ input: String) -> String {
90
+ guard let data = input.data(using: .utf8) else { return "" }
91
+ return sha256Hex(data)
92
+ }
93
+
94
+ private static func sha256Hex(_ data: Data) -> String {
95
+ var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
96
+ data.withUnsafeBytes {
97
+ _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &digest)
98
+ }
99
+ return digest.map { String(format: "%02x", $0) }.joined()
100
+ }
101
+
102
+ private static func base64UrlDecode(_ input: String) -> Data? {
103
+ var base64 = input.replacingOccurrences(of: "-", with: "+")
104
+ .replacingOccurrences(of: "_", with: "/")
105
+ let padding = base64.count % 4
106
+ if padding > 0 {
107
+ base64 += String(repeating: "=", count: 4 - padding)
108
+ }
109
+ return Data(base64Encoded: base64)
110
+ }
111
+
112
+ private static func convertPemToPublicKey(pemString: String) throws -> SecKey {
113
+ let cleaned = pemString
114
+ .replacingOccurrences(of: "-----BEGIN PUBLIC KEY-----\n", with: "")
115
+ .replacingOccurrences(of: "-----END PUBLIC KEY-----", with: "")
116
+ .replacingOccurrences(of: "\n", with: "")
117
+ .replacingOccurrences(of: "\r", with: "")
118
+
119
+ guard let keyData = Data(base64Encoded: cleaned, options: .ignoreUnknownCharacters) else {
120
+ throw NSError(domain: "Stallion", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid base64 key"])
121
+ }
122
+
123
+ let keyDict: [String: Any] = [
124
+ kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
125
+ kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
126
+ kSecAttrKeySizeInBits as String: 2048
127
+ ]
128
+
129
+ var error: Unmanaged<CFError>?
130
+ guard let secKey = SecKeyCreateWithData(keyData as CFData, keyDict as CFDictionary, &error) else {
131
+ throw error!.takeRetainedValue() as Error
132
+ }
133
+ return secKey
134
+ }
135
+ }
@@ -2,7 +2,7 @@
2
2
  // StallionSlotManager.h
3
3
  // react-native-stallion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 29/01/25.
5
+ // Created by Thor963 on 29/01/25.
6
6
  //
7
7
 
8
8
  #import <Foundation/Foundation.h>
@@ -2,7 +2,7 @@
2
2
  // StallionSlotManager.m
3
3
  // react-native-stallion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 29/01/25.
5
+ // Created by Thor963 on 29/01/25.
6
6
  //
7
7
 
8
8
  #import "StallionSlotManager.h"
@@ -2,7 +2,7 @@
2
2
  // StallionStageManager.swift
3
3
  // react-native-stallion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 29/01/25.
5
+ // Created by Thor963 on 29/01/25.
6
6
  //
7
7
 
8
8
  import Foundation
@@ -2,7 +2,7 @@
2
2
  // StallionStateManager.h
3
3
  // DoubleConversion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 28/01/25.
5
+ // Created by Thor963 on 28/01/25.
6
6
  //
7
7
 
8
8
  #import <Foundation/Foundation.h>
@@ -2,7 +2,7 @@
2
2
  // StallionStateManager.m
3
3
  // DoubleConversion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 28/01/25.
5
+ // Created by Thor963 on 28/01/25.
6
6
  //
7
7
 
8
8
  #import "StallionStateManager.h"
@@ -2,7 +2,7 @@
2
2
  // StallionSyncHandler.swift
3
3
  // react-native-stallion
4
4
  //
5
- // Created by Jasbir Singh Shergill on 29/01/25.
5
+ // Created by Thor963 on 29/01/25.
6
6
  //
7
7
 
8
8
  import Foundation
@@ -152,8 +152,9 @@ class StallionSyncHandler {
152
152
 
153
153
  let stateManager = StallionStateManager.sharedInstance()
154
154
  let lastRolledBackHash = stateManager?.stallionMeta?.lastRolledBackHash ?? ""
155
+ let lastUnverifiedHash = stateManager?.stallionConfig?.lastUnverifiedHash ?? ""
155
156
 
156
- if newReleaseHash != lastRolledBackHash {
157
+ if newReleaseHash != lastRolledBackHash && newReleaseHash != lastUnverifiedHash {
157
158
  if stateManager?.isMounted == true {
158
159
  downloadNewRelease(newReleaseHash: newReleaseHash, newReleaseUrl: newReleaseUrl)
159
160
  } else {
@@ -179,29 +180,52 @@ class StallionSyncHandler {
179
180
  let downloadPath = config.filesDirectory + "/" + StallionConstants.PROD_DIRECTORY + "/" + StallionConstants.TEMP_FOLDER_SLOT
180
181
  let projectId = config.projectId ?? ""
181
182
 
183
+ let publicSigningKey = config.publicSigningKey ?? ""
184
+
182
185
  guard let fromUrl = URL(string: newReleaseUrl + "?projectId=" + projectId) else { return }
183
186
 
184
187
  emitDownloadStarted(releaseHash: newReleaseHash)
185
188
 
186
- StallionFileDownloader().downloadBundle(url: fromUrl, downloadDirectory: downloadPath, onProgress: { progress in
187
- // Handle progress updates if necessary
188
- }, resolve: { _ in
189
- completeDownload()
190
- stateManager.stallionMeta?.currentProdSlot = SlotStates.newSlot
191
- stateManager.stallionMeta?.prodTempHash = newReleaseHash
192
- if let currentProdNewHash = stateManager.stallionMeta?.prodNewHash,
193
- !currentProdNewHash.isEmpty {
194
- StallionSlotManager.stabilizeProd()
195
- }
196
- stateManager.syncStallionMeta()
197
- emitDownloadSuccess(releaseHash: newReleaseHash)
198
- }, reject: { code, prefix, error in
199
- completeDownload()
200
- emitDownloadError(
201
- releaseHash: newReleaseHash,
202
- error: "\(String(describing: prefix))\(String(describing: error))"
189
+ StallionFileDownloader().downloadBundle(
190
+ url: fromUrl,
191
+ downloadDirectory: downloadPath,
192
+ onProgress: { progress in
193
+ // Handle progress updates if necessary
194
+ },
195
+ resolve: { _ in
196
+ completeDownload()
197
+
198
+ if(publicSigningKey != nil && !publicSigningKey.isEmpty) {
199
+ if(
200
+ !StallionSignatureVerification.verifyReleaseSignature(
201
+ downloadedBundlePath: downloadPath + "/" + StallionConstants.FilePaths.ZipFolderName,
202
+ publicKeyPem: publicSigningKey
203
+ )
204
+ ) {
205
+ // discard downloaded release
206
+ config.updateLastUnverifiedHash(newReleaseHash)
207
+ emitSignatureVerificationFailed(releaseHash: newReleaseHash)
208
+ StallionFileManager.deleteFileOrFolderSilently(downloadPath)
209
+ return;
210
+ }
211
+ }
212
+ stateManager.stallionMeta?.currentProdSlot = SlotStates.newSlot
213
+ stateManager.stallionMeta?.prodTempHash = newReleaseHash
214
+ if let currentProdNewHash = stateManager.stallionMeta?.prodNewHash,
215
+ !currentProdNewHash.isEmpty {
216
+ StallionSlotManager.stabilizeProd()
217
+ }
218
+ stateManager.syncStallionMeta()
219
+ emitDownloadSuccess(releaseHash: newReleaseHash)
220
+ },
221
+ reject: { code, prefix, error in
222
+ completeDownload()
223
+ emitDownloadError(
224
+ releaseHash: newReleaseHash,
225
+ error: "\(String(describing: prefix))\(String(describing: error))"
226
+ )
227
+ }
203
228
  )
204
- })
205
229
  }
206
230
 
207
231
  private static func completeDownload() {
@@ -246,5 +270,13 @@ class StallionSyncHandler {
246
270
  shouldCache: true
247
271
  )
248
272
  }
273
+
274
+ private static func emitSignatureVerificationFailed(releaseHash: String) {
275
+ let verificationFailurePayload: NSDictionary = ["releaseHash": releaseHash]
276
+ Stallion.sendEventToRn(eventName: StallionConstants.NativeEventTypesProd.SIGNATURE_VERIFICATION_FAILED,
277
+ eventBody: verificationFailurePayload,
278
+ shouldCache: true
279
+ )
280
+ }
249
281
  }
250
282
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-stallion",
3
- "version": "2.2.0",
3
+ "version": "2.3.0-alpha.1",
4
4
  "description": "Offical React Native SDK for Stallion",
5
5
  "main": "index",
6
6
  "types": "types/index.d.ts",
@@ -32,5 +32,8 @@ export const onLaunchNative = StallionNativeModule === null || StallionNativeMod
32
32
  export const sync = StallionNativeModule === null || StallionNativeModule === void 0 ? void 0 : StallionNativeModule.sync;
33
33
  export const popEventsNative = StallionNativeModule === null || StallionNativeModule === void 0 ? void 0 : StallionNativeModule.popEvents;
34
34
  export const acknowledgeEventsNative = StallionNativeModule === null || StallionNativeModule === void 0 ? void 0 : StallionNativeModule.acknowledgeEvents;
35
- export const restart = StallionNativeModule === null || StallionNativeModule === void 0 ? void 0 : StallionNativeModule.restart;
35
+ export const restart = () => {
36
+ var _StallionNativeModule;
37
+ StallionNativeModule === null || StallionNativeModule === void 0 ? void 0 : (_StallionNativeModule = StallionNativeModule.restart) === null || _StallionNativeModule === void 0 ? void 0 : _StallionNativeModule.call(StallionNativeModule);
38
+ };
36
39
  //# sourceMappingURL=StallionNativeUtils.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["StallionNativeModule","setSdkTokenNative","updateSdkToken","getStallionMetaNative","Promise","resolve","reject","getStallionMeta","then","metaString","JSON","parse","_","catch","getStallionConfigNative","getStallionConfig","configString","toggleStallionSwitchNative","toggleStallionSwitch","downloadBundleNative","downloadStageBundle","onLaunchNative","onLaunch","sync","popEventsNative","popEvents","acknowledgeEventsNative","acknowledgeEvents","restart"],"sourceRoot":"../../../../src","sources":["main/utils/StallionNativeUtils.ts"],"mappings":"AAAA,OAAOA,oBAAoB,MAAM,4BAA4B;AAW7D,OAAO,MAAMC,iBAAqC,GAChDD,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEE,cAAc;AAEtC,OAAO,MAAMC,qBAA6C,GAAGA,CAAA,KAAM;EACjE,OAAO,IAAIC,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;IACtCN,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEO,eAAe,CAAC,CAAC,CACpCC,IAAI,CAAEC,UAAkB,IAAK;MAC5B,IAAI;QACFJ,OAAO,CAACK,IAAI,CAACC,KAAK,CAACF,UAAU,CAAC,CAAC;MACjC,CAAC,CAAC,OAAOG,CAAC,EAAE;QACVN,MAAM,CAAC,qBAAqB,CAAC;MAC/B;IACF,CAAC,CAAC,CACDO,KAAK,CAAC,MAAM;MACXP,MAAM,CAAC,6BAA6B,CAAC;IACvC,CAAC,CAAC;EACN,CAAC,CAAC;AACJ,CAAC;AAED,OAAO,MAAMQ,uBAAiD,GAAGA,CAAA,KAAM;EACrE,OAAO,IAAIV,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;IACtCN,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEe,iBAAiB,CAAC,CAAC,CACtCP,IAAI,CAAEQ,YAAoB,IAAK;MAC9B,IAAI;QACFX,OAAO,CAACK,IAAI,CAACC,KAAK,CAACK,YAAY,CAAC,CAAC;MACnC,CAAC,CAAC,OAAOJ,CAAC,EAAE;QACVN,MAAM,CAAC,uBAAuB,CAAC;MACjC;IACF,CAAC,CAAC,CACDO,KAAK,CAAC,MAAM;MACXP,MAAM,CAAC,+BAA+B,CAAC;IACzC,CAAC,CAAC;EACN,CAAC,CAAC;AACJ,CAAC;AAED,OAAO,MAAMW,0BAAuD,GAClEjB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEkB,oBAAoB;AAE5C,OAAO,MAAMC,oBAA2C,GACtDnB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEoB,mBAAmB;AAE3C,OAAO,MAAMC,cAAqC,GAChDrB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEsB,QAAQ;AAEhC,OAAO,MAAMC,IAAgB,GAAGvB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEuB,IAAI;AAE1D,OAAO,MAAMC,eAAsC,GACjDxB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEyB,SAAS;AAEjC,OAAO,MAAMC,uBAA8D,GACzE1B,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAE2B,iBAAiB;AAEzC,OAAO,MAAMC,OAAmB,GAAG5B,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAE4B,OAAO"}
1
+ {"version":3,"names":["StallionNativeModule","setSdkTokenNative","updateSdkToken","getStallionMetaNative","Promise","resolve","reject","getStallionMeta","then","metaString","JSON","parse","_","catch","getStallionConfigNative","getStallionConfig","configString","toggleStallionSwitchNative","toggleStallionSwitch","downloadBundleNative","downloadStageBundle","onLaunchNative","onLaunch","sync","popEventsNative","popEvents","acknowledgeEventsNative","acknowledgeEvents","restart","_StallionNativeModule","call"],"sourceRoot":"../../../../src","sources":["main/utils/StallionNativeUtils.ts"],"mappings":"AAAA,OAAOA,oBAAoB,MAAM,4BAA4B;AAW7D,OAAO,MAAMC,iBAAqC,GAChDD,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEE,cAAc;AAEtC,OAAO,MAAMC,qBAA6C,GAAGA,CAAA,KAAM;EACjE,OAAO,IAAIC,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;IACtCN,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEO,eAAe,CAAC,CAAC,CACpCC,IAAI,CAAEC,UAAkB,IAAK;MAC5B,IAAI;QACFJ,OAAO,CAACK,IAAI,CAACC,KAAK,CAACF,UAAU,CAAC,CAAC;MACjC,CAAC,CAAC,OAAOG,CAAC,EAAE;QACVN,MAAM,CAAC,qBAAqB,CAAC;MAC/B;IACF,CAAC,CAAC,CACDO,KAAK,CAAC,MAAM;MACXP,MAAM,CAAC,6BAA6B,CAAC;IACvC,CAAC,CAAC;EACN,CAAC,CAAC;AACJ,CAAC;AAED,OAAO,MAAMQ,uBAAiD,GAAGA,CAAA,KAAM;EACrE,OAAO,IAAIV,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;IACtCN,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEe,iBAAiB,CAAC,CAAC,CACtCP,IAAI,CAAEQ,YAAoB,IAAK;MAC9B,IAAI;QACFX,OAAO,CAACK,IAAI,CAACC,KAAK,CAACK,YAAY,CAAC,CAAC;MACnC,CAAC,CAAC,OAAOJ,CAAC,EAAE;QACVN,MAAM,CAAC,uBAAuB,CAAC;MACjC;IACF,CAAC,CAAC,CACDO,KAAK,CAAC,MAAM;MACXP,MAAM,CAAC,+BAA+B,CAAC;IACzC,CAAC,CAAC;EACN,CAAC,CAAC;AACJ,CAAC;AAED,OAAO,MAAMW,0BAAuD,GAClEjB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEkB,oBAAoB;AAE5C,OAAO,MAAMC,oBAA2C,GACtDnB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEoB,mBAAmB;AAE3C,OAAO,MAAMC,cAAqC,GAChDrB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEsB,QAAQ;AAEhC,OAAO,MAAMC,IAAgB,GAAGvB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEuB,IAAI;AAE1D,OAAO,MAAMC,eAAsC,GACjDxB,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAEyB,SAAS;AAEjC,OAAO,MAAMC,uBAA8D,GACzE1B,oBAAoB,aAApBA,oBAAoB,uBAApBA,oBAAoB,CAAE2B,iBAAiB;AAEzC,OAAO,MAAMC,OAAO,GAAGA,CAAA,KAAM;EAAA,IAAAC,qBAAA;EAC3B7B,oBAAoB,aAApBA,oBAAoB,wBAAA6B,qBAAA,GAApB7B,oBAAoB,CAAE4B,OAAO,cAAAC,qBAAA,uBAA7BA,qBAAA,CAAAC,IAAA,CAAA9B,oBAAgC,CAAC;AACnC,CAAC"}
@@ -8,5 +8,5 @@ export declare const onLaunchNative: TOnLaunchBundleNative;
8
8
  export declare const sync: () => void;
9
9
  export declare const popEventsNative: () => Promise<string>;
10
10
  export declare const acknowledgeEventsNative: (eventIds: string) => Promise<string>;
11
- export declare const restart: () => null;
11
+ export declare const restart: () => void;
12
12
  //# sourceMappingURL=StallionNativeUtils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"StallionNativeUtils.d.ts","sourceRoot":"","sources":["../../../../src/main/utils/StallionNativeUtils.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,uBAAuB,CAAC;AAE/B,eAAO,MAAM,iBAAiB,EAAE,kBACM,CAAC;AAEvC,eAAO,MAAM,qBAAqB,EAAE,sBAcnC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,wBAcrC,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,2BACG,CAAC;AAE7C,eAAO,MAAM,oBAAoB,EAAE,qBACQ,CAAC;AAE5C,eAAO,MAAM,cAAc,EAAE,qBACG,CAAC;AAEjC,eAAO,MAAM,IAAI,EAAE,MAAM,IAAiC,CAAC;AAE3D,eAAO,MAAM,eAAe,EAAE,MAAM,OAAO,CAAC,MAAM,CACjB,CAAC;AAElC,eAAO,MAAM,uBAAuB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CACjC,CAAC;AAE1C,eAAO,MAAM,OAAO,EAAE,MAAM,IAAoC,CAAC"}
1
+ {"version":3,"file":"StallionNativeUtils.d.ts","sourceRoot":"","sources":["../../../../src/main/utils/StallionNativeUtils.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,uBAAuB,CAAC;AAE/B,eAAO,MAAM,iBAAiB,EAAE,kBACM,CAAC;AAEvC,eAAO,MAAM,qBAAqB,EAAE,sBAcnC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,wBAcrC,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,2BACG,CAAC;AAE7C,eAAO,MAAM,oBAAoB,EAAE,qBACQ,CAAC;AAE5C,eAAO,MAAM,cAAc,EAAE,qBACG,CAAC;AAEjC,eAAO,MAAM,IAAI,EAAE,MAAM,IAAiC,CAAC;AAE3D,eAAO,MAAM,eAAe,EAAE,MAAM,OAAO,CAAC,MAAM,CACjB,CAAC;AAElC,eAAO,MAAM,uBAAuB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CACjC,CAAC;AAE1C,eAAO,MAAM,OAAO,YAEnB,CAAC"}