cordova-plugin-hot-updates 2.1.1 → 2.2.0

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.
@@ -0,0 +1,45 @@
1
+ /*!
2
+ * @file HotUpdatesConstants.h
3
+ * @brief Constants for Hot Updates Plugin
4
+ * @details Defines error codes, storage keys, and directory names
5
+ * @version 2.2.0
6
+ * @date 2025-11-13
7
+ * @author Mustafin Vladimir
8
+ * @copyright Copyright (c) 2025. All rights reserved.
9
+ */
10
+
11
+ #import <Foundation/Foundation.h>
12
+
13
+ #pragma mark - Error Codes
14
+
15
+ extern NSString * const kErrorUpdateDataRequired;
16
+ extern NSString * const kErrorURLRequired;
17
+ extern NSString * const kErrorDownloadInProgress;
18
+ extern NSString * const kErrorDownloadFailed;
19
+ extern NSString * const kErrorHTTPError;
20
+ extern NSString * const kErrorTempDirError;
21
+ extern NSString * const kErrorExtractionFailed;
22
+ extern NSString * const kErrorWWWNotFound;
23
+ extern NSString * const kErrorNoUpdateReady;
24
+ extern NSString * const kErrorUpdateFilesNotFound;
25
+ extern NSString * const kErrorInstallFailed;
26
+ extern NSString * const kErrorVersionRequired;
27
+
28
+ #pragma mark - Storage Keys
29
+
30
+ extern NSString * const kInstalledVersion;
31
+ extern NSString * const kPendingVersion;
32
+ extern NSString * const kHasPending;
33
+ extern NSString * const kPreviousVersion;
34
+ extern NSString * const kIgnoreList;
35
+ extern NSString * const kCanaryVersion;
36
+ extern NSString * const kDownloadInProgress;
37
+ extern NSString * const kPendingUpdateURL;
38
+ extern NSString * const kPendingUpdateReady;
39
+
40
+ #pragma mark - Directory Names
41
+
42
+ extern NSString * const kWWWDirName;
43
+ extern NSString * const kPreviousWWWDirName;
44
+ extern NSString * const kBackupWWWDirName;
45
+ extern NSString * const kPendingUpdateDirName;
@@ -0,0 +1,46 @@
1
+ /*!
2
+ * @file HotUpdatesConstants.m
3
+ * @brief Implementation of constants for Hot Updates Plugin
4
+ * @details Defines all constant values used throughout the plugin
5
+ * @version 2.2.0
6
+ * @date 2025-11-13
7
+ * @author Mustafin Vladimir
8
+ * @copyright Copyright (c) 2025. All rights reserved.
9
+ */
10
+
11
+ #import <Foundation/Foundation.h>
12
+ #import "HotUpdatesConstants.h"
13
+
14
+ #pragma mark - Error Codes
15
+
16
+ NSString * const kErrorUpdateDataRequired = @"UPDATE_DATA_REQUIRED";
17
+ NSString * const kErrorURLRequired = @"URL_REQUIRED";
18
+ NSString * const kErrorDownloadInProgress = @"DOWNLOAD_IN_PROGRESS";
19
+ NSString * const kErrorDownloadFailed = @"DOWNLOAD_FAILED";
20
+ NSString * const kErrorHTTPError = @"HTTP_ERROR";
21
+ NSString * const kErrorTempDirError = @"TEMP_DIR_ERROR";
22
+ NSString * const kErrorExtractionFailed = @"EXTRACTION_FAILED";
23
+ NSString * const kErrorWWWNotFound = @"WWW_NOT_FOUND";
24
+ NSString * const kErrorNoUpdateReady = @"NO_UPDATE_READY";
25
+ NSString * const kErrorUpdateFilesNotFound = @"UPDATE_FILES_NOT_FOUND";
26
+ NSString * const kErrorInstallFailed = @"INSTALL_FAILED";
27
+ NSString * const kErrorVersionRequired = @"VERSION_REQUIRED";
28
+
29
+ #pragma mark - Storage Keys
30
+
31
+ NSString * const kInstalledVersion = @"hot_updates_installed_version";
32
+ NSString * const kPendingVersion = @"hot_updates_pending_version";
33
+ NSString * const kHasPending = @"hot_updates_has_pending";
34
+ NSString * const kPreviousVersion = @"hot_updates_previous_version";
35
+ NSString * const kIgnoreList = @"hot_updates_ignore_list";
36
+ NSString * const kCanaryVersion = @"hot_updates_canary_version";
37
+ NSString * const kDownloadInProgress = @"hot_updates_download_in_progress";
38
+ NSString * const kPendingUpdateURL = @"hot_updates_pending_update_url";
39
+ NSString * const kPendingUpdateReady = @"hot_updates_pending_ready";
40
+
41
+ #pragma mark - Directory Names
42
+
43
+ NSString * const kWWWDirName = @"www";
44
+ NSString * const kPreviousWWWDirName = @"www_previous";
45
+ NSString * const kBackupWWWDirName = @"www_backup";
46
+ NSString * const kPendingUpdateDirName = @"pending_update";
package/www/HotUpdates.js CHANGED
@@ -1,192 +1,212 @@
1
1
  var exec = require('cordova/exec');
