cordova-plugin-hot-updates 2.2.0 → 2.2.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.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Cordova Hot Updates Plugin v2.2.0
1
+ # Cordova Hot Updates Plugin v2.2.1
2
2
 
3
3
  Frontend-controlled manual hot updates for Cordova iOS applications using WebView Reload approach.
4
4
 
@@ -125,11 +125,11 @@ callback(null)
125
125
  ### Error Handling Example
126
126
 
127
127
  ```javascript
128
- window.hotUpdate.getUpdate({url: 'http://...'}, function(error) {
129
- if (error) {
130
- console.error('[HotUpdates]', error.code, ':', error.message);
128
+ window.hotUpdate.getUpdate({url: 'http://...'}, function(result) {
129
+ if (result && result.error) {
130
+ console.error('[HotUpdates]', result.error.code, ':', result.error.message);
131
131
 
132
- switch(error.code) {
132
+ switch(result.error.code) {
133
133
  case 'HTTP_ERROR':
134
134
  // Handle HTTP errors
135
135
  break;
@@ -137,7 +137,7 @@ window.hotUpdate.getUpdate({url: 'http://...'}, function(error) {
137
137
  // Handle network errors
138
138
  break;
139
139
  default:
140
- console.error('Unknown error:', error);
140
+ console.error('Unknown error:', result.error);
141
141
  }
142
142
  } else {
143
143
  console.log('Update downloaded successfully');
@@ -268,6 +268,32 @@ window.hotUpdate.getIgnoreList(function(result) {
268
268
 
269
269
  ---
270
270
 
271
+ ### window.hotUpdate.getVersionInfo(callback)
272
+
273
+ Returns version information (debug method).
274
+
275
+ **Parameters:**
276
+ - `callback` (Function) - `callback(info)`
277
+ - `info.appBundleVersion` (string) - Native app version from Info.plist
278
+ - `info.installedVersion` (string|null) - Current hot update version
279
+ - `info.previousVersion` (string|null) - Last working version (for rollback)
280
+ - `info.canaryVersion` (string|null) - Version confirmed by canary
281
+ - `info.pendingVersion` (string|null) - Version pending installation
282
+ - `info.hasPendingUpdate` (boolean) - Whether pending update exists
283
+ - `info.ignoreList` (string[]) - Array of problematic versions
284
+
285
+ **Example:**
286
+ ```javascript
287
+ window.hotUpdate.getVersionInfo(function(info) {
288
+ console.log('App version:', info.appBundleVersion);
289
+ console.log('Installed:', info.installedVersion);
290
+ console.log('Previous:', info.previousVersion);
291
+ console.log('Pending:', info.hasPendingUpdate ? info.pendingVersion : 'none');
292
+ });
293
+ ```
294
+
295
+ ---
296
+
271
297
  ## Complete Update Flow
272
298
 
273
299
  ```javascript
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cordova-plugin-hot-updates",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Frontend-controlled manual hot updates for Cordova iOS apps using WebView Reload approach. Manual updates only, JavaScript controls all decisions.",
5
5
  "main": "www/HotUpdates.js",
6
6
  "scripts": {
package/plugin.xml CHANGED
@@ -1,6 +1,6 @@
1
1
  <?xml version='1.0' encoding='utf-8'?>
2
2
  <plugin id="cordova-plugin-hot-updates"
3
- version="2.2.0"
3
+ version="2.2.1"
4
4
  xmlns="http://apache.org/cordova/ns/plugins/1.0"
5
5
  xmlns:android="http://schemas.android.com/apk/res/android">
6
6
 
@@ -2,7 +2,7 @@
2
2
  * @file HotUpdates+Helpers.h
3
3
  * @brief Helper methods for Hot Updates Plugin
4
4
  * @details Category extension providing utility methods for error handling
5
- * @version 2.2.0
5
+ * @version 2.1.0
6
6
  * @date 2025-11-13
7
7
  * @author Mustafin Vladimir
8
8
  * @copyright Copyright (c) 2025. All rights reserved.
@@ -2,7 +2,7 @@
2
2
  * @file HotUpdates+Helpers.m
3
3
  * @brief Implementation of helper methods for Hot Updates Plugin
4
4
  * @details Provides utility methods for error handling and response formatting
5
- * @version 2.2.0
5
+ * @version 2.1.0
6
6
  * @date 2025-11-13
7
7
  * @author Mustafin Vladimir
8
8
  * @copyright Copyright (c) 2025. All rights reserved.
@@ -32,39 +32,13 @@
32
32
  NSString *previousVersionPath; // Путь к предыдущей версии
33
33
  }
