react-native-nitro-version-check 1.0.2 → 1.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/README.md +8 -3
- package/android/src/main/java/com/margelo/nitro/nitroversioncheck/HybridVersionCheck.kt +4 -2
- package/ios/HybridVersionCheck.swift +10 -6
- package/lib/index.d.ts +58 -59
- package/lib/index.js +60 -61
- package/lib/specs/Version.nitro.d.ts +2 -2
- package/nitrogen/generated/android/c++/JHybridVersionCheckSpec.cpp +6 -6
- package/nitrogen/generated/android/c++/JHybridVersionCheckSpec.hpp +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroversioncheck/HybridVersionCheckSpec.kt +2 -2
- package/nitrogen/generated/ios/c++/HybridVersionCheckSpecSwift.hpp +4 -4
- package/nitrogen/generated/ios/swift/HybridVersionCheckSpec.swift +2 -2
- package/nitrogen/generated/ios/swift/HybridVersionCheckSpec_cxx.swift +18 -4
- package/nitrogen/generated/shared/c++/HybridVersionCheckSpec.hpp +2 -2
- package/package.json +8 -2
- package/src/__tests__/index.test.ts +283 -0
- package/src/__tests__/semver.test.ts +145 -0
- package/src/index.ts +60 -66
- package/src/specs/Version.nitro.ts +2 -2
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
## Example
|
|
16
16
|
|
|
17
17
|
```ts
|
|
18
|
-
import { VersionCheck
|
|
18
|
+
import { VersionCheck } from 'react-native-nitro-version-check'
|
|
19
19
|
|
|
20
20
|
// Sync — no bridge, no async
|
|
21
21
|
VersionCheck.version // "1.2.0"
|
|
@@ -23,9 +23,12 @@ VersionCheck.buildNumber // "42"
|
|
|
23
23
|
VersionCheck.packageName // "com.example.app"
|
|
24
24
|
VersionCheck.installSource // "appstore" | "testflight" | "playstore" | undefined
|
|
25
25
|
|
|
26
|
+
// Or destructure properties
|
|
27
|
+
const { version, buildNumber, packageName, installSource } = VersionCheck
|
|
28
|
+
|
|
26
29
|
// Check for updates
|
|
27
|
-
if (await needsUpdate()) {
|
|
28
|
-
Linking.openURL(await getStoreUrl())
|
|
30
|
+
if (await VersionCheck.needsUpdate()) {
|
|
31
|
+
Linking.openURL(await VersionCheck.getStoreUrl())
|
|
29
32
|
}
|
|
30
33
|
```
|
|
31
34
|
|
|
@@ -35,6 +38,8 @@ if (await needsUpdate()) {
|
|
|
35
38
|
bun add react-native-nitro-version-check
|
|
36
39
|
```
|
|
37
40
|
|
|
41
|
+
> Check the [full installation guide](https://alshehriali0.github.io/react-native-nitro-version-check/docs/installation) for platform setup and additional dependencies.
|
|
42
|
+
|
|
38
43
|
## Documentation
|
|
39
44
|
|
|
40
45
|
- [**Nitro Version Check** docs 📚](https://alshehriali0.github.io/react-native-nitro-version-check/)
|
|
@@ -36,15 +36,17 @@ class HybridVersionCheck : HybridVersionCheckSpec() {
|
|
|
36
36
|
return java.util.Locale.getDefault().country ?: "unknown"
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
override fun getStoreUrl(): Promise<String> {
|
|
39
|
+
override fun getStoreUrl(countryCode: String?): Promise<String> {
|
|
40
40
|
return Promise.async {
|
|
41
|
+
// Country code is not used on Android Play Store, but parameter is kept for API consistency
|
|
41
42
|
"https://play.google.com/store/apps/details?id=$packageName"
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
override fun getLatestVersion(): Promise<String> {
|
|
46
|
+
override fun getLatestVersion(countryCode: String?): Promise<String> {
|
|
46
47
|
return Promise.async {
|
|
47
48
|
try {
|
|
49
|
+
// Country code is not used on Android Play Store, but parameter is kept for API consistency
|
|
48
50
|
val url = URL("https://play.google.com/store/apps/details?id=$packageName&hl=en")
|
|
49
51
|
val connection = url.openConnection() as HttpURLConnection
|
|
50
52
|
connection.connectTimeout = TIMEOUT_MS
|
|
@@ -30,10 +30,12 @@ class HybridVersionCheck: HybridVersionCheckSpec {
|
|
|
30
30
|
return Locale.current.regionCode ?? "unknown"
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
func getStoreUrl() throws -> Promise<String> {
|
|
34
|
-
return Promise.async {
|
|
33
|
+
func getStoreUrl(countryCode: String? = nil) throws -> Promise<String> {
|
|
34
|
+
return Promise.async { [self] in
|
|
35
35
|
let bundleId = Bundle.main.bundleIdentifier ?? ""
|
|
36
|
-
let
|
|
36
|
+
let country = countryCode ?? (try? self.getCountry()) ?? "US"
|
|
37
|
+
let urlString = "https://itunes.apple.com/\(country.lowercased())/lookup?bundleId=\(bundleId)"
|
|
38
|
+
let url = URL(string: urlString)!
|
|
37
39
|
let (data, _) = try await HybridVersionCheck.session.data(from: url)
|
|
38
40
|
guard let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
|
|
39
41
|
let results = json["results"] as? [[String: Any]],
|
|
@@ -44,10 +46,12 @@ class HybridVersionCheck: HybridVersionCheckSpec {
|
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
func getLatestVersion() throws -> Promise<String> {
|
|
48
|
-
return Promise.async {
|
|
49
|
+
func getLatestVersion(countryCode: String? = nil) throws -> Promise<String> {
|
|
50
|
+
return Promise.async { [self] in
|
|
49
51
|
let bundleId = Bundle.main.bundleIdentifier ?? ""
|
|
50
|
-
let
|
|
52
|
+
let country = countryCode ?? (try? self.getCountry()) ?? "US"
|
|
53
|
+
let urlString = "https://itunes.apple.com/\(country.lowercased())/lookup?bundleId=\(bundleId)"
|
|
54
|
+
let url = URL(string: urlString)!
|
|
51
55
|
let (data, _) = try await HybridVersionCheck.session.data(from: url)
|
|
52
56
|
guard let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
|
|
53
57
|
let results = json["results"] as? [[String: Any]],
|
package/lib/index.d.ts
CHANGED
|
@@ -1,62 +1,11 @@
|
|
|
1
1
|
import type { UpdateLevel } from "./semver";
|
|
2
2
|
import { compareVersions } from "./semver";
|
|
3
|
-
/**
|
|
4
|
-
* Returns the device's current 2-letter ISO country code.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```ts
|
|
8
|
-
* getCountry() // "US"
|
|
9
|
-
* ```
|
|
10
|
-
*/
|
|
11
|
-
export declare const getCountry: () => string;
|
|
12
|
-
/**
|
|
13
|
-
* Returns the store URL for this app.
|
|
14
|
-
*
|
|
15
|
-
* Automatically resolves to the App Store on iOS and Play Store on Android.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```ts
|
|
19
|
-
* const url = await getStoreUrl();
|
|
20
|
-
* Linking.openURL(url);
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
23
|
-
export declare const getStoreUrl: () => Promise<string>;
|
|
24
|
-
/**
|
|
25
|
-
* Fetches the latest version of this app available in the store.
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* ```ts
|
|
29
|
-
* const latest = await getLatestVersion(); // "1.3.0"
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
|
-
export declare const getLatestVersion: () => Promise<string>;
|
|
33
|
-
/**
|
|
34
|
-
* Checks whether an app update is available.
|
|
35
|
-
*
|
|
36
|
-
* Uses semantic version comparison. By default checks for any version
|
|
37
|
-
* increase, but you can filter by granularity:
|
|
38
|
-
*
|
|
39
|
-
* - `"major"` — only returns `true` for major bumps (1.x → 2.x)
|
|
40
|
-
* - `"minor"` — returns `true` for major or minor bumps
|
|
41
|
-
* - `"patch"` — returns `true` for any version increase (default)
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* ```ts
|
|
45
|
-
* if (await needsUpdate()) {
|
|
46
|
-
* const url = await getStoreUrl();
|
|
47
|
-
* Linking.openURL(url);
|
|
48
|
-
* }
|
|
49
|
-
*
|
|
50
|
-
* // Only prompt for major updates
|
|
51
|
-
* const majorUpdate = await needsUpdate({ level: "major" });
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
|
-
export declare const needsUpdate: (options?: {
|
|
55
|
-
level?: UpdateLevel;
|
|
56
|
-
}) => Promise<boolean>;
|
|
57
3
|
/**
|
|
58
4
|
* All version-check APIs in one object.
|
|
59
5
|
*
|
|
6
|
+
* Provides access to app version information, store URLs, and update checking.
|
|
7
|
+
* Sync properties are cached at module init for zero native overhead.
|
|
8
|
+
*
|
|
60
9
|
* @example
|
|
61
10
|
* ```ts
|
|
62
11
|
* VersionCheck.version // "1.2.0"
|
|
@@ -65,6 +14,9 @@ export declare const needsUpdate: (options?: {
|
|
|
65
14
|
* VersionCheck.getCountry() // "US"
|
|
66
15
|
*
|
|
67
16
|
* const url = await VersionCheck.getStoreUrl();
|
|
17
|
+
* if (await VersionCheck.needsUpdate()) {
|
|
18
|
+
* Linking.openURL(url);
|
|
19
|
+
* }
|
|
68
20
|
* ```
|
|
69
21
|
*/
|
|
70
22
|
export declare const VersionCheck: {
|
|
@@ -132,37 +84,84 @@ export declare const VersionCheck: {
|
|
|
132
84
|
/**
|
|
133
85
|
* Returns the App Store (iOS) or Play Store (Android) URL for this app.
|
|
134
86
|
*
|
|
87
|
+
* @param options - Optional configuration
|
|
88
|
+
* @param options.countryCode - 2-letter ISO country code (e.g., "US", "GB")
|
|
89
|
+
* Defaults to the device's current country from `getCountry()`.
|
|
90
|
+
* Only used on iOS; ignored on Android.
|
|
91
|
+
*
|
|
135
92
|
* @example
|
|
136
93
|
* ```ts
|
|
137
94
|
* const url = await VersionCheck.getStoreUrl();
|
|
95
|
+
* const urlUS = await VersionCheck.getStoreUrl({ countryCode: "US" });
|
|
138
96
|
* Linking.openURL(url);
|
|
139
97
|
* ```
|
|
140
98
|
*/
|
|
141
|
-
readonly getStoreUrl: (
|
|
99
|
+
readonly getStoreUrl: (options?: {
|
|
100
|
+
countryCode?: string;
|
|
101
|
+
}) => Promise<string>;
|
|
142
102
|
/**
|
|
143
103
|
* Fetches the latest version of this app available in the store.
|
|
144
104
|
*
|
|
145
105
|
* Queries the iTunes API on iOS and the Play Store on Android.
|
|
106
|
+
* On iOS, uses the device's current country code by default but can be overridden.
|
|
107
|
+
*
|
|
108
|
+
* @param options - Optional configuration
|
|
109
|
+
* @param options.countryCode - 2-letter ISO country code (e.g., "US", "GB")
|
|
110
|
+
* Defaults to the device's current country from `getCountry()`.
|
|
111
|
+
* If the device region changes, the next call will use the new country.
|
|
112
|
+
* Only used on iOS; ignored on Android.
|
|
146
113
|
*
|
|
147
114
|
* @example
|
|
148
115
|
* ```ts
|
|
149
|
-
* const latest = await VersionCheck.getLatestVersion(); //
|
|
116
|
+
* const latest = await VersionCheck.getLatestVersion(); // Uses current device country
|
|
117
|
+
* const latestUS = await VersionCheck.getLatestVersion({ countryCode: "US" });
|
|
118
|
+
* const latestGB = await VersionCheck.getLatestVersion({ countryCode: "GB" });
|
|
150
119
|
* ```
|
|
151
120
|
*/
|
|
152
|
-
readonly getLatestVersion: (
|
|
121
|
+
readonly getLatestVersion: (options?: {
|
|
122
|
+
countryCode?: string;
|
|
123
|
+
}) => Promise<string>;
|
|
153
124
|
/**
|
|
154
125
|
* Checks whether an app update is available by comparing the current
|
|
155
126
|
* version against the latest store version.
|
|
156
127
|
*
|
|
128
|
+
* Uses semantic version comparison. By default checks for any version
|
|
129
|
+
* increase, but you can filter by granularity:
|
|
130
|
+
*
|
|
131
|
+
* - `"major"` — only returns `true` for major bumps (1.x → 2.x)
|
|
132
|
+
* - `"minor"` — returns `true` for major or minor bumps
|
|
133
|
+
* - `"patch"` — returns `true` for any version increase (default)
|
|
134
|
+
*
|
|
157
135
|
* @example
|
|
158
136
|
* ```ts
|
|
159
137
|
* if (await VersionCheck.needsUpdate()) {
|
|
160
138
|
* const url = await VersionCheck.getStoreUrl();
|
|
161
139
|
* Linking.openURL(url);
|
|
162
140
|
* }
|
|
141
|
+
*
|
|
142
|
+
* // Only prompt for major updates
|
|
143
|
+
* const majorUpdate = await VersionCheck.needsUpdate({ level: "major" });
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
readonly needsUpdate: (options?: {
|
|
147
|
+
level?: UpdateLevel;
|
|
148
|
+
}) => Promise<boolean>;
|
|
149
|
+
/**
|
|
150
|
+
* Compares two semantic version strings.
|
|
151
|
+
*
|
|
152
|
+
* @returns -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* VersionCheck.compareVersions('1.0.0', '1.0.1') // -1
|
|
157
|
+
* VersionCheck.compareVersions('2.0.0', '2.0.0') // 0
|
|
158
|
+
* VersionCheck.compareVersions('3.0.0', '2.9.9') // 1
|
|
159
|
+
*
|
|
160
|
+
* if (VersionCheck.compareVersions(currentVersion, minimumVersion) < 0) {
|
|
161
|
+
* // Current version is below minimum — force update
|
|
162
|
+
* }
|
|
163
163
|
* ```
|
|
164
164
|
*/
|
|
165
|
-
readonly
|
|
165
|
+
readonly compareVersions: typeof compareVersions;
|
|
166
166
|
};
|
|
167
|
-
export { compareVersions };
|
|
168
167
|
export type { UpdateLevel };
|
package/lib/index.js
CHANGED
|
@@ -6,64 +6,12 @@ const version = HybridVersionCheck.version;
|
|
|
6
6
|
const buildNumber = HybridVersionCheck.buildNumber;
|
|
7
7
|
const packageName = HybridVersionCheck.packageName;
|
|
8
8
|
const installSource = HybridVersionCheck.installSource;
|
|
9
|
-
/**
|
|
10
|
-
* Returns the device's current 2-letter ISO country code.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```ts
|
|
14
|
-
* getCountry() // "US"
|
|
15
|
-
* ```
|
|
16
|
-
*/
|
|
17
|
-
export const getCountry = () => HybridVersionCheck.getCountry();
|
|
18
|
-
/**
|
|
19
|
-
* Returns the store URL for this app.
|
|
20
|
-
*
|
|
21
|
-
* Automatically resolves to the App Store on iOS and Play Store on Android.
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* ```ts
|
|
25
|
-
* const url = await getStoreUrl();
|
|
26
|
-
* Linking.openURL(url);
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
export const getStoreUrl = () => HybridVersionCheck.getStoreUrl();
|
|
30
|
-
/**
|
|
31
|
-
* Fetches the latest version of this app available in the store.
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```ts
|
|
35
|
-
* const latest = await getLatestVersion(); // "1.3.0"
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
export const getLatestVersion = () => HybridVersionCheck.getLatestVersion();
|
|
39
|
-
/**
|
|
40
|
-
* Checks whether an app update is available.
|
|
41
|
-
*
|
|
42
|
-
* Uses semantic version comparison. By default checks for any version
|
|
43
|
-
* increase, but you can filter by granularity:
|
|
44
|
-
*
|
|
45
|
-
* - `"major"` — only returns `true` for major bumps (1.x → 2.x)
|
|
46
|
-
* - `"minor"` — returns `true` for major or minor bumps
|
|
47
|
-
* - `"patch"` — returns `true` for any version increase (default)
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* ```ts
|
|
51
|
-
* if (await needsUpdate()) {
|
|
52
|
-
* const url = await getStoreUrl();
|
|
53
|
-
* Linking.openURL(url);
|
|
54
|
-
* }
|
|
55
|
-
*
|
|
56
|
-
* // Only prompt for major updates
|
|
57
|
-
* const majorUpdate = await needsUpdate({ level: "major" });
|
|
58
|
-
* ```
|
|
59
|
-
*/
|
|
60
|
-
export const needsUpdate = async (options) => {
|
|
61
|
-
const latest = await HybridVersionCheck.getLatestVersion();
|
|
62
|
-
return isNewerVersion(version, latest, options?.level ?? "patch");
|
|
63
|
-
};
|
|
64
9
|
/**
|
|
65
10
|
* All version-check APIs in one object.
|
|
66
11
|
*
|
|
12
|
+
* Provides access to app version information, store URLs, and update checking.
|
|
13
|
+
* Sync properties are cached at module init for zero native overhead.
|
|
14
|
+
*
|
|
67
15
|
* @example
|
|
68
16
|
* ```ts
|
|
69
17
|
* VersionCheck.version // "1.2.0"
|
|
@@ -72,6 +20,9 @@ export const needsUpdate = async (options) => {
|
|
|
72
20
|
* VersionCheck.getCountry() // "US"
|
|
73
21
|
*
|
|
74
22
|
* const url = await VersionCheck.getStoreUrl();
|
|
23
|
+
* if (await VersionCheck.needsUpdate()) {
|
|
24
|
+
* Linking.openURL(url);
|
|
25
|
+
* }
|
|
75
26
|
* ```
|
|
76
27
|
*/
|
|
77
28
|
export const VersionCheck = {
|
|
@@ -135,40 +86,88 @@ export const VersionCheck = {
|
|
|
135
86
|
* VersionCheck.getCountry() // "US"
|
|
136
87
|
* ```
|
|
137
88
|
*/
|
|
138
|
-
getCountry,
|
|
89
|
+
getCountry: () => HybridVersionCheck.getCountry(),
|
|
139
90
|
/**
|
|
140
91
|
* Returns the App Store (iOS) or Play Store (Android) URL for this app.
|
|
141
92
|
*
|
|
93
|
+
* @param options - Optional configuration
|
|
94
|
+
* @param options.countryCode - 2-letter ISO country code (e.g., "US", "GB")
|
|
95
|
+
* Defaults to the device's current country from `getCountry()`.
|
|
96
|
+
* Only used on iOS; ignored on Android.
|
|
97
|
+
*
|
|
142
98
|
* @example
|
|
143
99
|
* ```ts
|
|
144
100
|
* const url = await VersionCheck.getStoreUrl();
|
|
101
|
+
* const urlUS = await VersionCheck.getStoreUrl({ countryCode: "US" });
|
|
145
102
|
* Linking.openURL(url);
|
|
146
103
|
* ```
|
|
147
104
|
*/
|
|
148
|
-
getStoreUrl
|
|
105
|
+
getStoreUrl: async (options) => {
|
|
106
|
+
return HybridVersionCheck.getStoreUrl(options?.countryCode);
|
|
107
|
+
},
|
|
149
108
|
/**
|
|
150
109
|
* Fetches the latest version of this app available in the store.
|
|
151
110
|
*
|
|
152
111
|
* Queries the iTunes API on iOS and the Play Store on Android.
|
|
112
|
+
* On iOS, uses the device's current country code by default but can be overridden.
|
|
113
|
+
*
|
|
114
|
+
* @param options - Optional configuration
|
|
115
|
+
* @param options.countryCode - 2-letter ISO country code (e.g., "US", "GB")
|
|
116
|
+
* Defaults to the device's current country from `getCountry()`.
|
|
117
|
+
* If the device region changes, the next call will use the new country.
|
|
118
|
+
* Only used on iOS; ignored on Android.
|
|
153
119
|
*
|
|
154
120
|
* @example
|
|
155
121
|
* ```ts
|
|
156
|
-
* const latest = await VersionCheck.getLatestVersion(); //
|
|
122
|
+
* const latest = await VersionCheck.getLatestVersion(); // Uses current device country
|
|
123
|
+
* const latestUS = await VersionCheck.getLatestVersion({ countryCode: "US" });
|
|
124
|
+
* const latestGB = await VersionCheck.getLatestVersion({ countryCode: "GB" });
|
|
157
125
|
* ```
|
|
158
126
|
*/
|
|
159
|
-
getLatestVersion
|
|
127
|
+
getLatestVersion: async (options) => {
|
|
128
|
+
return HybridVersionCheck.getLatestVersion(options?.countryCode);
|
|
129
|
+
},
|
|
160
130
|
/**
|
|
161
131
|
* Checks whether an app update is available by comparing the current
|
|
162
132
|
* version against the latest store version.
|
|
163
133
|
*
|
|
134
|
+
* Uses semantic version comparison. By default checks for any version
|
|
135
|
+
* increase, but you can filter by granularity:
|
|
136
|
+
*
|
|
137
|
+
* - `"major"` — only returns `true` for major bumps (1.x → 2.x)
|
|
138
|
+
* - `"minor"` — returns `true` for major or minor bumps
|
|
139
|
+
* - `"patch"` — returns `true` for any version increase (default)
|
|
140
|
+
*
|
|
164
141
|
* @example
|
|
165
142
|
* ```ts
|
|
166
143
|
* if (await VersionCheck.needsUpdate()) {
|
|
167
144
|
* const url = await VersionCheck.getStoreUrl();
|
|
168
145
|
* Linking.openURL(url);
|
|
169
146
|
* }
|
|
147
|
+
*
|
|
148
|
+
* // Only prompt for major updates
|
|
149
|
+
* const majorUpdate = await VersionCheck.needsUpdate({ level: "major" });
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
needsUpdate: async (options) => {
|
|
153
|
+
const latest = await HybridVersionCheck.getLatestVersion();
|
|
154
|
+
return isNewerVersion(version, latest, options?.level ?? "patch");
|
|
155
|
+
},
|
|
156
|
+
/**
|
|
157
|
+
* Compares two semantic version strings.
|
|
158
|
+
*
|
|
159
|
+
* @returns -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```ts
|
|
163
|
+
* VersionCheck.compareVersions('1.0.0', '1.0.1') // -1
|
|
164
|
+
* VersionCheck.compareVersions('2.0.0', '2.0.0') // 0
|
|
165
|
+
* VersionCheck.compareVersions('3.0.0', '2.9.9') // 1
|
|
166
|
+
*
|
|
167
|
+
* if (VersionCheck.compareVersions(currentVersion, minimumVersion) < 0) {
|
|
168
|
+
* // Current version is below minimum — force update
|
|
169
|
+
* }
|
|
170
170
|
* ```
|
|
171
171
|
*/
|
|
172
|
-
|
|
172
|
+
compareVersions,
|
|
173
173
|
};
|
|
174
|
-
export { compareVersions };
|
|
@@ -8,7 +8,7 @@ export interface VersionCheck extends HybridObject<{
|
|
|
8
8
|
readonly packageName: string;
|
|
9
9
|
readonly installSource: string | undefined;
|
|
10
10
|
getCountry(): string;
|
|
11
|
-
getStoreUrl(): Promise<string>;
|
|
12
|
-
getLatestVersion(): Promise<string>;
|
|
11
|
+
getStoreUrl(countryCode?: string): Promise<string>;
|
|
12
|
+
getLatestVersion(countryCode?: string): Promise<string>;
|
|
13
13
|
needsUpdate(): Promise<boolean>;
|
|
14
14
|
}
|
|
@@ -77,9 +77,9 @@ namespace margelo::nitro::nitroversioncheck {
|
|
|
77
77
|
auto __result = method(_javaPart);
|
|
78
78
|
return __result->toStdString();
|
|
79
79
|
}
|
|
80
|
-
std::shared_ptr<Promise<std::string>> JHybridVersionCheckSpec::getStoreUrl() {
|
|
81
|
-
static const auto method = javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>()>("getStoreUrl");
|
|
82
|
-
auto __result = method(_javaPart);
|
|
80
|
+
std::shared_ptr<Promise<std::string>> JHybridVersionCheckSpec::getStoreUrl(const std::optional<std::string>& countryCode) {
|
|
81
|
+
static const auto method = javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JString> /* countryCode */)>("getStoreUrl");
|
|
82
|
+
auto __result = method(_javaPart, countryCode.has_value() ? jni::make_jstring(countryCode.value()) : nullptr);
|
|
83
83
|
return [&]() {
|
|
84
84
|
auto __promise = Promise<std::string>::create();
|
|
85
85
|
__result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& __boxedResult) {
|
|
@@ -93,9 +93,9 @@ namespace margelo::nitro::nitroversioncheck {
|
|
|
93
93
|
return __promise;
|
|
94
94
|
}();
|
|
95
95
|
}
|
|
96
|
-
std::shared_ptr<Promise<std::string>> JHybridVersionCheckSpec::getLatestVersion() {
|
|
97
|
-
static const auto method = javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>()>("getLatestVersion");
|
|
98
|
-
auto __result = method(_javaPart);
|
|
96
|
+
std::shared_ptr<Promise<std::string>> JHybridVersionCheckSpec::getLatestVersion(const std::optional<std::string>& countryCode) {
|
|
97
|
+
static const auto method = javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JString> /* countryCode */)>("getLatestVersion");
|
|
98
|
+
auto __result = method(_javaPart, countryCode.has_value() ? jni::make_jstring(countryCode.value()) : nullptr);
|
|
99
99
|
return [&]() {
|
|
100
100
|
auto __promise = Promise<std::string>::create();
|
|
101
101
|
__result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& __boxedResult) {
|
|
@@ -59,8 +59,8 @@ namespace margelo::nitro::nitroversioncheck {
|
|
|
59
59
|
public:
|
|
60
60
|
// Methods
|
|
61
61
|
std::string getCountry() override;
|
|
62
|
-
std::shared_ptr<Promise<std::string>> getStoreUrl() override;
|
|
63
|
-
std::shared_ptr<Promise<std::string>> getLatestVersion() override;
|
|
62
|
+
std::shared_ptr<Promise<std::string>> getStoreUrl(const std::optional<std::string>& countryCode) override;
|
|
63
|
+
std::shared_ptr<Promise<std::string>> getLatestVersion(const std::optional<std::string>& countryCode) override;
|
|
64
64
|
std::shared_ptr<Promise<bool>> needsUpdate() override;
|
|
65
65
|
|
|
66
66
|
private:
|
|
@@ -66,11 +66,11 @@ abstract class HybridVersionCheckSpec: HybridObject() {
|
|
|
66
66
|
|
|
67
67
|
@DoNotStrip
|
|
68
68
|
@Keep
|
|
69
|
-
abstract fun getStoreUrl(): Promise<String>
|
|
69
|
+
abstract fun getStoreUrl(countryCode: String?): Promise<String>
|
|
70
70
|
|
|
71
71
|
@DoNotStrip
|
|
72
72
|
@Keep
|
|
73
|
-
abstract fun getLatestVersion(): Promise<String>
|
|
73
|
+
abstract fun getLatestVersion(countryCode: String?): Promise<String>
|
|
74
74
|
|
|
75
75
|
@DoNotStrip
|
|
76
76
|
@Keep
|
|
@@ -91,16 +91,16 @@ namespace margelo::nitro::nitroversioncheck {
|
|
|
91
91
|
auto __value = std::move(__result.value());
|
|
92
92
|
return __value;
|
|
93
93
|
}
|
|
94
|
-
inline std::shared_ptr<Promise<std::string>> getStoreUrl() override {
|
|
95
|
-
auto __result = _swiftPart.getStoreUrl();
|
|
94
|
+
inline std::shared_ptr<Promise<std::string>> getStoreUrl(const std::optional<std::string>& countryCode) override {
|
|
95
|
+
auto __result = _swiftPart.getStoreUrl(countryCode);
|
|
96
96
|
if (__result.hasError()) [[unlikely]] {
|
|
97
97
|
std::rethrow_exception(__result.error());
|
|
98
98
|
}
|
|
99
99
|
auto __value = std::move(__result.value());
|
|
100
100
|
return __value;
|
|
101
101
|
}
|
|
102
|
-
inline std::shared_ptr<Promise<std::string>> getLatestVersion() override {
|
|
103
|
-
auto __result = _swiftPart.getLatestVersion();
|
|
102
|
+
inline std::shared_ptr<Promise<std::string>> getLatestVersion(const std::optional<std::string>& countryCode) override {
|
|
103
|
+
auto __result = _swiftPart.getLatestVersion(countryCode);
|
|
104
104
|
if (__result.hasError()) [[unlikely]] {
|
|
105
105
|
std::rethrow_exception(__result.error());
|
|
106
106
|
}
|
|
@@ -17,8 +17,8 @@ public protocol HybridVersionCheckSpec_protocol: HybridObject {
|
|
|
17
17
|
|
|
18
18
|
// Methods
|
|
19
19
|
func getCountry() throws -> String
|
|
20
|
-
func getStoreUrl() throws -> Promise<String>
|
|
21
|
-
func getLatestVersion() throws -> Promise<String>
|
|
20
|
+
func getStoreUrl(countryCode: String?) throws -> Promise<String>
|
|
21
|
+
func getLatestVersion(countryCode: String?) throws -> Promise<String>
|
|
22
22
|
func needsUpdate() throws -> Promise<Bool>
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -169,9 +169,16 @@ open class HybridVersionCheckSpec_cxx {
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
@inline(__always)
|
|
172
|
-
public final func getStoreUrl() -> bridge.Result_std__shared_ptr_Promise_std__string___ {
|
|
172
|
+
public final func getStoreUrl(countryCode: bridge.std__optional_std__string_) -> bridge.Result_std__shared_ptr_Promise_std__string___ {
|
|
173
173
|
do {
|
|
174
|
-
let __result = try self.__implementation.getStoreUrl()
|
|
174
|
+
let __result = try self.__implementation.getStoreUrl(countryCode: { () -> String? in
|
|
175
|
+
if bridge.has_value_std__optional_std__string_(countryCode) {
|
|
176
|
+
let __unwrapped = bridge.get_std__optional_std__string_(countryCode)
|
|
177
|
+
return String(__unwrapped)
|
|
178
|
+
} else {
|
|
179
|
+
return nil
|
|
180
|
+
}
|
|
181
|
+
}())
|
|
175
182
|
let __resultCpp = { () -> bridge.std__shared_ptr_Promise_std__string__ in
|
|
176
183
|
let __promise = bridge.create_std__shared_ptr_Promise_std__string__()
|
|
177
184
|
let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_std__string__(__promise)
|
|
@@ -188,9 +195,16 @@ open class HybridVersionCheckSpec_cxx {
|
|
|
188
195
|
}
|
|
189
196
|
|
|
190
197
|
@inline(__always)
|
|
191
|
-
public final func getLatestVersion() -> bridge.Result_std__shared_ptr_Promise_std__string___ {
|
|
198
|
+
public final func getLatestVersion(countryCode: bridge.std__optional_std__string_) -> bridge.Result_std__shared_ptr_Promise_std__string___ {
|
|
192
199
|
do {
|
|
193
|
-
let __result = try self.__implementation.getLatestVersion()
|
|
200
|
+
let __result = try self.__implementation.getLatestVersion(countryCode: { () -> String? in
|
|
201
|
+
if bridge.has_value_std__optional_std__string_(countryCode) {
|
|
202
|
+
let __unwrapped = bridge.get_std__optional_std__string_(countryCode)
|
|
203
|
+
return String(__unwrapped)
|
|
204
|
+
} else {
|
|
205
|
+
return nil
|
|
206
|
+
}
|
|
207
|
+
}())
|
|
194
208
|
let __resultCpp = { () -> bridge.std__shared_ptr_Promise_std__string__ in
|
|
195
209
|
let __promise = bridge.create_std__shared_ptr_Promise_std__string__()
|
|
196
210
|
let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_std__string__(__promise)
|
|
@@ -54,8 +54,8 @@ namespace margelo::nitro::nitroversioncheck {
|
|
|
54
54
|
public:
|
|
55
55
|
// Methods
|
|
56
56
|
virtual std::string getCountry() = 0;
|
|
57
|
-
virtual std::shared_ptr<Promise<std::string>> getStoreUrl() = 0;
|
|
58
|
-
virtual std::shared_ptr<Promise<std::string>> getLatestVersion() = 0;
|
|
57
|
+
virtual std::shared_ptr<Promise<std::string>> getStoreUrl(const std::optional<std::string>& countryCode) = 0;
|
|
58
|
+
virtual std::shared_ptr<Promise<std::string>> getLatestVersion(const std::optional<std::string>& countryCode) = 0;
|
|
59
59
|
virtual std::shared_ptr<Promise<bool>> needsUpdate() = 0;
|
|
60
60
|
|
|
61
61
|
protected:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-version-check",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "A lightweight, fast version-checking library for React Native, powered by Nitro Modules",
|
|
5
5
|
"main": "lib/index",
|
|
6
6
|
"module": "lib/index",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"source": "src/index",
|
|
10
10
|
"files": [
|
|
11
11
|
"src",
|
|
12
|
-
"react-native.config.js",
|
|
13
12
|
"lib",
|
|
13
|
+
"react-native.config.js",
|
|
14
14
|
"nitrogen",
|
|
15
15
|
"android/build.gradle",
|
|
16
16
|
"android/gradle.properties",
|
|
@@ -34,6 +34,9 @@
|
|
|
34
34
|
"release": "release-it",
|
|
35
35
|
"build": "tsc",
|
|
36
36
|
"typecheck": "tsc --noEmit",
|
|
37
|
+
"test": "jest",
|
|
38
|
+
"test:watch": "jest --watch",
|
|
39
|
+
"test:coverage": "jest --coverage",
|
|
37
40
|
"lint": "biome check .",
|
|
38
41
|
"lint:fix": "biome check --write .",
|
|
39
42
|
"format": "biome format --write .",
|
|
@@ -149,12 +152,15 @@
|
|
|
149
152
|
},
|
|
150
153
|
"devDependencies": {
|
|
151
154
|
"@release-it/conventional-changelog": "^10.0.5",
|
|
155
|
+
"@types/jest": "^29.5.12",
|
|
152
156
|
"@types/react": "^19.1.03",
|
|
157
|
+
"jest": "^29.7.0",
|
|
153
158
|
"nitrogen": "*",
|
|
154
159
|
"react": "19.2.0",
|
|
155
160
|
"react-native": "0.83.0",
|
|
156
161
|
"react-native-nitro-modules": "*",
|
|
157
162
|
"release-it": "^19.2.4",
|
|
163
|
+
"ts-jest": "^29.1.2",
|
|
158
164
|
"typescript": "^5.8.3"
|
|
159
165
|
},
|
|
160
166
|
"peerDependencies": {
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { type UpdateLevel, VersionCheck } from "../index";
|
|
2
|
+
|
|
3
|
+
// Mock the NitroModules
|
|
4
|
+
jest.mock("react-native-nitro-modules", () => ({
|
|
5
|
+
NitroModules: {
|
|
6
|
+
createHybridObject: jest.fn(() => ({
|
|
7
|
+
version: "1.2.0",
|
|
8
|
+
buildNumber: "42",
|
|
9
|
+
packageName: "com.example.app",
|
|
10
|
+
installSource: "appstore",
|
|
11
|
+
getCountry: jest.fn(() => "US"),
|
|
12
|
+
getStoreUrl: jest.fn(() => Promise.resolve("https://apps.apple.com/app/example")),
|
|
13
|
+
getLatestVersion: jest.fn(() => Promise.resolve("1.3.0")),
|
|
14
|
+
})),
|
|
15
|
+
},
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
describe("VersionCheck API", () => {
|
|
19
|
+
describe("structure", () => {
|
|
20
|
+
it("should export VersionCheck object with all required properties", () => {
|
|
21
|
+
expect(VersionCheck).toBeDefined();
|
|
22
|
+
expect(typeof VersionCheck).toBe("object");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should have sync properties", () => {
|
|
26
|
+
expect(VersionCheck.version).toBeDefined();
|
|
27
|
+
expect(typeof VersionCheck.version).toBe("string");
|
|
28
|
+
|
|
29
|
+
expect(VersionCheck.buildNumber).toBeDefined();
|
|
30
|
+
expect(typeof VersionCheck.buildNumber).toBe("string");
|
|
31
|
+
|
|
32
|
+
expect(VersionCheck.packageName).toBeDefined();
|
|
33
|
+
expect(typeof VersionCheck.packageName).toBe("string");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should have optional installSource property", () => {
|
|
37
|
+
expect(VersionCheck.installSource).toBeDefined();
|
|
38
|
+
expect(
|
|
39
|
+
VersionCheck.installSource === undefined ||
|
|
40
|
+
VersionCheck.installSource === "appstore" ||
|
|
41
|
+
VersionCheck.installSource === "testflight" ||
|
|
42
|
+
VersionCheck.installSource === "playstore"
|
|
43
|
+
).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should have sync method getCountry", () => {
|
|
47
|
+
expect(typeof VersionCheck.getCountry).toBe("function");
|
|
48
|
+
expect(VersionCheck.getCountry()).toBeDefined();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should have async method getStoreUrl", () => {
|
|
52
|
+
expect(typeof VersionCheck.getStoreUrl).toBe("function");
|
|
53
|
+
const result = VersionCheck.getStoreUrl();
|
|
54
|
+
expect(result).toBeInstanceOf(Promise);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should have async method getLatestVersion", () => {
|
|
58
|
+
expect(typeof VersionCheck.getLatestVersion).toBe("function");
|
|
59
|
+
const result = VersionCheck.getLatestVersion();
|
|
60
|
+
expect(result).toBeInstanceOf(Promise);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should have async method needsUpdate", () => {
|
|
64
|
+
expect(typeof VersionCheck.needsUpdate).toBe("function");
|
|
65
|
+
const result = VersionCheck.needsUpdate();
|
|
66
|
+
expect(result).toBeInstanceOf(Promise);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("property access", () => {
|
|
71
|
+
it("should return string values for sync properties", () => {
|
|
72
|
+
expect(typeof VersionCheck.version).toBe("string");
|
|
73
|
+
expect(typeof VersionCheck.buildNumber).toBe("string");
|
|
74
|
+
expect(typeof VersionCheck.packageName).toBe("string");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should have consistent version format", () => {
|
|
78
|
+
const version = VersionCheck.version;
|
|
79
|
+
// Should be a semantic version like "1.2.0"
|
|
80
|
+
expect(version).toMatch(/^\d+\.\d+\.\d+$/);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should return a valid country code for getCountry()", () => {
|
|
84
|
+
const country = VersionCheck.getCountry();
|
|
85
|
+
// Should be a 2-letter ISO country code (uppercase letters)
|
|
86
|
+
expect(country).toMatch(/^[A-Z]{2}$/);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("needsUpdate", () => {
|
|
91
|
+
it("should return a Promise<boolean>", async () => {
|
|
92
|
+
const result = VersionCheck.needsUpdate();
|
|
93
|
+
expect(result).toBeInstanceOf(Promise);
|
|
94
|
+
const resolved = await result;
|
|
95
|
+
expect(typeof resolved).toBe("boolean");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should accept options with level parameter", async () => {
|
|
99
|
+
const levels: UpdateLevel[] = ["major", "minor", "patch"];
|
|
100
|
+
for (const level of levels) {
|
|
101
|
+
const result = VersionCheck.needsUpdate({ level });
|
|
102
|
+
expect(result).toBeInstanceOf(Promise);
|
|
103
|
+
const resolved = await result;
|
|
104
|
+
expect(typeof resolved).toBe("boolean");
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should default to patch level when no options provided", async () => {
|
|
109
|
+
const result = await VersionCheck.needsUpdate();
|
|
110
|
+
expect(typeof result).toBe("boolean");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should compare current version with latest", async () => {
|
|
114
|
+
// With mocked latest version as '1.3.0' and current as '1.2.0'
|
|
115
|
+
const needsUpdate = await VersionCheck.needsUpdate();
|
|
116
|
+
expect(typeof needsUpdate).toBe("boolean");
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe("API consistency", () => {
|
|
121
|
+
it("should provide consistent property access across multiple calls", () => {
|
|
122
|
+
expect(VersionCheck.version).toBe(VersionCheck.version);
|
|
123
|
+
expect(VersionCheck.buildNumber).toBe(VersionCheck.buildNumber);
|
|
124
|
+
expect(VersionCheck.packageName).toBe(VersionCheck.packageName);
|
|
125
|
+
expect(VersionCheck.getCountry()).toBe(VersionCheck.getCountry());
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("should have readonly type signature", () => {
|
|
129
|
+
// Type checking ensures VersionCheck object is readonly
|
|
130
|
+
// This test verifies all methods and properties exist as expected
|
|
131
|
+
expect(VersionCheck).toBeDefined();
|
|
132
|
+
expect(Object.keys(VersionCheck).length > 0).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe("async operations", () => {
|
|
137
|
+
it("should fetch store URL", async () => {
|
|
138
|
+
const url = await VersionCheck.getStoreUrl();
|
|
139
|
+
expect(url).toMatch(/^https?:\/\//);
|
|
140
|
+
expect(url).toMatch(/apps\.apple\.com|play\.google\.com/);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("should fetch store URL with custom country code", async () => {
|
|
144
|
+
const urlUS = await VersionCheck.getStoreUrl({ countryCode: "US" });
|
|
145
|
+
const urlGB = await VersionCheck.getStoreUrl({ countryCode: "GB" });
|
|
146
|
+
expect(urlUS).toMatch(/^https?:\/\//);
|
|
147
|
+
expect(urlGB).toMatch(/^https?:\/\//);
|
|
148
|
+
expect(urlUS).toMatch(/apps\.apple\.com|play\.google\.com/);
|
|
149
|
+
expect(urlGB).toMatch(/apps\.apple\.com|play\.google\.com/);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("should fetch store URL with undefined country code", async () => {
|
|
153
|
+
const url = await VersionCheck.getStoreUrl({ countryCode: undefined });
|
|
154
|
+
expect(url).toMatch(/^https?:\/\//);
|
|
155
|
+
expect(url).toMatch(/apps\.apple\.com|play\.google\.com/);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("should fetch latest version", async () => {
|
|
159
|
+
const latest = await VersionCheck.getLatestVersion();
|
|
160
|
+
expect(typeof latest).toBe("string");
|
|
161
|
+
// Should be a semantic version
|
|
162
|
+
expect(latest).toMatch(/^\d+\.\d+\.\d+$/);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("should fetch latest version with custom country code", async () => {
|
|
166
|
+
const latestUS = await VersionCheck.getLatestVersion({ countryCode: "US" });
|
|
167
|
+
const latestGB = await VersionCheck.getLatestVersion({ countryCode: "GB" });
|
|
168
|
+
expect(typeof latestUS).toBe("string");
|
|
169
|
+
expect(typeof latestGB).toBe("string");
|
|
170
|
+
expect(latestUS).toMatch(/^\d+\.\d+\.\d+$/);
|
|
171
|
+
expect(latestGB).toMatch(/^\d+\.\d+\.\d+$/);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("should use device country code by default", async () => {
|
|
175
|
+
const latest = await VersionCheck.getLatestVersion();
|
|
176
|
+
const latestWithDefault = await VersionCheck.getLatestVersion({ countryCode: undefined });
|
|
177
|
+
expect(typeof latest).toBe("string");
|
|
178
|
+
expect(typeof latestWithDefault).toBe("string");
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("should handle getLatestVersion and needsUpdate in parallel", async () => {
|
|
182
|
+
const [latest, needsUpdate] = await Promise.all([VersionCheck.getLatestVersion(), VersionCheck.needsUpdate()]);
|
|
183
|
+
expect(typeof latest).toBe("string");
|
|
184
|
+
expect(typeof needsUpdate).toBe("boolean");
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("should handle all async operations in parallel", async () => {
|
|
188
|
+
const [storeUrl, latestVersion, needsUpdate] = await Promise.all([
|
|
189
|
+
VersionCheck.getStoreUrl(),
|
|
190
|
+
VersionCheck.getLatestVersion(),
|
|
191
|
+
VersionCheck.needsUpdate(),
|
|
192
|
+
]);
|
|
193
|
+
expect(typeof storeUrl).toBe("string");
|
|
194
|
+
expect(typeof latestVersion).toBe("string");
|
|
195
|
+
expect(typeof needsUpdate).toBe("boolean");
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe("common usage patterns", () => {
|
|
200
|
+
it("should support destructuring", () => {
|
|
201
|
+
const { version, buildNumber, packageName, installSource } = VersionCheck;
|
|
202
|
+
expect(typeof version).toBe("string");
|
|
203
|
+
expect(typeof buildNumber).toBe("string");
|
|
204
|
+
expect(typeof packageName).toBe("string");
|
|
205
|
+
expect(installSource === undefined || typeof installSource === "string").toBe(true);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("should support destructuring with getCountry", () => {
|
|
209
|
+
const { getCountry } = VersionCheck;
|
|
210
|
+
expect(typeof getCountry).toBe("function");
|
|
211
|
+
expect(getCountry()).toBeDefined();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("should support common update flow", async () => {
|
|
215
|
+
const url = await VersionCheck.getStoreUrl();
|
|
216
|
+
const needsUpdate = await VersionCheck.needsUpdate();
|
|
217
|
+
|
|
218
|
+
expect(url).toMatch(/^https?:\/\//);
|
|
219
|
+
expect(url).toMatch(/apps\.apple\.com|play\.google\.com/);
|
|
220
|
+
expect(typeof needsUpdate).toBe("boolean");
|
|
221
|
+
|
|
222
|
+
if (needsUpdate) {
|
|
223
|
+
expect(url).toBeTruthy();
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it("should support granular update checks", async () => {
|
|
228
|
+
const majorUpdate = await VersionCheck.needsUpdate({ level: "major" });
|
|
229
|
+
const minorUpdate = await VersionCheck.needsUpdate({ level: "minor" });
|
|
230
|
+
const patchUpdate = await VersionCheck.needsUpdate({ level: "patch" });
|
|
231
|
+
|
|
232
|
+
// All should return boolean values
|
|
233
|
+
expect([majorUpdate, minorUpdate, patchUpdate]).toEqual(
|
|
234
|
+
expect.arrayContaining([true, false].includes(majorUpdate) ? [majorUpdate] : [])
|
|
235
|
+
);
|
|
236
|
+
expect(typeof majorUpdate).toBe("boolean");
|
|
237
|
+
expect(typeof minorUpdate).toBe("boolean");
|
|
238
|
+
expect(typeof patchUpdate).toBe("boolean");
|
|
239
|
+
|
|
240
|
+
// Logical consistency: granular checks have semantic ordering
|
|
241
|
+
// If major update, then minor and patch should also be true
|
|
242
|
+
if (majorUpdate) {
|
|
243
|
+
expect(minorUpdate).toBe(true);
|
|
244
|
+
expect(patchUpdate).toBe(true);
|
|
245
|
+
}
|
|
246
|
+
// If minor but not major, patch should still be true
|
|
247
|
+
if (minorUpdate && !majorUpdate) {
|
|
248
|
+
expect(patchUpdate).toBe(true);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("should support install source detection", () => {
|
|
253
|
+
const source = VersionCheck.installSource;
|
|
254
|
+
|
|
255
|
+
if (source !== undefined) {
|
|
256
|
+
expect(["appstore", "testflight", "playstore"]).toContain(source);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("should support region-specific store URLs", async () => {
|
|
261
|
+
const urlUS = await VersionCheck.getStoreUrl({ countryCode: "US" });
|
|
262
|
+
const urlGB = await VersionCheck.getStoreUrl({ countryCode: "GB" });
|
|
263
|
+
const urlJP = await VersionCheck.getStoreUrl({ countryCode: "JP" });
|
|
264
|
+
|
|
265
|
+
expect(urlUS).toMatch(/^https?:\/\//);
|
|
266
|
+
expect(urlGB).toMatch(/^https?:\/\//);
|
|
267
|
+
expect(urlJP).toMatch(/^https?:\/\//);
|
|
268
|
+
expect(urlUS).toMatch(/apps\.apple\.com|play\.google\.com/);
|
|
269
|
+
expect(urlGB).toMatch(/apps\.apple\.com|play\.google\.com/);
|
|
270
|
+
expect(urlJP).toMatch(/apps\.apple\.com|play\.google\.com/);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("should support region-specific version checks", async () => {
|
|
274
|
+
const latestUS = await VersionCheck.getLatestVersion({ countryCode: "US" });
|
|
275
|
+
const latestGB = await VersionCheck.getLatestVersion({ countryCode: "GB" });
|
|
276
|
+
const latestJP = await VersionCheck.getLatestVersion({ countryCode: "JP" });
|
|
277
|
+
|
|
278
|
+
expect(typeof latestUS).toBe("string");
|
|
279
|
+
expect(typeof latestGB).toBe("string");
|
|
280
|
+
expect(typeof latestJP).toBe("string");
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { compareVersions, isNewerVersion } from "../semver";
|
|
2
|
+
|
|
3
|
+
describe("semver", () => {
|
|
4
|
+
describe("compareVersions", () => {
|
|
5
|
+
it("should return -1 when first version is older", () => {
|
|
6
|
+
expect(compareVersions("1.2.0", "1.3.0")).toBe(-1);
|
|
7
|
+
expect(compareVersions("1.2.0", "2.0.0")).toBe(-1);
|
|
8
|
+
expect(compareVersions("1.2.3", "1.2.4")).toBe(-1);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("should return 1 when first version is newer", () => {
|
|
12
|
+
expect(compareVersions("2.0.0", "1.9.9")).toBe(1);
|
|
13
|
+
expect(compareVersions("1.3.0", "1.2.0")).toBe(1);
|
|
14
|
+
expect(compareVersions("1.2.4", "1.2.3")).toBe(1);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("should return 0 when versions are equal", () => {
|
|
18
|
+
expect(compareVersions("1.0.0", "1.0.0")).toBe(0);
|
|
19
|
+
expect(compareVersions("2.5.10", "2.5.10")).toBe(0);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should handle major version differences", () => {
|
|
23
|
+
expect(compareVersions("1.0.0", "2.0.0")).toBe(-1);
|
|
24
|
+
expect(compareVersions("3.0.0", "2.0.0")).toBe(1);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should handle minor version differences", () => {
|
|
28
|
+
expect(compareVersions("1.2.0", "1.3.0")).toBe(-1);
|
|
29
|
+
expect(compareVersions("1.5.0", "1.3.0")).toBe(1);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should handle patch version differences", () => {
|
|
33
|
+
expect(compareVersions("1.0.5", "1.0.10")).toBe(-1);
|
|
34
|
+
expect(compareVersions("1.0.10", "1.0.5")).toBe(1);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should handle versions with leading zeros", () => {
|
|
38
|
+
expect(compareVersions("01.02.03", "1.2.3")).toBe(0);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should handle malformed versions gracefully", () => {
|
|
42
|
+
// Missing parts should be treated as 0
|
|
43
|
+
expect(compareVersions("1", "1.0.0")).toBe(0);
|
|
44
|
+
expect(compareVersions("1.2", "1.2.0")).toBe(0);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should handle real-world version scenarios", () => {
|
|
48
|
+
// Bug fix scenario
|
|
49
|
+
expect(compareVersions("2.2.0", "2.0.22")).toBe(1); // 2.2.0 is newer than 2.0.22
|
|
50
|
+
expect(compareVersions("2.0.22", "2.2.0")).toBe(-1);
|
|
51
|
+
|
|
52
|
+
// Update checks
|
|
53
|
+
expect(compareVersions("1.0.0", "1.0.1")).toBe(-1);
|
|
54
|
+
expect(compareVersions("1.0.0", "1.1.0")).toBe(-1);
|
|
55
|
+
expect(compareVersions("1.0.0", "2.0.0")).toBe(-1);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("isNewerVersion", () => {
|
|
60
|
+
describe("with patch level (default)", () => {
|
|
61
|
+
it("should return true for any version increase", () => {
|
|
62
|
+
expect(isNewerVersion("1.0.0", "1.0.1", "patch")).toBe(true);
|
|
63
|
+
expect(isNewerVersion("1.0.0", "1.1.0", "patch")).toBe(true);
|
|
64
|
+
expect(isNewerVersion("1.0.0", "2.0.0", "patch")).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should return false when current is same or newer", () => {
|
|
68
|
+
expect(isNewerVersion("1.0.0", "1.0.0", "patch")).toBe(false);
|
|
69
|
+
expect(isNewerVersion("1.0.1", "1.0.0", "patch")).toBe(false);
|
|
70
|
+
expect(isNewerVersion("2.0.0", "1.0.0", "patch")).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("should use patch level as default", () => {
|
|
74
|
+
expect(isNewerVersion("1.0.0", "1.0.1")).toBe(true);
|
|
75
|
+
expect(isNewerVersion("1.0.0", "1.0.0")).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("with minor level", () => {
|
|
80
|
+
it("should return true for major or minor bumps", () => {
|
|
81
|
+
expect(isNewerVersion("1.0.0", "1.1.0", "minor")).toBe(true);
|
|
82
|
+
expect(isNewerVersion("1.0.0", "2.0.0", "minor")).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should return false for patch-only bumps", () => {
|
|
86
|
+
expect(isNewerVersion("1.0.0", "1.0.1", "minor")).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("should return false when current is same or newer", () => {
|
|
90
|
+
expect(isNewerVersion("1.1.0", "1.1.0", "minor")).toBe(false);
|
|
91
|
+
expect(isNewerVersion("1.1.0", "1.0.0", "minor")).toBe(false);
|
|
92
|
+
expect(isNewerVersion("2.0.0", "1.5.0", "minor")).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe("with major level", () => {
|
|
97
|
+
it("should return true only for major bumps", () => {
|
|
98
|
+
expect(isNewerVersion("1.0.0", "2.0.0", "major")).toBe(true);
|
|
99
|
+
expect(isNewerVersion("1.5.9", "2.0.0", "major")).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("should return false for minor or patch bumps", () => {
|
|
103
|
+
expect(isNewerVersion("1.0.0", "1.1.0", "major")).toBe(false);
|
|
104
|
+
expect(isNewerVersion("1.0.0", "1.0.1", "major")).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("should return false when current is same or newer", () => {
|
|
108
|
+
expect(isNewerVersion("1.0.0", "1.0.0", "major")).toBe(false);
|
|
109
|
+
expect(isNewerVersion("2.0.0", "1.0.0", "major")).toBe(false);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe("real-world scenarios", () => {
|
|
114
|
+
it("should handle the GitHub issue scenario", () => {
|
|
115
|
+
// From the issue: latest is 2.0.22, current is 2.2.0
|
|
116
|
+
expect(isNewerVersion("2.2.0", "2.0.22", "patch")).toBe(false);
|
|
117
|
+
expect(isNewerVersion("2.2.0", "2.0.22", "minor")).toBe(false);
|
|
118
|
+
expect(isNewerVersion("2.2.0", "2.0.22", "major")).toBe(false);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("should handle typical update scenarios", () => {
|
|
122
|
+
// App is on 1.2.0, store has 1.2.1
|
|
123
|
+
expect(isNewerVersion("1.2.0", "1.2.1", "patch")).toBe(true);
|
|
124
|
+
expect(isNewerVersion("1.2.0", "1.2.1", "minor")).toBe(false);
|
|
125
|
+
expect(isNewerVersion("1.2.0", "1.2.1", "major")).toBe(false);
|
|
126
|
+
|
|
127
|
+
// App is on 1.2.0, store has 1.3.0
|
|
128
|
+
expect(isNewerVersion("1.2.0", "1.3.0", "patch")).toBe(true);
|
|
129
|
+
expect(isNewerVersion("1.2.0", "1.3.0", "minor")).toBe(true);
|
|
130
|
+
expect(isNewerVersion("1.2.0", "1.3.0", "major")).toBe(false);
|
|
131
|
+
|
|
132
|
+
// App is on 1.2.0, store has 2.0.0
|
|
133
|
+
expect(isNewerVersion("1.2.0", "2.0.0", "patch")).toBe(true);
|
|
134
|
+
expect(isNewerVersion("1.2.0", "2.0.0", "minor")).toBe(true);
|
|
135
|
+
expect(isNewerVersion("1.2.0", "2.0.0", "major")).toBe(true);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("should correctly identify when app is already up to date", () => {
|
|
139
|
+
expect(isNewerVersion("1.5.0", "1.4.0")).toBe(false);
|
|
140
|
+
expect(isNewerVersion("2.0.0", "1.9.9")).toBe(false);
|
|
141
|
+
expect(isNewerVersion("3.0.0", "2.99.99")).toBe(false);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -11,68 +11,12 @@ const buildNumber = HybridVersionCheck.buildNumber;
|
|
|
11
11
|
const packageName = HybridVersionCheck.packageName;
|
|
12
12
|
const installSource = HybridVersionCheck.installSource;
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
* Returns the device's current 2-letter ISO country code.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```ts
|
|
19
|
-
* getCountry() // "US"
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
export const getCountry = () => HybridVersionCheck.getCountry();
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Returns the store URL for this app.
|
|
26
|
-
*
|
|
27
|
-
* Automatically resolves to the App Store on iOS and Play Store on Android.
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* ```ts
|
|
31
|
-
* const url = await getStoreUrl();
|
|
32
|
-
* Linking.openURL(url);
|
|
33
|
-
* ```
|
|
34
|
-
*/
|
|
35
|
-
export const getStoreUrl = () => HybridVersionCheck.getStoreUrl();
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Fetches the latest version of this app available in the store.
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* ```ts
|
|
42
|
-
* const latest = await getLatestVersion(); // "1.3.0"
|
|
43
|
-
* ```
|
|
44
|
-
*/
|
|
45
|
-
export const getLatestVersion = () => HybridVersionCheck.getLatestVersion();
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Checks whether an app update is available.
|
|
49
|
-
*
|
|
50
|
-
* Uses semantic version comparison. By default checks for any version
|
|
51
|
-
* increase, but you can filter by granularity:
|
|
52
|
-
*
|
|
53
|
-
* - `"major"` — only returns `true` for major bumps (1.x → 2.x)
|
|
54
|
-
* - `"minor"` — returns `true` for major or minor bumps
|
|
55
|
-
* - `"patch"` — returns `true` for any version increase (default)
|
|
56
|
-
*
|
|
57
|
-
* @example
|
|
58
|
-
* ```ts
|
|
59
|
-
* if (await needsUpdate()) {
|
|
60
|
-
* const url = await getStoreUrl();
|
|
61
|
-
* Linking.openURL(url);
|
|
62
|
-
* }
|
|
63
|
-
*
|
|
64
|
-
* // Only prompt for major updates
|
|
65
|
-
* const majorUpdate = await needsUpdate({ level: "major" });
|
|
66
|
-
* ```
|
|
67
|
-
*/
|
|
68
|
-
export const needsUpdate = async (options?: { level?: UpdateLevel }): Promise<boolean> => {
|
|
69
|
-
const latest = await HybridVersionCheck.getLatestVersion();
|
|
70
|
-
return isNewerVersion(version, latest, options?.level ?? "patch");
|
|
71
|
-
};
|
|
72
|
-
|
|
73
14
|
/**
|
|
74
15
|
* All version-check APIs in one object.
|
|
75
16
|
*
|
|
17
|
+
* Provides access to app version information, store URLs, and update checking.
|
|
18
|
+
* Sync properties are cached at module init for zero native overhead.
|
|
19
|
+
*
|
|
76
20
|
* @example
|
|
77
21
|
* ```ts
|
|
78
22
|
* VersionCheck.version // "1.2.0"
|
|
@@ -81,6 +25,9 @@ export const needsUpdate = async (options?: { level?: UpdateLevel }): Promise<bo
|
|
|
81
25
|
* VersionCheck.getCountry() // "US"
|
|
82
26
|
*
|
|
83
27
|
* const url = await VersionCheck.getStoreUrl();
|
|
28
|
+
* if (await VersionCheck.needsUpdate()) {
|
|
29
|
+
* Linking.openURL(url);
|
|
30
|
+
* }
|
|
84
31
|
* ```
|
|
85
32
|
*/
|
|
86
33
|
export const VersionCheck = {
|
|
@@ -144,42 +91,89 @@ export const VersionCheck = {
|
|
|
144
91
|
* VersionCheck.getCountry() // "US"
|
|
145
92
|
* ```
|
|
146
93
|
*/
|
|
147
|
-
getCountry,
|
|
94
|
+
getCountry: () => HybridVersionCheck.getCountry(),
|
|
148
95
|
/**
|
|
149
96
|
* Returns the App Store (iOS) or Play Store (Android) URL for this app.
|
|
150
97
|
*
|
|
98
|
+
* @param options - Optional configuration
|
|
99
|
+
* @param options.countryCode - 2-letter ISO country code (e.g., "US", "GB")
|
|
100
|
+
* Defaults to the device's current country from `getCountry()`.
|
|
101
|
+
* Only used on iOS; ignored on Android.
|
|
102
|
+
*
|
|
151
103
|
* @example
|
|
152
104
|
* ```ts
|
|
153
105
|
* const url = await VersionCheck.getStoreUrl();
|
|
106
|
+
* const urlUS = await VersionCheck.getStoreUrl({ countryCode: "US" });
|
|
154
107
|
* Linking.openURL(url);
|
|
155
108
|
* ```
|
|
156
109
|
*/
|
|
157
|
-
getStoreUrl
|
|
110
|
+
getStoreUrl: async (options?: { countryCode?: string }): Promise<string> => {
|
|
111
|
+
return HybridVersionCheck.getStoreUrl(options?.countryCode);
|
|
112
|
+
},
|
|
158
113
|
/**
|
|
159
114
|
* Fetches the latest version of this app available in the store.
|
|
160
115
|
*
|
|
161
116
|
* Queries the iTunes API on iOS and the Play Store on Android.
|
|
117
|
+
* On iOS, uses the device's current country code by default but can be overridden.
|
|
118
|
+
*
|
|
119
|
+
* @param options - Optional configuration
|
|
120
|
+
* @param options.countryCode - 2-letter ISO country code (e.g., "US", "GB")
|
|
121
|
+
* Defaults to the device's current country from `getCountry()`.
|
|
122
|
+
* If the device region changes, the next call will use the new country.
|
|
123
|
+
* Only used on iOS; ignored on Android.
|
|
162
124
|
*
|
|
163
125
|
* @example
|
|
164
126
|
* ```ts
|
|
165
|
-
* const latest = await VersionCheck.getLatestVersion(); //
|
|
127
|
+
* const latest = await VersionCheck.getLatestVersion(); // Uses current device country
|
|
128
|
+
* const latestUS = await VersionCheck.getLatestVersion({ countryCode: "US" });
|
|
129
|
+
* const latestGB = await VersionCheck.getLatestVersion({ countryCode: "GB" });
|
|
166
130
|
* ```
|
|
167
131
|
*/
|
|
168
|
-
getLatestVersion
|
|
132
|
+
getLatestVersion: async (options?: { countryCode?: string }): Promise<string> => {
|
|
133
|
+
return HybridVersionCheck.getLatestVersion(options?.countryCode);
|
|
134
|
+
},
|
|
169
135
|
/**
|
|
170
136
|
* Checks whether an app update is available by comparing the current
|
|
171
137
|
* version against the latest store version.
|
|
172
138
|
*
|
|
139
|
+
* Uses semantic version comparison. By default checks for any version
|
|
140
|
+
* increase, but you can filter by granularity:
|
|
141
|
+
*
|
|
142
|
+
* - `"major"` — only returns `true` for major bumps (1.x → 2.x)
|
|
143
|
+
* - `"minor"` — returns `true` for major or minor bumps
|
|
144
|
+
* - `"patch"` — returns `true` for any version increase (default)
|
|
145
|
+
*
|
|
173
146
|
* @example
|
|
174
147
|
* ```ts
|
|
175
148
|
* if (await VersionCheck.needsUpdate()) {
|
|
176
149
|
* const url = await VersionCheck.getStoreUrl();
|
|
177
150
|
* Linking.openURL(url);
|
|
178
151
|
* }
|
|
152
|
+
*
|
|
153
|
+
* // Only prompt for major updates
|
|
154
|
+
* const majorUpdate = await VersionCheck.needsUpdate({ level: "major" });
|
|
179
155
|
* ```
|
|
180
156
|
*/
|
|
181
|
-
needsUpdate: () =>
|
|
157
|
+
needsUpdate: async (options?: { level?: UpdateLevel }): Promise<boolean> => {
|
|
158
|
+
const latest = await HybridVersionCheck.getLatestVersion();
|
|
159
|
+
return isNewerVersion(version, latest, options?.level ?? "patch");
|
|
160
|
+
},
|
|
161
|
+
/**
|
|
162
|
+
* Compares two semantic version strings.
|
|
163
|
+
*
|
|
164
|
+
* @returns -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```ts
|
|
168
|
+
* VersionCheck.compareVersions('1.0.0', '1.0.1') // -1
|
|
169
|
+
* VersionCheck.compareVersions('2.0.0', '2.0.0') // 0
|
|
170
|
+
* VersionCheck.compareVersions('3.0.0', '2.9.9') // 1
|
|
171
|
+
*
|
|
172
|
+
* if (VersionCheck.compareVersions(currentVersion, minimumVersion) < 0) {
|
|
173
|
+
* // Current version is below minimum — force update
|
|
174
|
+
* }
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
compareVersions,
|
|
182
178
|
} as const;
|
|
183
|
-
|
|
184
|
-
export { compareVersions };
|
|
185
179
|
export type { UpdateLevel };
|
|
@@ -10,7 +10,7 @@ export interface VersionCheck
|
|
|
10
10
|
readonly packageName: string;
|
|
11
11
|
readonly installSource: string | undefined;
|
|
12
12
|
getCountry(): string;
|
|
13
|
-
getStoreUrl(): Promise<string>;
|
|
14
|
-
getLatestVersion(): Promise<string>;
|
|
13
|
+
getStoreUrl(countryCode?: string): Promise<string>;
|
|
14
|
+
getLatestVersion(countryCode?: string): Promise<string>;
|
|
15
15
|
needsUpdate(): Promise<boolean>;
|
|
16
16
|
}
|