@capawesome/capacitor-live-update 6.4.1 → 6.5.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.
Files changed (21) hide show
  1. package/README.md +2 -1
  2. package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdate.java +351 -251
  3. package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdateHttpClient.java +71 -0
  4. package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdatePlugin.java +42 -59
  5. package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/Manifest.java +58 -0
  6. package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/ManifestItem.java +37 -0
  7. package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/api/GetLatestBundleResponse.java +19 -27
  8. package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/results/GetChannelResult.java +2 -1
  9. package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/enums/ArtifactType.java +6 -0
  10. package/dist/docs.json +1 -1
  11. package/dist/esm/definitions.d.ts +1 -1
  12. package/dist/esm/definitions.js.map +1 -1
  13. package/ios/Plugin/Classes/Manifest.swift +20 -0
  14. package/ios/Plugin/Enums/ArtifactType.swift +4 -0
  15. package/ios/Plugin/{Classes → Enums}/CustomError.swift +3 -0
  16. package/ios/Plugin/LiveUpdate.swift +270 -199
  17. package/ios/Plugin/LiveUpdateHttpClient.swift +42 -0
  18. package/ios/Plugin/LiveUpdatePlugin.swift +65 -63
  19. package/ios/Plugin/Protocols/Api/GetLatestBundleResponse.swift +2 -3
  20. package/ios/Plugin/Protocols/Api/ManifestItem.swift +5 -0
  21. package/package.json +1 -1
@@ -0,0 +1,71 @@
1
+ package io.capawesome.capacitorjs.plugins.liveupdate;
2
+
3
+ import androidx.annotation.NonNull;
4
+ import androidx.annotation.Nullable;
5
+ import java.io.File;
6
+ import java.io.IOException;
7
+ import java.util.concurrent.TimeUnit;
8
+ import okhttp3.OkHttpClient;
9
+ import okhttp3.Request;
10
+ import okhttp3.Response;
11
+ import okhttp3.ResponseBody;
12
+ import okio.Buffer;
13
+ import okio.BufferedSink;
14
+ import okio.BufferedSource;
15
+ import okio.Okio;
16
+
17
+ public class LiveUpdateHttpClient {
18
+
19
+ @NonNull
20
+ private final LiveUpdateConfig config;
21
+
22
+ @Nullable
23
+ public static String getChecksumFromResponse(Response response) {
24
+ String checksum = response.header("X-Checksum");
25
+ if (checksum == null) {
26
+ return null;
27
+ }
28
+ return checksum;
29
+ }
30
+
31
+ @Nullable
32
+ public static String getSignatureFromResponse(Response response) {
33
+ String signature = response.header("X-Signature");
34
+ if (signature == null) {
35
+ return null;
36
+ }
37
+ return signature;
38
+ }
39
+
40
+ public LiveUpdateHttpClient(@NonNull LiveUpdateConfig config) {
41
+ this.config = config;
42
+ }
43
+
44
+ public Response execute(String url) throws IOException {
45
+ int httpTimeout = config.getHttpTimeout();
46
+ OkHttpClient okHttpClient = new OkHttpClient.Builder()
47
+ .connectTimeout(httpTimeout, TimeUnit.MILLISECONDS)
48
+ .readTimeout(httpTimeout, TimeUnit.MILLISECONDS)
49
+ .writeTimeout(httpTimeout, TimeUnit.MILLISECONDS)
50
+ .build();
51
+ Request request = new Request.Builder().url(url).build();
52
+ return okHttpClient.newCall(request).execute();
53
+ }
54
+
55
+ public static void writeResponseBodyToFile(ResponseBody body, File file) throws IOException {
56
+ long contentLength = body.contentLength();
57
+ BufferedSource source = body.source();
58
+ BufferedSink sink = Okio.buffer(Okio.sink(file));
59
+ Buffer sinkBuffer = sink.getBuffer();
60
+ long totalBytesRead = 0;
61
+ int bufferSize = 8 * 1024;
62
+ for (long bytesRead; (bytesRead = source.read(sinkBuffer, bufferSize)) != -1;) {
63
+ sink.emit();
64
+ totalBytesRead += bytesRead;
65
+ int progress = (int) ((totalBytesRead * 100) / contentLength);
66
+ }
67
+ sink.flush();
68
+ sink.close();
69
+ source.close();
70
+ }
71
+ }
@@ -19,7 +19,7 @@ import io.capawesome.capacitorjs.plugins.liveupdate.interfaces.Result;
19
19
  public class LiveUpdatePlugin extends Plugin {
20
20
 
21
21
  public static final String TAG = "LiveUpdate";
22
- public static final String VERSION = "6.4.1";
22
+ public static final String VERSION = "6.5.0";
23
23
  public static final String SHARED_PREFERENCES_NAME = "CapawesomeLiveUpdate"; // DO NOT CHANGE
24
24
  public static final String ERROR_APP_ID_MISSING = "appId must be configured.";
25
25
  public static final String ERROR_BUNDLE_EXISTS = "bundle already exists.";
@@ -36,6 +36,7 @@ public class LiveUpdatePlugin extends Plugin {
36
36
  public static final String ERROR_PUBLIC_KEY_INVALID = "Invalid public key.";
37
37
  public static final String ERROR_SIGNATURE_MISSING = "Bundle does not contain a signature.";
38
38
  public static final String ERROR_SYNC_IN_PROGRESS = "Sync is already in progress.";
39
+ public static final String ERROR_UNKNOWN_ERROR = "An unknown error has occurred.";
39
40
 
40
41
  private boolean syncInProgress = false;
41
42
 
@@ -72,15 +73,13 @@ public class LiveUpdatePlugin extends Plugin {
72
73
 
73
74
  @Override
74
75
  public void error(Exception exception) {
75
- Logger.error(TAG, exception.getMessage(), exception);
76
- call.reject(exception.getMessage());
76
+ rejectCall(call, exception);
77
77
  }
78
78
  };
79
79
 
80
80
  implementation.deleteBundle(options, callback);
81
81
  } catch (Exception exception) {
82
- Logger.error(TAG, exception.getMessage(), exception);
83
- call.reject(exception.getMessage());
82
+ rejectCall(call, exception);
84
83
  }
85
84
  }
