@capawesome/capacitor-live-update 7.0.0 → 7.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/README.md CHANGED
@@ -5,9 +5,9 @@ Capacitor plugin to update your app remotely in real-time.
5
5
  ## Features
6
6
 
7
7
  - 🔋 Supports **Android and iOS**
8
- - ⚡️ **Capacitor 6** support
8
+ - ⚡️ **Capacitor 7** support
9
9
  - 📦 **Bundle Management**: Download, set, and delete bundles.
10
- - ☁️ **Cloud Support**: Use the [Capawesome Cloud](https://capawesome.io/cloud/live-updates/) to manage your app updates.
10
+ - ☁️ **Cloud Support**: Use the [Capawesome Cloud](https://cloud.capawesome.io/) to manage your app updates.
11
11
  - 📺 **Channel Support**: Set a channel for the app to manage different versions.
12
12
  - 🔄 **Auto Update**: Automatically download and set the latest bundle for the app.
13
13
  - 🛟 **Rollback**: Reset the app to the default bundle if an incompatible bundle has been set.
@@ -222,7 +222,7 @@ const sync = async () => {
222
222
  const isNewBundleAvailable = async () => {
223
223
  const { bundleId: latestBundleId } = await LiveUpdate.fetchLatestBundle();
224
224
  if (latestBundleId) {
225
- const { bundleId: currentBundleId } = await LiveUpdate.getBundle();
225
+ const { bundleId: currentBundleId } = await LiveUpdate.getCurrentBundle();
226
226
  return latestBundleId !== currentBundleId;
227
227
  } else {
228
228
  return false;
@@ -633,21 +633,25 @@ Only available on Android and iOS.
633
633
 
634
634
  #### DownloadBundleOptions
635
635
 
636
- | Prop | Type | Description | Default | Since |
637
- | ------------------ | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----- |
638
- | **`artifactType`** | <code>'manifest' \| 'zip'</code> | The artifact type of the bundle. | <code>'zip'</code> | 6.6.0 |
639
- | **`bundleId`** | <code>string</code> | The unique identifier of the bundle. **Attention**: The value `public` is reserved and cannot be used as a bundle identifier. | | 5.0.0 |
640
- | **`url`** | <code>string</code> | The URL of the bundle to download. For the `zip` artifact type, the URL must point to a ZIP file. For the `manifest` artifact type, the URL serves as the base URL to download the individual files. For example, if the URL is `https://example.com/download`, the plugin will download the file with the href `index.html` from `https://example.com/download?href=index.html`. To **verify the integrity** of the file, the server must return a `X-Checksum` header with the SHA-256 hash in base64 format. To **verify the signature** of the file, the server must return a `X-Signature` header with the signed SHA-256 hash in base64 format. | | 5.0.0 |
636
+ | Prop | Type | Description | Default | Since |
637
+ | ------------------ | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----- |
638
+ | **`artifactType`** | <code>'manifest' \| 'zip'</code> | The artifact type of the bundle. | <code>'zip'</code> | 6.6.0 |
639
+ | **`bundleId`** | <code>string</code> | The unique identifier of the bundle. **Attention**: The value `public` is reserved and cannot be used as a bundle identifier. | | 5.0.0 |
640
+ | **`checksum`** | <code>string</code> | The checksum of the self-hosted bundle as a SHA-256 hash in base64 format to verify the integrity of the bundle. **Attention**: Only supported for the `zip` artifact type. | | 7.1.0 |
641
+ | **`signature`** | <code>string</code> | The signature of the self-hosted bundle as a signed SHA-256 hash in base64 format to verify the integrity of the bundle. **Attention**: Only supported for the `zip` artifact type. | | 7.1.0 |
642
+ | **`url`** | <code>string</code> | The URL of the bundle to download. For the `zip` artifact type, the URL must point to a ZIP file. For the `manifest` artifact type, the URL serves as the base URL to download the individual files. For example, if the URL is `https://example.com/download`, the plugin will download the file with the href `index.html` from `https://example.com/download?href=index.html`. To **verify the integrity** of the file, the server should return a `X-Checksum` header with the SHA-256 hash in base64 format. To **verify the signature** of the file, the server should return a `X-Signature` header with the signed SHA-256 hash in base64 format. | | 5.0.0 |
641
643
 
642
644
 
643
645
  #### FetchLatestBundleResult
644
646
 
645
- | Prop | Type | Description | Since |
646
- | ---------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ----- |
647
- | **`artifactType`** | <code>'manifest' \| 'zip'</code> | The artifact type of the bundle. | 6.7.0 |
648
- | **`bundleId`** | <code>string \| null</code> | The unique identifier of the latest bundle. If `null`, no bundle is available. | 6.6.0 |
649
- | **`customProperties`** | <code>{ [key: string]: string; }</code> | Custom properties that are associated with the latest bundle. | 7.0.0 |
650
- | **`downloadUrl`** | <code>string</code> | The URL of the latest bundle to download. Pass this URL to the `downloadBundle(...)` method to download the bundle. | 6.7.0 |
647
+ | Prop | Type | Description | Since |
648
+ | ---------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
649
+ | **`artifactType`** | <code>'manifest' \| 'zip'</code> | The artifact type of the bundle. | 6.7.0 |
650
+ | **`bundleId`** | <code>string \| null</code> | The unique identifier of the latest bundle. If `null`, no bundle is available. | 6.6.0 |
651
+ | **`checksum`** | <code>string</code> | The checksum of the latest bundle if the bundle is self-hosted. If the bundle is hosted on Capawesome Cloud, the checksum will be returned as response header when downloading the bundle. | 7.1.0 |
652
+ | **`customProperties`** | <code>{ [key: string]: string; }</code> | Custom properties that are associated with the latest bundle. | 7.0.0 |
653
+ | **`downloadUrl`** | <code>string</code> | The URL of the latest bundle to download. Pass this URL to the `downloadBundle(...)` method to download the bundle. | 6.7.0 |
654
+ | **`signature`** | <code>string</code> | The signature of the latest bundle if the bundle is self-hosted. If the bundle is hosted on Capawesome Cloud, the signature will be returned as response header when downloading the bundle. | 7.1.0 |
651
655
 
652
656
 
653
657
  #### FetchLatestBundleOptions
@@ -118,6 +118,8 @@ public class LiveUpdate {
118
118
  public void downloadBundle(@NonNull DownloadBundleOptions options, @NonNull EmptyCallback callback) throws Exception {
119
119
  ArtifactType artifactType = options.getArtifactType();
120
120
  String bundleId = options.getBundleId();
121
+ String checksum = options.getChecksum();
122
+ String signature = options.getSignature();
121
123
  String url = options.getUrl();
122
124
 
123
125
  // Check if the bundle already exists
@@ -131,7 +133,7 @@ public class LiveUpdate {
131
133
  if (artifactType == ArtifactType.MANIFEST) {
132
134
  downloadBundleOfTypeManifest(bundleId, url);
133
135
  } else {
134
- downloadBundleOfTypeZip(bundleId, url);
136
+ downloadBundleOfTypeZip(bundleId, checksum, signature, url);
135
137
  }
136
138
  callback.success();
137
139
  }
@@ -140,9 +142,18 @@ public class LiveUpdate {
140
142
  GetLatestBundleResponse response = fetchLatestBundle(options);
141
143
  ArtifactType artifactType = response == null ? null : response.getArtifactType();
142
144
  String bundleId = response == null ? null : response.getBundleId();
145
+ String checksum = response == null ? null : response.getChecksum();
143
146
  JSONObject customProperties = response == null ? null : response.getCustomProperties();
144
147
  String downloadUrl = response == null ? null : response.getUrl();
145
- FetchLatestBundleResult result = new FetchLatestBundleResult(artifactType, bundleId, customProperties, downloadUrl);
148
+ String signature = response == null ? null : response.getSignature();
149
+ FetchLatestBundleResult result = new FetchLatestBundleResult(
150
+ artifactType,
151
+ bundleId,
152
+ checksum,
153
+ customProperties,
154
+ downloadUrl,
155
+ signature
156
+ );
146
157
  callback.success(result);
147
158
  }
148
159
 
@@ -271,6 +282,8 @@ public class LiveUpdate {
271
282
  }
272
283
  ArtifactType artifactType = response.getArtifactType();
273
284
  String latestBundleId = response.getBundleId();
285
+ String checksum = response.getChecksum();
286
+ String signature = response.getSignature();
274
287
  String url = response.getUrl();
275
288
  // Check if the bundle already exists
276
289
  if (hasBundleById(latestBundleId)) {
@@ -289,7 +302,7 @@ public class LiveUpdate {
289
302
  if (artifactType == ArtifactType.MANIFEST) {
290
303
  downloadBundleOfTypeManifest(latestBundleId, url);
291
304
  } else {
292
- downloadBundleOfTypeZip(latestBundleId, url);
305
+ downloadBundleOfTypeZip(latestBundleId, checksum, signature, url);
293
306
  }
294
307
  // Set the next bundle
295
308
  setNextBundleById(latestBundleId);
@@ -450,15 +463,24 @@ public class LiveUpdate {
450
463
  }
451
464
  }
452
465
 
453
- private void downloadAndVerifyFile(@NonNull String url, @NonNull File file, @Nullable DownloadProgressCallback callback)
454
- throws Exception {
466
+ private void downloadAndVerifyFile(
467
+ @NonNull String url,
468
+ @NonNull File file,
469
+ @Nullable String checksum,
470
+ @Nullable String signature,
471
+ @Nullable DownloadProgressCallback callback
472
+ ) throws Exception {
455
473
  Response response = httpClient.execute(url);
456
474
  ResponseBody responseBody = response.body();
457
475
  if (response.isSuccessful()) {
458
476
  LiveUpdateHttpClient.writeResponseBodyToFile(responseBody, file, callback);
459
477
  // Verify the file
460
- String checksum = LiveUpdateHttpClient.getChecksumFromResponse(response);
461
- String signature = LiveUpdateHttpClient.getSignatureFromResponse(response);
478
+ if (checksum == null) {
479
+ checksum = LiveUpdateHttpClient.getChecksumFromResponse(response);
480
+ }
481
+ if (signature == null) {
482
+ signature = LiveUpdateHttpClient.getSignatureFromResponse(response);
483
+ }
462
484
  verifyFile(file, checksum, signature);
463
485
  } else {
464
486
  Exception exception = new Exception(responseBody.string());
@@ -479,7 +501,7 @@ public class LiveUpdate {
479
501
 
480
502
  File destinationFile = new File(destinationDirectory, href);
481
503
  destinationFile.getParentFile().mkdirs();
482
- downloadAndVerifyFile(url, destinationFile, callback);
504
+ downloadAndVerifyFile(url, destinationFile, null, null, callback);
483
505
  return destinationFile;
484
506
  }
485
507
 
@@ -580,10 +602,15 @@ public class LiveUpdate {
580
602
  addBundleOfTypeManifest(bundleId, temporaryDirectory);
581
603
  }
582
604
 
583
- private void downloadBundleOfTypeZip(@NonNull String bundleId, @NonNull String downloadUrl) throws Exception {
605
+ private void downloadBundleOfTypeZip(
606
+ @NonNull String bundleId,
607
+ @Nullable String checksum,
608
+ @Nullable String signature,
609
+ @NonNull String downloadUrl
610
+ ) throws Exception {
584
611
  File file = buildTemporaryZipFile();
585
612
  // Download the bundle
586
- downloadAndVerifyFile(downloadUrl, file, (downloadedBytes, totalBytes) -> {
613
+ downloadAndVerifyFile(downloadUrl, file, checksum, signature, (downloadedBytes, totalBytes) -> {
587
614
  DownloadBundleProgressEvent event = new DownloadBundleProgressEvent(bundleId, downloadedBytes, totalBytes);
588
615
  notifyDownloadBundleProgressListeners(event);
589
616
  });
@@ -26,7 +26,7 @@ import io.capawesome.capacitorjs.plugins.liveupdate.interfaces.Result;
26
26
  public class LiveUpdatePlugin extends Plugin {
27
27
 
28
28
  public static final String TAG = "LiveUpdate";
29
- public static final String VERSION = "7.0.0";
29
+ public static final String VERSION = "7.1.0";
30
30
  public static final String SHARED_PREFERENCES_NAME = "CapawesomeLiveUpdate"; // DO NOT CHANGE
31
31
  public static final String ERROR_APP_ID_MISSING = "appId must be configured.";
32
32
  public static final String ERROR_BUNDLE_EXISTS = "bundle already exists.";
@@ -100,13 +100,15 @@ public class LiveUpdatePlugin extends Plugin {
100
100
  call.reject(ERROR_BUNDLE_ID_MISSING);
101
101
  return;
102
102
  }
103
+ String checksum = call.getString("checksum");
104
+ String signature = call.getString("signature");
103
105
  String url = call.getString("url");
104
106
  if (url == null) {
105
107
  call.reject(ERROR_URL_MISSING);
106
108
  return;
107
109
  }
108
110
 
109
- DownloadBundleOptions options = new DownloadBundleOptions(artifactType, bundleId, url);
111
+ DownloadBundleOptions options = new DownloadBundleOptions(artifactType, bundleId, checksum, signature, url);
110
112
  EmptyCallback callback = new EmptyCallback() {
111
113
  @Override
112
114
  public void success() {
@@ -16,9 +16,15 @@ public class GetLatestBundleResponse {
16
16
  @NonNull
17
17
  private String bundleId;
18
18
 
19
+ @Nullable
20
+ private String checksum;
21
+
19
22
  @Nullable
20
23
  private JSONObject customProperties;
21
24
 
25
+ @Nullable
26
+ private String signature;
27
+
22
28
  @NonNull
23
29
  private String url;
24
30
 
@@ -30,7 +36,9 @@ public class GetLatestBundleResponse {
30
36
  this.artifactType = ArtifactType.ZIP;
31
37
  }
32
38
  this.bundleId = responseJson.optString("bundleId");
39
+ this.checksum = responseJson.optString("checksum");
33
40
  this.customProperties = responseJson.optJSONObject("customProperties");
41
+ this.signature = responseJson.optString("signature");
34
42
  this.url = responseJson.optString("url");
35
43
  }
36
44
 
@@ -49,6 +57,16 @@ public class GetLatestBundleResponse {
49
57
  return customProperties;
50
58
  }
51
59
 
60
+ @Nullable
61
+ public String getChecksum() {
62
+ return checksum;
63
+ }
64
+
65
+ @Nullable
66
+ public String getSignature() {
67
+ return signature;
68
+ }
69
+
52
70
  @NonNull
53
71
  public String getUrl() {
54
72
  return url;
@@ -12,16 +12,30 @@ public class DownloadBundleOptions {
12
12
  @NonNull
13
13
  private String bundleId;
14
14
 
15
+ @Nullable
16
+ private String checksum;
17
+
18
+ @Nullable
19
+ private String signature;
20
+
15
21
  @NonNull
16
22
  private String url;
17
23
 
18
- public DownloadBundleOptions(@NonNull String artifactType, @NonNull String bundleId, @NonNull String url) {
24
+ public DownloadBundleOptions(
25
+ @NonNull String artifactType,
26
+ @NonNull String bundleId,
27
+ @Nullable String checksum,
28
+ @Nullable String signature,
29
+ @NonNull String url
30
+ ) {
19
31
  if (artifactType.equals("manifest")) {
20
32
  this.artifactType = ArtifactType.MANIFEST;
21
33
  } else {
22
34
  this.artifactType = ArtifactType.ZIP;
23
35
  }
24
36
  this.bundleId = bundleId;
37
+ this.checksum = checksum;
38
+ this.signature = signature;
25
39
  this.url = url;
26
40
  }
27
41
 
@@ -35,6 +49,16 @@ public class DownloadBundleOptions {
35
49
  return bundleId;
36
50
  }
37
51
 
52
+ @Nullable
53
+ public String getChecksum() {
54
+ return checksum;
55
+ }
56
+
57
+ @Nullable
58
+ public String getSignature() {
59
+ return signature;
60
+ }
61
+
38
62
  @NonNull
39
63
  public String getUrl() {
40
64
  return url;
@@ -15,22 +15,32 @@ public class FetchLatestBundleResult implements Result {
15
15
  @Nullable
16
16
  private final String bundleId;
17
17
 
18
+ @Nullable
19
+ private final String checksum;
20
+
18
21
  @Nullable
19
22
  private final JSONObject customProperties;
20
23
 
21
24
  @Nullable
22
25
  private final String downloadUrl;
23
26
 
27
+ @Nullable
28
+ private final String signature;
29
+
24
30
  public FetchLatestBundleResult(
25
31
  @Nullable ArtifactType artifactType,
26
32
  @Nullable String bundleId,
33
+ @Nullable String checksum,
27
34
  @Nullable JSONObject customProperties,
28
- @Nullable String downloadUrl
35
+ @Nullable String downloadUrl,
36
+ @Nullable String signature
29
37
  ) {
30
38
  this.artifactType = artifactType;
31
39
  this.bundleId = bundleId;
40
+ this.checksum = checksum;
32
41
  this.customProperties = customProperties;
33
42
  this.downloadUrl = downloadUrl;
43
+ this.signature = signature;
34
44
  }
35
45
 
36
46
  @NonNull
@@ -42,12 +52,18 @@ public class FetchLatestBundleResult implements Result {
42
52
  result.put("artifactType", "zip");
43
53
  }
44
54
  result.put("bundleId", bundleId == null ? JSONObject.NULL : bundleId);
55
+ if (checksum != null) {
56
+ result.put("checksum", checksum);
57
+ }
45
58
  if (customProperties != null) {
46
59
  result.put("customProperties", customProperties);
47
60
  }
48
61
  if (downloadUrl != null) {
49
62
  result.put("downloadUrl", downloadUrl);
50
63
  }
64
+ if (signature != null) {
65
+ result.put("signature", signature);
66
+ }
51
67
  return result;
52
68
  }
53
69
  }
package/dist/docs.json CHANGED
@@ -457,6 +457,30 @@
457
457
  "complexTypes": [],
458
458
  "type": "string"
459
459
  },
460
+ {
461
+ "name": "checksum",
462
+ "tags": [
463
+ {
464
+ "text": "7.1.0",
465
+ "name": "since"
466
+ }
467
+ ],
468
+ "docs": "The checksum of the self-hosted bundle as a SHA-256 hash\nin base64 format to verify the integrity of the bundle.\n\n**Attention**: Only supported for the `zip` artifact type.",
469
+ "complexTypes": [],
470
+ "type": "string | undefined"
471
+ },
472
+ {
473
+ "name": "signature",
474
+ "tags": [
475
+ {
476
+ "text": "7.1.0",
477
+ "name": "since"
478
+ }
479
+ ],
480
+ "docs": "The signature of the self-hosted bundle as a signed SHA-256 hash\nin base64 format to verify the integrity of the bundle.\n\n**Attention**: Only supported for the `zip` artifact type.",
481
+ "complexTypes": [],
482
+ "type": "string | undefined"
483
+ },
460
484
  {
461
485
  "name": "url",
462
486
  "tags": [
@@ -469,7 +493,7 @@
469
493
  "name": "example"
470
494
  }
471
495
  ],
472
- "docs": "The URL of the bundle to download.\n\nFor the `zip` artifact type, the URL must point to a ZIP file.\nFor the `manifest` artifact type, the URL serves as the base URL\nto download the individual files. For example, if the URL is\n`https://example.com/download`, the plugin will download the file\nwith the href `index.html` from `https://example.com/download?href=index.html`.\n\nTo **verify the integrity** of the file, the server must return\na `X-Checksum` header with the SHA-256 hash in base64 format.\n\nTo **verify the signature** of the file, the server must return\na `X-Signature` header with the signed SHA-256 hash in base64 format.",
496
+ "docs": "The URL of the bundle to download.\n\nFor the `zip` artifact type, the URL must point to a ZIP file.\nFor the `manifest` artifact type, the URL serves as the base URL\nto download the individual files. For example, if the URL is\n`https://example.com/download`, the plugin will download the file\nwith the href `index.html` from `https://example.com/download?href=index.html`.\n\nTo **verify the integrity** of the file, the server should return\na `X-Checksum` header with the SHA-256 hash in base64 format.\n\nTo **verify the signature** of the file, the server should return\na `X-Signature` header with the signed SHA-256 hash in base64 format.",
473
497
  "complexTypes": [],
474
498
  "type": "string"
475
499
  }
@@ -511,6 +535,18 @@
511
535
  "complexTypes": [],
512
536
  "type": "string | null"
513
537
  },
538
+ {
539
+ "name": "checksum",
540
+ "tags": [
541
+ {
542
+ "text": "7.1.0",
543
+ "name": "since"
544
+ }
545
+ ],
546
+ "docs": "The checksum of the latest bundle if the bundle is self-hosted.\n\nIf the bundle is hosted on Capawesome Cloud, the checksum will be\nreturned as response header when downloading the bundle.",
547
+ "complexTypes": [],
548
+ "type": "string | undefined"
549
+ },
514
550
  {
515
551
  "name": "customProperties",
516
552
  "tags": [
@@ -538,6 +574,18 @@
538
574
  "docs": "The URL of the latest bundle to download.\nPass this URL to the `downloadBundle(...)` method to download the bundle.",
539
575
  "complexTypes": [],
540
576
  "type": "string | undefined"
577
+ },
578
+ {
579
+ "name": "signature",
580
+ "tags": [
581
+ {
582
+ "text": "7.1.0",
583
+ "name": "since"
584
+ }
585
+ ],
586
+ "docs": "The signature of the latest bundle if the bundle is self-hosted.\n\nIf the bundle is hosted on Capawesome Cloud, the signature will be\nreturned as response header when downloading the bundle.",
587
+ "complexTypes": [],
588
+ "type": "string | undefined"
541
589
  }
542
590
  ]
543
591
  },
@@ -272,6 +272,24 @@ export interface DownloadBundleOptions {
272
272
  * @example '1.0.0'
273
273
  */
274
274
  bundleId: string;
275
+ /**
276
+ * The checksum of the self-hosted bundle as a SHA-256 hash
277
+ * in base64 format to verify the integrity of the bundle.
278
+ *
279
+ * **Attention**: Only supported for the `zip` artifact type.
280
+ *
281
+ * @since 7.1.0
282
+ */
283
+ checksum?: string;
284
+ /**
285
+ * The signature of the self-hosted bundle as a signed SHA-256 hash
286
+ * in base64 format to verify the integrity of the bundle.
287
+ *
288
+ * **Attention**: Only supported for the `zip` artifact type.
289
+ *
290
+ * @since 7.1.0
291
+ */
292
+ signature?: string;
275
293
  /**
276
294
  * The URL of the bundle to download.
277
295
  *
@@ -281,10 +299,10 @@ export interface DownloadBundleOptions {
281
299
  * `https://example.com/download`, the plugin will download the file
282
300
  * with the href `index.html` from `https://example.com/download?href=index.html`.
283
301
  *
284
- * To **verify the integrity** of the file, the server must return
302
+ * To **verify the integrity** of the file, the server should return
285
303
  * a `X-Checksum` header with the SHA-256 hash in base64 format.
286
304
  *
287
- * To **verify the signature** of the file, the server must return
305
+ * To **verify the signature** of the file, the server should return
288
306
  * a `X-Signature` header with the signed SHA-256 hash in base64 format.
289
307
  *
290
308
  * @since 5.0.0
@@ -321,6 +339,15 @@ export interface FetchLatestBundleResult {
321
339
  * @since 6.6.0
322
340
  */
323
341
  bundleId: string | null;
342
+ /**
343
+ * The checksum of the latest bundle if the bundle is self-hosted.
344
+ *
345
+ * If the bundle is hosted on Capawesome Cloud, the checksum will be
346
+ * returned as response header when downloading the bundle.
347
+ *
348
+ * @since 7.1.0
349
+ */
350
+ checksum?: string;
324
351
  /**
325
352
  * Custom properties that are associated with the latest bundle.
326
353
  *
@@ -337,6 +364,15 @@ export interface FetchLatestBundleResult {
337
364
  * @since 6.7.0
338
365
  */
339
366
  downloadUrl?: string;
367
+ /**
368
+ * The signature of the latest bundle if the bundle is self-hosted.
369
+ *
370
+ * If the bundle is hosted on Capawesome Cloud, the signature will be
371
+ * returned as response header when downloading the bundle.
372
+ *
373
+ * @since 7.1.0
374
+ */
375
+ signature?: string;
340
376
  }
341
377
  /**
342
378
  * @since 5.0.0
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,wCAAwC","sourcesContent":["/// <reference types=\"@capacitor/cli\" />\n\nimport type { PluginListenerHandle } from '@capacitor/core';\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 * The timeout in milliseconds for HTTP requests.\n *\n * @since 6.4.0\n * @default 60000\n */\n httpTimeout?: number;\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\n * before resetting to the default bundle.\n *\n * It is strongly **recommended** to configure this option so that\n * the plugin can roll back to the default bundle in case of problems.\n * If configured, the plugin will wait for the app to call the `ready()`\n * method before resetting to the default bundle.\n *\n * Set to `0` to disable the timeout.\n *\n * @since 5.0.0\n * @default 0\n * @example 10000\n */\n readyTimeout?: number;\n /**\n * The API domain of the [Capawesome Cloud](https://cloud.capawesome.io) server.\n *\n * @since 7.0.0\n * @default 'api.cloud.capawesome.io'\n */\n serverDomain?: string;\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 * Fetch the latest bundle using the [Capawesome Cloud](https://capawesome.io/cloud/).\n *\n * Only available on Android and iOS.\n *\n * @since 6.6.0\n */\n fetchLatestBundle(\n options?: FetchLatestBundleOptions,\n ): Promise<FetchLatestBundleResult>;\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 that is used for the update.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getChannel(): Promise<GetChannelResult>;\n /**\n * Get the bundle identifier of the current bundle.\n * The current bundle is the bundle that is currently used by the app.\n *\n * Only available on Android and iOS.\n *\n * @since 6.7.0\n */\n getCurrentBundle(): Promise<GetCurrentBundleResult>;\n /**\n * Get the custom identifier of the device.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getCustomId(): Promise<GetCustomIdResult>;\n /**\n * Get the unique device identifier.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getDeviceId(): Promise<GetDeviceIdResult>;\n /**\n * Get the bundle identifier of the next bundle.\n * The next bundle is the bundle that will be used after calling `reload()`\n * or restarting the app.\n *\n * Only available on Android and iOS.\n *\n * @since 6.7.0\n */\n getNextBundle(): Promise<GetNextBundleResult>;\n /**\n * Get 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 * 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 * 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 * 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<ReadyResult>;\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 channel to use for the update.\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 device.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setCustomId(options: SetCustomIdOptions): 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 6.7.0\n */\n setNextBundle(options: SetNextBundleOptions): 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(options?: SyncOptions): Promise<SyncResult>;\n /**\n * Listen for the download progress of a bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 7.0.0\n */\n addListener(\n eventName: 'downloadBundleProgress',\n listenerFunc: DownloadBundleProgressListener,\n ): Promise<PluginListenerHandle>;\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 artifact type of the bundle.\n *\n * @since 6.6.0\n * @default 'zip'\n * @example 'manifest'\n */\n artifactType?: 'manifest' | 'zip';\n /**\n * The unique identifier of the bundle.\n *\n * **Attention**: The value `public` is reserved and cannot be used as a bundle identifier.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n /**\n * The URL of the bundle to download.\n *\n * For the `zip` artifact type, the URL must point to a ZIP file.\n * For the `manifest` artifact type, the URL serves as the base URL\n * to download the individual files. For example, if the URL is\n * `https://example.com/download`, the plugin will download the file\n * with the href `index.html` from `https://example.com/download?href=index.html`.\n *\n * To **verify the integrity** of the file, the server must return\n * a `X-Checksum` header with the SHA-256 hash in base64 format.\n *\n * To **verify the signature** of the file, the server must return\n * a `X-Signature` header with the signed SHA-256 hash in base64 format.\n *\n * @since 5.0.0\n * @example 'https://example.com/bundle.zip'\n */\n url: string;\n}\n\n/**\n * @since 6.7.0\n */\nexport interface FetchLatestBundleOptions {\n /**\n * The name of the channel where the latest bundle is fetched from.\n *\n * @since 6.7.0\n */\n channel?: string;\n}\n\n/**\n * @since 6.6.0\n */\nexport interface FetchLatestBundleResult {\n /**\n * The artifact type of the bundle.\n *\n * @since 6.7.0\n */\n artifactType?: 'manifest' | 'zip';\n /**\n * The unique identifier of the latest bundle.\n *\n * If `null`, no bundle is available.\n *\n * @since 6.6.0\n */\n bundleId: string | null;\n /**\n * Custom properties that are associated with the latest bundle.\n *\n * @since 7.0.0\n * @example { \"key\": \"value\" }\n */\n customProperties?: { [key: string]: string };\n /**\n * The URL of the latest bundle to download.\n * Pass this URL to the `downloadBundle(...)` method to download the bundle.\n *\n * @since 6.7.0\n */\n downloadUrl?: 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 name.\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 6.7.0\n */\nexport interface GetCurrentBundleResult {\n /**\n * The unique identifier of the current bundle.\n *\n * If `null`, the default bundle is being used.\n *\n * @since 6.7.0\n */\n bundleId: 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 6.7.0\n */\nexport interface GetNextBundleResult {\n /**\n * The unique identifier of the next bundle.\n *\n * If `null`, the default bundle is being used.\n *\n * @since 6.7.0\n */\n bundleId: string | null;\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 device.\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 7.0.0\n */\nexport interface ReadyResult {\n /**\n * The identifier of the previous bundle used.\n *\n * If `null`, the default bundle was used.\n *\n * @since 7.0.0\n */\n previousBundleId: string | null;\n /**\n * The identifier of the current bundle used.\n *\n * If `null`, the default bundle is being used.\n *\n * @since 7.0.0\n */\n currentBundleId: string | null;\n /**\n * Whether or not the app was reset to the default bundle.\n *\n * @since 7.0.0\n */\n rollback: boolean;\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 name.\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 device.\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 6.7.0\n */\nexport interface SetNextBundleOptions {\n /**\n * The unique identifier of the bundle to use.\n *\n * Set `null` to use the default bundle (same as calling `reset()`).\n *\n * @since 6.7.0\n * @example '1.0.0'\n */\n bundleId: string | null;\n}\n\n/**\n * @since 6.7.0\n */\nexport interface SyncOptions {\n /**\n * The name of the channel where the latest bundle is fetched from.\n *\n * @since 6.7.0\n */\n channel?: string;\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\n/**\n * Listener for the download progress of a bundle.\n *\n * @since 7.0.0\n */\nexport type DownloadBundleProgressListener = (\n event: DownloadBundleProgressEvent,\n) => void;\n\n/**\n * Event that is triggered when the download progress of a bundle changes.\n *\n * @since 7.0.0\n */\nexport interface DownloadBundleProgressEvent {\n /**\n * The unique identifier of the bundle that is being downloaded.\n *\n * @since 7.0.0\n */\n bundleId: string;\n /**\n * The number of bytes that have been downloaded.\n *\n * @since 7.0.0\n */\n downloadedBytes: number;\n /**\n * The progress of the download in percent as a value between `0` and `1`.\n *\n * @since 7.0.0\n * @example 0.5\n */\n progress: number;\n /**\n * The total number of bytes to download.\n *\n * @since 7.0.0\n */\n totalBytes: number;\n}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,wCAAwC","sourcesContent":["/// <reference types=\"@capacitor/cli\" />\n\nimport type { PluginListenerHandle } from '@capacitor/core';\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 * The timeout in milliseconds for HTTP requests.\n *\n * @since 6.4.0\n * @default 60000\n */\n httpTimeout?: number;\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\n * before resetting to the default bundle.\n *\n * It is strongly **recommended** to configure this option so that\n * the plugin can roll back to the default bundle in case of problems.\n * If configured, the plugin will wait for the app to call the `ready()`\n * method before resetting to the default bundle.\n *\n * Set to `0` to disable the timeout.\n *\n * @since 5.0.0\n * @default 0\n * @example 10000\n */\n readyTimeout?: number;\n /**\n * The API domain of the [Capawesome Cloud](https://cloud.capawesome.io) server.\n *\n * @since 7.0.0\n * @default 'api.cloud.capawesome.io'\n */\n serverDomain?: string;\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 * Fetch the latest bundle using the [Capawesome Cloud](https://capawesome.io/cloud/).\n *\n * Only available on Android and iOS.\n *\n * @since 6.6.0\n */\n fetchLatestBundle(\n options?: FetchLatestBundleOptions,\n ): Promise<FetchLatestBundleResult>;\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 that is used for the update.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getChannel(): Promise<GetChannelResult>;\n /**\n * Get the bundle identifier of the current bundle.\n * The current bundle is the bundle that is currently used by the app.\n *\n * Only available on Android and iOS.\n *\n * @since 6.7.0\n */\n getCurrentBundle(): Promise<GetCurrentBundleResult>;\n /**\n * Get the custom identifier of the device.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getCustomId(): Promise<GetCustomIdResult>;\n /**\n * Get the unique device identifier.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getDeviceId(): Promise<GetDeviceIdResult>;\n /**\n * Get the bundle identifier of the next bundle.\n * The next bundle is the bundle that will be used after calling `reload()`\n * or restarting the app.\n *\n * Only available on Android and iOS.\n *\n * @since 6.7.0\n */\n getNextBundle(): Promise<GetNextBundleResult>;\n /**\n * Get 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 * 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 * 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 * 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<ReadyResult>;\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 channel to use for the update.\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 device.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setCustomId(options: SetCustomIdOptions): 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 6.7.0\n */\n setNextBundle(options: SetNextBundleOptions): 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(options?: SyncOptions): Promise<SyncResult>;\n /**\n * Listen for the download progress of a bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 7.0.0\n */\n addListener(\n eventName: 'downloadBundleProgress',\n listenerFunc: DownloadBundleProgressListener,\n ): Promise<PluginListenerHandle>;\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 artifact type of the bundle.\n *\n * @since 6.6.0\n * @default 'zip'\n * @example 'manifest'\n */\n artifactType?: 'manifest' | 'zip';\n /**\n * The unique identifier of the bundle.\n *\n * **Attention**: The value `public` is reserved and cannot be used as a bundle identifier.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n /**\n * The checksum of the self-hosted bundle as a SHA-256 hash\n * in base64 format to verify the integrity of the bundle.\n *\n * **Attention**: Only supported for the `zip` artifact type.\n *\n * @since 7.1.0\n */\n checksum?: string;\n /**\n * The signature of the self-hosted bundle as a signed SHA-256 hash\n * in base64 format to verify the integrity of the bundle.\n *\n * **Attention**: Only supported for the `zip` artifact type.\n *\n * @since 7.1.0\n */\n signature?: string;\n /**\n * The URL of the bundle to download.\n *\n * For the `zip` artifact type, the URL must point to a ZIP file.\n * For the `manifest` artifact type, the URL serves as the base URL\n * to download the individual files. For example, if the URL is\n * `https://example.com/download`, the plugin will download the file\n * with the href `index.html` from `https://example.com/download?href=index.html`.\n *\n * To **verify the integrity** of the file, the server should return\n * a `X-Checksum` header with the SHA-256 hash in base64 format.\n *\n * To **verify the signature** of the file, the server should return\n * a `X-Signature` header with the signed SHA-256 hash in base64 format.\n *\n * @since 5.0.0\n * @example 'https://example.com/bundle.zip'\n */\n url: string;\n}\n\n/**\n * @since 6.7.0\n */\nexport interface FetchLatestBundleOptions {\n /**\n * The name of the channel where the latest bundle is fetched from.\n *\n * @since 6.7.0\n */\n channel?: string;\n}\n\n/**\n * @since 6.6.0\n */\nexport interface FetchLatestBundleResult {\n /**\n * The artifact type of the bundle.\n *\n * @since 6.7.0\n */\n artifactType?: 'manifest' | 'zip';\n /**\n * The unique identifier of the latest bundle.\n *\n * If `null`, no bundle is available.\n *\n * @since 6.6.0\n */\n bundleId: string | null;\n /**\n * The checksum of the latest bundle if the bundle is self-hosted.\n *\n * If the bundle is hosted on Capawesome Cloud, the checksum will be\n * returned as response header when downloading the bundle.\n *\n * @since 7.1.0\n */\n checksum?: string;\n /**\n * Custom properties that are associated with the latest bundle.\n *\n * @since 7.0.0\n * @example { \"key\": \"value\" }\n */\n customProperties?: { [key: string]: string };\n /**\n * The URL of the latest bundle to download.\n * Pass this URL to the `downloadBundle(...)` method to download the bundle.\n *\n * @since 6.7.0\n */\n downloadUrl?: string;\n /**\n * The signature of the latest bundle if the bundle is self-hosted.\n *\n * If the bundle is hosted on Capawesome Cloud, the signature will be\n * returned as response header when downloading the bundle.\n *\n * @since 7.1.0\n */\n signature?: 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 name.\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 6.7.0\n */\nexport interface GetCurrentBundleResult {\n /**\n * The unique identifier of the current bundle.\n *\n * If `null`, the default bundle is being used.\n *\n * @since 6.7.0\n */\n bundleId: 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 6.7.0\n */\nexport interface GetNextBundleResult {\n /**\n * The unique identifier of the next bundle.\n *\n * If `null`, the default bundle is being used.\n *\n * @since 6.7.0\n */\n bundleId: string | null;\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 device.\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 7.0.0\n */\nexport interface ReadyResult {\n /**\n * The identifier of the previous bundle used.\n *\n * If `null`, the default bundle was used.\n *\n * @since 7.0.0\n */\n previousBundleId: string | null;\n /**\n * The identifier of the current bundle used.\n *\n * If `null`, the default bundle is being used.\n *\n * @since 7.0.0\n */\n currentBundleId: string | null;\n /**\n * Whether or not the app was reset to the default bundle.\n *\n * @since 7.0.0\n */\n rollback: boolean;\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 name.\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 device.\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 6.7.0\n */\nexport interface SetNextBundleOptions {\n /**\n * The unique identifier of the bundle to use.\n *\n * Set `null` to use the default bundle (same as calling `reset()`).\n *\n * @since 6.7.0\n * @example '1.0.0'\n */\n bundleId: string | null;\n}\n\n/**\n * @since 6.7.0\n */\nexport interface SyncOptions {\n /**\n * The name of the channel where the latest bundle is fetched from.\n *\n * @since 6.7.0\n */\n channel?: string;\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\n/**\n * Listener for the download progress of a bundle.\n *\n * @since 7.0.0\n */\nexport type DownloadBundleProgressListener = (\n event: DownloadBundleProgressEvent,\n) => void;\n\n/**\n * Event that is triggered when the download progress of a bundle changes.\n *\n * @since 7.0.0\n */\nexport interface DownloadBundleProgressEvent {\n /**\n * The unique identifier of the bundle that is being downloaded.\n *\n * @since 7.0.0\n */\n bundleId: string;\n /**\n * The number of bytes that have been downloaded.\n *\n * @since 7.0.0\n */\n downloadedBytes: number;\n /**\n * The progress of the download in percent as a value between `0` and `1`.\n *\n * @since 7.0.0\n * @example 0.5\n */\n progress: number;\n /**\n * The total number of bytes to download.\n *\n * @since 7.0.0\n */\n totalBytes: number;\n}\n"]}
@@ -4,15 +4,19 @@ import Capacitor
4
4
  @objc public class DownloadBundleOptions: NSObject {
5
5
  private var artifactType: ArtifactType
6
6
  private var bundleId: String
7
+ private var checksum: String?
8
+ private var signature: String?
7
9
  private var url: String
8
10
 
9
- init(artifactType: String, bundleId: String, url: String) {
11
+ init(artifactType: String, bundleId: String, checksum: String?, signature: String?, url: String) {
10
12
  if artifactType == "manifest" {
11
13
  self.artifactType = .manifest
12
14
  } else {
13
15
  self.artifactType = .zip
14
16
  }
15
17
  self.bundleId = bundleId
18
+ self.checksum = checksum
19
+ self.signature = signature
16
20
  self.url = url
17
21
  }
18
22
 
@@ -24,6 +28,14 @@ import Capacitor
24
28
  return bundleId
25
29
  }
26
30
 
31
+ func getChecksum() -> String? {
32
+ return checksum
33
+ }
34
+
35
+ func getSignature() -> String? {
36
+ return signature
37
+ }
38
+
27
39
  func getUrl() -> String {
28
40
  return url
29
41
  }
@@ -4,14 +4,18 @@ import Capacitor
4
4
  @objc public class FetchLatestBundleResult: NSObject, Result {
5
5
  private let artifactType: ArtifactType?
6
6
  private let bundleId: String?
7
+ private let checksum: String?
7
8
  private let customProperties: [String: Any]?
8
9
  private let downloadUrl: String?
10
+ private let signature: String?
9
11
 
10
- init(artifactType: ArtifactType?, bundleId: String?, customProperties: [String: Any]?, downloadUrl: String?) {
12
+ init(artifactType: ArtifactType?, bundleId: String?, checksum: String?, customProperties: [String: Any]?, downloadUrl: String?, signature: String?) {
11
13
  self.artifactType = artifactType
12
14
  self.bundleId = bundleId
15
+ self.checksum = checksum
13
16
  self.customProperties = customProperties
14
17
  self.downloadUrl = downloadUrl
18
+ self.signature = signature
15
19
  }
16
20
 
17
21
  public func toJSObject() -> AnyObject {
@@ -22,12 +26,18 @@ import Capacitor
22
26
  result["artifactType"] = "zip"
23
27
  }
24
28
  result["bundleId"] = bundleId == nil ? NSNull() : bundleId
29
+ if let checksum = checksum {
30
+ result["checksum"] = checksum
31
+ }
25
32
  if let customProperties = JSTypes.coerceDictionaryToJSObject(customProperties) {
26
33
  result["customProperties"] = customProperties
27
34
  }
28
35
  if let downloadUrl = downloadUrl {
29
36
  result["downloadUrl"] = downloadUrl
30
37
  }
38
+ if let signature = signature {
39
+ result["signature"] = signature
40
+ }
31
41
  return result as AnyObject
32
42
  }
33
43
  }
@@ -57,6 +57,8 @@ import CommonCrypto
57
57
  @objc public func downloadBundle(_ options: DownloadBundleOptions) async throws {
58
58
  let artifactType = options.getArtifactType()
59
59
  let bundleId = options.getBundleId()
60
+ let checksum = options.getChecksum()
61
+ let signature = options.getSignature()
60
62
  let url = options.getUrl()
61
63
 
62
64
  // Check if the bundle already exists
@@ -68,13 +70,13 @@ import CommonCrypto
68
70
  if artifactType == .manifest {
69
71
  try await downloadBundleOfTypeManifest(bundleId: bundleId, url: url)
70
72
  } else {
71
- try await downloadBundleOfTypeZip(bundleId: bundleId, url: url)
73
+ try await downloadBundleOfTypeZip(bundleId: bundleId, checksum: checksum, signature: signature, url: url)
72
74
  }
73
75
  }
74
76
 
75
77
  @objc public func fetchLatestBundle(_ options: FetchLatestBundleOptions) async throws -> FetchLatestBundleResult {
76
78
  let response: GetLatestBundleResponse? = try await self.fetchLatestBundle(options)
77
- return FetchLatestBundleResult(artifactType: response?.artifactType, bundleId: response?.bundleId, customProperties: response?.customProperties, downloadUrl: response?.url)
79
+ return FetchLatestBundleResult(artifactType: response?.artifactType, bundleId: response?.bundleId, checksum: response?.checksum, customProperties: response?.customProperties, downloadUrl: response?.url, signature: response?.signature)
78
80
  }
79
81
 
80
82
  @objc public func getBundles(completion: @escaping (Result?, Error?) -> Void) {
@@ -201,6 +203,8 @@ import CommonCrypto
201
203
  }
202
204
  let artifactType = response.artifactType
203
205
  let latestBundleId = response.bundleId
206
+ let checksum = response.checksum
207
+ let signature = response.signature
204
208
  let downloadUrl = response.url
205
209
  // Check if bundle already exists
206
210
  if hasBundleById(latestBundleId) {
@@ -217,7 +221,7 @@ import CommonCrypto
217
221
  if artifactType == .manifest {
218
222
  try await downloadBundleOfTypeManifest(bundleId: latestBundleId, url: downloadUrl)
219
223
  } else {
220
- try await downloadBundleOfTypeZip(bundleId: latestBundleId, url: downloadUrl)
224
+ try await downloadBundleOfTypeZip(bundleId: latestBundleId, checksum: checksum, signature: signature, url: downloadUrl)
221
225
  }
222
226
  // Set the next bundle
223
227
  setNextBundleById(latestBundleId)
@@ -338,7 +342,7 @@ import CommonCrypto
338
342
  }
339
343
  }
340
344
 
341
- private func downloadAndVerifyFile(url: String, file: URL, callback: ((Progress) -> Void)?) async throws {
345
+ private func downloadAndVerifyFile(url: String, file: URL, checksum: String?, signature: String?, callback: ((Progress) -> Void)?) async throws {
342
346
  let destination: DownloadRequest.Destination = { _, _ in
343
347
  return (file, [.createIntermediateDirectories])
344
348
  }
@@ -356,8 +360,8 @@ import CommonCrypto
356
360
  guard let response = result.response else {
357
361
  throw CustomError.unknown
358
362
  }
359
- let checksum = LiveUpdateHttpClient.getChecksumFromResponse(response: response)
360
- let signature = LiveUpdateHttpClient.getSignatureFromResponse(response: response)
363
+ let checksum = checksum ?? LiveUpdateHttpClient.getChecksumFromResponse(response: response)
364
+ let signature = signature ?? LiveUpdateHttpClient.getSignatureFromResponse(response: response)
361
365
  try verifyFile(url: file, checksum: checksum, signature: signature)
362
366
  }
363
367
 
@@ -368,7 +372,7 @@ import CommonCrypto
368
372
  var urlComponents = URLComponents(string: baseUrl)!
369
373
  urlComponents.queryItems = parameters.map { URLQueryItem(name: $0.key, value: $0.value) }
370
374
  let url = urlComponents.string!
371
- try await self.downloadAndVerifyFile(url: url, file: fileURL, callback: callback)
375
+ try await self.downloadAndVerifyFile(url: url, file: fileURL, checksum: nil, signature: nil, callback: callback)
372
376
  return fileURL
373
377
  }
374
378
 
@@ -440,11 +444,11 @@ import CommonCrypto
440
444
  try await addBundleOfTypeManifest(bundleId: bundleId, directory: temporaryDirectory)
441
445
  }
442
446
 
443
- private func downloadBundleOfTypeZip(bundleId: String, url: String) async throws {
447
+ private func downloadBundleOfTypeZip(bundleId: String, checksum: String?, signature: String?, url: String) async throws {
444
448
  let timestamp = String(Int(Date().timeIntervalSince1970))
445
449
  let temporaryZipFileUrl = self.cachesDirectoryUrl.appendingPathComponent(timestamp + ".zip")
446
450
  // Download the bundle
447
- try await downloadAndVerifyFile(url: url, file: temporaryZipFileUrl, callback: { progress in
451
+ try await downloadAndVerifyFile(url: url, file: temporaryZipFileUrl, checksum: checksum, signature: signature, callback: { progress in
448
452
  let event = DownloadBundleProgressEvent(bundleId: bundleId, downloadedBytes: progress.completedUnitCount, totalBytes: progress.totalUnitCount)
449
453
  self.notifyDownloadBundleProgressListeners(event)
450
454
  })
@@ -8,7 +8,7 @@ import Capacitor
8
8
  @objc(LiveUpdatePlugin)
9
9
  public class LiveUpdatePlugin: CAPPlugin, CAPBridgedPlugin {
10
10
  public static let tag = "LiveUpdate"
11
- public static let version = "7.0.0"
11
+ public static let version = "7.1.0"
12
12
  public static let userDefaultsPrefix = "CapawesomeLiveUpdate" // DO NOT CHANGE
13
13
 
14
14
  public let identifier = "LiveUpdatePlugin"
@@ -70,12 +70,14 @@ public class LiveUpdatePlugin: CAPPlugin, CAPBridgedPlugin {
70
70
  call.reject(CustomError.bundleIdMissing.localizedDescription)
71
71
  return
72
72
  }
73
+ let checksum = call.getString("checksum")
74
+ let signature = call.getString("signature")
73
75
  guard let url = call.getString("url") else {
74
76
  call.reject(CustomError.urlMissing.localizedDescription)
75
77
  return
76
78
  }
77
79
 
78
- let options = DownloadBundleOptions(artifactType: artifactType, bundleId: bundleId, url: url)
80
+ let options = DownloadBundleOptions(artifactType: artifactType, bundleId: bundleId, checksum: checksum, signature: signature, url: url)
79
81
 
80
82
  try await implementation?.downloadBundle(options)
81
83
  self.resolveCall(call)
@@ -1,6 +1,8 @@
1
1
  public struct GetLatestBundleResponse: Codable {
2
2
  var artifactType: ArtifactType
3
3
  var bundleId: String
4
+ var checksum: String?
4
5
  var customProperties: [String: String]?
6
+ var signature: String?
5
7
  var url: String
6
8
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capawesome/capacitor-live-update",
3
- "version": "7.0.0",
3
+ "version": "7.1.0",
4
4
  "description": "Capacitor plugin to update your app remotely in real-time.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",