34
34
 
35
- // Plugin lifecycle methods
36
- - (void)pluginInitialize;
37
- - (void)loadConfiguration;
38
- - (void)initializeWWWFolder;
39
- - (void)checkAndInstallPendingUpdate;
40
- - (void)switchToUpdatedContentWithReload;
41
- - (void)reloadWebView;
42
-
43
- // Update management methods (internal)
44
- - (void)installPendingUpdate:(NSString*)newVersion;
45
- - (BOOL)unzipFile:(NSString *)zipPath toDestination:(NSString *)destinationPath;
46
-
47
- // Version comparison utilities
48
- - (NSComparisonResult)compareVersion:(NSString*)version1 withVersion:(NSString*)version2;
49
-
50
- // JavaScript callable methods (minimal set for debugging)
51
- - (void)getCurrentVersion:(CDVInvokedUrlCommand*)command;
52
- - (void)getPendingUpdateInfo:(CDVInvokedUrlCommand*)command;
53
-
54
- // Ignore List management (JS can only read, native controls)
55
- - (void)getIgnoreList:(CDVInvokedUrlCommand*)command;
56
-
57
- // Debug methods (for manual testing only)
58
- - (void)addToIgnoreList:(CDVInvokedUrlCommand*)command;
59
- - (void)removeFromIgnoreList:(CDVInvokedUrlCommand*)command;
60
- - (void)clearIgnoreList:(CDVInvokedUrlCommand*)command;
61
-
62
- // Update methods (v2.1.0 - manual updates only)
35
+ // JavaScript API methods (v2.1.0)
63
36
  - (void)getUpdate:(CDVInvokedUrlCommand*)command; // Download update
64
37
  - (void)forceUpdate:(CDVInvokedUrlCommand*)command; // Install downloaded update
65
38
  - (void)canary:(CDVInvokedUrlCommand*)command; // Confirm successful load
39
+ - (void)getIgnoreList:(CDVInvokedUrlCommand*)command; // Get ignore list (JS reads only)
66
40
 
67
- // Debug methods
41
+ // Debug method
68
42
  - (void)getVersionInfo:(CDVInvokedUrlCommand*)command; // Get all version info for debugging
69
43
 
70
44
  @end
@@ -12,8 +12,8 @@
12
12
  * - IgnoreList for tracking problematic versions
13
13
  * - Auto-install pending updates on next app launch
14
14
  *
15
- * @version 2.2.0
16
- * @date 2025-11-03
15
+ * @version 2.1.2
16
+ * @date 2025-11-26
17
17
  * @author Mustafin Vladimir
18
18
  * @copyright Copyright (c) 2025. All rights reserved.
19
19
  */
@@ -56,6 +56,10 @@ static BOOL hasPerformedInitialReload = NO;
56
56
  [self loadConfiguration];
57
57
  [self loadIgnoreList];
58
58
 
59
+ // Сбрасываем флаг загрузки (если приложение было убито во время загрузки)
60
+ isDownloadingUpdate = NO;
61
+ [[NSUserDefaults standardUserDefaults] setBool:NO forKey:kDownloadInProgress];
62
+
59
63
  isUpdateReadyToInstall = NO;
60
64
  pendingUpdateURL = nil;
61
65
  pendingUpdateVersion = nil;
@@ -83,11 +87,7 @@ static BOOL hasPerformedInitialReload = NO;
83
87
  if (!canaryVersion || ![canaryVersion isEqualToString:currentVersion]) {
84
88
  NSLog(@"[HotUpdates] Starting canary timer (20 seconds) for version %@", currentVersion);
85
89
 
86
- canaryTimer = [NSTimer scheduledTimerWithTimeInterval:20.0
87
- target:self
88
- selector:@selector(canaryTimeout)
89
- userInfo:nil
90
- repeats:NO];
90
+ [self startCanaryTimer];
91
91
  } else {
92
92
  NSLog(@"[HotUpdates] Canary already confirmed for version %@", currentVersion);
93
93
  }