86
85
 
@@ -108,15 +107,13 @@ public class LiveUpdatePlugin extends Plugin {
108
107
 
109
108
  @Override
110
109
  public void error(Exception exception) {
111
- Logger.error(TAG, exception.getMessage(), exception);
112
- call.reject(exception.getMessage());
110
+ rejectCall(call, exception);
113
111
  }
114
112
  };
115
113
 
116
114
  implementation.downloadBundle(options, callback);
117
115
  } catch (Exception exception) {
118
- Logger.error(TAG, exception.getMessage(), exception);
119
- call.reject(exception.getMessage());
116
+ rejectCall(call, exception);
120
117
  }
121
118
  }
122
119
 
@@ -131,15 +128,13 @@ public class LiveUpdatePlugin extends Plugin {
131
128
 
132
129
  @Override
133
130
  public void error(Exception exception) {
134
- Logger.error(TAG, exception.getMessage(), exception);
135
- call.reject(exception.getMessage());
131
+ rejectCall(call, exception);
136
132
  }
137
133
  };
138
134
 
139
135
  implementation.getBundle(callback);
140
136
  } catch (Exception exception) {
141
- Logger.error(TAG, exception.getMessage(), exception);
142
- call.reject(exception.getMessage());
137
+ rejectCall(call, exception);
143
138
  }
144
139
  }
145
140
 
@@ -154,15 +149,13 @@ public class LiveUpdatePlugin extends Plugin {
154
149
 
155
150
  @Override
156
151
  public void error(Exception exception) {
157
- Logger.error(TAG, exception.getMessage(), exception);
158
- call.reject(exception.getMessage());
152
+ rejectCall(call, exception);
159
153
  }
160
154
  };
161
155
 
162
156
  implementation.getBundles(callback);
163
157
  } catch (Exception exception) {
164
- Logger.error(TAG, exception.getMessage(), exception);
165
- call.reject(exception.getMessage());
158
+ rejectCall(call, exception);
166
159
  }
167
160
  }
168
161
 
@@ -177,15 +170,13 @@ public class LiveUpdatePlugin extends Plugin {
177
170
 
178
171
  @Override
179
172
  public void error(Exception exception) {
180
- Logger.error(TAG, exception.getMessage(), exception);
181
- call.reject(exception.getMessage());
173
+ rejectCall(call, exception);
182
174
  }
183
175
  };
184
176
 
185
177
  implementation.getChannel(callback);
186
178
  } catch (Exception exception) {
187
- Logger.error(TAG, exception.getMessage(), exception);
188
- call.reject(exception.getMessage());
179
+ rejectCall(call, exception);
189
180
  }
190
181
  }
191
182
 
@@ -200,15 +191,13 @@ public class LiveUpdatePlugin extends Plugin {
200
191
 
201
192
  @Override
202
193
  public void error(Exception exception) {
203
- Logger.error(TAG, exception.getMessage(), exception);
204
- call.reject(exception.getMessage());
194
+ rejectCall(call, exception);
205
195
  }
206
196
  };
207
197
 
208
198
  implementation.getCustomId(callback);
209
199
  } catch (Exception exception) {
210
- Logger.error(TAG, exception.getMessage(), exception);
211
- call.reject(exception.getMessage());
200
+ rejectCall(call, exception);
212
201
  }
213
202
  }
214
203
 
@@ -223,15 +212,13 @@ public class LiveUpdatePlugin extends Plugin {
223
212
 
224
213
  @Override
225
214
  public void error(Exception exception) {
226
- Logger.error(TAG, exception.getMessage(), exception);
227
- call.reject(exception.getMessage());
215
+ rejectCall(call, exception);
228
216
  }
229
217
  };
230
218
 
231
219
  implementation.getDeviceId(callback);
232
220
  } catch (Exception exception) {
233
- Logger.error(TAG, exception.getMessage(), exception);
234
- call.reject(exception.getMessage());
221
+ rejectCall(call, exception);
235
222
  }
