cordova-plugin-hot-updates 2.0.0 → 2.1.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,239 @@
1
+ # Changelog
2
+
3
+ All notable changes to cordova-plugin-hot-updates will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [2.1.0] - 2025-11-04
9
+
10
+ ### 🛠️ Fixed
11
+
12
+ - **getUpdate() duplicate downloads**: Added check for already installed version - `getUpdate()` now returns success without re-downloading if the requested version is already installed
13
+ - **Duplicate code**: Removed duplicate `setBool:YES forKey:kPendingUpdateReady` call in `saveDownloadedUpdate()`
14
+
15
+ ### 🧹 Improved
16
+
17
+ - **Cleaner logs**: Removed all emoji and debug messages from native code - logs are now professional and suitable for production
18
+ - **Code cleanup**: Removed 3 unused internal methods (~80 lines of dead code):
19
+ - `removeVersionFromIgnoreList()`
20
+ - `isVersionIgnored()`
21
+ - `compareVersion:withVersion:`
22
+ - **Updated documentation**: Header comment now accurately reflects frontend-controlled architecture
23
+
24
+ ### 📊 Technical Details
25
+
26
+ **Files changed**: 1 (HotUpdates.m)
27
+ - Added: ~15 lines
28
+ - Removed: ~80 lines
29
+ - Methods removed: 3
30
+
31
+ **No breaking changes** - API remains fully compatible with v2.0.0
32
+
33
+ ---
34
+
35
+ ## [2.0.0] - 2025-10-30
36
+
37
+ ### 🔥 Breaking Changes
38
+
39
+ - **Rollback behavior changed**: After successful rollback, `previousVersion` is now cleared to prevent infinite rollback loops. This means you can only rollback once without performing a new update first.
40
+
41
+ ### ✨ Added
42
+
43
+ - **Loop Prevention**: Added protection against infinite update/rollback cycles
44
+ - `forceUpdate` now rejects attempts to install already installed version (error: `version_already_installed`)
45
+ - `rollback` validates that previous version differs from current (error: `same_version`)
46
+ - `rollback` clears `previousVersion` metadata after successful rollback
47
+
48
+ - **Enhanced Error Codes**: 13 detailed error codes for better error handling
49
+ - **forceUpdate**: 9 error codes (`version_already_installed`, `version_ignored`, `version_incompatible`, `download_failed`, `http_error`, `unzip_failed`, `invalid_package`, `install_failed`, `Download already in progress`)
50
+ - **rollback**: 4 error codes (`no_previous_version`, `previous_files_missing`, `same_version`, `file_operation_failed`)
51
+
52
+ - **Improved Version Checking**: `getVersionInfo` now properly validates all conditions for rollback availability
53
+ - Checks: folder exists + metadata exists + versions are different
54
+ - New fields: `rollbackAvailable` (folder exists), `rollbackReady` (all conditions met)
55
+
56
+ - **Enhanced API Responses**:
57
+ - `rollback` success response includes `canRollbackAgain: false` field
58
+ - `rollback` error response includes detailed error information: `error`, `message`, `currentVersion`, `previousVersion`, `canRollback`, `hasPreviousMetadata`
59
+ - `forceUpdate` error includes `currentVersion` and `requestedVersion` for `version_already_installed` error
60
+
61
+ ### 🛠️ Fixed
62
+
63
+ - Fixed infinite rollback loop caused by version metadata swapping
64
+ - Fixed `canRollback` incorrectly returning `true` when versions are identical
65
+ - Fixed ability to install the same version multiple times
66
+ - Fixed rollback not detecting corrupted metadata
67
+
68
+ ### 📝 Changed
69
+
70
+ - Reduced excessive debug logging, keeping only essential logs
71
+ - Improved log clarity and consistency across all methods
72
+ - Metadata management now uses strict version validation
73
+
74
+ ### 🔒 Security
75
+
76
+ - Added validation to prevent rollback to same version (potential attack vector)
77
+ - Enhanced version comparison logic to prevent bypass attempts
78
+
79
+ ## [1.0.0] - 2025-10-27
80
+
81
+ ### ✨ Initial Release
82
+
83
+ - Frontend-controlled WebView Reload approach
84
+ - Automatic background update checking
85
+ - Force update capability with instant WebView reload
86
+ - Rollback mechanism with automatic backup
87
+ - IgnoreList for blocking problematic versions
88
+ - Canary system for version verification
89
+ - Semantic version comparison
90
+ - minAppVersion compatibility checking
91
+ - Auto-update toggle (disabled by default)
92
+ - First launch detection and handling
93
+ - Support for iOS 11.2+
94
+
95
+ ---
96
+
97
+ ## Migration Guide: 1.0.0 → 2.0.0
98
+
99
+ ### Breaking Changes
100
+
101
+ **1. Rollback Behavior**
102
+
103
+ **Before (v1.0.0):**
104
+ ```
105
+ Installed: 2.7.8, Previous: 2.7.7
106
+ rollback() → Installed: 2.7.7, Previous: 2.7.8 ⚠️ (could create infinite loop)
107
+ ```
108
+
109
+ **After (v2.0.0):**
110
+ ```
111
+ Installed: 2.7.8, Previous: 2.7.7
112
+ rollback() → Installed: 2.7.7, Previous: nil ✅ (loop prevented)
113
+ ```
114
+
115
+ ### What You Need to Update
116
+
117
+ **1. Error Handling for forceUpdate**
118
+
119
+ Add handler for new `version_already_installed` error:
120
+
121
+ ```javascript
122
+ window.HotUpdates.forceUpdate(data, function(result) {
123
+ if (result.error === 'version_already_installed') {
124
+ // New in v2.0.0: Handle already installed version
125
+ showNotification('This version is already installed');
126
+ return;
127
+ }
128
+ // ... rest of error handling
129
+ });
130
+ ```
131
+
132
+ **2. Error Handling for rollback**
133
+
134
+ Update rollback error handling with new detailed errors:
135
+
136
+ ```javascript
137
+ window.HotUpdates.rollback(function(result) {
138
+ if (!result.success) {
139
+ // New in v2.0.0: Detailed error codes
140
+ switch(result.error) {
141
+ case 'no_previous_version':
142
+ showError('No previous version to rollback to');
143
+ break;
144
+ case 'previous_files_missing':
145
+ showError('Previous version files not found');
146
+ break;
147
+ case 'same_version':
148
+ showError('Cannot rollback to the same version');
149
+ break;
150
+ case 'file_operation_failed':
151
+ showError('Rollback failed');
152
+ break;
153
+ }
154
+ }
155
+ });
156
+ ```
157
+
158
+ **3. UI Updates for Rollback**
159
+
160
+ Check `canRollbackAgain` field after rollback:
161
+
162
+ ```javascript
163
+ window.HotUpdates.rollback(function(result) {
164
+ if (result.success) {
165
+ console.log('Rollback successful');
166
+ console.log('Can rollback again:', result.canRollbackAgain); // Always false in v2.0.0
167
+
168
+ // Hide rollback button after successful rollback
169
+ if (!result.canRollbackAgain) {
170
+ hideRollbackButton();
171
+ }
172
+ }
173
+ });
174
+ ```
175
+
176
+ **4. Version Info Checks**
177
+
178
+ Use improved `canRollback` field:
179
+
180
+ ```javascript
181
+ window.HotUpdates.getVersionInfo(function(info) {
182
+ // v2.0.0: More accurate rollback availability check
183
+ if (info.canRollback) {
184
+ // All conditions met: folder + metadata + different versions
185
+ enableRollbackButton();
186
+ } else {
187
+ disableRollbackButton();
188
+ }
189
+
190
+ // New fields in v2.0.0:
191
+ console.log('Rollback folder exists:', info.rollbackAvailable);
192
+ console.log('Rollback ready:', info.rollbackReady);
193
+ });
194
+ ```
195
+
196
+ ### Testing After Migration
197
+
198
+ Run these tests to ensure v2.0.0 works correctly:
199
+
200
+ ```javascript
201
+ // Test 1: Prevent duplicate installation
202
+ window.HotUpdates.getCurrentVersion(function(current) {
203
+ window.HotUpdates.forceUpdate({
204
+ url: 'http://example.com/update.zip',
205
+ version: current // Same as current!
206
+ }, function(result) {
207
+ // Should return error: version_already_installed ✅
208
+ console.assert(result.error === 'version_already_installed');
209
+ });
210
+ });
211
+
212
+ // Test 2: Rollback loop prevention
213
+ window.HotUpdates.forceUpdate({url: '...', version: '2.7.8'}, function(r1) {
214
+ if (r1.status === 'installed') {
215
+ window.HotUpdates.rollback(function(r2) {
216
+ if (r2.success) {
217
+ // First rollback: should succeed ✅
218
+ window.HotUpdates.rollback(function(r3) {
219
+ // Second rollback: should fail with no_previous_version ✅
220
+ console.assert(!r3.success);
221
+ console.assert(r3.error === 'no_previous_version');
222
+ });
223
+ }
224
+ });
225
+ }
226
+ });
227
+ ```
228
+
229
+ ### Performance Impact
230
+
231
+ - **No performance degradation**: All changes are optimizations
232
+ - **Reduced log output**: Less console spam in production
233
+ - **Faster error detection**: Version checks happen earlier
234
+
235
+ ### Support
236
+
237
+ - Documentation: [README.md](README.md)
238
+ - Issues: [GitHub Issues](https://github.com/vladimirDarksy/Cordova_hot_update/issues)
239
+ - Version comparison: [1.0.0...2.0.0](https://github.com/vladimirDarksy/Cordova_hot_update/compare/v1.0.0...v2.0.0)
package/docs/API.md ADDED
@@ -0,0 +1,352 @@
1
+ # Hot Updates API v2.1.0
2
+
3
+ ## 🎯 4 метода для управления обновлениями
4
+
5
+ ---
6
+
7
+ ## 1. getUpdate() - Скачать обновление
8
+
9
+ Скачивает обновление в фоновом режиме. **Не устанавливает!**
10
+
11
+ ```javascript
12
+ window.flasher.getUpdate({url, version}, callback)
13
+ ```
14
+
15
+ ### Параметры:
16
+
17
+ | Параметр | Тип | Обязательный | Описание |
18
+ |----------|-----|--------------|----------|
19
+ | url | string | ✅ | URL архива обновления (.zip) |
20
+ | version | string | ⚠️ | Версия обновления (опционально) |
21
+ | callback | function | ✅ | Вызывается после завершения |
22
+
23
+ ### Callback:
24
+
25
+ ```javascript
26
+ function callback(error) {
27
+ if (error) {
28
+ // Ошибка: {error: {message: "текст ошибки"}}
29
+ console.error('Ошибка скачивания:', error.error.message);
30
+ } else {
31
+ // Успех: null или undefined
32
+ console.log('Обновление скачано');
33
+ }
34
+ }
35
+ ```
36
+
37
+ ### Пример:
38
+
39
+ ```javascript
40
+ window.flasher.getUpdate({
41
+ url: 'https://api.example.com/updates/2.7.8.zip',
42
+ version: '2.7.8'
43
+ }, function(error) {
44
+ if (error) {
45
+ alert('Не удалось скачать обновление');
46
+ return;
47
+ }
48
+ // Показать popup "Обновить приложение?"
49
+ showUpdatePopup();
50
+ });
51
+ ```
52
+
53
+ ### Возможные ошибки:
54
+
55
+ | Ошибка | Причина |
56
+ |--------|---------|
57
+ | `URL required` | Не передан URL |
58
+ | `Download failed` | Проблемы с сетью |
59
+ | `HTTP error` | Сервер вернул ошибку (не 200) |
60
+ | `Unzip failed` | Битый архив |
61
+ | `Invalid package` | В архиве нет папки www |
62
+ | `Download already in progress` | Уже идет загрузка |
63
+
64
+ ### Важно:
65
+
66
+ - Если версия уже скачана - вернет **success** (не будет перезагружать)
67
+ - Обновление НЕ устанавливается автоматически
68
+ - Нужно вызвать `forceUpdate()` для установки
69
+
70
+ ---
71
+
72
+ ## 2. forceUpdate() - Установить обновление
73
+
74
+ Устанавливает скачанное обновление и перезагружает WebView.
75
+
76
+ ```javascript
77
+ window.flasher.forceUpdate(callback)
78
+ ```
79
+
80
+ ### Параметры:
81
+
82
+ | Параметр | Тип | Обязательный | Описание |
83
+ |----------|-----|--------------|----------|
84
+ | callback | function | ✅ | Вызывается перед перезагрузкой |
85
+
86
+ ### Callback:
87
+
88
+ ```javascript
89
+ function callback(error) {
90
+ if (error) {
91
+ // Ошибка: {error: {message: "текст ошибки"}}
92
+ console.error('Ошибка установки:', error.error.message);
93
+ } else {
94
+ // Успех: null или undefined
95
+ console.log('Обновление установлено, перезагрузка...');
96
+ // Через ~1 секунду WebView перезагрузится
97
+ }
98
+ }
99
+ ```
100
+
101
+ ### Пример:
102
+
103
+ ```javascript
104
+ window.flasher.forceUpdate(function(error) {
105
+ if (error) {
106
+ alert('Не удалось установить обновление');
107
+ return;
108
+ }
109
+ // WebView сейчас перезагрузится с новой версией
110
+ });
111
+ ```
112
+
113
+ ### Возможные ошибки:
114
+
115
+ | Ошибка | Причина |
116
+ |--------|---------|
117
+ | `No update ready to install` | Не было вызвано `getUpdate()` |
118
+ | `Install failed` | Ошибка файловой системы |
119
+
120
+ ### Важно:
121
+
122
+ - Можно вызвать только после успешного `getUpdate()`
123
+ - WebView перезагружается автоматически через ~1 секунду
124
+ - После перезагрузки **обязательно** вызовите `canary()`
125
+
126
+ ---
127
+
128
+ ## 3. canary() - Подтвердить успешную загрузку
129
+
130
+ Подтверждает, что обновление загрузилось без ошибок. **ОБЯЗАТЕЛЕН при каждом старте!**
131
+
132
+ ```javascript
133
+ window.flasher.canary(version, callback)
134
+ ```
135
+
136
+ ### Параметры:
137
+
138
+ | Параметр | Тип | Обязательный | Описание |
139
+ |----------|-----|--------------|----------|
140
+ | version | string | ✅ | Текущая версия приложения |
141
+ | callback | function | ⚠️ | Не используется, можно передать null |
142
+
143
+ ### Пример:
144
+
145
+ ```javascript
146
+ document.addEventListener('deviceready', function() {
147
+ // ОБЯЗАТЕЛЬНО при каждом старте приложения!
148
+ window.flasher.canary('2.7.8', function() {
149
+ console.log('Canary confirmed');
150
+ });
151
+ });
152
+ ```
153
+
154
+ ### Как получить версию:
155
+
156
+ ```javascript
157
+ // Способ 1: Динамическая загрузка из native (рекомендуется)
158
+ cordova.exec(function(info) {
159
+ const currentVersion = info.installedVersion || info.appBundleVersion;
160
+ window.flasher.canary(currentVersion, null);
161
+ }, null, 'HotUpdates', 'getVersionInfo', []);
162
+
163
+ // Способ 2: Статическая (нужно обновлять вручную)
164
+ window.APP_VERSION = "2.7.8";
165
+ window.flasher.canary(window.APP_VERSION, null);
166
+ ```
167
+
168
+ ### Важно:
169
+
170
+ - **ОБЯЗАТЕЛЬНО** вызывать при каждом старте приложения
171
+ - Если не вызвать в течение 20 секунд → автоматический откат на предыдущую версию
172
+ - Версия должна быть строкой (не числом!)
173
+
174
+ ---
175
+
176
+ ## 4. getIgnoreList() - Получить черный список версий
177
+
178
+ Возвращает список версий, которые вызвали проблемы и были откачены.
179
+
180
+ ```javascript
181
+ window.flasher.getIgnoreList(callback)
182
+ ```
183
+
184
+ ### Параметры:
185
+
186
+ | Параметр | Тип | Обязательный | Описание |
187
+ |----------|-----|--------------|----------|
188
+ | callback | function | ✅ | Получает список версий |
189
+
190
+ ### Callback:
191
+
192
+ ```javascript
193
+ function callback(result) {
194
+ const badVersions = result.versions; // ["2.7.5", "2.7.6"]
195
+ }
196
+ ```
197
+
198
+ ### Пример:
199
+
200
+ ```javascript
201
+ window.flasher.getIgnoreList(function(result) {
202
+ const ignoredVersions = result.versions;
203
+
204
+ // Проверяем новую версию перед скачиванием
205
+ if (ignoredVersions.includes(newVersion)) {
206
+ console.log('Эта версия в черном списке, не скачиваем');
207
+ return;
208
+ }
209
+
210
+ // Версия безопасна, можно скачивать
211
+ window.flasher.getUpdate({url: updateUrl}, ...);
212
+ });
213
+ ```
214
+
215
+ ### Важно:
216
+
217
+ - Список управляется **только native кодом**
218
+ - JavaScript может только **читать** список
219
+ - Версии добавляются автоматически при откате (rollback)
220
+ - Вы сами решаете, скачивать версию из списка или нет
221
+
222
+ ---
223
+
224
+ ## 📋 Полный сценарий работы
225
+
226
+ ```javascript
227
+ document.addEventListener('deviceready', function() {
228
+ // 1. ОБЯЗАТЕЛЬНО подтверждаем canary
229
+ loadCurrentVersion(function(currentVersion) {
230
+ window.flasher.canary(currentVersion, function() {
231
+ console.log('✅ Canary OK');
232
+ });
233
+ });
234
+
235
+ // 2. Получаем черный список
236
+ window.flasher.getIgnoreList(function(result) {
237
+ const ignoredVersions = result.versions;
238
+
239
+ // 3. Проверяем обновления на сервере
240
+ checkServerForUpdates(function(updateInfo) {
241
+ if (!updateInfo.hasUpdate) {
242
+ console.log('Обновлений нет');
243
+ return;
244
+ }
245
+
246
+ // 4. Проверяем черный список
247
+ if (ignoredVersions.includes(updateInfo.version)) {
248
+ console.log('Версия в черном списке, пропускаем');
249
+ return;
250
+ }
251
+
252
+ // 5. Скачиваем обновление
253
+ window.flasher.getUpdate({
254
+ url: updateInfo.downloadUrl,
255
+ version: updateInfo.version
256
+ }, function(error) {
257
+ if (error) {
258
+ console.error('Ошибка загрузки:', error.error.message);
259
+ return;
260
+ }
261
+
262
+ // 6. Показываем popup
263
+ if (confirm('Обновить до версии ' + updateInfo.version + '?')) {
264
+ // 7. Устанавливаем
265
+ window.flasher.forceUpdate(function(error) {
266
+ if (error) {
267
+ console.error('Ошибка установки:', error.error.message);
268
+ }
269
+ // WebView перезагружается автоматически
270
+ });
271
+ }
272
+ // Если пользователь нажал "Отмена" - обновление установится
273
+ // автоматически при следующем запуске приложения
274
+ });
275
+ });
276
+ });
277
+ });
278
+
279
+ // Вспомогательная функция для получения версии
280
+ function loadCurrentVersion(callback) {
281
+ cordova.exec(
282
+ function(info) {
283
+ callback(info.installedVersion || info.appBundleVersion);
284
+ },
285
+ function(error) {
286
+ console.error('Не удалось получить версию');
287
+ callback('2.7.7'); // fallback
288
+ },
289
+ 'HotUpdates',
290
+ 'getVersionInfo',
291
+ []
292
+ );
293
+ }
294
+
295
+ // Вспомогательная функция для проверки обновлений
296
+ function checkServerForUpdates(callback) {
297
+ fetch('https://api.example.com/updates/check?version=' + currentVersion)
298
+ .then(r => r.json())
299
+ .then(callback)
300
+ .catch(err => console.error('Ошибка проверки обновлений:', err));
301
+ }
302
+ ```
303
+
304
+ ---
305
+
306
+ ## ⚠️ Важные особенности
307
+
308
+ ### Автоустановка
309
+
310
+ Если пользователь **проигнорировал popup** (закрыл приложение), обновление автоматически установится при следующем запуске.
311
+
312
+ ### Откат (Rollback)
313
+
314
+ Если после обновления приложение крашится или не вызывается `canary()` в течение 20 секунд:
315
+ 1. Автоматический откат на предыдущую версию
316
+ 2. Сбойная версия добавляется в ignore list
317
+ 3. Приложение продолжает работать на старой версии
318
+
319
+ ### Черный список (Ignore List)
320
+
321
+ - **Не блокирует** установку (вы сами решаете)
322
+ - Служит для **информирования** о проблемных версиях
323
+ - Управляется только native кодом
324
+ - Можно установить версию из списка, если хотите (например, исправленную)
325
+
326
+ ### Формат callback
327
+
328
+ ```javascript
329
+ // ✅ Успех
330
+ callback() // null или undefined
331
+ callback(null) // null
332
+
333
+ // ❌ Ошибка
334
+ callback({
335
+ error: {
336
+ message: "текст ошибки" // может отсутствовать
337
+ }
338
+ })
339
+ ```
340
+
341
+ ---
342
+
343
+ ## 🔗 Дополнительные файлы
344
+
345
+ - **README.md** - быстрый старт и примеры
346
+ - **hot-updates-admin.html** - тестовый интерфейс для отладки
347
+ - **BUGFIXES_2025-11-04.md** - список исправленных багов
348
+ - **QUICK_REFERENCE.md** - краткая справка
349
+
350
+ ---
351
+
352
+ **Версия API:** 2.1.0 (Bugfix Update 2025-11-04)