@capawesome/capacitor-live-update 7.0.0 → 7.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CapawesomeCapacitorLiveUpdate.podspec +1 -1
- package/README.md +48 -21
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdate.java +37 -10
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdatePlugin.java +4 -2
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/api/GetLatestBundleResponse.java +18 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/options/DownloadBundleOptions.java +25 -1
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/classes/results/FetchLatestBundleResult.java +17 -1
- package/dist/docs.json +49 -1
- package/dist/esm/definitions.d.ts +38 -2
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Plugin/Classes/Options/DownloadBundleOptions.swift +13 -1
- package/ios/Plugin/Classes/Results/FetchLatestBundleResult.swift +11 -1
- package/ios/Plugin/LiveUpdate.swift +13 -9
- package/ios/Plugin/LiveUpdatePlugin.swift +4 -2
- package/ios/Plugin/Protocols/Api/GetLatestBundleResponse.swift +2 -0
- package/package.json +1 -1
|
@@ -13,7 +13,7 @@ Pod::Spec.new do |s|
|
|
|
13
13
|
s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
|
|
14
14
|
s.ios.deployment_target = '14.0'
|
|
15
15
|
s.dependency 'Capacitor'
|
|
16
|
-
s.dependency 'Alamofire', '~> 5.9
|
|
16
|
+
s.dependency 'Alamofire', '~> 5.9'
|
|
17
17
|
s.dependency 'SSZipArchive', '~> 2.2.3'
|
|
18
18
|
s.swift_version = '5.1'
|
|
19
19
|
s.static_framework = true
|
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
|
|
8
|
+
- ⚡️ **Capacitor 7** support
|
|
9
9
|
- 📦 **Bundle Management**: Download, set, and delete bundles.
|
|
10
|
-
- ☁️ **Cloud Support**: Use the [Capawesome Cloud](https://capawesome.io/
|
|
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.
|
|
@@ -191,7 +191,16 @@ const getVersionName = async () => {
|
|
|
191
191
|
};
|
|
192
192
|
|
|
193
193
|
const ready = async () => {
|
|
194
|
-
await LiveUpdate.ready();
|
|
194
|
+
const result = await LiveUpdate.ready();
|
|
195
|
+
if (result.currentBundleId) {
|
|
196
|
+
console.log(`The app is now using the bundle with the identifier ${result.currentBundleId}.`);
|
|
197
|
+
}
|
|
198
|
+
if (result.previousBundleId) {
|
|
199
|
+
console.log(`The app was using the bundle with the identifier ${result.previousBundleId}.`);
|
|
200
|
+
}
|
|
201
|
+
if (result.rollback) {
|
|
202
|
+
console.log('The app was reset to the default bundle.');
|
|
203
|
+
}
|
|
195
204
|
};
|
|
196
205
|
|
|
197
206
|
const reload = async () => {
|
|
@@ -203,7 +212,7 @@ const reset = async () => {
|
|
|
203
212
|
};
|
|
204
213
|
|
|
205
214
|
const setChannel = async () => {
|
|
206
|
-
await LiveUpdate.setChannel({ channel: '
|
|
215
|
+
await LiveUpdate.setChannel({ channel: 'production-5' });
|
|
207
216
|
};
|
|
208
217
|
|
|
209
218
|
const setCustomId = async () => {
|
|
@@ -211,18 +220,22 @@ const setCustomId = async () => {
|
|
|
211
220
|
};
|
|
212
221
|
|
|
213
222
|
const setNextBundle = async () => {
|
|
214
|
-
await LiveUpdate.setNextBundle({ bundleId: '
|
|
223
|
+
await LiveUpdate.setNextBundle({ bundleId: '7f0b9bf2-dff6-4be2-bcac-b068cc5ea756' });
|
|
215
224
|
};
|
|
216
225
|
|
|
217
226
|
const sync = async () => {
|
|
218
|
-
const result = await LiveUpdate.sync(
|
|
227
|
+
const result = await LiveUpdate.sync({
|
|
228
|
+
channel: 'production-5',
|
|
229
|
+
});
|
|
219
230
|
return result.nextBundleId;
|
|
220
231
|
};
|
|
221
232
|
|
|
222
233
|
const isNewBundleAvailable = async () => {
|
|
223
|
-
const { bundleId: latestBundleId } = await LiveUpdate.fetchLatestBundle(
|
|
234
|
+
const { bundleId: latestBundleId } = await LiveUpdate.fetchLatestBundle({
|
|
235
|
+
channel: 'production-5',
|
|
236
|
+
});
|
|
224
237
|
if (latestBundleId) {
|
|
225
|
-
const { bundleId: currentBundleId } = await LiveUpdate.
|
|
238
|
+
const { bundleId: currentBundleId } = await LiveUpdate.getCurrentBundle();
|
|
226
239
|
return latestBundleId !== currentBundleId;
|
|
227
240
|
} else {
|
|
228
241
|
return false;
|
|
@@ -633,21 +646,25 @@ Only available on Android and iOS.
|
|
|
633
646
|
|
|
634
647
|
#### DownloadBundleOptions
|
|
635
648
|
|
|
636
|
-
| Prop | Type | Description
|
|
637
|
-
| ------------------ | -------------------------------- |
|
|
638
|
-
| **`artifactType`** | <code>'manifest' \| 'zip'</code> | The artifact type of the bundle.
|
|
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.
|
|
640
|
-
| **`
|
|
649
|
+
| Prop | Type | Description | Default | Since |
|
|
650
|
+
| ------------------ | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----- |
|
|
651
|
+
| **`artifactType`** | <code>'manifest' \| 'zip'</code> | The artifact type of the bundle. | <code>'zip'</code> | 6.6.0 |
|
|
652
|
+
| **`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 |
|
|
653
|
+
| **`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 |
|
|
654
|
+
| **`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 |
|
|
655
|
+
| **`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
656
|
|
|
642
657
|
|
|
643
658
|
#### FetchLatestBundleResult
|
|
644
659
|
|
|
645
|
-
| Prop | Type | Description
|
|
646
|
-
| ---------------------- | --------------------------------------- |
|
|
647
|
-
| **`artifactType`** | <code>'manifest' \| 'zip'</code> | The artifact type of the bundle.
|
|
648
|
-
| **`bundleId`** | <code>string \| null</code> | The unique identifier of the latest bundle. If `null`, no bundle is available.
|
|
649
|
-
| **`
|
|
650
|
-
| **`
|
|
660
|
+
| Prop | Type | Description | Since |
|
|
661
|
+
| ---------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
|
|
662
|
+
| **`artifactType`** | <code>'manifest' \| 'zip'</code> | The artifact type of the bundle. | 6.7.0 |
|
|
663
|
+
| **`bundleId`** | <code>string \| null</code> | The unique identifier of the latest bundle. If `null`, no bundle is available. | 6.6.0 |
|
|
664
|
+
| **`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 |
|
|
665
|
+
| **`customProperties`** | <code>{ [key: string]: string; }</code> | Custom properties that are associated with the latest bundle. | 7.0.0 |
|
|
666
|
+
| **`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 |
|
|
667
|
+
| **`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
668
|
|
|
652
669
|
|
|
653
670
|
#### FetchLatestBundleOptions
|
|
@@ -789,7 +806,7 @@ Listener for the download progress of a bundle.
|
|
|
789
806
|
|
|
790
807
|
## Testing
|
|
791
808
|
|
|
792
|
-
When testing the plugin, you must make sure that you do not use the [Live Reload](https://ionicframework.com/docs/cli/livereload) option, as in this case a development server is used to load the bundle.
|
|
809
|
+
When testing the plugin, you must make sure that you do not use the [Live Reload](https://ionicframework.com/docs/cli/livereload) option, as in this case a development server is used to load the bundle and not the local file system.
|
|
793
810
|
|
|
794
811
|
Therefore, simply start your app without the live reload option, for example with the following command:
|
|
795
812
|
|
|
@@ -797,7 +814,17 @@ Therefore, simply start your app without the live reload option, for example wit
|
|
|
797
814
|
npx ionic cap run android --open
|
|
798
815
|
```
|
|
799
816
|
|
|
800
|
-
If you want to
|
|
817
|
+
If you want to **not** receive live updates to test other parts of your app, you can simply set a non-existent channel, for example:
|
|
818
|
+
|
|
819
|
+
```typescript
|
|
820
|
+
import { LiveUpdate } from '@capawesome/capacitor-live-update';
|
|
821
|
+
|
|
822
|
+
const sync = async () => {
|
|
823
|
+
await LiveUpdate.sync({ channel: 'non-existent-channel' });
|
|
824
|
+
};
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
This way, the app will check for updates, but no updates will be found.
|
|
801
828
|
|
|
802
829
|
## Limitations
|
|
803
830
|
|
|
@@ -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
|
-
|
|
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(
|
|
454
|
-
|
|
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
|
-
|
|
461
|
-
|
|
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(
|
|
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
|
});
|
package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdatePlugin.java
CHANGED
|
@@ -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.
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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)
|