2
2
 
3
3
  /**
4
- * Cordova Hot Updates Plugin
4
+ * Cordova Hot Updates Plugin v2.1.2
5
+ * Frontend-controlled manual hot updates for iOS
5
6
  *
6
- * Provides automatic over-the-air (OTA) updates for Cordova applications
7
- * using the WebView Reload approach for instant updates.
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.
8
10
  *
9
11
  * Features:
10
- * - Automatic background update checking
11
- * - Seamless download and installation
12
- * - Version compatibility checks
13
- * - Configurable update intervals
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
14
18
  * - No App Store approval required for web content updates
15
19
  *
16
- * @version 1.0.0
20
+ * @version 2.1.2
17
21
  * @author Mustafin Vladimir
18
22
  */
19
23
  var HotUpdates = {
20
24
 
21
25
  /**
22
- * Get current version information
23
- * Returns the currently active version (installed update or bundle version)
26
+ * Download update from server
24
27
  *
25
- * @param {Function} successCallback - Success callback with version string
26
- * @param {Function} errorCallback - Error callback
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)
27
31
  *
28
- * @example
29
- * HotUpdates.getCurrentVersion(
30
- * function(version) {
31
- * console.log('Current version:', version);
32
- * },
33
- * function(error) {
34
- * console.error('Error getting version:', error);
35
- * }
36
- * );
37
- */
38
- getCurrentVersion: function(successCallback, errorCallback) {
39
- exec(successCallback, errorCallback, 'HotUpdates', 'getCurrentVersion', []);
40
- },
41
-
42
- /**
43
- * Get pending update information
44
- * Returns information about downloaded updates waiting to be installed
45
- *
46
- * @param {Function} successCallback - Success callback with update info object
47
- * @param {Function} errorCallback - Error callback
32
+ * If the specified version is already downloaded, returns success without re-downloading.
33
+ * Does NOT check ignoreList - JavaScript controls all installation decisions.
48
34
  *
49
- * Success callback receives object with:
50
- * - hasPendingUpdate: boolean
51
- * - pendingVersion: string (if available)
52
- * - appBundleVersion: string
53
- * - installedVersion: string (current hot update version)
54
- * - message: string (human readable status)
35
+ * @param {Object} options - Update options
36
+ * @param {string} options.url - URL to download ZIP archive (required)
37
+ * @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
55
41
  *
56
42
  * @example
57
- * HotUpdates.getPendingUpdateInfo(
58
- * function(info) {
59
- * if (info.hasPendingUpdate) {
60
- * console.log('Update ready:', info.pendingVersion);
61
- * console.log('Will install on next app restart');
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
62
53
  * }
63
- * },
64
- * function(error) {
65
- * console.error('Error getting update info:', error);
66
- * }
67
- * );
54
+ * });
68
55
  */
69
- getPendingUpdateInfo: function(successCallback, errorCallback) {
70
- exec(successCallback, errorCallback, 'HotUpdates', 'getPendingUpdateInfo', []);
56
+ getUpdate: function(options, callback) {
57
+ if (!options || !options.url) {
58
+ if (callback) {
59
+ callback({error: {message: 'URL is required'}});
60
+ }
61
+ return;
62
+ }
63
+
64
+ exec(
65
+ function() {
66
+ // Success
67
+ if (callback) callback(null);
68
+ },
69
+ function(error) {
70
+ // Error
71
+ if (callback) callback({error: error});
72
+ },
73
+ 'HotUpdates',
74
+ 'getUpdate',
75
+ [options]
76
+ );
71
77
  },
72
78
 
73
79
  /**
74
- * Check for updates manually
75
- * Triggers an immediate check for available updates on the server
80
+ * Install downloaded update immediately
81
+ *
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
76
89
  *
77
- * @param {Function} successCallback - Success callback with check result
78
- * @param {Function} errorCallback - Error callback
90
+ * IMPORTANT: JavaScript MUST call canary(version) within 20 seconds
91
+ * after reload to confirm successful bundle load. Otherwise automatic
92
+ * rollback will occur.
79
93
  *
80
- * Success callback receives object with:
81
- * - hasUpdate: boolean
82
- * - currentVersion: string
83
- * - availableVersion: string (if hasUpdate is true)
84
- * - downloadURL: string (if hasUpdate is true)
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
85
99
  *
86
100
  * @example
87
- * HotUpdates.checkForUpdates(
88
- * function(result) {
89
- * if (result.hasUpdate) {
90
- * console.log('Update available:', result.availableVersion);
101
+ * window.hotUpdate.forceUpdate(function(error) {
102
+ * if (error) {
103
+ * console.error('Install failed:', error);
91
104
  * } else {
92
- * console.log('No updates available');
105
+ * console.log('Update installing, WebView will reload...');
106
+ * // After reload, MUST call canary() within 20 seconds!
93
107
  * }
94
- * },
95
- * function(error) {
96
- * console.error('Update check failed:', error);
97
- * }
98
- * );
108
+ * });
99
109
  */
100
- checkForUpdates: function(successCallback, errorCallback) {
101
- exec(successCallback, errorCallback, 'HotUpdates', 'checkForUpdates', []);
110
+ forceUpdate: function(callback) {
111
+ exec(
112
+ function() {
113
+ // Success
114
+ if (callback) callback(null);
115
+ },
116
+ function(error) {
117
+ // Error
118
+ if (callback) callback({error: error});
119
+ },
120
+ 'HotUpdates',
121
+ 'forceUpdate',
122
+ []
123
+ );
102
124
  },
103
125
 
104
126
  /**
105
- * Download available update
106
- * Manually triggers download of an available update
127
+ * Confirm successful bundle load (canary check)
107
128
  *
108
- * @param {string} downloadURL - URL to download the update from
109
- * @param {string} version - Version string of the update
110
- * @param {Function} successCallback - Success callback when download completes
111
- * @param {Function} errorCallback - Error callback
112
- * @param {Function} progressCallback - Optional progress callback with download percentage
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.
113
132
  *
114
- * @example
115
- * HotUpdates.downloadUpdate(
116
- * 'https://server.com/updates/v2.0.0.zip',
117
- * '2.0.0',
118
- * function() {
119
- * console.log('Download completed, update will install on restart');
120
- * },
121
- * function(error) {
122
- * console.error('Download failed:', error);
123
- * },
124
- * function(progress) {
125
- * console.log('Download progress:', progress + '%');
126
- * }
127
- * );
128
- */
129
- downloadUpdate: function(downloadURL, version, successCallback, errorCallback, progressCallback) {
130
- var callbackId = successCallback ? 'HotUpdates' + Date.now() : null;
131
-
132
- if (progressCallback) {
133
- // Register progress callback if provided
134
- exec(progressCallback, null, 'HotUpdates', 'setProgressCallback', [callbackId]);
135
- }
136
-
137
- exec(successCallback, errorCallback, 'HotUpdates', 'downloadUpdate', [downloadURL, version, callbackId]);
138
- },
139
-
140
- /**
141
- * Get plugin configuration
142
- * Returns current plugin configuration settings
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
143
137
  *
144
- * @param {Function} successCallback - Success callback with config object
145
- * @param {Function} errorCallback - Error callback
138
+ * Call this immediately after your app initialization completes.
146
139
  *
147
- * Success callback receives object with:
148
- * - serverURL: string (update server URL)
149
- * - checkInterval: number (check interval in milliseconds)
150
- * - appBundleVersion: string (native app version)
151
- * - autoDownload: boolean (automatic download enabled)
140
+ * @param {string} version - Version that loaded successfully
141
+ * @param {Function} [callback] - Optional callback (not used, method is synchronous)
152
142
  *
153
143
  * @example
154
- * HotUpdates.getConfiguration(
155
- * function(config) {
156
- * console.log('Server URL:', config.serverURL);
157
- * console.log('Check interval:', config.checkInterval + 'ms');
158
- * },
159
- * function(error) {
160
- * console.error('Error getting config:', error);
161
- * }
162
- * );
144
+ * // Call as early as possible after app loads
145
+ * document.addEventListener('deviceready', function() {
146
+ * window.hotUpdate.canary('2.0.0');
147
+ * console.log('Canary confirmed, update successful');
148
+ * }, false);
163
149
  */
164
- getConfiguration: function(successCallback, errorCallback) {
165
- exec(successCallback, errorCallback, 'HotUpdates', 'getConfiguration', []);
150
+ canary: function(version, callback) {
151
+ exec(
152
+ function() {
153
+ if (callback) callback();
154
+ },
155
+ function() {
156
+ if (callback) callback();
157
+ },
158
+ 'HotUpdates',
159
+ 'canary',
160
+ [version]
161
+ );
166
162
  },
167
163
 
168
164
  /**
169
- * Install pending update immediately (requires app restart)
170
- * Forces installation of a downloaded update without waiting for next app launch
171
- * This will restart the application!
165
+ * Get list of problematic versions (information only)
172
166
  *
173
- * @param {Function} successCallback - Success callback (app will restart before this is called)
174
- * @param {Function} errorCallback - Error callback
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
175
180
  *
176
181
  * @example
177
- * HotUpdates.installUpdate(
178
- * function() {
179
- * // This callback may not be called as app restarts
180
- * console.log('Update installed, app restarting...');
181
- * },
182
- * function(error) {
183
- * console.error('Install failed:', error);
184
- * }
185
- * );
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
192
+ * }
193
+ * });
186
194
  */
187
- installUpdate: function(successCallback, errorCallback) {
188
- exec(successCallback, errorCallback, 'HotUpdates', 'installUpdate', []);
195
+ getIgnoreList: function(callback) {
196
+ exec(
197
+ function(versions) {
198
+ // Success - native returns array of version strings
199
+ if (callback) callback({versions: versions || []});
200
+ },
201
+ function(error) {
202
+ // Error - return empty list
203
+ if (callback) callback({versions: []});
204
+ },
205
+ 'HotUpdates',
206
+ 'getIgnoreList',
207
+ []
208
+ );
189
209
  }
190
210
  };
191
211
 
192
- module.exports = HotUpdates;
212
+ module.exports = HotUpdates;