236
223
  }
237
224
 
@@ -246,15 +233,13 @@ public class LiveUpdatePlugin extends Plugin {
246
233
 
247
234
  @Override
248
235
  public void error(Exception exception) {
249
- Logger.error(TAG, exception.getMessage(), exception);
250
- call.reject(exception.getMessage());
236
+ rejectCall(call, exception);
251
237
  }
252
238
  };
253
239
 
254
240
  implementation.getVersionCode(callback);
255
241
  } catch (Exception exception) {
256
- Logger.error(TAG, exception.getMessage(), exception);
257
- call.reject(exception.getMessage());
242
+ rejectCall(call, exception);
258
243
  }
259
244
  }
260
245
 
@@ -269,15 +254,13 @@ public class LiveUpdatePlugin extends Plugin {
269
254
 
270
255
  @Override
271
256
  public void error(Exception exception) {
272
- Logger.error(TAG, exception.getMessage(), exception);
273
- call.reject(exception.getMessage());
257
+ rejectCall(call, exception);
274
258
  }
275
259
  };
276
260
 
277
261
  implementation.getVersionName(callback);
278
262
  } catch (Exception exception) {
279
- Logger.error(TAG, exception.getMessage(), exception);
280
- call.reject(exception.getMessage());
263
+ rejectCall(call, exception);
281
264
  }
282
265
  }
283
266
 
@@ -287,8 +270,7 @@ public class LiveUpdatePlugin extends Plugin {
287
270
  implementation.ready();
288
271
  call.resolve();
289
272
  } catch (Exception exception) {
290
- Logger.error(TAG, exception.getMessage(), exception);
291
- call.reject(exception.getMessage());
273
+ rejectCall(call, exception);
292
274
  }
293
275
  }
294
276
 
@@ -298,8 +280,7 @@ public class LiveUpdatePlugin extends Plugin {
298
280
  implementation.reload();
299
281
  call.resolve();
300
282
  } catch (Exception exception) {
301
- Logger.error(TAG, exception.getMessage(), exception);
302
- call.reject(exception.getMessage());
283
+ rejectCall(call, exception);
303
284
  }
304
285
  }
305
286
 
@@ -309,8 +290,7 @@ public class LiveUpdatePlugin extends Plugin {
309
290
  implementation.reset();
310
291
  call.resolve();
311
292
  } catch (Exception exception) {
312
- Logger.error(TAG, exception.getMessage(), exception);
313
- call.reject(exception.getMessage());
293
+ rejectCall(call, exception);
314
294
  }
315
295
  }
316
296
 
@@ -332,15 +312,13 @@ public class LiveUpdatePlugin extends Plugin {
332
312
 
333
313
  @Override
334
314
  public void error(Exception exception) {
335
- Logger.error(TAG, exception.getMessage(), exception);
336
- call.reject(exception.getMessage());
315
+ rejectCall(call, exception);
337
316
  }
338
317
  };
339
318
 
340
319
  implementation.setBundle(options, callback);
341
320
  } catch (Exception exception) {
342
- Logger.error(TAG, exception.getMessage(), exception);
343
- call.reject(exception.getMessage());
321
+ rejectCall(call, exception);
344
322
  }
345
323
  }
346
324
 
@@ -358,15 +336,13 @@ public class LiveUpdatePlugin extends Plugin {
358
336
 
359
337
  @Override
360
338
  public void error(Exception exception) {
361
- Logger.error(TAG, exception.getMessage(), exception);
362
- call.reject(exception.getMessage());
339
+ rejectCall(call, exception);
363
340
  }
364
341
  };
365
342
 
366
343
  implementation.setChannel(options, callback);
367
344
  } catch (Exception exception) {
368
- Logger.error(TAG, exception.getMessage(), exception);
369
- call.reject(exception.getMessage());
345
+ rejectCall(call, exception);
370
346
  }
371
347
  }
372
348
 
@@ -388,15 +364,13 @@ public class LiveUpdatePlugin extends Plugin {
388
364
 
389
365
  @Override
390
366
  public void error(Exception exception) {
391
- Logger.error(TAG, exception.getMessage(), exception);
392
- call.reject(exception.getMessage());
367
+ rejectCall(call, exception);
393
368
  }
394
369
  };
395
370
 
396
371
  implementation.setCustomId(options, callback);
397
372
  } catch (Exception exception) {
398
- Logger.error(TAG, exception.getMessage(), exception);
399
- call.reject(exception.getMessage());
373
+ rejectCall(call, exception);
400
374
  }
401
375
  }
402
376
 
@@ -425,16 +399,14 @@ public class LiveUpdatePlugin extends Plugin {
425
399
  @Override
426
400
  public void error(Exception exception) {
427
401
  syncInProgress = false;
428
- Logger.error(TAG, exception.getMessage(), exception);
429
- call.reject(exception.getMessage());
402
+ rejectCall(call, exception);
430
403
  }
431
404
  };
432
405
 
433
406
  implementation.sync(callback);