@@ -113,7 +113,6 @@ static BOOL hasPerformedInitialReload = NO;
113
113
  - (void)checkAndInstallPendingUpdate {
114
114
  BOOL hasPendingUpdate = [[NSUserDefaults standardUserDefaults] boolForKey:kHasPending];
115
115
  NSString *pendingVersion = [[NSUserDefaults standardUserDefaults] stringForKey:kPendingVersion];
116
- NSString *installedVersion = [[NSUserDefaults standardUserDefaults] stringForKey:kInstalledVersion];
117
116
 
118
117
  if (hasPendingUpdate && pendingVersion) {
119
118
  NSLog(@"[HotUpdates] Installing pending update %@ to Documents/www (auto-install on launch)", pendingVersion);
@@ -174,11 +173,13 @@ static BOOL hasPerformedInitialReload = NO;
174
173
  ((CDVViewController *)self.viewController).wwwFolderName = documentsWwwPath;
175
174
  NSLog(@"[HotUpdates] Changed wwwFolderName to: %@", documentsWwwPath);
176
175
 
177
- [self reloadWebView];
178
-
179
176
  hasPerformedInitialReload = YES;
180
177
 
181
- NSLog(@"[HotUpdates] WebView reloaded with updated content (version: %@)", installedVersion);
178
+ // Очищаем кэш перед перезагрузкой, иначе может загрузиться старая версия
179
+ [self clearWebViewCacheWithCompletion:^{
180
+ [self reloadWebView];
181
+ NSLog(@"[HotUpdates] WebView reloaded with updated content (version: %@)", installedVersion);
182
+ }];
182
183
  } else {
183
184
  NSLog(@"[HotUpdates] Documents/www/index.html not found, keeping bundle www");
184
185
  }
@@ -189,10 +190,18 @@ static BOOL hasPerformedInitialReload = NO;
189
190
  }
190
191
 
191
192
  /*!
192
- * @brief Force reload the WebView
193
- * @details Uses WKWebView loadFileURL with proper sandbox permissions
193
+ * @brief Clear WebView cache
194
+ * @details Clears disk cache, memory cache, offline storage and service workers
194
195
  */
