@capgo/capacitor-updater 5.35.0 → 5.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,7 @@ Pod::Spec.new do |s|
11
11
  s.author = package['author']
12
12
  s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
13
13
  s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
- s.ios.deployment_target = '14.0'
14
+ s.ios.deployment_target = '13.0'
15
15
  s.dependency 'Capacitor'
16
16
  s.dependency 'SSZipArchive', '2.4.3'
17
17
  s.dependency 'Alamofire', '5.10.2'
package/Package.swift CHANGED
@@ -3,7 +3,7 @@ import PackageDescription
3
3
 
4
4
  let package = Package(
5
5
  name: "CapgoCapacitorUpdater",
6
- platforms: [.iOS(.v14)],
6
+ platforms: [.iOS(.v13)],
7
7
  products: [
8
8
  .library(
9
9
  name: "CapgoCapacitorUpdater",
package/README.md CHANGED
@@ -66,6 +66,8 @@ The most complete [documentation here](https://capgo.app/docs/).
66
66
  ## Community
67
67
  Join the [discord](https://discord.gg/VnYRvBfgA6) to get help.
68
68
 
69
+ ## Migration to v8
70
+
69
71
  ## Migration to v7.34
70
72
 
71
73
  - **Channel storage change**: `setChannel()` now stores channel assignments locally on the device instead of in the cloud. This provides better offline support and reduces backend load.
@@ -76,13 +78,15 @@ Join the [discord](https://discord.gg/VnYRvBfgA6) to get help.
76
78
 
77
79
  ## Migration to v7
78
80
 
79
- - `privateKey` is not available anymore, it was used for the old encryption method. to migrate follow this guide : [https://capgo.app/docs/plugin/cloud-mode/getting-started/](https://capgo.app/docs/cli/migrations/encryption/)
80
- - To capacitor v7 : [https://capacitorjs.com/docs/updating/7-0](https://capacitorjs.com/docs/updating/7-0)
81
+ The min version of IOS is now 15.5 instead of 15 as Capacitor 8 requirement.
82
+ This is due to bump of ZipArchive to latest, a key dependency of this project is the zlib library. zlib before version 1.2.12 allows memory corruption when deflating (i.e., when compressing) if the input has many distant matches according to [CVE-2018-25032](https://nvd.nist.gov/vuln/detail/cve-2018-25032).
83
+ zlib is a native library so we need to bump the minimum iOS version to 15.5 as ZipArchive did the same in their latest versions.
81
84
 
82
85
  ## Compatibility
83
86
 
84
87
  | Plugin version | Capacitor compatibility | Maintained |
85
88
  | -------------- | ----------------------- | ----------------- |
89
+ | v8.\*.\* | v8.\*.\* | Beta |
86
90
  | v7.\*.\* | v7.\*.\* | ✅ |
87
91
  | v6.\*.\* | v6.\*.\* | ✅ |
88
92
  | v5.\*.\* | v5.\*.\* | ⚠️ Deprecated |
@@ -256,7 +260,7 @@ CapacitorUpdater can be configured with these options:
256
260
  | **`autoDeleteFailed`** | <code>boolean</code> | Configure whether the plugin should use automatically delete failed bundles. Only available for Android and iOS. | <code>true</code> | |
257
261
  | **`autoDeletePrevious`** | <code>boolean</code> | Configure whether the plugin should use automatically delete previous bundles after a successful update. Only available for Android and iOS. | <code>true</code> | |
258
262
  | **`autoUpdate`** | <code>boolean</code> | Configure whether the plugin should use Auto Update via an update server. Only available for Android and iOS. | <code>true</code> | |
259
- | **`resetWhenUpdate`** | <code>boolean</code> | Automatically delete previous downloaded bundles when a newer native app bundle is installed to the device. Only available for Android and iOS. | <code>true</code> | |
263
+ | **`resetWhenUpdate`** | <code>boolean</code> | Automatically delete previous downloaded bundles when a newer native app bundle is installed to the device. Setting this to false can broke the auto update flow if the user download from the store a native app bundle that is older than the current downloaded bundle. Upload will be prevented by channel setting downgrade_under_native. Only available for Android and iOS. | <code>true</code> | |
260
264
  | **`updateUrl`** | <code>string</code> | Configure the URL / endpoint to which update checks are sent. Only available for Android and iOS. | <code>https://plugin.capgo.app/updates</code> | |
261
265
  | **`channelUrl`** | <code>string</code> | Configure the URL / endpoint for channel operations. Only available for Android and iOS. | <code>https://plugin.capgo.app/channel_self</code> | |
262
266
  | **`statsUrl`** | <code>string</code> | Configure the URL / endpoint to which update statistics are sent. Only available for Android and iOS. Set to "" to disable stats reporting. | <code>https://plugin.capgo.app/stats</code> | |
@@ -294,7 +298,7 @@ In `capacitor.config.json`:
294
298
  {
295
299
  "plugins": {
296
300
  "CapacitorUpdater": {
297
- "appReadyTimeout": 1000 // (1 second),
301
+ "appReadyTimeout": 1000 // (1 second, minimum 1000),
298
302
  "responseTimeout": 10 // (10 second),
299
303
  "autoDeleteFailed": false,
300
304
  "autoDeletePrevious": false,
@@ -343,7 +347,7 @@ import { CapacitorConfig } from '@capacitor/cli';
343
347
  const config: CapacitorConfig = {
344
348
  plugins: {
345
349
  CapacitorUpdater: {
346
- appReadyTimeout: 1000 // (1 second),
350
+ appReadyTimeout: 1000 // (1 second, minimum 1000),
347
351
  responseTimeout: 10 // (10 second),
348
352
  autoDeleteFailed: false,
349
353
  autoDeletePrevious: false,
@@ -435,8 +439,15 @@ export default config;
435
439
  * [`isShakeMenuEnabled()`](#isshakemenuenabled)
436
440
  * [`getAppId()`](#getappid)
437
441
  * [`setAppId(...)`](#setappid)
442
+ * [`getAppUpdateInfo(...)`](#getappupdateinfo)
443
+ * [`openAppStore(...)`](#openappstore)
444
+ * [`performImmediateUpdate()`](#performimmediateupdate)
445
+ * [`startFlexibleUpdate()`](#startflexibleupdate)
446
+ * [`completeFlexibleUpdate()`](#completeflexibleupdate)
447
+ * [`addListener('onFlexibleUpdateStateChange', ...)`](#addlisteneronflexibleupdatestatechange-)
438
448
  * [Interfaces](#interfaces)
439
449
  * [Type Aliases](#type-aliases)
450
+ * [Enums](#enums)
440
451
 
441
452
  </docgen-index>
442
453
 
@@ -1638,6 +1649,201 @@ app IDs, or multi-tenant configurations).
1638
1649
  --------------------
1639
1650
 
1640
1651
 
1652
+ #### getAppUpdateInfo(...)
1653
+
1654
+ ```typescript
1655
+ getAppUpdateInfo(options?: GetAppUpdateInfoOptions | undefined) => Promise<AppUpdateInfo>
1656
+ ```
1657
+
1658
+ Get information about the app's availability in the App Store or Play Store.
1659
+
1660
+ This method checks the native app stores to see if a newer version of the app
1661
+ is available for download. This is different from Capgo's OTA updates - this
1662
+ checks for native app updates that require going through the app stores.
1663
+
1664
+ **Platform differences:**
1665
+ - **Android**: Uses Play Store's In-App Updates API for accurate update information
1666
+ - **iOS**: Queries the App Store lookup API (requires country code for accurate results)
1667
+
1668
+ **Returns information about:**
1669
+ - Current installed version
1670
+ - Available version in the store (if any)
1671
+ - Whether an update is available
1672
+ - Update priority (Android only)
1673
+ - Whether immediate/flexible updates are allowed (Android only)
1674
+
1675
+ Use this to:
1676
+ - Check if users need to update from the app store
1677
+ - Show "Update Available" prompts for native updates
1678
+ - Implement version gating (require minimum native version)
1679
+ - Combine with Capgo OTA updates for a complete update strategy
1680
+
1681
+ | Param | Type | Description |
1682
+ | ------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
1683
+ | **`options`** | <code><a href="#getappupdateinfooptions">GetAppUpdateInfoOptions</a></code> | Optional {@link <a href="#getappupdateinfooptions">GetAppUpdateInfoOptions</a>} with country code for iOS. |
1684
+
1685
+ **Returns:** <code>Promise&lt;<a href="#appupdateinfo">AppUpdateInfo</a>&gt;</code>
1686
+
1687
+ **Since:** 8.0.0
1688
+
1689
+ --------------------
1690
+
1691
+
1692
+ #### openAppStore(...)
1693
+
1694
+ ```typescript
1695
+ openAppStore(options?: OpenAppStoreOptions | undefined) => Promise<void>
1696
+ ```
1697
+
1698
+ Open the app's page in the App Store or Play Store.
1699
+
1700
+ This navigates the user to your app's store listing where they can manually
1701
+ update the app. Use this as a fallback when in-app updates are not available
1702
+ or when the user needs to update on iOS.
1703
+
1704
+ **Platform behavior:**
1705
+ - **Android**: Opens Play Store to the app's page
1706
+ - **iOS**: Opens App Store to the app's page
1707
+
1708
+ **Customization options:**
1709
+ - `appId`: Specify a custom App Store ID (iOS) - useful for opening a different app's page
1710
+ - `packageName`: Specify a custom package name (Android) - useful for opening a different app's page
1711
+
1712
+ | Param | Type | Description |
1713
+ | ------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
1714
+ | **`options`** | <code><a href="#openappstoreoptions">OpenAppStoreOptions</a></code> | Optional {@link <a href="#openappstoreoptions">OpenAppStoreOptions</a>} to customize which app's store page to open. |
1715
+
1716
+ **Since:** 8.0.0
1717
+
1718
+ --------------------
1719
+
1720
+
1721
+ #### performImmediateUpdate()
1722
+
1723
+ ```typescript
1724
+ performImmediateUpdate() => Promise<AppUpdateResult>
1725
+ ```
1726
+
1727
+ Perform an immediate in-app update on Android.
1728
+
1729
+ This triggers Google Play's immediate update flow, which:
1730
+ 1. Shows a full-screen update UI
1731
+ 2. Downloads and installs the update
1732
+ 3. Restarts the app automatically
1733
+
1734
+ The user cannot continue using the app until the update is complete.
1735
+ This is ideal for critical updates that must be installed immediately.
1736
+
1737
+ **Requirements:**
1738
+ - Android only (throws error on iOS)
1739
+ - An update must be available (check with {@link getAppUpdateInfo} first)
1740
+ - The update must allow immediate updates (`immediateUpdateAllowed: true`)
1741
+
1742
+ **User experience:**
1743
+ - Full-screen blocking UI
1744
+ - Progress shown during download
1745
+ - App automatically restarts after installation
1746
+
1747
+ **Returns:** <code>Promise&lt;<a href="#appupdateresult">AppUpdateResult</a>&gt;</code>
1748
+
1749
+ **Since:** 8.0.0
1750
+
1751
+ --------------------
1752
+
1753
+
1754
+ #### startFlexibleUpdate()
1755
+
1756
+ ```typescript
1757
+ startFlexibleUpdate() => Promise<AppUpdateResult>
1758
+ ```
1759
+
1760
+ Start a flexible in-app update on Android.
1761
+
1762
+ This triggers Google Play's flexible update flow, which:
1763
+ 1. Downloads the update in the background
1764
+ 2. Allows the user to continue using the app
1765
+ 3. Notifies when download is complete
1766
+ 4. Requires calling {@link completeFlexibleUpdate} to install
1767
+
1768
+ Monitor the download progress using the `onFlexibleUpdateStateChange` listener.
1769
+
1770
+ **Requirements:**
1771
+ - Android only (throws error on iOS)
1772
+ - An update must be available (check with {@link getAppUpdateInfo} first)
1773
+ - The update must allow flexible updates (`flexibleUpdateAllowed: true`)
1774
+
1775
+ **Typical flow:**
1776
+ 1. Call `startFlexibleUpdate()` to begin download
1777
+ 2. Listen to `onFlexibleUpdateStateChange` for progress
1778
+ 3. When status is `DOWNLOADED`, prompt user to restart
1779
+ 4. Call `completeFlexibleUpdate()` to install and restart
1780
+
1781
+ **Returns:** <code>Promise&lt;<a href="#appupdateresult">AppUpdateResult</a>&gt;</code>
1782
+
1783
+ **Since:** 8.0.0
1784
+
1785
+ --------------------
1786
+
1787
+
1788
+ #### completeFlexibleUpdate()
1789
+
1790
+ ```typescript
1791
+ completeFlexibleUpdate() => Promise<void>
1792
+ ```
1793
+
1794
+ Complete a flexible in-app update on Android.
1795
+
1796
+ After a flexible update has been downloaded (status `DOWNLOADED` in
1797
+ `onFlexibleUpdateStateChange`), call this method to install the update
1798
+ and restart the app.
1799
+
1800
+ **Important:** This will immediately restart the app. Make sure to:
1801
+ - Save any user data before calling
1802
+ - Prompt the user before restarting
1803
+ - Only call when the download status is `DOWNLOADED`
1804
+
1805
+ **Since:** 8.0.0
1806
+
1807
+ --------------------
1808
+
1809
+
1810
+ #### addListener('onFlexibleUpdateStateChange', ...)
1811
+
1812
+ ```typescript
1813
+ addListener(eventName: 'onFlexibleUpdateStateChange', listenerFunc: (state: FlexibleUpdateState) => void) => Promise<PluginListenerHandle>
1814
+ ```
1815
+
1816
+ Listen for flexible update state changes on Android.
1817
+
1818
+ This event fires during the flexible update download process, providing:
1819
+ - Download progress (bytes downloaded / total bytes)
1820
+ - Installation status changes
1821
+
1822
+ **Install status values:**
1823
+ - `UNKNOWN` (0): Unknown status
1824
+ - `PENDING` (1): Download pending
1825
+ - `DOWNLOADING` (2): Download in progress
1826
+ - `INSTALLING` (3): Installing the update
1827
+ - `INSTALLED` (4): Update installed (app restart needed)
1828
+ - `FAILED` (5): Update failed
1829
+ - `CANCELED` (6): Update was canceled
1830
+ - `DOWNLOADED` (11): Download complete, ready to install
1831
+
1832
+ When status is `DOWNLOADED`, you should prompt the user and call
1833
+ {@link completeFlexibleUpdate} to finish the installation.
1834
+
1835
+ | Param | Type |
1836
+ | ------------------ | --------------------------------------------------------------------------------------- |
1837
+ | **`eventName`** | <code>'onFlexibleUpdateStateChange'</code> |
1838
+ | **`listenerFunc`** | <code>(state: <a href="#flexibleupdatestate">FlexibleUpdateState</a>) =&gt; void</code> |
1839
+
1840
+ **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code>
1841
+
1842
+ **Since:** 8.0.0
1843
+
1844
+ --------------------
1845
+
1846
+
1641
1847
  #### Interfaces
1642
1848
 
1643
1849
 
@@ -1974,6 +2180,65 @@ If you don't use backend, you need to provide the URL and version of the bundle.
1974
2180
  | **`appId`** | <code>string</code> |
1975
2181
 
1976
2182
 
2183
+ ##### AppUpdateInfo
2184
+
2185
+ Information about app updates available in the App Store or Play Store.
2186
+
2187
+ | Prop | Type | Description | Since |
2188
+ | --------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- |
2189
+ | **`currentVersionName`** | <code>string</code> | The currently installed version name (e.g., "1.2.3"). | 8.0.0 |
2190
+ | **`availableVersionName`** | <code>string</code> | The version name available in the store, if an update is available. May be undefined if no update information is available. | 8.0.0 |
2191
+ | **`currentVersionCode`** | <code>string</code> | The currently installed version code (Android) or build number (iOS). | 8.0.0 |
2192
+ | **`availableVersionCode`** | <code>string</code> | The version code available in the store (Android only). On iOS, this will be the same as `availableVersionName`. | 8.0.0 |
2193
+ | **`availableVersionReleaseDate`** | <code>string</code> | The release date of the available version (iOS only). Format: ISO 8601 date string. | 8.0.0 |
2194
+ | **`updateAvailability`** | <code><a href="#appupdateavailability">AppUpdateAvailability</a></code> | The current update availability status. | 8.0.0 |
2195
+ | **`updatePriority`** | <code>number</code> | The priority of the update as set by the developer in Play Console (Android only). Values range from 0 (default/lowest) to 5 (highest priority). Use this to decide whether to show an update prompt or force an update. | 8.0.0 |
2196
+ | **`immediateUpdateAllowed`** | <code>boolean</code> | Whether an immediate update is allowed (Android only). If `true`, you can call {@link CapacitorUpdaterPlugin.performImmediateUpdate}. | 8.0.0 |
2197
+ | **`flexibleUpdateAllowed`** | <code>boolean</code> | Whether a flexible update is allowed (Android only). If `true`, you can call {@link CapacitorUpdaterPlugin.startFlexibleUpdate}. | 8.0.0 |
2198
+ | **`clientVersionStalenessDays`** | <code>number</code> | Number of days since the update became available (Android only). Use this to implement "update nagging" - remind users more frequently as the update ages. | 8.0.0 |
2199
+ | **`installStatus`** | <code><a href="#flexibleupdateinstallstatus">FlexibleUpdateInstallStatus</a></code> | The current install status of a flexible update (Android only). | 8.0.0 |
2200
+ | **`minimumOsVersion`** | <code>string</code> | The minimum OS version required for the available update (iOS only). | 8.0.0 |
2201
+
2202
+
2203
+ ##### GetAppUpdateInfoOptions
2204
+
2205
+ Options for {@link CapacitorUpdaterPlugin.getAppUpdateInfo}.
2206
+
2207
+ | Prop | Type | Description | Since |
2208
+ | ------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
2209
+ | **`country`** | <code>string</code> | Two-letter country code (ISO 3166-1 alpha-2) for the App Store lookup. This is required on iOS to get accurate App Store information, as app availability and versions can vary by country. Examples: "US", "GB", "DE", "JP", "FR" On Android, this option is ignored as the Play Store handles region detection automatically. | 8.0.0 |
2210
+
2211
+
2212
+ ##### OpenAppStoreOptions
2213
+
2214
+ Options for {@link CapacitorUpdaterPlugin.openAppStore}.
2215
+
2216
+ | Prop | Type | Description | Since |
2217
+ | ----------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- |
2218
+ | **`packageName`** | <code>string</code> | The Android package name to open in the Play Store. If not specified, uses the current app's package name. Use this to open a different app's store page. Only used on Android. | 8.0.0 |
2219
+ | **`appId`** | <code>string</code> | The iOS App Store ID to open. If not specified, uses the current app's bundle identifier to look up the app. Use this to open a different app's store page or when automatic lookup fails. Only used on iOS. | 8.0.0 |
2220
+
2221
+
2222
+ ##### AppUpdateResult
2223
+
2224
+ Result of an app update operation.
2225
+
2226
+ | Prop | Type | Description | Since |
2227
+ | ---------- | ------------------------------------------------------------------- | ---------------------------------------- | ----- |
2228
+ | **`code`** | <code><a href="#appupdateresultcode">AppUpdateResultCode</a></code> | The result code of the update operation. | 8.0.0 |
2229
+
2230
+
2231
+ ##### FlexibleUpdateState
2232
+
2233
+ State information for flexible update progress (Android only).
2234
+
2235
+ | Prop | Type | Description | Since |
2236
+ | -------------------------- | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ----- |
2237
+ | **`installStatus`** | <code><a href="#flexibleupdateinstallstatus">FlexibleUpdateInstallStatus</a></code> | The current installation status. | 8.0.0 |
2238
+ | **`bytesDownloaded`** | <code>number</code> | Number of bytes downloaded so far. Only available during the `DOWNLOADING` status. | 8.0.0 |
2239
+ | **`totalBytesToDownload`** | <code>number</code> | Total number of bytes to download. Only available during the `DOWNLOADING` status. | 8.0.0 |
2240
+
2241
+
1977
2242
  #### Type Aliases
1978
2243
 
1979
2244
 
@@ -1998,6 +2263,45 @@ Payload emitted by {@link CapacitorUpdaterPlugin.addListener} with `breakingAvai
1998
2263
 
1999
2264
  <code><a href="#majoravailableevent">MajorAvailableEvent</a></code>
2000
2265
 
2266
+
2267
+ #### Enums
2268
+
2269
+
2270
+ ##### AppUpdateAvailability
2271
+
2272
+ | Members | Value | Description |
2273
+ | -------------------------- | -------------- | ------------------------------------------------------------------------------------------ |
2274
+ | **`UNKNOWN`** | <code>0</code> | Update availability is unknown. This typically means the check hasn't completed or failed. |
2275
+ | **`UPDATE_NOT_AVAILABLE`** | <code>1</code> | No update is available. The installed version is the latest. |
2276
+ | **`UPDATE_AVAILABLE`** | <code>2</code> | An update is available for download. |
2277
+ | **`UPDATE_IN_PROGRESS`** | <code>3</code> | An update is currently being downloaded or installed. |
2278
+
2279
+
2280
+ ##### FlexibleUpdateInstallStatus
2281
+
2282
+ | Members | Value | Description |
2283
+ | ----------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------ |
2284
+ | **`UNKNOWN`** | <code>0</code> | Unknown install status. |
2285
+ | **`PENDING`** | <code>1</code> | Download is pending and will start soon. |
2286
+ | **`DOWNLOADING`** | <code>2</code> | Download is in progress. Check `bytesDownloaded` and `totalBytesToDownload` for progress. |
2287
+ | **`INSTALLING`** | <code>3</code> | The update is being installed. |
2288
+ | **`INSTALLED`** | <code>4</code> | The update has been installed. The app needs to be restarted to use the new version. |
2289
+ | **`FAILED`** | <code>5</code> | The update failed to download or install. |
2290
+ | **`CANCELED`** | <code>6</code> | The update was canceled by the user. |
2291
+ | **`DOWNLOADED`** | <code>11</code> | The update has been downloaded and is ready to install. Call {@link CapacitorUpdaterPlugin.completeFlexibleUpdate} to install. |
2292
+
2293
+
2294
+ ##### AppUpdateResultCode
2295
+
2296
+ | Members | Value | Description |
2297
+ | ------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------- |
2298
+ | **`OK`** | <code>0</code> | The update completed successfully. |
2299
+ | **`CANCELED`** | <code>1</code> | The user canceled the update. |
2300
+ | **`FAILED`** | <code>2</code> | The update failed. |
2301
+ | **`NOT_AVAILABLE`** | <code>3</code> | No update is available. |
2302
+ | **`NOT_ALLOWED`** | <code>4</code> | The requested update type is not allowed. For example, trying to perform an immediate update when only flexible is allowed. |
2303
+ | **`INFO_MISSING`** | <code>5</code> | Required information is missing. This can happen if {@link CapacitorUpdaterPlugin.getAppUpdateInfo} wasn't called first. |
2304
+
2001
2305
  </docgen-api>
2002
2306
 
2003
2307
  ### Listen to download events
@@ -57,6 +57,9 @@ dependencies {
57
57
  implementation project(':capacitor-android')
58
58
  implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
59
59
  implementation 'io.github.g00fy2:versioncompare:1.5.0'
60
+ // Play Core library for in-app updates
61
+ implementation 'com.google.android.play:app-update:2.1.0'
62
+ implementation 'com.google.android.play:app-update-ktx:2.1.0'
60
63
  testImplementation "junit:junit:$junitVersion"
61
64
  testImplementation 'org.mockito:mockito-core:5.20.0'
62
65
  testImplementation 'org.json:json:20250517'