434
407
  } catch (Exception exception) {
435
408
  syncInProgress = false;
436
- Logger.error(TAG, exception.getMessage(), exception);
437
- call.reject(exception.getMessage());
409
+ rejectCall(call, exception);
438
410
  }
439
411
  }
440
412
 
@@ -462,4 +434,15 @@ public class LiveUpdatePlugin extends Plugin {
462
434
 
463
435
  return config;
464
436
  }
437
+
438
+ private void rejectCall(PluginCall call, Exception exception) {
439
+ String message = exception.getMessage();
440
+ if (exception instanceof java.net.SocketTimeoutException) {
441
+ message = ERROR_HTTP_TIMEOUT;
442
+ } else {
443
+ message = message == null ? ERROR_UNKNOWN_ERROR : message;
444
+ }
445
+ Logger.error(TAG, message, exception);
446
+ call.reject(message);
447
+ }
465
448
  }
@@ -0,0 +1,58 @@
1
+ package io.capawesome.capacitorjs.plugins.liveupdate.classes;
2
+
3
+ import java.util.ArrayList;
4
+ import java.util.HashSet;
5
+ import java.util.List;
6
+ import java.util.Set;
7
+ import org.json.JSONArray;
8
+ import org.json.JSONException;
9
+
10
+ public class Manifest {
11
+
12
+ private List<ManifestItem> items = new ArrayList<>();
13
+
14
+ public Manifest(JSONArray jsonArray) throws JSONException {
15
+ for (int i = 0; i < jsonArray.length(); i++) {
16
+ ManifestItem manifestItem = new ManifestItem(jsonArray.getJSONObject(i));
17
+ items.add(manifestItem);
18
+ }
19
+ }
20
+
21
+ public List<ManifestItem> getItems() {
22
+ return items;
23
+ }
24
+
25
+ public static List<ManifestItem> findDuplicateItems(Manifest manifest1, Manifest manifest2) {
26
+ List<ManifestItem> duplicateItems = new ArrayList<>();
27
+ Set<String> checksums = new HashSet<>();
28
+
29
+ for (ManifestItem item : manifest2.getItems()) {
30
+ checksums.add(item.getChecksum());
31
+ }
32
+
33
+ for (ManifestItem item : manifest1.getItems()) {
34
+ if (checksums.contains(item.getChecksum())) {
35
+ duplicateItems.add(item);
36
+ }
37
+ }
38
+
39
+ return duplicateItems;
40
+ }
41
+
42
+ public static List<ManifestItem> findMissingItems(Manifest manifest1, Manifest manifest2) {
43
+ List<ManifestItem> missingItems = new ArrayList<>();
44
+ Set<String> checksums = new HashSet<>();
45
+
46
+ for (ManifestItem item : manifest2.getItems()) {
47
+ checksums.add(item.getChecksum());
48
+ }
49
+
50
+ for (ManifestItem item : manifest1.getItems()) {
51
+ if (!checksums.contains(item.getChecksum())) {
52
+ missingItems.add(item);
53
+ }
54
+ }
55
+
56
+ return missingItems;
57
+ }
58
+ }
@@ -0,0 +1,37 @@
1
+ package io.capawesome.capacitorjs.plugins.liveupdate.classes;
2
+
3
+ import androidx.annotation.NonNull;
4
+ import org.json.JSONObject;
5
+
6
+ public class ManifestItem {
7
+
8
+ @NonNull
9
+ private String checksum;
10
+
11
+ @NonNull
12
+ private String href;
13
+
14
+ @NonNull
15
+ private int sizeInBytes;
16
+
17
+ public ManifestItem(JSONObject json) {
18
+ this.href = json.optString("href");
19
+ this.checksum = json.optString("checksum");
20
+ this.sizeInBytes = json.optInt("sizeInBytes");
21
+ }
22
+
23
+ @NonNull
24
+ public String getHref() {
25
+ return href;
26
+ }
27
+
28
+ @NonNull
29
+ public String getChecksum() {
30
+ return checksum;
31
+ }
32
+
33
+ @NonNull
34
+ public int getSizeInBytes() {
35
+ return sizeInBytes;
36
+ }
37
+ }
@@ -1,49 +1,41 @@
1
1
  package io.capawesome.capacitorjs.plugins.liveupdate.classes.api;
2
2
 
3
+ import androidx.annotation.NonNull;
3
4
  import androidx.annotation.Nullable;
5
+ import io.capawesome.capacitorjs.plugins.liveupdate.enums.ArtifactType;
6
+ import java.util.ArrayList;
7
+ import java.util.List;
8
+ import org.json.JSONArray;
4
9
  import org.json.JSONObject;
5
10
 
