@lucidaquarian/app-version-checker 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/examples/capacitor-usage.js +149 -0
- package/examples/cordova-usage.js +75 -0
- package/package.json +48 -0
- package/src/index.d.ts +160 -0
- package/src/index.js +265 -0
- package/src/providers/capacitor.js +143 -0
- package/src/providers/cordova.js +143 -0
- package/src/semver.js +198 -0
- package/src/stores.js +201 -0
package/src/index.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lucidaquarian/app-version-checker
|
|
3
|
+
*
|
|
4
|
+
* A cross-platform version checker for Cordova and Capacitor mobile applications.
|
|
5
|
+
* Detects the current app version, compares it against the latest published version
|
|
6
|
+
* (from the App Store, Google Play, or a custom endpoint), and returns actionable
|
|
7
|
+
* update information.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const { AppVersionChecker } = require('@lucidaquarian/app-version-checker');
|
|
11
|
+
*
|
|
12
|
+
* const checker = new AppVersionChecker({
|
|
13
|
+
* platform: 'auto', // 'cordova' | 'capacitor' | 'auto'
|
|
14
|
+
* iosBundleId: 'com.example.myapp', // for App Store lookup
|
|
15
|
+
* androidPackageName: 'com.example.myapp', // for Play Store lookup
|
|
16
|
+
* country: 'us', // App Store region
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* const result = await checker.checkForUpdate();
|
|
20
|
+
* if (result.updateAvailable) {
|
|
21
|
+
* console.log(`New version ${result.latestVersion} available!`);
|
|
22
|
+
* }
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const SemVer = require('./semver');
|
|
26
|
+
const CordovaProvider = require('./providers/cordova');
|
|
27
|
+
const CapacitorProvider = require('./providers/capacitor');
|
|
28
|
+
const {
|
|
29
|
+
fetchAppStoreVersion,
|
|
30
|
+
fetchPlayStoreVersion,
|
|
31
|
+
fetchCustomEndpoint,
|
|
32
|
+
} = require('./stores');
|
|
33
|
+
|
|
34
|
+
// ─── Default Configuration ───────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
const DEFAULT_CONFIG = {
|
|
37
|
+
/** @type {'cordova' | 'capacitor' | 'auto'} */
|
|
38
|
+
platform: 'auto',
|
|
39
|
+
|
|
40
|
+
/** iOS bundle identifier for App Store lookup */
|
|
41
|
+
iosBundleId: '',
|
|
42
|
+
|
|
43
|
+
/** Android package name for Play Store lookup */
|
|
44
|
+
androidPackageName: '',
|
|
45
|
+
|
|
46
|
+
/** Two-letter country code for App Store */
|
|
47
|
+
country: 'us',
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Optional custom endpoint URL that returns JSON with a `version` field.
|
|
51
|
+
* When set, the store lookup is skipped in favor of this endpoint.
|
|
52
|
+
*/
|
|
53
|
+
customEndpoint: '',
|
|
54
|
+
|
|
55
|
+
/** Extra fetch options passed to the custom endpoint request */
|
|
56
|
+
customEndpointOptions: {},
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* If set, any installed version below this is treated as a forced update,
|
|
60
|
+
* regardless of what the store says. Overridden by the remote `minVersion`.
|
|
61
|
+
*/
|
|
62
|
+
minVersion: '',
|
|
63
|
+
|
|
64
|
+
/** Network timeout (ms) for store / custom-endpoint requests. */
|
|
65
|
+
timeout: 10000,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// ─── Main Class ──────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
class AppVersionChecker {
|
|
71
|
+
/**
|
|
72
|
+
* @param {Partial<typeof DEFAULT_CONFIG>} config
|
|
73
|
+
*/
|
|
74
|
+
constructor(config = {}) {
|
|
75
|
+
/** @type {typeof DEFAULT_CONFIG} */
|
|
76
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
77
|
+
|
|
78
|
+
this._cordova = new CordovaProvider();
|
|
79
|
+
this._capacitor = new CapacitorProvider();
|
|
80
|
+
this._provider = null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ── Provider Detection ─────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Detect and return the appropriate platform provider.
|
|
87
|
+
* @returns {CordovaProvider | CapacitorProvider}
|
|
88
|
+
*/
|
|
89
|
+
getProvider() {
|
|
90
|
+
if (this._provider) return this._provider;
|
|
91
|
+
|
|
92
|
+
const { platform } = this.config;
|
|
93
|
+
|
|
94
|
+
if (platform === 'cordova') {
|
|
95
|
+
this._provider = this._cordova;
|
|
96
|
+
} else if (platform === 'capacitor') {
|
|
97
|
+
this._provider = this._capacitor;
|
|
98
|
+
} else {
|
|
99
|
+
// Auto-detect: prefer Capacitor (newer), fall back to Cordova
|
|
100
|
+
if (this._capacitor.isAvailable()) {
|
|
101
|
+
this._provider = this._capacitor;
|
|
102
|
+
} else if (this._cordova.isAvailable()) {
|
|
103
|
+
this._provider = this._cordova;
|
|
104
|
+
} else {
|
|
105
|
+
throw new Error(
|
|
106
|
+
'No mobile platform detected. ' +
|
|
107
|
+
'Ensure Cordova or Capacitor is initialized, or set the "platform" config explicitly.'
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return this._provider;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get the name of the detected platform.
|
|
117
|
+
* @returns {string}
|
|
118
|
+
*/
|
|
119
|
+
getPlatformName() {
|
|
120
|
+
return this.getProvider().name;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ── Local Version ──────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get the currently installed app version.
|
|
127
|
+
* @returns {Promise<string>}
|
|
128
|
+
*/
|
|
129
|
+
async getCurrentVersion() {
|
|
130
|
+
return this.getProvider().getVersion();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get the full app info object from the platform.
|
|
135
|
+
* @returns {Promise<object>}
|
|
136
|
+
*/
|
|
137
|
+
async getAppInfo() {
|
|
138
|
+
return this.getProvider().getAppInfo();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get the current OS-level platform (ios / android / web).
|
|
143
|
+
* @returns {string}
|
|
144
|
+
*/
|
|
145
|
+
getDevicePlatform() {
|
|
146
|
+
return this.getProvider().getPlatform();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ── Remote Version Lookup ──────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Fetch the latest version info from the appropriate store or custom endpoint.
|
|
153
|
+
* @returns {Promise<{ version: string, storeUrl?: string, releaseNotes?: string, minVersion?: string, forceUpdate?: boolean }>}
|
|
154
|
+
*/
|
|
155
|
+
async getLatestVersion() {
|
|
156
|
+
const { timeout } = this.config;
|
|
157
|
+
|
|
158
|
+
// Prefer custom endpoint if configured
|
|
159
|
+
if (this.config.customEndpoint) {
|
|
160
|
+
return fetchCustomEndpoint(
|
|
161
|
+
this.config.customEndpoint,
|
|
162
|
+
this.config.customEndpointOptions,
|
|
163
|
+
timeout
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const devicePlatform = this.getDevicePlatform();
|
|
168
|
+
|
|
169
|
+
if (devicePlatform === 'ios') {
|
|
170
|
+
const bundleId = this.config.iosBundleId;
|
|
171
|
+
if (!bundleId) {
|
|
172
|
+
throw new Error('iosBundleId must be set in config for App Store lookups.');
|
|
173
|
+
}
|
|
174
|
+
return fetchAppStoreVersion(bundleId, this.config.country, timeout);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (devicePlatform === 'android') {
|
|
178
|
+
const packageName = this.config.androidPackageName;
|
|
179
|
+
if (!packageName) {
|
|
180
|
+
throw new Error('androidPackageName must be set in config for Play Store lookups.');
|
|
181
|
+
}
|
|
182
|
+
return fetchPlayStoreVersion(packageName, timeout);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
throw new Error(
|
|
186
|
+
`Unsupported device platform "${devicePlatform}". Use a customEndpoint for web-based checks.`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ── Update Check ───────────────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Perform a full version check: compare local version against latest remote version.
|
|
194
|
+
*
|
|
195
|
+
* @returns {Promise<VersionCheckResult>}
|
|
196
|
+
*
|
|
197
|
+
* @typedef {Object} VersionCheckResult
|
|
198
|
+
* @property {string} currentVersion - The installed app version
|
|
199
|
+
* @property {string} latestVersion - The latest available version
|
|
200
|
+
* @property {boolean} updateAvailable - True if a newer version exists
|
|
201
|
+
* @property {'major'|'minor'|'patch'|'prerelease'|'none'} updateType - Kind of update
|
|
202
|
+
* @property {boolean} forceUpdate - True if the current version is below the minimum
|
|
203
|
+
* @property {string} storeUrl - URL to the store listing (if available)
|
|
204
|
+
* @property {string} releaseNotes - Release notes (if available)
|
|
205
|
+
* @property {string} platform - Detected platform name
|
|
206
|
+
*/
|
|
207
|
+
async checkForUpdate() {
|
|
208
|
+
const [currentVersion, remoteInfo] = await Promise.all([
|
|
209
|
+
this.getCurrentVersion(),
|
|
210
|
+
this.getLatestVersion(),
|
|
211
|
+
]);
|
|
212
|
+
|
|
213
|
+
const latestVersion = remoteInfo.version;
|
|
214
|
+
const updateAvailable = SemVer.lt(currentVersion, latestVersion);
|
|
215
|
+
const updateType = SemVer.updateType(currentVersion, latestVersion);
|
|
216
|
+
|
|
217
|
+
// Determine forced update: use remote minVersion if provided, else local config
|
|
218
|
+
const effectiveMinVersion = remoteInfo.minVersion || this.config.minVersion || '';
|
|
219
|
+
let forceUpdate = remoteInfo.forceUpdate || false;
|
|
220
|
+
if (effectiveMinVersion && SemVer.lt(currentVersion, effectiveMinVersion)) {
|
|
221
|
+
forceUpdate = true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
currentVersion,
|
|
226
|
+
latestVersion,
|
|
227
|
+
updateAvailable,
|
|
228
|
+
updateType,
|
|
229
|
+
forceUpdate,
|
|
230
|
+
storeUrl: remoteInfo.storeUrl || '',
|
|
231
|
+
releaseNotes: remoteInfo.releaseNotes || '',
|
|
232
|
+
platform: this.getPlatformName(),
|
|
233
|
+
devicePlatform: this.getDevicePlatform(),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ── Static Convenience ─────────────────────────────────────────────
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Compare two arbitrary version strings without any platform dependency.
|
|
241
|
+
* Useful for quick checks or unit tests.
|
|
242
|
+
*
|
|
243
|
+
* @param {string} currentVersion
|
|
244
|
+
* @param {string} latestVersion
|
|
245
|
+
* @returns {{ updateAvailable: boolean, updateType: string }}
|
|
246
|
+
*/
|
|
247
|
+
static compareVersions(currentVersion, latestVersion) {
|
|
248
|
+
return {
|
|
249
|
+
updateAvailable: SemVer.lt(currentVersion, latestVersion),
|
|
250
|
+
updateType: SemVer.updateType(currentVersion, latestVersion),
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ─── Exports ─────────────────────────────────────────────────────────
|
|
256
|
+
|
|
257
|
+
module.exports = {
|
|
258
|
+
AppVersionChecker,
|
|
259
|
+
SemVer,
|
|
260
|
+
CordovaProvider,
|
|
261
|
+
CapacitorProvider,
|
|
262
|
+
fetchAppStoreVersion,
|
|
263
|
+
fetchPlayStoreVersion,
|
|
264
|
+
fetchCustomEndpoint,
|
|
265
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capacitor platform provider.
|
|
3
|
+
* Retrieves the current app version using @capacitor/app or @capacitor/device APIs.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class CapacitorProvider {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.name = 'capacitor';
|
|
9
|
+
this._appPlugin = null;
|
|
10
|
+
this._devicePlugin = null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Check if we are running inside a Capacitor environment.
|
|
15
|
+
* @returns {boolean}
|
|
16
|
+
*/
|
|
17
|
+
isAvailable() {
|
|
18
|
+
return (
|
|
19
|
+
typeof window !== 'undefined' &&
|
|
20
|
+
typeof window.Capacitor !== 'undefined' &&
|
|
21
|
+
window.Capacitor.isNativePlatform !== undefined
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check if the app is running on a native device (not web).
|
|
27
|
+
* @returns {boolean}
|
|
28
|
+
*/
|
|
29
|
+
isNative() {
|
|
30
|
+
return this.isAvailable() && window.Capacitor.isNativePlatform();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Dynamically import the @capacitor/app plugin.
|
|
35
|
+
* @returns {Promise<any>}
|
|
36
|
+
* @private
|
|
37
|
+
*/
|
|
38
|
+
async _getAppPlugin() {
|
|
39
|
+
if (this._appPlugin) return this._appPlugin;
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
// Try Capacitor 4+ / 5+ dynamic import
|
|
43
|
+
const mod = await import('@capacitor/app');
|
|
44
|
+
this._appPlugin = mod.App;
|
|
45
|
+
return this._appPlugin;
|
|
46
|
+
} catch {
|
|
47
|
+
// Fallback: check global Capacitor Plugins
|
|
48
|
+
if (window.Capacitor?.Plugins?.App) {
|
|
49
|
+
this._appPlugin = window.Capacitor.Plugins.App;
|
|
50
|
+
return this._appPlugin;
|
|
51
|
+
}
|
|
52
|
+
throw new Error(
|
|
53
|
+
'@capacitor/app plugin is not installed. ' +
|
|
54
|
+
'Install it with: npm install @capacitor/app'
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Dynamically import the @capacitor/device plugin.
|
|
61
|
+
* @returns {Promise<any>}
|
|
62
|
+
* @private
|
|
63
|
+
*/
|
|
64
|
+
async _getDevicePlugin() {
|
|
65
|
+
if (this._devicePlugin) return this._devicePlugin;
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const mod = await import('@capacitor/device');
|
|
69
|
+
this._devicePlugin = mod.Device;
|
|
70
|
+
return this._devicePlugin;
|
|
71
|
+
} catch {
|
|
72
|
+
if (window.Capacitor?.Plugins?.Device) {
|
|
73
|
+
this._devicePlugin = window.Capacitor.Plugins.Device;
|
|
74
|
+
return this._devicePlugin;
|
|
75
|
+
}
|
|
76
|
+
// Device plugin is optional, return null
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get the current app version info from Capacitor.
|
|
83
|
+
*
|
|
84
|
+
* @returns {Promise<{ versionName: string, versionCode: string, platform: string }>}
|
|
85
|
+
*/
|
|
86
|
+
async getAppInfo() {
|
|
87
|
+
if (!this.isAvailable()) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
'Capacitor is not available. Ensure this is running inside a Capacitor app.'
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const App = await this._getAppPlugin();
|
|
94
|
+
const info = await App.getInfo();
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
versionName: info.version, // e.g. "1.2.3"
|
|
98
|
+
versionCode: info.build, // e.g. "42"
|
|
99
|
+
packageName: info.id, // e.g. "com.example.app"
|
|
100
|
+
appName: info.name, // e.g. "My App"
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get only the version string (e.g. "1.2.3").
|
|
106
|
+
* @returns {Promise<string>}
|
|
107
|
+
*/
|
|
108
|
+
async getVersion() {
|
|
109
|
+
const info = await this.getAppInfo();
|
|
110
|
+
return info.versionName;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get the build/version code.
|
|
115
|
+
* @returns {Promise<string>}
|
|
116
|
+
*/
|
|
117
|
+
async getBuildNumber() {
|
|
118
|
+
const info = await this.getAppInfo();
|
|
119
|
+
return info.versionCode;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get the current platform ("ios", "android", or "web").
|
|
124
|
+
* @returns {string}
|
|
125
|
+
*/
|
|
126
|
+
getPlatform() {
|
|
127
|
+
if (!this.isAvailable()) return 'unknown';
|
|
128
|
+
return window.Capacitor.getPlatform();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get device information (OS version, model, etc.).
|
|
133
|
+
* Requires @capacitor/device plugin.
|
|
134
|
+
* @returns {Promise<object|null>}
|
|
135
|
+
*/
|
|
136
|
+
async getDeviceInfo() {
|
|
137
|
+
const Device = await this._getDevicePlugin();
|
|
138
|
+
if (!Device) return null;
|
|
139
|
+
return Device.getInfo();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = CapacitorProvider;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cordova platform provider.
|
|
3
|
+
* Retrieves the current app version using the cordova-plugin-app-version API.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class CordovaProvider {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.name = 'cordova';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Check if we are running inside a Cordova environment.
|
|
13
|
+
* @returns {boolean}
|
|
14
|
+
*/
|
|
15
|
+
isAvailable() {
|
|
16
|
+
return (
|
|
17
|
+
typeof window !== 'undefined' &&
|
|
18
|
+
typeof window.cordova !== 'undefined'
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get the current app version from Cordova.
|
|
24
|
+
* Requires `cordova-plugin-app-version` to be installed.
|
|
25
|
+
*
|
|
26
|
+
* @returns {Promise<{ versionName: string, versionCode: string|number, packageName: string, appName: string }>}
|
|
27
|
+
*/
|
|
28
|
+
async getAppInfo() {
|
|
29
|
+
if (!this.isAvailable()) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
'Cordova is not available. Ensure this is running inside a Cordova app.'
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
await this._waitForDeviceReady();
|
|
36
|
+
|
|
37
|
+
const cordova = window.cordova;
|
|
38
|
+
|
|
39
|
+
// Check for cordova-plugin-app-version
|
|
40
|
+
if (
|
|
41
|
+
typeof cordova.getAppVersion === 'undefined' &&
|
|
42
|
+
typeof window.AppVersion === 'undefined'
|
|
43
|
+
) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
'cordova-plugin-app-version is not installed. ' +
|
|
46
|
+
'Install it with: cordova plugin add cordova-plugin-app-version'
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const appVersion = cordova.getAppVersion || window.AppVersion;
|
|
51
|
+
|
|
52
|
+
// Bind each method to the plugin object so callers that rely on `this`
|
|
53
|
+
// inside the plugin keep working when the function is passed to _promisify.
|
|
54
|
+
const [versionName, versionCode, packageName, appName] = await Promise.all([
|
|
55
|
+
this._promisify(appVersion.getVersionNumber.bind(appVersion)),
|
|
56
|
+
this._promisify(appVersion.getVersionCode.bind(appVersion)),
|
|
57
|
+
this._promisify(appVersion.getPackageName.bind(appVersion)),
|
|
58
|
+
this._promisify(appVersion.getAppName.bind(appVersion)),
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
return { versionName, versionCode, packageName, appName };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get only the version string (e.g. "1.2.3").
|
|
66
|
+
* @returns {Promise<string>}
|
|
67
|
+
*/
|
|
68
|
+
async getVersion() {
|
|
69
|
+
const info = await this.getAppInfo();
|
|
70
|
+
return info.versionName;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get the build/version code (e.g. 42).
|
|
75
|
+
* @returns {Promise<string|number>}
|
|
76
|
+
*/
|
|
77
|
+
async getBuildNumber() {
|
|
78
|
+
const info = await this.getAppInfo();
|
|
79
|
+
return info.versionCode;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get the current platform (ios or android).
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/
|
|
86
|
+
getPlatform() {
|
|
87
|
+
if (!this.isAvailable()) return 'unknown';
|
|
88
|
+
return window.cordova.platformId || window.device?.platform?.toLowerCase() || 'unknown';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Wait for the Cordova deviceready event.
|
|
93
|
+
* @returns {Promise<void>}
|
|
94
|
+
* @private
|
|
95
|
+
*/
|
|
96
|
+
_waitForDeviceReady() {
|
|
97
|
+
return new Promise((resolve) => {
|
|
98
|
+
if (
|
|
99
|
+
typeof document === 'undefined' ||
|
|
100
|
+
document.readyState === 'complete' ||
|
|
101
|
+
window.cordova?.plugins
|
|
102
|
+
) {
|
|
103
|
+
resolve();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let settled = false;
|
|
108
|
+
let timerId = null;
|
|
109
|
+
const done = () => {
|
|
110
|
+
if (settled) return;
|
|
111
|
+
settled = true;
|
|
112
|
+
if (timerId !== null) clearTimeout(timerId);
|
|
113
|
+
document.removeEventListener('deviceready', done, false);
|
|
114
|
+
resolve();
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
document.addEventListener('deviceready', done, false);
|
|
118
|
+
|
|
119
|
+
// Safety net: `deviceready` fires only once, so if it already fired
|
|
120
|
+
// before this listener was attached we would wait forever. Resolve
|
|
121
|
+
// after a bounded delay rather than hang the update check.
|
|
122
|
+
timerId = setTimeout(done, 5000);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Convert a Cordova callback-style function to a Promise.
|
|
128
|
+
* @param {Function} fn
|
|
129
|
+
* @returns {Promise<any>}
|
|
130
|
+
* @private
|
|
131
|
+
*/
|
|
132
|
+
_promisify(fn) {
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
try {
|
|
135
|
+
fn(resolve, reject);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
reject(err);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = CordovaProvider;
|