@otakit/capacitor-updater 1.1.0 → 2.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/README.md +39 -14
- package/android/src/main/java/com/updatekit/updater/BundleInfo.java +34 -2
- package/android/src/main/java/com/updatekit/updater/BundleStore.java +9 -2
- package/android/src/main/java/com/updatekit/updater/HostedManifestKeys.java +5 -6
- package/android/src/main/java/com/updatekit/updater/ManifestClient.java +44 -55
- package/android/src/main/java/com/updatekit/updater/ManifestVerifier.java +24 -25
- package/android/src/main/java/com/updatekit/updater/UpdaterPlugin.java +164 -74
- package/dist/esm/definitions.d.ts +11 -25
- package/dist/esm/definitions.d.ts.map +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +0 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +0 -4
- package/dist/esm/web.d.ts.map +1 -1
- package/dist/esm/web.js +0 -8
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +0 -10
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +0 -10
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/UpdaterPlugin/BundleInfo.swift +5 -0
- package/ios/Sources/UpdaterPlugin/BundleStore.swift +3 -0
- package/ios/Sources/UpdaterPlugin/HostedManifestKeys.swift +5 -5
- package/ios/Sources/UpdaterPlugin/ManifestClient.swift +44 -45
- package/ios/Sources/UpdaterPlugin/ManifestVerifier.swift +25 -24
- package/ios/Sources/UpdaterPlugin/UpdaterPlugin.m +0 -2
- package/ios/Sources/UpdaterPlugin/UpdaterPlugin.swift +141 -67
- package/package.json +1 -1
|
@@ -23,8 +23,6 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
23
23
|
CAPPluginMethod(name: "download", returnType: CAPPluginReturnPromise),
|
|
24
24
|
CAPPluginMethod(name: "apply", returnType: CAPPluginReturnPromise),
|
|
25
25
|
CAPPluginMethod(name: "debugGetState", returnType: CAPPluginReturnPromise),
|
|
26
|
-
CAPPluginMethod(name: "debugCheck", returnType: CAPPluginReturnPromise),
|
|
27
|
-
CAPPluginMethod(name: "debugDownload", returnType: CAPPluginReturnPromise),
|
|
28
26
|
CAPPluginMethod(name: "notifyAppReady", returnType: CAPPluginReturnPromise),
|
|
29
27
|
CAPPluginMethod(name: "debugReset", returnType: CAPPluginReturnPromise),
|
|
30
28
|
CAPPluginMethod(name: "debugListBundles", returnType: CAPPluginReturnPromise),
|
|
@@ -41,8 +39,10 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
41
39
|
private var allowInsecureUrls = false
|
|
42
40
|
private var updateMode: UpdateMode = .nextLaunch
|
|
43
41
|
private var updateUrl = UpdaterPlugin.defaultUpdateURL
|
|
42
|
+
private var cdnUrl = UpdaterPlugin.defaultCdnURL
|
|
44
43
|
private var appId: String?
|
|
45
44
|
private var channel: String?
|
|
45
|
+
private var runtimeVersion: String?
|
|
46
46
|
private var manifestKeys: [(kid: String, key: Data)] = []
|
|
47
47
|
private var trialTimeoutWorkItem: DispatchWorkItem?
|
|
48
48
|
private var checkIntervalMs: Int = 600_000
|
|
@@ -50,17 +50,26 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
50
50
|
private var checkInProgress = false
|
|
51
51
|
private var foregroundObserver: NSObjectProtocol?
|
|
52
52
|
private static let defaultUpdateURL = "https://www.otakit.app/api/v1"
|
|
53
|
+
private static let defaultCdnURL = "https://cdn.otakit.app"
|
|
53
54
|
private static let apiPathSuffix = "/api/v1"
|
|
54
55
|
private static let lastCheckTimestampKey = "otakit_last_check_timestamp"
|
|
55
56
|
|
|
56
57
|
public override func load() {
|
|
57
58
|
let envUpdateUrl = ProcessInfo.processInfo.environment["OTAKIT_SERVER_URL"]
|
|
59
|
+
let envCdnUrl = ProcessInfo.processInfo.environment["OTAKIT_CDN_URL"]
|
|
58
60
|
updateUrl = resolveUpdateUrl(
|
|
59
61
|
configured: getConfig().getString("serverUrl"),
|
|
60
62
|
env: envUpdateUrl
|
|
61
63
|
)
|
|
64
|
+
cdnUrl = resolveCdnUrl(
|
|
65
|
+
configured: getConfig().getString("cdnUrl"),
|
|
66
|
+
env: envCdnUrl,
|
|
67
|
+
resolvedUpdateUrl: updateUrl
|
|
68
|
+
)
|
|
62
69
|
appId = getConfig().getString("appId")
|
|
63
70
|
channel = trimToNil(getConfig().getString("channel"))
|
|
71
|
+
runtimeVersion = trimToNil(getConfig().getString("runtimeVersion"))
|
|
72
|
+
store.appRuntimeVersion = runtimeVersion
|
|
64
73
|
allowInsecureUrls = getConfig().getBoolean("allowInsecureUrls", false)
|
|
65
74
|
let updateModeRaw = getConfig().getString("updateMode", UpdateMode.nextLaunch.rawValue)
|
|
66
75
|
updateMode = resolveUpdateMode(configured: updateModeRaw)
|
|
@@ -85,10 +94,12 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
85
94
|
manifestKeys = [(kid: "_invalid_", key: Data())]
|
|
86
95
|
}
|
|
87
96
|
|
|
88
|
-
if manifestKeys.isEmpty && HostedManifestKeys.
|
|
97
|
+
if manifestKeys.isEmpty && HostedManifestKeys.matchesManagedManifestURL(cdnUrl) {
|
|
89
98
|
manifestKeys = HostedManifestKeys.defaults
|
|
90
99
|
}
|
|
91
100
|
|
|
101
|
+
pruneIncompatibleBundles()
|
|
102
|
+
|
|
92
103
|
var current = store.getCurrentBundle()
|
|
93
104
|
if current.status == .trial {
|
|
94
105
|
rollbackCurrentBundle(reason: "app_restarted_before_notify")
|
|
@@ -149,7 +160,11 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
149
160
|
return
|
|
150
161
|
}
|
|
151
162
|
|
|
152
|
-
if shouldThrottleCheck() { return }
|
|
163
|
+
if updateMode != .immediate && shouldThrottleCheck() { return }
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if trigger == .launch && updateMode != .immediate && shouldThrottleCheck() {
|
|
167
|
+
return
|
|
153
168
|
}
|
|
154
169
|
|
|
155
170
|
// Acquire in-flight guard (submit-time)
|
|
@@ -161,7 +176,6 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
161
176
|
defer { releaseCheckInProgress() }
|
|
162
177
|
do {
|
|
163
178
|
let result = try await performCheckAndDownload(channel: nil, emitEvents: true)
|
|
164
|
-
recordCheckTimestamp()
|
|
165
179
|
if result != nil {
|
|
166
180
|
activateStagedBundleForReload()
|
|
167
181
|
reloadWebView()
|
|
@@ -179,7 +193,6 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
179
193
|
defer { releaseCheckInProgress() }
|
|
180
194
|
do {
|
|
181
195
|
let result = try await performCheckAndDownload(channel: nil, emitEvents: true)
|
|
182
|
-
recordCheckTimestamp()
|
|
183
196
|
if result != nil {
|
|
184
197
|
activateStagedBundleForReload()
|
|
185
198
|
reloadWebView()
|
|
@@ -277,27 +290,8 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
277
290
|
call.resolve(payload)
|
|
278
291
|
}
|
|
279
292
|
|
|
280
|
-
@objc func debugCheck(_ call: CAPPluginCall) {
|
|
281
|
-
let requestedChannel = call.getString("channel")
|
|
282
|
-
let targetChannel = resolveTargetChannel(requestedChannel)
|
|
283
|
-
Task {
|
|
284
|
-
do {
|
|
285
|
-
let latest = try await fetchLatest(channel: targetChannel)
|
|
286
|
-
if let latest {
|
|
287
|
-
let staged = findMatchingStagedBundle(latest: latest, targetChannel: targetChannel)
|
|
288
|
-
call.resolve(manifestToDictionary(latest, downloaded: staged != nil))
|
|
289
|
-
} else {
|
|
290
|
-
call.resolve()
|
|
291
|
-
}
|
|
292
|
-
} catch {
|
|
293
|
-
call.reject("debugCheck failed: \(error.localizedDescription)")
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
293
|
@objc func check(_ call: CAPPluginCall) {
|
|
299
|
-
|
|
300
|
-
if shouldThrottleCheck() || !claimCheckInProgress() {
|
|
294
|
+
if !claimCheckInProgress() {
|
|
301
295
|
if let stagedId = store.getStagedBundleId(),
|
|
302
296
|
let staged = store.getBundle(id: stagedId) {
|
|
303
297
|
var payload: [String: Any] = [
|
|
@@ -307,6 +301,9 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
307
301
|
"size": 0,
|
|
308
302
|
"downloaded": true,
|
|
309
303
|
]
|
|
304
|
+
if let runtimeVersion = staged.runtimeVersion {
|
|
305
|
+
payload["runtimeVersion"] = runtimeVersion
|
|
306
|
+
}
|
|
310
307
|
if let releaseId = staged.releaseId {
|
|
311
308
|
payload["releaseId"] = releaseId
|
|
312
309
|
}
|
|
@@ -321,9 +318,11 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
321
318
|
defer { releaseCheckInProgress() }
|
|
322
319
|
do {
|
|
323
320
|
let latest = try await fetchLatest(channel: targetChannel)
|
|
324
|
-
// check() does NOT record timestamp — only download() and automatic paths do.
|
|
325
|
-
// This keeps check() -> download() working without the throttle blocking download().
|
|
326
321
|
if let latest {
|
|
322
|
+
if isCurrentBundleLatest(latest: latest, targetChannel: targetChannel) {
|
|
323
|
+
call.resolve()
|
|
324
|
+
return
|
|
325
|
+
}
|
|
327
326
|
let staged = findMatchingStagedBundle(latest: latest, targetChannel: targetChannel)
|
|
328
327
|
call.resolve(manifestToDictionary(latest, downloaded: staged != nil))
|
|
329
328
|
} else {
|
|
@@ -335,28 +334,8 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
335
334
|
}
|
|
336
335
|
}
|
|
337
336
|
|
|
338
|
-
@objc func debugDownload(_ call: CAPPluginCall) {
|
|
339
|
-
let requestedChannel = call.getString("channel")
|
|
340
|
-
Task {
|
|
341
|
-
do {
|
|
342
|
-
let bundle = try await performCheckAndDownload(
|
|
343
|
-
channel: requestedChannel,
|
|
344
|
-
emitEvents: true
|
|
345
|
-
)
|
|
346
|
-
if let bundle {
|
|
347
|
-
call.resolve(bundle.toDictionary())
|
|
348
|
-
} else {
|
|
349
|
-
call.resolve()
|
|
350
|
-
}
|
|
351
|
-
} catch {
|
|
352
|
-
call.reject("debugDownload failed: \(error.localizedDescription)")
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
337
|
@objc func download(_ call: CAPPluginCall) {
|
|
358
|
-
|
|
359
|
-
if shouldThrottleCheck() || !claimCheckInProgress() {
|
|
338
|
+
if !claimCheckInProgress() {
|
|
360
339
|
if let stagedId = store.getStagedBundleId(),
|
|
361
340
|
let staged = store.getBundle(id: stagedId) {
|
|
362
341
|
call.resolve(staged.toDictionary())
|
|
@@ -369,7 +348,6 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
369
348
|
defer { releaseCheckInProgress() }
|
|
370
349
|
do {
|
|
371
350
|
let bundle = try await performCheckAndDownload(channel: nil, emitEvents: true)
|
|
372
|
-
recordCheckTimestamp()
|
|
373
351
|
if let bundle {
|
|
374
352
|
call.resolve(bundle.toDictionary())
|
|
375
353
|
} else {
|
|
@@ -496,15 +474,11 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
496
474
|
throw NSError(domain: "OtaKit", code: 1, userInfo: [NSLocalizedDescriptionKey: "Missing appId in plugin config"])
|
|
497
475
|
}
|
|
498
476
|
|
|
499
|
-
let current = store.getCurrentBundle()
|
|
500
477
|
return try await ManifestClient.fetchLatest(
|
|
501
|
-
|
|
478
|
+
cdnUrl: cdnUrl,
|
|
502
479
|
appId: appId,
|
|
503
480
|
channel: channel,
|
|
504
|
-
|
|
505
|
-
currentReleaseId: current.releaseId,
|
|
506
|
-
nativeBuild: store.nativeBuild,
|
|
507
|
-
platform: "ios",
|
|
481
|
+
runtimeVersion: runtimeVersion,
|
|
508
482
|
allowInsecureUrls: allowInsecureUrls,
|
|
509
483
|
manifestKeys: manifestKeys.map {
|
|
510
484
|
ManifestKey(kid: $0.kid, derData: $0.key)
|
|
@@ -526,6 +500,21 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
526
500
|
return nil
|
|
527
501
|
}
|
|
528
502
|
|
|
503
|
+
guard isCompatibleRuntime(manifest.runtimeVersion) else {
|
|
504
|
+
throw NSError(
|
|
505
|
+
domain: "OtaKit",
|
|
506
|
+
code: 1,
|
|
507
|
+
userInfo: [NSLocalizedDescriptionKey: "Manifest runtimeVersion does not match the installed app runtime"]
|
|
508
|
+
)
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if isCurrentBundleLatest(latest: manifest, targetChannel: targetChannel) {
|
|
512
|
+
if emitEvents {
|
|
513
|
+
notifyListeners("noUpdateAvailable", data: [:])
|
|
514
|
+
}
|
|
515
|
+
return nil
|
|
516
|
+
}
|
|
517
|
+
|
|
529
518
|
let staged = findMatchingStagedBundle(latest: manifest, targetChannel: targetChannel)
|
|
530
519
|
if emitEvents {
|
|
531
520
|
notifyListeners("updateAvailable", data: manifestToDictionary(manifest, downloaded: staged != nil))
|
|
@@ -549,6 +538,7 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
549
538
|
version: manifest.version,
|
|
550
539
|
expectedSha256: manifest.sha256,
|
|
551
540
|
expectedSize: manifest.size,
|
|
541
|
+
runtimeVersion: manifest.runtimeVersion,
|
|
552
542
|
channel: targetChannel,
|
|
553
543
|
releaseId: manifest.releaseId
|
|
554
544
|
)
|
|
@@ -568,6 +558,7 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
568
558
|
version: manifest.version,
|
|
569
559
|
expectedSha256: manifest.sha256,
|
|
570
560
|
expectedSize: manifest.size,
|
|
561
|
+
runtimeVersion: manifest.runtimeVersion,
|
|
571
562
|
channel: targetChannel,
|
|
572
563
|
releaseId: manifest.releaseId
|
|
573
564
|
)
|
|
@@ -588,6 +579,7 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
588
579
|
version: String,
|
|
589
580
|
expectedSha256: String,
|
|
590
581
|
expectedSize: Int? = nil,
|
|
582
|
+
runtimeVersion: String? = nil,
|
|
591
583
|
channel: String? = nil,
|
|
592
584
|
releaseId: String? = nil
|
|
593
585
|
) async throws -> BundleInfo {
|
|
@@ -652,6 +644,7 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
652
644
|
let info = BundleInfo(
|
|
653
645
|
id: bundleId,
|
|
654
646
|
version: version,
|
|
647
|
+
runtimeVersion: runtimeVersion,
|
|
655
648
|
status: .pending,
|
|
656
649
|
downloadedAt: Date(),
|
|
657
650
|
sha256: expectedSha256,
|
|
@@ -723,6 +716,10 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
723
716
|
store.setStagedBundleId(nil)
|
|
724
717
|
return store.getCurrentBundle()
|
|
725
718
|
}
|
|
719
|
+
guard isCompatibleRuntime(staged) else {
|
|
720
|
+
try? store.deleteBundle(id: staged.id)
|
|
721
|
+
return store.getCurrentBundle()
|
|
722
|
+
}
|
|
726
723
|
|
|
727
724
|
store.setCurrentBundleId(staged.id)
|
|
728
725
|
store.setStagedBundleId(nil)
|
|
@@ -737,6 +734,10 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
737
734
|
store.setStagedBundleId(nil)
|
|
738
735
|
return
|
|
739
736
|
}
|
|
737
|
+
guard isCompatibleRuntime(staged) else {
|
|
738
|
+
try? store.deleteBundle(id: staged.id)
|
|
739
|
+
return
|
|
740
|
+
}
|
|
740
741
|
|
|
741
742
|
store.setCurrentBundleId(staged.id)
|
|
742
743
|
store.setStagedBundleId(nil)
|
|
@@ -795,6 +796,7 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
795
796
|
let failed = BundleInfo(
|
|
796
797
|
id: current.id,
|
|
797
798
|
version: current.version,
|
|
799
|
+
runtimeVersion: current.runtimeVersion,
|
|
798
800
|
status: .error,
|
|
799
801
|
downloadedAt: current.downloadedAt,
|
|
800
802
|
sha256: current.sha256,
|
|
@@ -901,15 +903,51 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
901
903
|
"size": latest.size,
|
|
902
904
|
"downloaded": downloaded,
|
|
903
905
|
]
|
|
906
|
+
if let runtimeVersion = latest.runtimeVersion {
|
|
907
|
+
payload["runtimeVersion"] = runtimeVersion
|
|
908
|
+
}
|
|
904
909
|
if let releaseId = latest.releaseId {
|
|
905
910
|
payload["releaseId"] = releaseId
|
|
906
911
|
}
|
|
907
|
-
if let minNativeBuild = latest.minNativeBuild {
|
|
908
|
-
payload["minNativeBuild"] = minNativeBuild
|
|
909
|
-
}
|
|
910
912
|
return payload
|
|
911
913
|
}
|
|
912
914
|
|
|
915
|
+
private func isCurrentBundleLatest(
|
|
916
|
+
latest: LatestManifest,
|
|
917
|
+
targetChannel: String?
|
|
918
|
+
) -> Bool {
|
|
919
|
+
doesBundleMatchLatest(store.getCurrentBundle(), latest: latest, targetChannel: targetChannel)
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
private func doesBundleMatchLatest(
|
|
923
|
+
_ bundle: BundleInfo,
|
|
924
|
+
latest: LatestManifest,
|
|
925
|
+
targetChannel: String?
|
|
926
|
+
) -> Bool {
|
|
927
|
+
if trimToNil(bundle.channel) != targetChannel {
|
|
928
|
+
return false
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
if trimToNil(bundle.runtimeVersion) != trimToNil(latest.runtimeVersion) {
|
|
932
|
+
return false
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
if let latestReleaseId = latest.releaseId,
|
|
936
|
+
let bundleReleaseId = bundle.releaseId,
|
|
937
|
+
latestReleaseId == bundleReleaseId {
|
|
938
|
+
return true
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
if !latest.sha256.isEmpty,
|
|
942
|
+
let bundleSha = bundle.sha256,
|
|
943
|
+
!bundleSha.isEmpty,
|
|
944
|
+
latest.sha256 == bundleSha {
|
|
945
|
+
return true
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
return latest.version == bundle.version
|
|
949
|
+
}
|
|
950
|
+
|
|
913
951
|
private func findMatchingStagedBundle(
|
|
914
952
|
latest: LatestManifest,
|
|
915
953
|
targetChannel: String?
|
|
@@ -926,18 +964,29 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
926
964
|
return nil
|
|
927
965
|
}
|
|
928
966
|
|
|
929
|
-
if
|
|
930
|
-
|
|
931
|
-
latestReleaseId == stagedReleaseId {
|
|
932
|
-
return staged
|
|
967
|
+
if trimToNil(staged.runtimeVersion) != trimToNil(latest.runtimeVersion) {
|
|
968
|
+
return nil
|
|
933
969
|
}
|
|
934
970
|
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
971
|
+
return doesBundleMatchLatest(staged, latest: latest, targetChannel: targetChannel) ? staged : nil
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
private func pruneIncompatibleBundles() {
|
|
975
|
+
for bundle in store.listDownloadedBundles() where !isCompatibleRuntime(bundle) {
|
|
976
|
+
try? store.deleteBundle(id: bundle.id)
|
|
938
977
|
}
|
|
939
978
|
|
|
940
|
-
|
|
979
|
+
if let failed = store.getFailedBundle(), !isCompatibleRuntime(failed) {
|
|
980
|
+
store.setFailedBundle(nil)
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
private func isCompatibleRuntime(_ runtimeVersion: String?) -> Bool {
|
|
985
|
+
trimToNil(runtimeVersion) == self.runtimeVersion
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
private func isCompatibleRuntime(_ bundle: BundleInfo) -> Bool {
|
|
989
|
+
isCompatibleRuntime(bundle.runtimeVersion)
|
|
941
990
|
}
|
|
942
991
|
|
|
943
992
|
private func buildBundleId(from version: String) -> String {
|
|
@@ -1026,6 +1075,25 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1026
1075
|
return UpdaterPlugin.defaultUpdateURL
|
|
1027
1076
|
}
|
|
1028
1077
|
|
|
1078
|
+
private func resolveCdnUrl(configured: String?, env: String?, resolvedUpdateUrl: String) -> String {
|
|
1079
|
+
let configuredValue = configured?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
1080
|
+
if let configuredValue, !configuredValue.isEmpty {
|
|
1081
|
+
return normalizeCdnUrl(configuredValue)
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
let envValue = env?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
1085
|
+
if let envValue, !envValue.isEmpty {
|
|
1086
|
+
return normalizeCdnUrl(envValue)
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
if resolvedUpdateUrl.lowercased() != UpdaterPlugin.defaultUpdateURL.lowercased(),
|
|
1090
|
+
resolvedUpdateUrl.lowercased().hasSuffix(UpdaterPlugin.apiPathSuffix) {
|
|
1091
|
+
return String(resolvedUpdateUrl.dropLast(UpdaterPlugin.apiPathSuffix.count))
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
return UpdaterPlugin.defaultCdnURL
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1029
1097
|
private func normalizeUpdateUrl(_ raw: String) -> String {
|
|
1030
1098
|
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
1031
1099
|
let withoutTrailingSlash = trimmed.replacingOccurrences(
|
|
@@ -1041,6 +1109,12 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1041
1109
|
return withoutTrailingSlash + UpdaterPlugin.apiPathSuffix
|
|
1042
1110
|
}
|
|
1043
1111
|
|
|
1112
|
+
private func normalizeCdnUrl(_ raw: String) -> String {
|
|
1113
|
+
raw
|
|
1114
|
+
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
1115
|
+
.replacingOccurrences(of: "/+$", with: "", options: .regularExpression)
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1044
1118
|
private func getFreeDiskSpace() -> Int64 {
|
|
1045
1119
|
do {
|
|
1046
1120
|
let attributes = try fileManager.attributesOfFileSystem(
|