6
11
  public class GetLatestBundleResponse {
7
12
 
8
- private String bundleId;
9
-
10
- @Nullable
11
- private String checksum;
13
+ @NonNull
14
+ private ArtifactType artifactType;
12
15
 
13
- @Nullable
14
- private String signature;
16
+ @NonNull
17
+ private String bundleId;
15
18
 
19
+ @NonNull
16
20
  private String url;
17
21
 
18
22
  public GetLatestBundleResponse(JSONObject responseJson) {
19
- this.bundleId = responseJson.optString("bundleId");
20
- String checksum = responseJson.optString("checksum", "null");
21
- if (checksum.equals("null")) {
22
- this.checksum = null;
23
+ String artifactType = responseJson.optString("artifactType");
24
+ if (artifactType.equals("manifest")) {
25
+ this.artifactType = ArtifactType.MANIFEST;
23
26
  } else {
24
- this.checksum = checksum;
25
- }
26
- String signature = responseJson.optString("signature", "null");
27
- if (signature.equals("null")) {
28
- this.signature = null;
29
- } else {
30
- this.signature = signature;
27
+ this.artifactType = ArtifactType.ZIP;
31
28
  }
29
+ this.bundleId = responseJson.optString("bundleId");
32
30
  this.url = responseJson.optString("url");
33
31
  }
34
32
 
35
- public String getBundleId() {
36
- return bundleId;
37
- }
38
-
39
- @Nullable
40
- public String getChecksum() {
41
- return checksum;
33
+ public ArtifactType getArtifactType() {
34
+ return artifactType;
42
35
  }
43
36
 
44
- @Nullable
45
- public String getSignature() {
46
- return signature;
37
+ public String getBundleId() {
38
+ return bundleId;
47
39
  }
48
40
 
49
41
  public String getUrl() {
@@ -3,6 +3,7 @@ package io.capawesome.capacitorjs.plugins.liveupdate.classes.results;
3
3
  import androidx.annotation.Nullable;
4
4
  import com.getcapacitor.JSObject;
5
5
  import io.capawesome.capacitorjs.plugins.liveupdate.interfaces.Result;
6
+ import org.json.JSONObject;
6
7
 
7
8
  public class GetChannelResult implements Result {
8
9
 
@@ -15,7 +16,7 @@ public class GetChannelResult implements Result {
15
16
 
16
17
  public JSObject toJSObject() {
17
18
  JSObject result = new JSObject();
18
- result.put("channel", channel);
19
+ result.put("channel", channel == null ? JSONObject.NULL : channel);
19
20
  return result;
20
21
  }
21
22
  }
@@ -0,0 +1,6 @@
1
+ package io.capawesome.capacitorjs.plugins.liveupdate.enums;
2
+
3
+ public enum ArtifactType {
4
+ MANIFEST,
5
+ ZIP
6
+ }
package/dist/docs.json CHANGED
@@ -79,7 +79,7 @@
79
79
  "text": "5.0.0"
80
80
  }
81
81
  ],
82
- "docs": "Get all available bundle identifiers.\n\nOnly available on Android and iOS.",
82
+ "docs": "Get all identifiers of bundles that have been downloaded.\n\nOnly available on Android and iOS.",
83
83
  "complexTypes": [
84
84
  "GetBundlesResult"
85
85
  ],
@@ -102,7 +102,7 @@ export interface LiveUpdatePlugin {
102
102
  */
103
103
  getBundle(): Promise<GetBundleResult>;
104
104
  /**
105
- * Get all available bundle identifiers.
105
+ * Get all identifiers of bundles that have been downloaded.
106
106
  *
107
107
  * Only available on Android and iOS.
108
108
  *
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,wCAAwC","sourcesContent":["/// <reference types=\"@capacitor/cli\" />\n\ndeclare module '@capacitor/cli' {\n export interface PluginsConfig {\n LiveUpdate?: {\n /**\n * The app ID is used to identify the app when using [Capawesome Cloud](https://capawesome.io/cloud).\n *\n * This is **NOT** the same as the app identifier (e.g. `com.example.app`).\n * This is a unique identifier generated by Capawesome Cloud (e.g. `6e351b4f-69a7-415e-a057-4567df7ffe94`).\n *\n * @since 5.0.0\n * @example '6e351b4f-69a7-415e-a057-4567df7ffe94'\n */\n appId?: string;\n /**\n * Whether or not to automatically delete unused bundles.\n *\n * When enabled, the plugin will automatically delete unused bundles after calling `ready()`.\n *\n * @since 5.0.0\n * @default false\n */\n autoDeleteBundles?: boolean;\n /**\n * The default channel of the app.\n *\n * @since 6.3.0\n * @example 'production'\n */\n defaultChannel?: string;\n /**\n * Whether or not the plugin is enabled.\n *\n * @since 5.0.0\n * @default true\n */\n enabled?: boolean;\n /**\n * The timeout in milliseconds for HTTP requests.\n *\n * @since 6.4.0\n * @default 60000\n */\n httpTimeout?: number;\n /**\n * The location of the server to use when using [Capawesome Cloud](https://capawesome.io/cloud).\n *\n * @since 6.2.0\n * @default 'us'\n * @example 'eu'\n * @see https://capawesome.io/cloud/live-updates/advanced/privacy/\n */\n location?: 'us' | 'eu';\n /**\n * The public key to verify the integrity of the bundle.\n *\n * The public key must be a PEM-encoded RSA public key.\n *\n * @since 6.1.0\n * @example '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDodf1SD0OOn6hIlDuKBza0Ed0OqtwyVJwiyjmE9BJaZ7y8ZUfcF+SKmd0l2cDPM45XIg2tAFux5n29uoKyHwSt+6tCi5CJA5Z1/1eZruRRqABLonV77KS3HUtvOgqRLDnKSV89dYZkM++NwmzOPgIF422mvc+VukcVOBfc8/AHQIDAQAB-----END PUBLIC KEY-----'\n */\n publicKey?: string;\n /**\n * The timeout in milliseconds to wait for the app to be ready before resetting to the default bundle.\n *\n * @since 5.0.0\n * @default 10000\n */\n readyTimeout?: number;\n /**\n * Whether or not the app should be reset to the default bundle during an update.\n *\n * @since 5.0.0\n * @default true\n */\n resetOnUpdate?: boolean;\n };\n }\n}\n\nexport interface LiveUpdatePlugin {\n /**\n * Delete a bundle from the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n deleteBundle(options: DeleteBundleOptions): Promise<void>;\n /**\n * Download a bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n downloadBundle(options: DownloadBundleOptions): Promise<void>;\n /**\n * Get the active bundle identifier.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getBundle(): Promise<GetBundleResult>;\n /**\n * Get all available bundle identifiers.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getBundles(): Promise<GetBundlesResult>;\n /**\n * Get the channel of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getChannel(): Promise<GetChannelResult>;\n /**\n * Get the custom identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getCustomId(): Promise<GetCustomIdResult>;\n /**\n * Get the device identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getDeviceId(): Promise<GetDeviceIdResult>;\n /**\n * Get the version code of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getVersionCode(): Promise<GetVersionCodeResult>;\n /**\n * Get the version name of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getVersionName(): Promise<GetVersionNameResult>;\n /**\n * Notify the plugin that the app is ready to use and no rollback is needed.\n *\n * **Attention**: This method should be called as soon as the app is ready to use\n * to prevent the app from being reset to the default bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n ready(): Promise<void>;\n /**\n * Reload the app to apply the new bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n reload(): Promise<void>;\n /**\n * Reset the app to the default bundle.\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n reset(): Promise<void>;\n /**\n * Set the next bundle to use for the app.\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setBundle(options: SetBundleOptions): Promise<void>;\n /**\n * Set the channel of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setChannel(options: SetChannelOptions): Promise<void>;\n /**\n * Set the custom identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setCustomId(options: SetCustomIdOptions): Promise<void>;\n /**\n * Automatically download and set the latest bundle for the app using the [Capawesome Cloud](https://capawesome.io/cloud/).\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n sync(): Promise<SyncResult>;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface DeleteBundleOptions {\n /**\n * The unique identifier of the bundle to delete.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n}\n\nexport interface DownloadBundleOptions {\n /**\n * The unique identifier of the bundle.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n /**\n * The checksum of the bundle to verify the integrity of the ZIP file.\n *\n * Must be a SHA-256 hash in hexadecimal format.\n *\n * @since 6.1.0\n * @example 'aab8e0a312e807c8eaeb923e2bdccd7731c5508869c62ad8e6e43ee881414c84'\n */\n checksum?: string;\n /**\n * The URL of the bundle to download.\n *\n * The bundle must be a ZIP file containing at least a `index.html` file.\n *\n * @since 5.0.0\n * @example 'https://example.com/bundle.zip'\n */\n url: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetBundleResult {\n /**\n * The unique identifier of the active bundle.\n *\n * If `null`, the default bundle is being used.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetBundlesResult {\n /**\n * An array of unique identifiers of all available bundles.\n *\n * @since 5.0.0\n */\n bundleIds: string[];\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetChannelResult {\n /**\n * The channel of the app.\n *\n * If `null`, the app is using the default channel.\n *\n * @since 5.0.0\n * @example 'production'\n */\n channel: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetDeviceIdResult {\n /**\n * The unique identifier of the device.\n *\n * On iOS, [`identifierForVendor`](https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor) is used.\n * The value of this property is the same for apps that come from the same vendor running on the same device.\n *\n * @since 5.0.0\n *\n * @example '50d2a548-80b7-4dad-adc7-97c0e79d8a89'\n */\n deviceId: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetVersionCodeResult {\n /**\n * The version code of the app.\n *\n * On **Android**, this is the `versionCode` from the `android/app/build.gradle` file.\n * On **iOS**, this is the `CFBundleVersion` from the `Info.plist` file.\n *\n * @since 5.0.0\n * @example \"1\"\n */\n versionCode: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetVersionNameResult {\n /**\n * The version name of the app.\n *\n * On **Android**, this is the `versionName` from the `android/app/build.gradle` file.\n * On **iOS**, this is the `CFBundleShortVersionString` from the `Info.plist` file.\n *\n * @since 5.0.0\n * @example \"1.0.0\"\n */\n versionName: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetCustomIdResult {\n /**\n * The custom identifier of the app.\n *\n * If `null`, no custom identifier is set.\n *\n * @since 5.0.0\n * @example '50d2a548-80b7-4dad-adc7-97c0e79d8a89'\n */\n customId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetBundleOptions {\n /**\n * The unique identifier of the bundle to use.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetChannelOptions {\n /**\n * The channel of the app.\n *\n * Set `null` to remove the channel.\n *\n * @since 5.0.0\n */\n channel: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetCustomIdOptions {\n /**\n * The custom identifier of the app.\n *\n * Set `null` to remove the custom identifier.\n *\n * @since 5.0.0\n */\n customId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SyncResult {\n /**\n * The identifier of the next bundle to use.\n *\n * If `null`, the app is up-to-date and no new bundle is available.\n *\n * @since 5.0.0\n */\n nextBundleId: string | null;\n}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,wCAAwC","sourcesContent":["/// <reference types=\"@capacitor/cli\" />\n\ndeclare module '@capacitor/cli' {\n export interface PluginsConfig {\n LiveUpdate?: {\n /**\n * The app ID is used to identify the app when using [Capawesome Cloud](https://capawesome.io/cloud).\n *\n * This is **NOT** the same as the app identifier (e.g. `com.example.app`).\n * This is a unique identifier generated by Capawesome Cloud (e.g. `6e351b4f-69a7-415e-a057-4567df7ffe94`).\n *\n * @since 5.0.0\n * @example '6e351b4f-69a7-415e-a057-4567df7ffe94'\n */\n appId?: string;\n /**\n * Whether or not to automatically delete unused bundles.\n *\n * When enabled, the plugin will automatically delete unused bundles after calling `ready()`.\n *\n * @since 5.0.0\n * @default false\n */\n autoDeleteBundles?: boolean;\n /**\n * The default channel of the app.\n *\n * @since 6.3.0\n * @example 'production'\n */\n defaultChannel?: string;\n /**\n * Whether or not the plugin is enabled.\n *\n * @since 5.0.0\n * @default true\n */\n enabled?: boolean;\n /**\n * The timeout in milliseconds for HTTP requests.\n *\n * @since 6.4.0\n * @default 60000\n */\n httpTimeout?: number;\n /**\n * The location of the server to use when using [Capawesome Cloud](https://capawesome.io/cloud).\n *\n * @since 6.2.0\n * @default 'us'\n * @example 'eu'\n * @see https://capawesome.io/cloud/live-updates/advanced/privacy/\n */\n location?: 'us' | 'eu';\n /**\n * The public key to verify the integrity of the bundle.\n *\n * The public key must be a PEM-encoded RSA public key.\n *\n * @since 6.1.0\n * @example '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDodf1SD0OOn6hIlDuKBza0Ed0OqtwyVJwiyjmE9BJaZ7y8ZUfcF+SKmd0l2cDPM45XIg2tAFux5n29uoKyHwSt+6tCi5CJA5Z1/1eZruRRqABLonV77KS3HUtvOgqRLDnKSV89dYZkM++NwmzOPgIF422mvc+VukcVOBfc8/AHQIDAQAB-----END PUBLIC KEY-----'\n */\n publicKey?: string;\n /**\n * The timeout in milliseconds to wait for the app to be ready before resetting to the default bundle.\n *\n * @since 5.0.0\n * @default 10000\n */\n readyTimeout?: number;\n /**\n * Whether or not the app should be reset to the default bundle during an update.\n *\n * @since 5.0.0\n * @default true\n */\n resetOnUpdate?: boolean;\n };\n }\n}\n\nexport interface LiveUpdatePlugin {\n /**\n * Delete a bundle from the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n deleteBundle(options: DeleteBundleOptions): Promise<void>;\n /**\n * Download a bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n downloadBundle(options: DownloadBundleOptions): Promise<void>;\n /**\n * Get the active bundle identifier.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getBundle(): Promise<GetBundleResult>;\n /**\n * Get all identifiers of bundles that have been downloaded.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getBundles(): Promise<GetBundlesResult>;\n /**\n * Get the channel of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getChannel(): Promise<GetChannelResult>;\n /**\n * Get the custom identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getCustomId(): Promise<GetCustomIdResult>;\n /**\n * Get the device identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getDeviceId(): Promise<GetDeviceIdResult>;\n /**\n * Get the version code of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getVersionCode(): Promise<GetVersionCodeResult>;\n /**\n * Get the version name of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getVersionName(): Promise<GetVersionNameResult>;\n /**\n * Notify the plugin that the app is ready to use and no rollback is needed.\n *\n * **Attention**: This method should be called as soon as the app is ready to use\n * to prevent the app from being reset to the default bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n ready(): Promise<void>;\n /**\n * Reload the app to apply the new bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n reload(): Promise<void>;\n /**\n * Reset the app to the default bundle.\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n reset(): Promise<void>;\n /**\n * Set the next bundle to use for the app.\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setBundle(options: SetBundleOptions): Promise<void>;\n /**\n * Set the channel of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setChannel(options: SetChannelOptions): Promise<void>;\n /**\n * Set the custom identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setCustomId(options: SetCustomIdOptions): Promise<void>;\n /**\n * Automatically download and set the latest bundle for the app using the [Capawesome Cloud](https://capawesome.io/cloud/).\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n sync(): Promise<SyncResult>;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface DeleteBundleOptions {\n /**\n * The unique identifier of the bundle to delete.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n}\n\nexport interface DownloadBundleOptions {\n /**\n * The unique identifier of the bundle.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n /**\n * The checksum of the bundle to verify the integrity of the ZIP file.\n *\n * Must be a SHA-256 hash in hexadecimal format.\n *\n * @since 6.1.0\n * @example 'aab8e0a312e807c8eaeb923e2bdccd7731c5508869c62ad8e6e43ee881414c84'\n */\n checksum?: string;\n /**\n * The URL of the bundle to download.\n *\n * The bundle must be a ZIP file containing at least a `index.html` file.\n *\n * @since 5.0.0\n * @example 'https://example.com/bundle.zip'\n */\n url: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetBundleResult {\n /**\n * The unique identifier of the active bundle.\n *\n * If `null`, the default bundle is being used.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetBundlesResult {\n /**\n * An array of unique identifiers of all available bundles.\n *\n * @since 5.0.0\n */\n bundleIds: string[];\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetChannelResult {\n /**\n * The channel of the app.\n *\n * If `null`, the app is using the default channel.\n *\n * @since 5.0.0\n * @example 'production'\n */\n channel: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetDeviceIdResult {\n /**\n * The unique identifier of the device.\n *\n * On iOS, [`identifierForVendor`](https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor) is used.\n * The value of this property is the same for apps that come from the same vendor running on the same device.\n *\n * @since 5.0.0\n *\n * @example '50d2a548-80b7-4dad-adc7-97c0e79d8a89'\n */\n deviceId: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetVersionCodeResult {\n /**\n * The version code of the app.\n *\n * On **Android**, this is the `versionCode` from the `android/app/build.gradle` file.\n * On **iOS**, this is the `CFBundleVersion` from the `Info.plist` file.\n *\n * @since 5.0.0\n * @example \"1\"\n */\n versionCode: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetVersionNameResult {\n /**\n * The version name of the app.\n *\n * On **Android**, this is the `versionName` from the `android/app/build.gradle` file.\n * On **iOS**, this is the `CFBundleShortVersionString` from the `Info.plist` file.\n *\n * @since 5.0.0\n * @example \"1.0.0\"\n */\n versionName: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetCustomIdResult {\n /**\n * The custom identifier of the app.\n *\n * If `null`, no custom identifier is set.\n *\n * @since 5.0.0\n * @example '50d2a548-80b7-4dad-adc7-97c0e79d8a89'\n */\n customId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetBundleOptions {\n /**\n * The unique identifier of the bundle to use.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetChannelOptions {\n /**\n * The channel of the app.\n *\n * Set `null` to remove the channel.\n *\n * @since 5.0.0\n */\n channel: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetCustomIdOptions {\n /**\n * The custom identifier of the app.\n *\n * Set `null` to remove the custom identifier.\n *\n * @since 5.0.0\n */\n customId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SyncResult {\n /**\n * The identifier of the next bundle to use.\n *\n * If `null`, the app is up-to-date and no new bundle is available.\n *\n * @since 5.0.0\n */\n nextBundleId: string | null;\n}\n"]}
@@ -0,0 +1,20 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ @objc public class Manifest: NSObject, Codable {
5
+ let items: [ManifestItem]
6
+
7
+ init(items: [ManifestItem]) {
8
+ self.items = items
9
+ }
10
+
11
+ public static func findDuplicateItems(_ manifest1: Manifest, _ manifest2: Manifest) -> [ManifestItem] {
12
+ let checksumSet = Set(manifest2.items.map { $0.checksum })
13
+ return manifest1.items.filter { checksumSet.contains($0.checksum) }
14
+ }
15
+
16
+ public static func findMissingItems(_ manifest1: Manifest, _ manifest2: Manifest) -> [ManifestItem] {
17
+ let checksumSet = Set(manifest2.items.map { $0.checksum })
18
+ return manifest1.items.filter { !checksumSet.contains($0.checksum) }
19
+ }
20
+ }
@@ -0,0 +1,4 @@
1
+ enum ArtifactType: String, Codable {
2
+ case manifest
3
+ case zip
4
+ }
@@ -15,6 +15,7 @@ public enum CustomError: Error {
15
15
  case signatureMissing
16
16
  case signatureVerificationFailed
17
17
  case syncInProgress
18
+ case unknown
18
19
  case urlMissing
19
20
  }
20
21
 
@@ -49,6 +50,8 @@ extension CustomError: LocalizedError {
49
50
  return NSLocalizedString("Signature verification failed.", comment: "signatureVerificationFailed")
50
51
  case .syncInProgress:
51
52
  return NSLocalizedString("Sync is already in progress.", comment: "syncInProgress")
53
+ case .unknown:
54
+ return NSLocalizedString("An unknown error occurred.", comment: "unknown")
52
55
  case .urlMissing:
53
56
  return NSLocalizedString("url must be provided.", comment: "urlMissing")
54
57
  }