@capawesome/capacitor-live-update 6.1.0 → 6.2.0-dev.4ebc29c.1723183892

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
@@ -7,7 +7,7 @@ Capacitor plugin to update your app remotely in real-time.
7
7
  - 🔋 Supports **Android and iOS**
8
8
  - ⚡️ **Capacitor 6** support
9
9
  - 📦 **Bundle Management**: Download, set, and delete bundles.
10
- - ☁️ **Cloud Support**: Use the [Capawesome Cloud](https://capawesome.io/cloud) to manage your app updates.
10
+ - ☁️ **Cloud Support**: Use the [Capawesome Cloud](https://capawesome.io/cloud/live-updates/) 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.
@@ -65,14 +65,16 @@ We recommend to declare [`CA92.1`](https://developer.apple.com/documentation/bun
65
65
  <docgen-config>
66
66
  <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
67
67
 
68
- | Prop | Type | Description | Default | Since |
69
- | ----------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----- |
70
- | **`appId`** | <code>string</code> | The app ID is used to identify the app when using [Capawesome Cloud](https://capawesome.io/cloud). | | 5.0.0 |
71
- | **`autoDeleteBundles`** | <code>boolean</code> | Whether or not to automatically delete unused bundles. When enabled, the plugin will automatically delete unused bundles after calling `ready()`. | <code>false</code> | 5.0.0 |
72
- | **`enabled`** | <code>boolean</code> | Whether or not the plugin is enabled. | <code>true</code> | 5.0.0 |
73
- | **`publicKey`** | <code>string</code> | The public key to verify the integrity of the bundle. The public key must be a PEM-encoded RSA public key. | | 6.1.0 |
74
- | **`readyTimeout`** | <code>number</code> | The timeout in milliseconds to wait for the app to be ready before resetting to the default bundle. | <code>10000</code> | 5.0.0 |
75
- | **`resetOnUpdate`** | <code>boolean</code> | Whether or not the app should be reset to the default bundle during an update. | <code>true</code> | 5.0.0 |
68
+ | Prop | Type | Description | Default | Since |
69
+ | ----------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----- |
70
+ | **`appId`** | <code>string</code> | The app ID is used to identify the app when using [Capawesome Cloud](https://capawesome.io/cloud). | | 5.0.0 |
71
+ | **`autoDeleteBundles`** | <code>boolean</code> | Whether or not to automatically delete unused bundles. When enabled, the plugin will automatically delete unused bundles after calling `ready()`. | <code>false</code> | 5.0.0 |
72
+ | **`defaultChannel`** | <code>string</code> | The default channel of the app. | | 6.3.0 |
73
+ | **`enabled`** | <code>boolean</code> | Whether or not the plugin is enabled. | <code>true</code> | 5.0.0 |
74
+ | **`location`** | <code>'us' \| 'eu'</code> | The location of the server to use when using [Capawesome Cloud](https://capawesome.io/cloud). | <code>'us'</code> | 6.2.0 |
75
+ | **`publicKey`** | <code>string</code> | The public key to verify the integrity of the bundle. The public key must be a PEM-encoded RSA public key. | | 6.1.0 |
76
+ | **`readyTimeout`** | <code>number</code> | The timeout in milliseconds to wait for the app to be ready before resetting to the default bundle. | <code>10000</code> | 5.0.0 |
77
+ | **`resetOnUpdate`** | <code>boolean</code> | Whether or not the app should be reset to the default bundle during an update. | <code>true</code> | 5.0.0 |
76
78
 
77
79
  ### Examples
78
80
 
@@ -84,7 +86,9 @@ In `capacitor.config.json`:
84
86
  "LiveUpdate": {
85
87
  "appId": '4100e356-e851-47fe-9d3c-b411eb325a99',
86
88
  "autoDeleteBundles": undefined,
89
+ "defaultChannel": 'production',
87
90
  "enabled": undefined,
91
+ "location": 'eu',
88
92
  "publicKey": '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDodf1SD0OOn6hIlDuKBza0Ed0OqtwyVJwiyjmE9BJaZ7y8ZUfcF+SKmd0l2cDPM45XIg2tAFux5n29uoKyHwSt+6tCi5CJA5Z1/1eZruRRqABLonV77KS3HUtvOgqRLDnKSV89dYZkM++NwmzOPgIF422mvc+VukcVOBfc8/AHQIDAQAB-----END PUBLIC KEY-----',
89
93
  "readyTimeout": undefined,
90
94
  "resetOnUpdate": undefined
@@ -105,7 +109,9 @@ const config: CapacitorConfig = {
105
109
  LiveUpdate: {
106
110
  appId: '4100e356-e851-47fe-9d3c-b411eb325a99',
107
111
  autoDeleteBundles: undefined,
112
+ defaultChannel: 'production',
108
113
  enabled: undefined,
114
+ location: 'eu',
109
115
  publicKey: '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDodf1SD0OOn6hIlDuKBza0Ed0OqtwyVJwiyjmE9BJaZ7y8ZUfcF+SKmd0l2cDPM45XIg2tAFux5n29uoKyHwSt+6tCi5CJA5Z1/1eZruRRqABLonV77KS3HUtvOgqRLDnKSV89dYZkM++NwmzOPgIF422mvc+VukcVOBfc8/AHQIDAQAB-----END PUBLIC KEY-----',
110
116
  readyTimeout: undefined,
111
117
  resetOnUpdate: undefined,
@@ -133,7 +133,7 @@ public class LiveUpdate {
133
133
  }
134
134
 
135
135
  public void getChannel(@NonNull NonEmptyCallback callback) {
136
- String channel = preferences.getChannel();
136
+ String channel = getChannel();
137
137
  GetChannelResult result = new GetChannelResult(channel);
138
138
  callback.success(result);
139
139
  }
@@ -472,9 +472,13 @@ public class LiveUpdate {
472
472
  @Nullable
473
473
  private void fetchLatestBundle(@NonNull NonEmptyCallback<GetLatestBundleResponse> callback)
474
474
  throws PackageManager.NameNotFoundException {
475
+ String host = "api.cloud.capawesome.io";
476
+ if (config.getLocation() != null && config.getLocation().equals("eu")) {
477
+ host = "api.cloud.capawesome.eu";
478
+ }
475
479
  HttpUrl url = new HttpUrl.Builder()
476
480
  .scheme("https")
477
- .host("api.cloud.capawesome.io")
481
+ .host(host)
478
482
  .addPathSegment("v1")
479
483
  .addPathSegment("apps")
480
484
  .addPathSegment(config.getAppId())
@@ -483,7 +487,7 @@ public class LiveUpdate {
483
487
  .addQueryParameter("appVersionCode", getVersionCode())
484
488
  .addQueryParameter("appVersionName", getVersionName())
485
489
  .addQueryParameter("bundleId", getCurrentBundleId())
486
- .addQueryParameter("channelName", preferences.getChannel())
490
+ .addQueryParameter("channelName", getChannel())
487
491
  .addQueryParameter("customId", preferences.getCustomId())
488
492
  .addQueryParameter("deviceId", getDeviceId())
489
493
  .addQueryParameter("osVersion", String.valueOf(Build.VERSION.SDK_INT))
@@ -526,14 +530,6 @@ public class LiveUpdate {
526
530
  );
527
531
  }
528
532
 
529
- private String getVersionCode() throws PackageManager.NameNotFoundException {
530
- return String.valueOf(getPackageInfo().versionCode);
531
- }
532
-
533
- private String getVersionName() throws PackageManager.NameNotFoundException {
534
- return getPackageInfo().versionName;
535
- }
536
-
537
533
  private String[] getBundleIds() {
538
534
  File bundlesDirectory = buildBundlesDirectory();
539
535
  File[] bundles = bundlesDirectory.listFiles();
@@ -549,6 +545,18 @@ public class LiveUpdate {
549
545
  return bundleIds;
550
546
  }
551
547
 
548
+ @Nullable
549
+ private String getChannel() {
550
+ String channel = null;
551
+ if (config.getDefaultChannel() != null) {
552
+ channel = config.getDefaultChannel();
553
+ }
554
+ if (preferences.getChannel() != null) {
555
+ channel = preferences.getChannel();
556
+ }
557
+ return channel;
558
+ }
559
+
552
560
  /**
553
561
  * @return The current bundle ID (`public` for the built-in bundle).
554
562
  */
@@ -600,6 +608,14 @@ public class LiveUpdate {
600
608
  .getString(WebView.CAP_SERVER_PATH, defaultWebAssetDir);
601
609
  }
602
610
 
611
+ private String getVersionCode() throws PackageManager.NameNotFoundException {
612
+ return String.valueOf(getPackageInfo().versionCode);
613
+ }
614
+
615
+ private String getVersionName() throws PackageManager.NameNotFoundException {
616
+ return getPackageInfo().versionName;
617
+ }
618
+
603
619
  private boolean hasBundle(@NonNull String bundleId) {
604
620
  File bundleDirectory = buildBundleDirectoryFor(bundleId);
605
621
  return bundleDirectory.exists();
@@ -9,8 +9,15 @@ public class LiveUpdateConfig {
9
9
  private String appId = null;
10
10
 
11
11
  private boolean autoDeleteBundles = false;
12
+
13
+ @Nullable
14
+ private String defaultChannel = null;
15
+
12
16
  private boolean enabled = true;
13
17
 
18
+ @Nullable
19
+ private String location = null;
20
+
14
21
  @Nullable
15
22
  private String publicKey = null;
16
23
 
@@ -26,10 +33,20 @@ public class LiveUpdateConfig {
26
33
  return autoDeleteBundles;
27
34
  }
28
35
 
36
+ @Nullable
37
+ public String getDefaultChannel() {
38
+ return defaultChannel;
39
+ }
40
+
29
41
  public boolean getEnabled() {
30
42
  return enabled;
31
43
  }
32
44
 
45
+ @Nullable
46
+ public String getLocation() {
47
+ return location;
48
+ }
49
+
33
50
  @Nullable
34
51
  public String getPublicKey() {
35
52
  return publicKey;
@@ -51,10 +68,18 @@ public class LiveUpdateConfig {
51
68
  this.autoDeleteBundles = autoDeleteBundles;
52
69
  }
53
70
 
71
+ public void setDefaultChannel(@Nullable String defaultChannel) {
72
+ this.defaultChannel = defaultChannel;
73
+ }
74
+
54
75
  public void setEnabled(boolean enabled) {
55
76
  this.enabled = enabled;
56
77
  }
57
78
 
79
+ public void setLocation(@Nullable String location) {
80
+ this.location = location;
81
+ }
82
+
58
83
  public void setPublicKey(@Nullable String publicKey) {
59
84
  this.publicKey = publicKey;
60
85
  }
@@ -19,7 +19,7 @@ import io.capawesome.capacitorjs.plugins.liveupdate.interfaces.Result;
19
19
  public class LiveUpdatePlugin extends Plugin {
20
20
 
21
21
  public static final String TAG = "LiveUpdate";
22
- public static final String VERSION = "6.1.0";
22
+ public static final String VERSION = "6.3.0";
23
23
  public static final String SHARED_PREFERENCES_NAME = "CapawesomeLiveUpdate"; // DO NOT CHANGE
24
24
  public static final String ERROR_APP_ID_MISSING = "appId must be configured.";
25
25
  public static final String ERROR_BUNDLE_EXISTS = "bundle already exists.";
@@ -431,8 +431,12 @@ public class LiveUpdatePlugin extends Plugin {
431
431
  config.setAppId(appId);
432
432
  boolean autoDeleteBundles = getConfig().getBoolean("autoDeleteBundles", config.getAutoDeleteBundles());
433
433
  config.setAutoDeleteBundles(autoDeleteBundles);
434
+ String defaultChannel = getConfig().getString("defaultChannel", config.getDefaultChannel());
435
+ config.setDefaultChannel(defaultChannel);
434
436
  boolean enabled = getConfig().getBoolean("enabled", config.getEnabled());
435
437
  config.setEnabled(enabled);
438
+ String location = getConfig().getString("location", config.getLocation());
439
+ config.setLocation(location);
436
440
  String publicKey = getConfig().getString("publicKey", config.getPublicKey());
437
441
  config.setPublicKey(publicKey);
438
442
  int readyTimeout = getConfig().getInt("readyTimeout", config.getReadyTimeout());
package/dist/docs.json CHANGED
@@ -746,6 +746,22 @@
746
746
  "complexTypes": [],
747
747
  "type": "boolean | undefined"
748
748
  },
749
+ {
750
+ "name": "defaultChannel",
751
+ "tags": [
752
+ {
753
+ "text": "6.3.0",
754
+ "name": "since"
755
+ },
756
+ {
757
+ "text": "'production'",
758
+ "name": "example"
759
+ }
760
+ ],
761
+ "docs": "The default channel of the app.",
762
+ "complexTypes": [],
763
+ "type": "string | undefined"
764
+ },
749
765
  {
750
766
  "name": "enabled",
751
767
  "tags": [
@@ -762,6 +778,30 @@
762
778
  "complexTypes": [],
763
779
  "type": "boolean | undefined"
764
780
  },
781
+ {
782
+ "name": "location",
783
+ "tags": [
784
+ {
785
+ "text": "6.2.0",
786
+ "name": "since"
787
+ },
788
+ {
789
+ "text": "'us'",
790
+ "name": "default"
791
+ },
792
+ {
793
+ "text": "'eu'",
794
+ "name": "example"
795
+ },
796
+ {
797
+ "text": "https ://capawesome.io/cloud/live-updates/advanced/privacy/",
798
+ "name": "see"
799
+ }
800
+ ],
801
+ "docs": "The location of the server to use when using [Capawesome Cloud](https://capawesome.io/cloud).",
802
+ "complexTypes": [],
803
+ "type": "'us' | 'eu' | undefined"
804
+ },
765
805
  {
766
806
  "name": "publicKey",
767
807
  "tags": [
@@ -17,6 +17,13 @@ declare module '@capacitor/cli' {
17
17
  * @default false
18
18
  */
19
19
  autoDeleteBundles?: boolean;
20
+ /**
21
+ * The default channel of the app.
22
+ *
23
+ * @since 6.3.0
24
+ * @example 'production'
25
+ */
26
+ defaultChannel?: string;
20
27
  /**
21
28
  * Whether or not the plugin is enabled.
22
29
  *
@@ -24,6 +31,15 @@ declare module '@capacitor/cli' {
24
31
  * @default true
25
32
  */
26
33
  enabled?: boolean;
34
+ /**
35
+ * The location of the server to use when using [Capawesome Cloud](https://capawesome.io/cloud).
36
+ *
37
+ * @since 6.2.0
38
+ * @default 'us'
39
+ * @example 'eu'
40
+ * @see https://capawesome.io/cloud/live-updates/advanced/privacy/
41
+ */
42
+ location?: 'us' | 'eu';
27
43
  /**
28
44
  * The public key to verify the integrity of the bundle.
29
45
  *
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,wCAAwC","sourcesContent":["/// <reference types=\"@capacitor/cli\" />\n\ndeclare module '@capacitor/cli' {\n export interface PluginsConfig {\n LiveUpdate?: {\n /**\n * The app ID is used to identify the app when using [Capawesome Cloud](https://capawesome.io/cloud).\n *\n * @since 5.0.0\n * @example '4100e356-e851-47fe-9d3c-b411eb325a99'\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 * Whether or not the plugin is enabled.\n *\n * @since 5.0.0\n * @default true\n */\n enabled?: boolean;\n /**\n * The public key to verify the integrity of the bundle.\n *\n * The public key must be a PEM-encoded RSA public key.\n *\n * @since 6.1.0\n * @example '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDodf1SD0OOn6hIlDuKBza0Ed0OqtwyVJwiyjmE9BJaZ7y8ZUfcF+SKmd0l2cDPM45XIg2tAFux5n29uoKyHwSt+6tCi5CJA5Z1/1eZruRRqABLonV77KS3HUtvOgqRLDnKSV89dYZkM++NwmzOPgIF422mvc+VukcVOBfc8/AHQIDAQAB-----END PUBLIC KEY-----'\n */\n publicKey?: string;\n /**\n * The timeout in milliseconds to wait for the app to be ready before resetting to the default bundle.\n *\n * @since 5.0.0\n * @default 10000\n */\n readyTimeout?: number;\n /**\n * Whether or not the app should be reset to the default bundle during an update.\n *\n * @since 5.0.0\n * @default true\n */\n resetOnUpdate?: boolean;\n };\n }\n}\n\nexport interface LiveUpdatePlugin {\n /**\n * Delete a bundle from the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n deleteBundle(options: DeleteBundleOptions): Promise<void>;\n /**\n * Download a bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n downloadBundle(options: DownloadBundleOptions): Promise<void>;\n /**\n * Get the active bundle identifier.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getBundle(): Promise<GetBundleResult>;\n /**\n * Get all available bundle identifiers.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getBundles(): Promise<GetBundlesResult>;\n /**\n * Get the channel of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getChannel(): Promise<GetChannelResult>;\n /**\n * Get the custom identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getCustomId(): Promise<GetCustomIdResult>;\n /**\n * Get the device identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getDeviceId(): Promise<GetDeviceIdResult>;\n /**\n * Get the version code of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getVersionCode(): Promise<GetVersionCodeResult>;\n /**\n * Get the version name of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getVersionName(): Promise<GetVersionNameResult>;\n /**\n * Notify the plugin that the app is ready to use and no rollback is needed.\n *\n * **Attention**: This method should be called as soon as the app is ready to use\n * to prevent the app from being reset to the default bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n ready(): Promise<void>;\n /**\n * Reload the app to apply the new bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n reload(): Promise<void>;\n /**\n * Reset the app to the default bundle.\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n reset(): Promise<void>;\n /**\n * Set the next bundle to use for the app.\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setBundle(options: SetBundleOptions): Promise<void>;\n /**\n * Set the channel of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setChannel(options: SetChannelOptions): Promise<void>;\n /**\n * Set the custom identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setCustomId(options: SetCustomIdOptions): Promise<void>;\n /**\n * Automatically download and set the latest bundle for the app using the [Capawesome Cloud](https://capawesome.io/cloud/).\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n sync(): Promise<SyncResult>;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface DeleteBundleOptions {\n /**\n * The unique identifier of the bundle to delete.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n}\n\nexport interface DownloadBundleOptions {\n /**\n * The unique identifier of the bundle.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n /**\n * The checksum of the bundle to verify the integrity of the ZIP file.\n *\n * Must be a SHA-256 hash in hexadecimal format.\n *\n * @since 6.1.0\n * @example 'aab8e0a312e807c8eaeb923e2bdccd7731c5508869c62ad8e6e43ee881414c84'\n */\n checksum?: string;\n /**\n * The URL of the bundle to download.\n *\n * The bundle must be a ZIP file containing at least a `index.html` file.\n *\n * @since 5.0.0\n * @example 'https://example.com/bundle.zip'\n */\n url: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetBundleResult {\n /**\n * The unique identifier of the active bundle.\n *\n * If `null`, the default bundle is being used.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetBundlesResult {\n /**\n * An array of unique identifiers of all available bundles.\n *\n * @since 5.0.0\n */\n bundleIds: string[];\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetChannelResult {\n /**\n * The channel of the app.\n *\n * If `null`, the app is using the default channel.\n *\n * @since 5.0.0\n * @example 'production'\n */\n channel: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetDeviceIdResult {\n /**\n * The unique identifier of the device.\n *\n * On iOS, [`identifierForVendor`](https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor) is used.\n * The value of this property is the same for apps that come from the same vendor running on the same device.\n *\n * @since 5.0.0\n *\n * @example '50d2a548-80b7-4dad-adc7-97c0e79d8a89'\n */\n deviceId: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetVersionCodeResult {\n /**\n * The version code of the app.\n *\n * On **Android**, this is the `versionCode` from the `android/app/build.gradle` file.\n * On **iOS**, this is the `CFBundleVersion` from the `Info.plist` file.\n *\n * @since 5.0.0\n * @example \"1\"\n */\n versionCode: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetVersionNameResult {\n /**\n * The version name of the app.\n *\n * On **Android**, this is the `versionName` from the `android/app/build.gradle` file.\n * On **iOS**, this is the `CFBundleShortVersionString` from the `Info.plist` file.\n *\n * @since 5.0.0\n * @example \"1.0.0\"\n */\n versionName: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetCustomIdResult {\n /**\n * The custom identifier of the app.\n *\n * If `null`, no custom identifier is set.\n *\n * @since 5.0.0\n * @example '50d2a548-80b7-4dad-adc7-97c0e79d8a89'\n */\n customId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetBundleOptions {\n /**\n * The unique identifier of the bundle to use.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetChannelOptions {\n /**\n * The channel of the app.\n *\n * Set `null` to remove the channel.\n *\n * @since 5.0.0\n */\n channel: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetCustomIdOptions {\n /**\n * The custom identifier of the app.\n *\n * Set `null` to remove the custom identifier.\n *\n * @since 5.0.0\n */\n customId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SyncResult {\n /**\n * The identifier of the next bundle to use.\n *\n * If `null`, the app is up-to-date and no new bundle is available.\n *\n * @since 5.0.0\n */\n nextBundleId: string | null;\n}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,wCAAwC","sourcesContent":["/// <reference types=\"@capacitor/cli\" />\n\ndeclare module '@capacitor/cli' {\n export interface PluginsConfig {\n LiveUpdate?: {\n /**\n * The app ID is used to identify the app when using [Capawesome Cloud](https://capawesome.io/cloud).\n *\n * @since 5.0.0\n * @example '4100e356-e851-47fe-9d3c-b411eb325a99'\n */\n appId?: string;\n /**\n * Whether or not to automatically delete unused bundles.\n *\n * When enabled, the plugin will automatically delete unused bundles after calling `ready()`.\n *\n * @since 5.0.0\n * @default false\n */\n autoDeleteBundles?: boolean;\n /**\n * The default channel of the app.\n *\n * @since 6.3.0\n * @example 'production'\n */\n defaultChannel?: string;\n /**\n * Whether or not the plugin is enabled.\n *\n * @since 5.0.0\n * @default true\n */\n enabled?: boolean;\n /**\n * The location of the server to use when using [Capawesome Cloud](https://capawesome.io/cloud).\n *\n * @since 6.2.0\n * @default 'us'\n * @example 'eu'\n * @see https://capawesome.io/cloud/live-updates/advanced/privacy/\n */\n location?: 'us' | 'eu';\n /**\n * The public key to verify the integrity of the bundle.\n *\n * The public key must be a PEM-encoded RSA public key.\n *\n * @since 6.1.0\n * @example '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDodf1SD0OOn6hIlDuKBza0Ed0OqtwyVJwiyjmE9BJaZ7y8ZUfcF+SKmd0l2cDPM45XIg2tAFux5n29uoKyHwSt+6tCi5CJA5Z1/1eZruRRqABLonV77KS3HUtvOgqRLDnKSV89dYZkM++NwmzOPgIF422mvc+VukcVOBfc8/AHQIDAQAB-----END PUBLIC KEY-----'\n */\n publicKey?: string;\n /**\n * The timeout in milliseconds to wait for the app to be ready before resetting to the default bundle.\n *\n * @since 5.0.0\n * @default 10000\n */\n readyTimeout?: number;\n /**\n * Whether or not the app should be reset to the default bundle during an update.\n *\n * @since 5.0.0\n * @default true\n */\n resetOnUpdate?: boolean;\n };\n }\n}\n\nexport interface LiveUpdatePlugin {\n /**\n * Delete a bundle from the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n deleteBundle(options: DeleteBundleOptions): Promise<void>;\n /**\n * Download a bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n downloadBundle(options: DownloadBundleOptions): Promise<void>;\n /**\n * Get the active bundle identifier.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getBundle(): Promise<GetBundleResult>;\n /**\n * Get all available bundle identifiers.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getBundles(): Promise<GetBundlesResult>;\n /**\n * Get the channel of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getChannel(): Promise<GetChannelResult>;\n /**\n * Get the custom identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getCustomId(): Promise<GetCustomIdResult>;\n /**\n * Get the device identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getDeviceId(): Promise<GetDeviceIdResult>;\n /**\n * Get the version code of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getVersionCode(): Promise<GetVersionCodeResult>;\n /**\n * Get the version name of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n getVersionName(): Promise<GetVersionNameResult>;\n /**\n * Notify the plugin that the app is ready to use and no rollback is needed.\n *\n * **Attention**: This method should be called as soon as the app is ready to use\n * to prevent the app from being reset to the default bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n ready(): Promise<void>;\n /**\n * Reload the app to apply the new bundle.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n reload(): Promise<void>;\n /**\n * Reset the app to the default bundle.\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n reset(): Promise<void>;\n /**\n * Set the next bundle to use for the app.\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setBundle(options: SetBundleOptions): Promise<void>;\n /**\n * Set the channel of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setChannel(options: SetChannelOptions): Promise<void>;\n /**\n * Set the custom identifier of the app.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n setCustomId(options: SetCustomIdOptions): Promise<void>;\n /**\n * Automatically download and set the latest bundle for the app using the [Capawesome Cloud](https://capawesome.io/cloud/).\n *\n * Call `reload()` or restart the app to apply the changes.\n *\n * Only available on Android and iOS.\n *\n * @since 5.0.0\n */\n sync(): Promise<SyncResult>;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface DeleteBundleOptions {\n /**\n * The unique identifier of the bundle to delete.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n}\n\nexport interface DownloadBundleOptions {\n /**\n * The unique identifier of the bundle.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n /**\n * The checksum of the bundle to verify the integrity of the ZIP file.\n *\n * Must be a SHA-256 hash in hexadecimal format.\n *\n * @since 6.1.0\n * @example 'aab8e0a312e807c8eaeb923e2bdccd7731c5508869c62ad8e6e43ee881414c84'\n */\n checksum?: string;\n /**\n * The URL of the bundle to download.\n *\n * The bundle must be a ZIP file containing at least a `index.html` file.\n *\n * @since 5.0.0\n * @example 'https://example.com/bundle.zip'\n */\n url: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetBundleResult {\n /**\n * The unique identifier of the active bundle.\n *\n * If `null`, the default bundle is being used.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetBundlesResult {\n /**\n * An array of unique identifiers of all available bundles.\n *\n * @since 5.0.0\n */\n bundleIds: string[];\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetChannelResult {\n /**\n * The channel of the app.\n *\n * If `null`, the app is using the default channel.\n *\n * @since 5.0.0\n * @example 'production'\n */\n channel: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetDeviceIdResult {\n /**\n * The unique identifier of the device.\n *\n * On iOS, [`identifierForVendor`](https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor) is used.\n * The value of this property is the same for apps that come from the same vendor running on the same device.\n *\n * @since 5.0.0\n *\n * @example '50d2a548-80b7-4dad-adc7-97c0e79d8a89'\n */\n deviceId: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetVersionCodeResult {\n /**\n * The version code of the app.\n *\n * On **Android**, this is the `versionCode` from the `android/app/build.gradle` file.\n * On **iOS**, this is the `CFBundleVersion` from the `Info.plist` file.\n *\n * @since 5.0.0\n * @example \"1\"\n */\n versionCode: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetVersionNameResult {\n /**\n * The version name of the app.\n *\n * On **Android**, this is the `versionName` from the `android/app/build.gradle` file.\n * On **iOS**, this is the `CFBundleShortVersionString` from the `Info.plist` file.\n *\n * @since 5.0.0\n * @example \"1.0.0\"\n */\n versionName: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface GetCustomIdResult {\n /**\n * The custom identifier of the app.\n *\n * If `null`, no custom identifier is set.\n *\n * @since 5.0.0\n * @example '50d2a548-80b7-4dad-adc7-97c0e79d8a89'\n */\n customId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetBundleOptions {\n /**\n * The unique identifier of the bundle to use.\n *\n * @since 5.0.0\n * @example '1.0.0'\n */\n bundleId: string;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetChannelOptions {\n /**\n * The channel of the app.\n *\n * Set `null` to remove the channel.\n *\n * @since 5.0.0\n */\n channel: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SetCustomIdOptions {\n /**\n * The custom identifier of the app.\n *\n * Set `null` to remove the custom identifier.\n *\n * @since 5.0.0\n */\n customId: string | null;\n}\n\n/**\n * @since 5.0.0\n */\nexport interface SyncResult {\n /**\n * The identifier of the next bundle to use.\n *\n * If `null`, the app is up-to-date and no new bundle is available.\n *\n * @since 5.0.0\n */\n nextBundleId: string | null;\n}\n"]}
@@ -89,7 +89,7 @@ import CommonCrypto
89
89
  }
90
90
 
91
91
  @objc public func getChannel(completion: @escaping (Result?, Error?) -> Void) {
92
- let channel = preferences.getChannel()
92
+ let channel = getChannel()
93
93
  let result = GetChannelResult(channel: channel)
94
94
  completion(result, nil)
95
95
  }
@@ -372,13 +372,19 @@ import CommonCrypto
372
372
  parameters["appVersionCode"] = getVersionCode()
373
373
  parameters["appVersionName"] = getVersionName()
374
374
  parameters["bundleId"] = getCurrentBundleId()
375
- parameters["channelName"] = preferences.getChannel()
375
+ parameters["channelName"] = getChannel()
376
376
  parameters["customId"] = preferences.getCustomId()
377
377
  parameters["deviceId"] = getDeviceId()
378
378
  parameters["osVersion"] = UIDevice.current.systemVersion
379
379
  parameters["platform"] = "1"
380
380
  parameters["pluginVersion"] = LiveUpdatePlugin.version
381
- let url = URL(string: "https://api.cloud.capawesome.io/v1/apps/\(config.appId ?? "")/bundles/latest")!
381
+ var host = "api.cloud.capawesome.io"
382
+ if let location = config.location {
383
+ if location == "eu" {
384
+ host = "api.cloud.capawesome.eu"
385
+ }
386
+ }
387
+ let url = URL(string: "https://\(host)/v1/apps/\(config.appId ?? "")/bundles/latest")!
382
388
  AF.request(url, method: .get, parameters: parameters).validate().responseDecodable(of: GetLatestBundleResponse.self) { response in
383
389
  CAPLog.print("[", LiveUpdatePlugin.tag, "] Fetching latest bundle from ", response.request?.url?.absoluteString ?? "")
384
390
  if let error = response.error {
@@ -393,20 +399,6 @@ import CommonCrypto
393
399
  }
394
400
  }
395
401
 
396
- private func getVersionCode() -> String {
397
- guard let appVersionCode = Bundle.main.infoDictionary?["CFBundleVersion"] as? String else {
398
- return ""
399
- }
400
- return appVersionCode
401
- }
402
-
403
- private func getVersionName() -> String {
404
- guard let appVersionName = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
405
- return ""
406
- }
407
- return appVersionName
408
- }
409
-
410
402
  private func getBundleIds() -> [String] {
411
403
  let url = libraryDirectoryUrl.appendingPathComponent(bundlesDirectory)
412
404
  do {
@@ -421,6 +413,17 @@ import CommonCrypto
421
413
  }
422
414
  }
423
415
 
416
+ private func getChannel() -> String? {
417
+ var channel: String?
418
+ if let _ = config.defaultChannel {
419
+ channel = config.defaultChannel
420
+ }
421
+ if let _ = preferences.getChannel() {
422
+ channel = preferences.getChannel()
423
+ }
424
+ return channel
425
+ }
426
+
424
427
  /// - Returns: The current bundle ID (`public` for the built-in bundle) or `nil` if no view controller was found.
425
428
  private func getCurrentBundleId() -> String? {
426
429
  guard let path = getCurrentCapacitorServerPath() else {
@@ -457,6 +460,20 @@ import CommonCrypto
457
460
  return defaultCapacitorServerPath
458
461
  }
459
462
 
463
+ private func getVersionCode() -> String {
464
+ guard let appVersionCode = Bundle.main.infoDictionary?["CFBundleVersion"] as? String else {
465
+ return ""
466
+ }
467
+ return appVersionCode
468
+ }
469
+
470
+ private func getVersionName() -> String {
471
+ guard let appVersionName = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
472
+ return ""
473
+ }
474
+ return appVersionName
475
+ }
476
+
460
477
  private func hasBundle(bundleId: String) -> Bool {
461
478
  let path = buildBundlePathFor(bundleId: bundleId)
462
479
  return FileManager.default.fileExists(atPath: path)
@@ -480,11 +497,28 @@ import CommonCrypto
480
497
  }
481
498
 
482
499
  private func searchIndexHtmlFile(url: URL) -> URL? {
483
- let enumerator = FileManager.default.enumerator(atPath: url.path)
484
- while let element = enumerator?.nextObject() as? String {
485
- if element.hasSuffix("index.html") {
486
- return url.appendingPathComponent(element)
500
+ do {
501
+ let directoryContents = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: [])
502
+ if directoryContents.isEmpty {
503
+ return nil
504
+ }
505
+ let fileNames = directoryContents.map { $0.lastPathComponent }
506
+ if fileNames.contains("index.html") {
507
+ return url.appendingPathComponent("index.html")
508
+ } else {
509
+ for fileUrl in directoryContents {
510
+ var isDirectory: ObjCBool = false
511
+ if FileManager.default.fileExists(atPath: fileUrl.path, isDirectory: &isDirectory) {
512
+ if isDirectory.boolValue {
513
+ if let indexHtmlFile = searchIndexHtmlFile(url: fileUrl) {
514
+ return indexHtmlFile
515
+ }
516
+ }
517
+ }
518
+ }
487
519
  }
520
+ } catch {
521
+ CAPLog.print("[", LiveUpdatePlugin.tag, "] ", "Failed to search index.html file: \(error.localizedDescription)")
488
522
  }
489
523
  return nil
490
524
  }
@@ -1,7 +1,9 @@
1
1
  public struct LiveUpdateConfig {
2
2
  var appId: String?
3
3
  var autoDeleteBundles = false
4
+ var defaultChannel: String?
4
5
  var enabled = true
6
+ var location: String?
5
7
  var publicKey: String?
6
8
  var readyTimeout = 10000
7
9
  var resetOnUpdate = true
@@ -8,7 +8,7 @@ import Capacitor
8
8
  @objc(LiveUpdatePlugin)
9
9
  public class LiveUpdatePlugin: CAPPlugin {
10
10
  public static let tag = "LiveUpdate"
11
- public static let version = "6.1.0"
11
+ public static let version = "6.3.0"
12
12
  public static let userDefaultsPrefix = "CapawesomeLiveUpdate" // DO NOT CHANGE
13
13
 
14
14
  private var config: LiveUpdateConfig?
@@ -239,7 +239,9 @@ public class LiveUpdatePlugin: CAPPlugin {
239
239
 
240
240
  config.appId = getConfig().getString("appId", config.appId)
241
241
  config.autoDeleteBundles = getConfig().getBoolean("autoDeleteBundles", config.autoDeleteBundles)
242
+ config.defaultChannel = getConfig().getString("defaultChannel", config.defaultChannel)
242
243
  config.enabled = getConfig().getBoolean("enabled", config.enabled)
244
+ config.location = getConfig().getString("location", config.location)
243
245
  config.publicKey = getConfig().getString("publicKey", config.publicKey)
244
246
  config.readyTimeout = getConfig().getInt("readyTimeout", config.readyTimeout)
245
247
  config.resetOnUpdate = getConfig().getBoolean("resetOnUpdate", config.resetOnUpdate)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capawesome/capacitor-live-update",
3
- "version": "6.1.0",
3
+ "version": "6.2.0-dev.4ebc29c.1723183892",
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",