195
196
  - (void)clearWebViewCache {
197
+ [self clearWebViewCacheWithCompletion:nil];
198
+ }
199
+
200
+ /*!
201
+ * @brief Clear WebView cache with completion handler
202
+ * @param completion Block called after cache is cleared (on main thread)
203
+ */
204
+ - (void)clearWebViewCacheWithCompletion:(void (^)(void))completion {
196
205
  NSLog(@"[HotUpdates] Clearing WebView cache");
197
206
 
198
207
  NSSet *websiteDataTypes = [NSSet setWithArray:@[
@@ -207,6 +216,9 @@ static BOOL hasPerformedInitialReload = NO;
207
216
  modifiedSince:dateFrom
208
217
  completionHandler:^{
209
218
  NSLog(@"[HotUpdates] WebView cache cleared");
219
+ if (completion) {
220
+ dispatch_async(dispatch_get_main_queue(), completion);
221
+ }
210
222
  }];
211
223
  }
212
224
 
@@ -397,6 +409,28 @@ static BOOL hasPerformedInitialReload = NO;
397
409
  [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
398
410
  }
399
411
 
412
+ #pragma mark - Canary Timer
413
+
414
+ /*!
415
+ * @brief Start canary timer with weak self to prevent retain cycle
416
+ * @details Uses block-based timer (iOS 10+) with weak reference
417
+ */
418
+ - (void)startCanaryTimer {
419
+ // Инвалидируем предыдущий таймер если есть
420
+ if (canaryTimer && [canaryTimer isValid]) {
421
+ [canaryTimer invalidate];
422
+ canaryTimer = nil;
423
+ }
424
+
425
+ // Используем weak self для предотвращения retain cycle
426
+ __weak __typeof__(self) weakSelf = self;
427
+ canaryTimer = [NSTimer scheduledTimerWithTimeInterval:20.0
428
+ repeats:NO
429
+ block:^(NSTimer * _Nonnull timer) {
430
+ [weakSelf canaryTimeout];
431
+ }];
432
+ }
433
+
400
434
  #pragma mark - Canary Timeout Handler
401
435
 
402
436
  - (void)canaryTimeout {
@@ -412,10 +446,7 @@ static BOOL hasPerformedInitialReload = NO;
412
446
 
413
447
  NSLog(@"[HotUpdates] Version %@ considered faulty, performing rollback", currentVersion);
414
448
 
415
- if (currentVersion) {
416
- [self addVersionToIgnoreList:currentVersion];
417
- }
418
-
449
+ // Примечание: версия добавляется в ignoreList внутри rollbackToPreviousVersion
419
450
  BOOL rollbackSuccess = [self rollbackToPreviousVersion];
420
451
 
421
452
  if (rollbackSuccess) {
@@ -423,8 +454,9 @@ static BOOL hasPerformedInitialReload = NO;
423
454
 
424
455
  hasPerformedInitialReload = NO;
425
456
 
426
- [self clearWebViewCache];
427
- [self reloadWebView];
457
+ [self clearWebViewCacheWithCompletion:^{
458
+ [self reloadWebView];
459
+ }];
428
460
  } else {
429
461
  NSLog(@"[HotUpdates] Automatic rollback failed");
430
462
  }
@@ -542,7 +574,11 @@ static BOOL hasPerformedInitialReload = NO;
542
574
  #pragma mark - Get Update (Download Only)
543
575
 
544
576
  - (void)getUpdate:(CDVInvokedUrlCommand*)command {
545
- NSDictionary *updateData = [command.arguments objectAtIndex:0];
577
+ // Безопасное получение первого аргумента
578
+ NSDictionary *updateData = nil;
579
+ if (command.arguments.count > 0 && [command.arguments[0] isKindOfClass:[NSDictionary class]]) {
580
+ updateData = command.arguments[0];
581
+ }
546
582
 
547
583
  if (!updateData) {
548
584
  CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
@@ -613,15 +649,30 @@ static BOOL hasPerformedInitialReload = NO;
613
649
  NSLog(@"[HotUpdates] Starting download");
614
650
 
615
651
  NSURL *url = [NSURL URLWithString:downloadURL];
652
+ if (!url) {
653
+ NSLog(@"[HotUpdates] Invalid URL: %@", downloadURL);
654
+ isDownloadingUpdate = NO;
655
+ [[NSUserDefaults standardUserDefaults] setBool:NO forKey:kDownloadInProgress];
656
+ [[NSUserDefaults standardUserDefaults] synchronize];
657
+
658
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
659
+ messageAsDictionary:[self createError:kErrorURLRequired
660
+ message:@"Invalid URL format"]];
661
+ [self.commandDelegate sendPluginResult:result callbackId:callbackId];
662
+ return;
663
+ }
616
664
 
617
665
  NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
618
- config.timeoutIntervalForRequest = 60.0;
619
- config.timeoutIntervalForResource = 300.0;
666
+ config.timeoutIntervalForRequest = 30.0; // ТЗ: 30-60 секунд
667
+ config.timeoutIntervalForResource = 60.0; // ТЗ: максимум 60 секунд на всю загрузку
620
668
 
621
669
  NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
622
670
 
623
671
  NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:url
624
672
  completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
673
+ // Инвалидируем сессию для предотвращения утечки памяти
674
+ [session finishTasksAndInvalidate];
675
+
625
676
  self->isDownloadingUpdate = NO;
626
677
  [[NSUserDefaults standardUserDefaults] setBool:NO forKey:kDownloadInProgress];
627
678
  [[NSUserDefaults standardUserDefaults] synchronize];
@@ -813,6 +864,7 @@ static BOOL hasPerformedInitialReload = NO;
813
864
 
814
865
  isUpdateReadyToInstall = NO;
815
866
  pendingUpdateURL = nil;
867
+ pendingUpdateVersion = nil;
816
868
 
817
869
  NSLog(@"[HotUpdates] Update installed successfully");
818
870
 
@@ -822,28 +874,24 @@ static BOOL hasPerformedInitialReload = NO;
822
874
  // После reloadWebView pluginInitialize НЕ вызывается, поэтому canary timer запускаем вручную
823
875
  NSLog(@"[HotUpdates] Starting canary timer (20 seconds) for version %@", newVersion);
824
876
 
825
- if (canaryTimer && [canaryTimer isValid]) {
826
- [canaryTimer invalidate];
827
- }
828
-
829
- canaryTimer = [NSTimer scheduledTimerWithTimeInterval:20.0
830
- target:self
831
- selector:@selector(canaryTimeout)
832
- userInfo:nil
833
- repeats:NO];
877
+ [self startCanaryTimer];
834
878
 
835
879
  hasPerformedInitialReload = NO;
836
880
 
837
881
  // Очищаем кэш WebView перед перезагрузкой, иначе может загрузиться старая версия
838
- [self clearWebViewCache];
839
-
840
- [self reloadWebView];
882
+ [self clearWebViewCacheWithCompletion:^{
883
+ [self reloadWebView];
884
+ }];
841
885
  }
842
886
 
843
887
  #pragma mark - Canary
844
888
 
845
889
  - (void)canary:(CDVInvokedUrlCommand*)command {
846
- NSString *canaryVersion = [command.arguments objectAtIndex:0];
890
+ // Безопасное получение первого аргумента
891
+ NSString *canaryVersion = nil;
892
+ if (command.arguments.count > 0 && [command.arguments[0] isKindOfClass:[NSString class]]) {
893
+ canaryVersion = command.arguments[0];
894
+ }
847
895
 
848
896
  if (!canaryVersion || canaryVersion.length == 0) {
849
897
  CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
@@ -866,12 +914,8 @@ static BOOL hasPerformedInitialReload = NO;
866
914
  NSLog(@"[HotUpdates] Canary timer stopped - JS confirmed bundle is working");
867
915
  }
868
916
 
869
- CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
870
- messageAsDictionary:@{
871
- @"success": @YES,
872
- @"canaryVersion": canaryVersion,
873
- @"message": @"Canary version confirmed"
874
- }];
917
+ // ТЗ: при успехе callback возвращает null
918
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
875
919
  [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
876
920
  }
877
921
 
@@ -1,17 +1,19 @@
1
1
  /*!
2
2
  * @file HotUpdatesConstants.h
3
3
  * @brief Constants for Hot Updates Plugin
4
- * @details Defines error codes, storage keys, and directory names
5
- * @version 2.2.0
4
+ * @details Contains error codes, storage keys, and directory names used by the plugin
5
+ * @version 2.1.0
6
6
  * @date 2025-11-13
7
7
  * @author Mustafin Vladimir
8
8
  * @copyright Copyright (c) 2025. All rights reserved.
9
9
  */
10
10
 
11
- #import <Foundation/Foundation.h>
11
+ #ifndef HotUpdatesConstants_h
12
+ #define HotUpdatesConstants_h
12
13
 
13
14
  #pragma mark - Error Codes
14
15
 
16
+ // Error codes returned to JavaScript
15
17
  extern NSString * const kErrorUpdateDataRequired;
16
18
  extern NSString * const kErrorURLRequired;
17
19
  extern NSString * const kErrorDownloadInProgress;
@@ -27,6 +29,7 @@ extern NSString * const kErrorVersionRequired;
27
29
 
28
30
  #pragma mark - Storage Keys
29
31
 
32
+ // NSUserDefaults keys
30
33
  extern NSString * const kInstalledVersion;
31
34
  extern NSString * const kPendingVersion;
32
35
  extern NSString * const kHasPending;
@@ -39,7 +42,10 @@ extern NSString * const kPendingUpdateReady;
39
42
 
40
43
  #pragma mark - Directory Names
41
44
 
45
+ // Directory names
42
46
  extern NSString * const kWWWDirName;
43
47
  extern NSString * const kPreviousWWWDirName;
44
48
  extern NSString * const kBackupWWWDirName;
45
49
  extern NSString * const kPendingUpdateDirName;
50
+
51
+ #endif /* HotUpdatesConstants_h */
@@ -2,7 +2,7 @@
2
2
  * @file HotUpdatesConstants.m
3
3
  * @brief Implementation of constants for Hot Updates Plugin
4
4
  * @details Defines all constant values used throughout the plugin
5
- * @version 2.2.0
5
+ * @version 2.1.0
6
6
  * @date 2025-11-13
7
7
  * @author Mustafin Vladimir
8
8
  * @copyright Copyright (c) 2025. All rights reserved.
package/www/HotUpdates.js CHANGED
@@ -1,73 +1,80 @@
1
1
  var exec = require('cordova/exec');
2
2
 
3
3
  /**
4
- * Cordova Hot Updates Plugin v2.1.2
4
+ * Cordova Hot Updates Plugin v2.2.1
5
5
  * Frontend-controlled manual hot updates for iOS
6
6
  *
7
- * Provides manual over-the-air (OTA) updates for Cordova applications
8
- * using the WebView Reload approach. All update decisions are controlled
9
- * by JavaScript - the native plugin only executes commands.
10
- *
11
- * Features:
12
- * - Frontend-controlled manual updates (no automatic checking)
13
- * - Two-step update flow: getUpdate() downloads, forceUpdate() installs
14
- * - Automatic rollback with 20-second canary timer
15
- * - IgnoreList system for tracking problematic versions (information only)
16
- * - Auto-install pending updates on next app launch
17
- * - WebView reload approach for instant updates without app restart
18
- * - No App Store approval required for web content updates
19
- *
20
- * @version 2.1.2
7
+ * @version 2.2.1
21
8
  * @author Mustafin Vladimir
22
9
  */
10
+
11
+ /**
12
+ * Error codes returned by the plugin
13
+ * @readonly
14
+ * @enum {string}
15
+ */
16
+ var ErrorCodes = {
17
+ // getUpdate errors
18
+ UPDATE_DATA_REQUIRED: 'UPDATE_DATA_REQUIRED',
19
+ URL_REQUIRED: 'URL_REQUIRED',
20
+ DOWNLOAD_IN_PROGRESS: 'DOWNLOAD_IN_PROGRESS',
21
+ DOWNLOAD_FAILED: 'DOWNLOAD_FAILED',
22
+ HTTP_ERROR: 'HTTP_ERROR',
23
+ TEMP_DIR_ERROR: 'TEMP_DIR_ERROR',
24
+ EXTRACTION_FAILED: 'EXTRACTION_FAILED',
25
+ WWW_NOT_FOUND: 'WWW_NOT_FOUND',
26
+ // forceUpdate errors
27
+ NO_UPDATE_READY: 'NO_UPDATE_READY',
28
+ UPDATE_FILES_NOT_FOUND: 'UPDATE_FILES_NOT_FOUND',
29
+ INSTALL_FAILED: 'INSTALL_FAILED',
30
+ // canary errors
31
+ VERSION_REQUIRED: 'VERSION_REQUIRED'
32
+ };
33
+
23
34
  var HotUpdates = {
24
35
 
36
+ /**
37
+ * Error codes enum
38
+ * @type {Object}
39
+ */
40
+ ErrorCodes: ErrorCodes,
41
+
25
42
  /**
26
43
  * Download update from server
27
44
  *
28
- * Downloads update ZIP archive from the provided URL and saves to two locations:
29
- * - temp_downloaded_update (for immediate installation via forceUpdate)
30
- * - pending_update (for auto-installation on next app launch)
31
- *
32
- * If the specified version is already downloaded, returns success without re-downloading.
33
- * Does NOT check ignoreList - JavaScript controls all installation decisions.
34
- *
35
45
  * @param {Object} options - Update options
36
46
  * @param {string} options.url - URL to download ZIP archive (required)
37
47
  * @param {string} [options.version] - Version string (optional)
38
- * @param {Function} callback - Callback function
39
- * - Called with null on success
40
- * - Called with {error: {message?: string}} on error
48
+ * @param {Function} callback - Callback(error)
49
+ * - null on success
50
+ * - {error: {code: string, message: string}} on error
41
51
  *
42
52
  * @example
43
- * window.hotUpdate.getUpdate({
44
- * url: 'https://your-server.com/updates/2.0.0.zip',
45
- * version: '2.0.0'
46
- * }, function(error) {
47
- * if (error) {
48
- * console.error('Download failed:', error);
49
- * } else {
50
- * console.log('Update downloaded successfully');
51
- * // Can now call forceUpdate() to install immediately
52
- * // Or user can ignore and it will auto-install on next launch
53
- * }
53
+ * hotUpdate.getUpdate({url: 'https://server.com/update.zip', version: '2.0.0'}, function(err) {
54
+ * if (err) console.error(err.error.code, err.error.message);
55
+ * else console.log('Downloaded');
54
56
  * });
55
57
  */
56
58
  getUpdate: function(options, callback) {
57
- if (!options || !options.url) {
59
+ if (!options) {
60
+ if (callback) {
61
+ callback({error: {code: ErrorCodes.UPDATE_DATA_REQUIRED, message: 'Update data required'}});
62
+ }
63
+ return;
64
+ }
65
+
66
+ if (!options.url) {
58
67
  if (callback) {
59
- callback({error: {message: 'URL is required'}});
68
+ callback({error: {code: ErrorCodes.URL_REQUIRED, message: 'URL is required'}});
60
69
  }
61
70
  return;
62
71
  }
63
72
 
64
73
  exec(
65
74
  function() {
66
- // Success
67
75
  if (callback) callback(null);
68
76
  },
69
77
  function(error) {
70
- // Error
71
78
  if (callback) callback({error: error});
72
79
  },
73
80
  'HotUpdates',
@@ -79,42 +86,22 @@ var HotUpdates = {
79
86
  /**
80
87
  * Install downloaded update immediately
81
88
  *
82
- * Installs the update that was downloaded via getUpdate().
83
- * This will:
84
- * 1. Backup current version to www_previous
85
- * 2. Copy downloaded update to Documents/www
86
- * 3. Clear WebView cache (disk, memory, Service Worker)
87
- * 4. Reload WebView
88
- * 5. Start 20-second canary timer
89
- *
90
- * IMPORTANT: JavaScript MUST call canary(version) within 20 seconds
91
- * after reload to confirm successful bundle load. Otherwise automatic
92
- * rollback will occur.
93
- *
94
- * Does NOT check ignoreList - JavaScript decides what to install.
95
- *
96
- * @param {Function} callback - Callback function
97
- * - Called with null on success (before WebView reload)
98
- * - Called with {error: {message?: string}} on error
89
+ * @param {Function} callback - Callback(error)
90
+ * - null on success (WebView will reload)
91
+ * - {error: {code: string, message: string}} on error
99
92
  *
100
93
  * @example
101
- * window.hotUpdate.forceUpdate(function(error) {
102
- * if (error) {
103
- * console.error('Install failed:', error);
104
- * } else {
105
- * console.log('Update installing, WebView will reload...');
106
- * // After reload, MUST call canary() within 20 seconds!
107
- * }
94
+ * hotUpdate.forceUpdate(function(err) {
95
+ * if (err) console.error(err.error.code);
96
+ * // WebView reloads, call canary() within 20 sec
108
97
  * });
109
98
  */
110
99
  forceUpdate: function(callback) {
111
100
  exec(
112
101
  function() {
113
- // Success
114
102
  if (callback) callback(null);
115
103
  },
116
104
  function(error) {
117
- // Error
118
105
  if (callback) callback({error: error});
119
106
  },
120
107
  'HotUpdates',
@@ -124,36 +111,30 @@ var HotUpdates = {
124
111
  },
125
112
 
126
113
  /**
127
- * Confirm successful bundle load (canary check)
128
- *
129
- * MUST be called within 20 seconds after forceUpdate() to confirm
130
- * that the new bundle loaded successfully. This stops the canary timer
131
- * and prevents automatic rollback.
132
- *
133
- * If not called within 20 seconds:
134
- * - Automatic rollback to previous version
135
- * - Failed version added to ignoreList
136
- * - WebView reloaded with previous version
137
- *
138
- * Call this immediately after your app initialization completes.
114
+ * Confirm successful bundle load (MUST call within 20 sec after forceUpdate)
139
115
  *
140
- * @param {string} version - Version that loaded successfully
141
- * @param {Function} [callback] - Optional callback (not used, method is synchronous)
116
+ * @param {string} version - Current version
117
+ * @param {Function} [callback] - Optional callback
142
118
  *
143
119
  * @example
144
- * // Call as early as possible after app loads
145
120
  * document.addEventListener('deviceready', function() {
146
- * window.hotUpdate.canary('2.0.0');
147
- * console.log('Canary confirmed, update successful');
148
- * }, false);
121
+ * hotUpdate.canary('2.0.0');
122
+ * });
149
123
  */
150
124
  canary: function(version, callback) {
125
+ if (!version) {
126
+ if (callback) {
127
+ callback({error: {code: ErrorCodes.VERSION_REQUIRED, message: 'Version is required'}});
128
+ }
129
+ return;
130
+ }
131
+
151
132
  exec(
152
133
  function() {
153
- if (callback) callback();
134
+ if (callback) callback(null);
154
135
  },
155
- function() {
156
- if (callback) callback();
136
+ function(error) {
137
+ if (callback) callback({error: error});
157
138
  },
158
139
  'HotUpdates',
159
140
  'canary',
@@ -162,50 +143,62 @@ var HotUpdates = {
162
143
  },
163
144
 
164
145
  /**
165
- * Get list of problematic versions (information only)
146
+ * Get list of problematic versions
166
147
  *
167
- * Returns array of version strings that failed to load (triggered rollback).
168
- * This is an INFORMATION-ONLY system - native does NOT block installation
169
- * of versions in this list.
170
- *
171
- * JavaScript should read this list and decide whether to skip downloading/
172
- * installing these versions. If JS decides to install a version from the
173
- * ignoreList, that's allowed (per TS requirements).
174
- *
175
- * Native automatically adds versions to this list when rollback occurs.
176
- * JavaScript cannot modify the list (no add/remove/clear methods per TS v2.1.0).
177
- *
178
- * @param {Function} callback - Callback function
179
- * - Called with {versions: string[]} - Array of problematic version strings
148
+ * @param {Function} callback - Callback({versions: string[]})
180
149
  *
181
150
  * @example
182
- * window.hotUpdate.getIgnoreList(function(result) {
183
- * console.log('Problematic versions:', result.versions);
184
- * // Example: {versions: ['1.9.0', '2.0.1']}
185
- *
186
- * // JavaScript decides what to do with this information
187
- * var shouldSkip = result.versions.includes(availableVersion);
188
- * if (shouldSkip) {
189
- * console.log('Skipping known problematic version');
190
- * } else {
191
- * // Download and install
151
+ * hotUpdate.getIgnoreList(function(result) {
152
+ * if (result.versions.includes(newVersion)) {
153
+ * console.log('Version is blacklisted');
192
154
  * }
193
155
  * });
194
156
  */
195
157
  getIgnoreList: function(callback) {
196
158
  exec(
197
- function(versions) {
198
- // Success - native returns array of version strings
199
- if (callback) callback({versions: versions || []});
159
+ function(result) {
160
+ if (callback) callback(result || {versions: []});
200
161
  },
201
- function(error) {
202
- // Error - return empty list
162
+ function() {
203
163
  if (callback) callback({versions: []});
204
164
  },
205
165
  'HotUpdates',
206
166
  'getIgnoreList',
207
167
  []
208
168
  );
169
+ },
170
+
171
+ /**
172
+ * Get version info (debug method)
173
+ *
174
+ * @param {Function} callback - Callback with version info
175
+ * {
176
+ * appBundleVersion: string,
177
+ * installedVersion: string|null,
178
+ * previousVersion: string|null,
179
+ * canaryVersion: string|null,
180
+ * pendingVersion: string|null,
181
+ * hasPendingUpdate: boolean,
182
+ * ignoreList: string[]
183
+ * }
184
+ *
185
+ * @example
186
+ * hotUpdate.getVersionInfo(function(info) {
187
+ * console.log('Current:', info.installedVersion || info.appBundleVersion);
188
+ * });
189
+ */
190
+ getVersionInfo: function(callback) {
191
+ exec(
192
+ function(info) {
193
+ if (callback) callback(info);
194
+ },
195
+ function(error) {
196
+ if (callback) callback({error: error});
197
+ },
198
+ 'HotUpdates',
199
+ 'getVersionInfo',
200
+ []
201
+ );
209
202
  }
210
203
  };
211
204