@otakit/capacitor-updater 2.0.0 → 2.1.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/{UpdatekitUpdater.podspec → OtaKitUpdater.podspec} +1 -1
- package/README.md +5 -8
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/{updatekit → otakit}/updater/BundleInfo.java +1 -1
- package/android/src/main/java/com/{updatekit → otakit}/updater/BundleStatus.java +1 -1
- package/android/src/main/java/com/{updatekit → otakit}/updater/BundleStore.java +1 -1
- package/android/src/main/java/com/{updatekit → otakit}/updater/DateUtils.java +1 -1
- package/android/src/main/java/com/{updatekit/updater/StatsClient.java → otakit/updater/DeviceEventClient.java} +31 -20
- package/android/src/main/java/com/{updatekit → otakit}/updater/HashUtils.java +1 -1
- package/android/src/main/java/com/{updatekit → otakit}/updater/HostedManifestKeys.java +1 -1
- package/android/src/main/java/com/{updatekit → otakit}/updater/ManifestClient.java +19 -9
- package/android/src/main/java/com/{updatekit → otakit}/updater/ManifestVerifier.java +1 -1
- package/android/src/main/java/com/{updatekit → otakit}/updater/UpdaterPlugin.java +74 -105
- package/android/src/main/java/com/{updatekit → otakit}/updater/ZipUtils.java +1 -1
- package/dist/esm/definitions.d.ts +12 -42
- package/dist/esm/definitions.d.ts.map +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +2 -14
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +4 -9
- package/dist/esm/web.d.ts.map +1 -1
- package/dist/esm/web.js +3 -12
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +5 -26
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +5 -26
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/UpdaterPlugin/{StatsClient.swift → DeviceEventClient.swift} +21 -21
- package/ios/Sources/UpdaterPlugin/Downloader.swift +1 -1
- package/ios/Sources/UpdaterPlugin/ManifestClient.swift +7 -3
- package/ios/Sources/UpdaterPlugin/UpdaterPlugin.m +2 -5
- package/ios/Sources/UpdaterPlugin/UpdaterPlugin.swift +67 -104
- package/package.json +2 -2
|
@@ -19,15 +19,12 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
19
19
|
public let identifier = "UpdaterPlugin"
|
|
20
20
|
public let jsName = "OtaKit"
|
|
21
21
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
22
|
+
CAPPluginMethod(name: "getState", returnType: CAPPluginReturnPromise),
|
|
22
23
|
CAPPluginMethod(name: "check", returnType: CAPPluginReturnPromise),
|
|
23
24
|
CAPPluginMethod(name: "download", returnType: CAPPluginReturnPromise),
|
|
24
25
|
CAPPluginMethod(name: "apply", returnType: CAPPluginReturnPromise),
|
|
25
|
-
CAPPluginMethod(name: "debugGetState", returnType: CAPPluginReturnPromise),
|
|
26
26
|
CAPPluginMethod(name: "notifyAppReady", returnType: CAPPluginReturnPromise),
|
|
27
|
-
CAPPluginMethod(name: "
|
|
28
|
-
CAPPluginMethod(name: "debugListBundles", returnType: CAPPluginReturnPromise),
|
|
29
|
-
CAPPluginMethod(name: "debugDeleteBundle", returnType: CAPPluginReturnPromise),
|
|
30
|
-
CAPPluginMethod(name: "debugGetLastFailure", returnType: CAPPluginReturnPromise),
|
|
27
|
+
CAPPluginMethod(name: "getLastFailure", returnType: CAPPluginReturnPromise),
|
|
31
28
|
]
|
|
32
29
|
|
|
33
30
|
private let store = BundleStore()
|
|
@@ -38,7 +35,7 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
38
35
|
private var appReadyTimeoutMs = 10_000
|
|
39
36
|
private var allowInsecureUrls = false
|
|
40
37
|
private var updateMode: UpdateMode = .nextLaunch
|
|
41
|
-
private var
|
|
38
|
+
private var ingestUrl = UpdaterPlugin.defaultIngestURL
|
|
42
39
|
private var cdnUrl = UpdaterPlugin.defaultCdnURL
|
|
43
40
|
private var appId: String?
|
|
44
41
|
private var channel: String?
|
|
@@ -49,22 +46,21 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
49
46
|
private let isCheckInProgress = NSLock()
|
|
50
47
|
private var checkInProgress = false
|
|
51
48
|
private var foregroundObserver: NSObjectProtocol?
|
|
52
|
-
private static let
|
|
49
|
+
private static let defaultIngestURL = "https://ingest.otakit.app/v1"
|
|
53
50
|
private static let defaultCdnURL = "https://cdn.otakit.app"
|
|
54
|
-
private static let
|
|
51
|
+
private static let ingestPathSuffix = "/v1"
|
|
55
52
|
private static let lastCheckTimestampKey = "otakit_last_check_timestamp"
|
|
56
53
|
|
|
57
54
|
public override func load() {
|
|
58
|
-
let
|
|
55
|
+
let envIngestUrl = ProcessInfo.processInfo.environment["OTAKIT_INGEST_URL"]
|
|
59
56
|
let envCdnUrl = ProcessInfo.processInfo.environment["OTAKIT_CDN_URL"]
|
|
60
|
-
|
|
61
|
-
configured: getConfig().getString("
|
|
62
|
-
env:
|
|
57
|
+
ingestUrl = resolveIngestUrl(
|
|
58
|
+
configured: getConfig().getString("ingestUrl"),
|
|
59
|
+
env: envIngestUrl
|
|
63
60
|
)
|
|
64
61
|
cdnUrl = resolveCdnUrl(
|
|
65
62
|
configured: getConfig().getString("cdnUrl"),
|
|
66
|
-
env: envCdnUrl
|
|
67
|
-
resolvedUpdateUrl: updateUrl
|
|
63
|
+
env: envCdnUrl
|
|
68
64
|
)
|
|
69
65
|
appId = getConfig().getString("appId")
|
|
70
66
|
channel = trimToNil(getConfig().getString("channel"))
|
|
@@ -86,11 +82,11 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
86
82
|
return (kid: kid, key: keyData)
|
|
87
83
|
}
|
|
88
84
|
if manifestKeys.isEmpty && !rawKeys.isEmpty {
|
|
89
|
-
print("[
|
|
85
|
+
print("[OtaKit] ERROR: manifestKeys configured but all entries are invalid. Manifest verification will reject all updates.")
|
|
90
86
|
manifestKeys = [(kid: "_invalid_", key: Data())]
|
|
91
87
|
}
|
|
92
88
|
} else if rawKeysValue != nil {
|
|
93
|
-
print("[
|
|
89
|
+
print("[OtaKit] ERROR: manifestKeys has wrong format (expected array of {kid, key}). Manifest verification will reject all updates.")
|
|
94
90
|
manifestKeys = [(kid: "_invalid_", key: Data())]
|
|
95
91
|
}
|
|
96
92
|
|
|
@@ -269,7 +265,7 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
269
265
|
}
|
|
270
266
|
}
|
|
271
267
|
|
|
272
|
-
@objc func
|
|
268
|
+
@objc func getState(_ call: CAPPluginCall) {
|
|
273
269
|
let current = store.getCurrentBundle()
|
|
274
270
|
let staged: [String: Any]? = {
|
|
275
271
|
guard let stagedId = store.getStagedBundleId() else {
|
|
@@ -394,9 +390,10 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
394
390
|
notifyListeners("appReady", data: current.toDictionary())
|
|
395
391
|
}
|
|
396
392
|
|
|
397
|
-
|
|
393
|
+
sendDeviceEvent(
|
|
398
394
|
action: .applied,
|
|
399
395
|
bundleVersion: current.version,
|
|
396
|
+
runtimeVersion: current.runtimeVersion,
|
|
400
397
|
channel: current.channel,
|
|
401
398
|
releaseId: current.releaseId
|
|
402
399
|
)
|
|
@@ -410,58 +407,7 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
410
407
|
call.resolve()
|
|
411
408
|
}
|
|
412
409
|
|
|
413
|
-
@objc func
|
|
414
|
-
cancelTrialTimeout()
|
|
415
|
-
store.setCurrentBundleId(nil)
|
|
416
|
-
store.setStagedBundleId(nil)
|
|
417
|
-
store.setFallbackBundleId(nil)
|
|
418
|
-
store.setFailedBundle(nil)
|
|
419
|
-
applyServerBasePath(nil)
|
|
420
|
-
call.resolve()
|
|
421
|
-
reloadWebView()
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
@objc func debugListBundles(_ call: CAPPluginCall) {
|
|
425
|
-
let bundles = store.listDownloadedBundles().map { $0.toDictionary() }
|
|
426
|
-
call.resolve(["bundles": bundles])
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
@objc func debugDeleteBundle(_ call: CAPPluginCall) {
|
|
430
|
-
guard let bundleId = call.getString("bundleId") else {
|
|
431
|
-
call.reject("Missing bundleId")
|
|
432
|
-
return
|
|
433
|
-
}
|
|
434
|
-
guard store.bundleExists(id: bundleId) else {
|
|
435
|
-
call.reject("Bundle not found")
|
|
436
|
-
return
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
let current = store.getCurrentBundle()
|
|
440
|
-
if current.id == bundleId {
|
|
441
|
-
call.reject("Cannot delete current bundle")
|
|
442
|
-
return
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
let fallback = store.getFallbackBundle()
|
|
446
|
-
if fallback.id == bundleId {
|
|
447
|
-
call.reject("Cannot delete fallback bundle")
|
|
448
|
-
return
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
if store.getStagedBundleId() == bundleId {
|
|
452
|
-
call.reject("Cannot delete staged bundle")
|
|
453
|
-
return
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
do {
|
|
457
|
-
try store.deleteBundle(id: bundleId)
|
|
458
|
-
call.resolve()
|
|
459
|
-
} catch {
|
|
460
|
-
call.reject("Failed to delete bundle: \(error.localizedDescription)")
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
@objc func debugGetLastFailure(_ call: CAPPluginCall) {
|
|
410
|
+
@objc func getLastFailure(_ call: CAPPluginCall) {
|
|
465
411
|
guard let failed = store.getFailedBundle() else {
|
|
466
412
|
call.resolve()
|
|
467
413
|
return
|
|
@@ -593,12 +539,13 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
593
539
|
code: 1,
|
|
594
540
|
userInfo: [NSLocalizedDescriptionKey: "Insufficient disk space"]
|
|
595
541
|
)
|
|
596
|
-
|
|
542
|
+
sendDeviceEvent(
|
|
597
543
|
action: .downloadError,
|
|
598
544
|
bundleVersion: version,
|
|
545
|
+
runtimeVersion: runtimeVersion,
|
|
599
546
|
channel: channel,
|
|
600
547
|
releaseId: releaseId,
|
|
601
|
-
|
|
548
|
+
detail: "insufficient_disk_space"
|
|
602
549
|
)
|
|
603
550
|
throw error
|
|
604
551
|
}
|
|
@@ -608,7 +555,7 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
608
555
|
let zipURL = try await downloader.download(from: url)
|
|
609
556
|
|
|
610
557
|
let extractDirectory = fileManager.temporaryDirectory
|
|
611
|
-
.appendingPathComponent("
|
|
558
|
+
.appendingPathComponent("otakit-extract-\(UUID().uuidString)", isDirectory: true)
|
|
612
559
|
defer {
|
|
613
560
|
try? fileManager.removeItem(at: zipURL)
|
|
614
561
|
try? fileManager.removeItem(at: extractDirectory)
|
|
@@ -659,9 +606,10 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
659
606
|
cleanupSupersededStagedBundle(previousStagedId: previousStagedId, replacementId: bundleId)
|
|
660
607
|
|
|
661
608
|
notifyListeners("downloadComplete", data: info.toDictionary())
|
|
662
|
-
|
|
609
|
+
sendDeviceEvent(
|
|
663
610
|
action: .downloaded,
|
|
664
611
|
bundleVersion: version,
|
|
612
|
+
runtimeVersion: runtimeVersion,
|
|
665
613
|
channel: channel,
|
|
666
614
|
releaseId: releaseId
|
|
667
615
|
)
|
|
@@ -671,12 +619,13 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
671
619
|
"version": version,
|
|
672
620
|
"error": error.localizedDescription,
|
|
673
621
|
])
|
|
674
|
-
|
|
622
|
+
sendDeviceEvent(
|
|
675
623
|
action: .downloadError,
|
|
676
624
|
bundleVersion: version,
|
|
625
|
+
runtimeVersion: runtimeVersion,
|
|
677
626
|
channel: channel,
|
|
678
627
|
releaseId: releaseId,
|
|
679
|
-
|
|
628
|
+
detail: error.localizedDescription
|
|
680
629
|
)
|
|
681
630
|
throw error
|
|
682
631
|
}
|
|
@@ -808,12 +757,13 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
808
757
|
store.setFailedBundle(failed)
|
|
809
758
|
store.setStagedBundleId(nil)
|
|
810
759
|
|
|
811
|
-
|
|
760
|
+
sendDeviceEvent(
|
|
812
761
|
action: .rollback,
|
|
813
762
|
bundleVersion: current.version,
|
|
763
|
+
runtimeVersion: current.runtimeVersion,
|
|
814
764
|
channel: current.channel,
|
|
815
765
|
releaseId: current.releaseId,
|
|
816
|
-
|
|
766
|
+
detail: reason
|
|
817
767
|
)
|
|
818
768
|
|
|
819
769
|
let fallback = store.getFallbackBundle()
|
|
@@ -906,9 +856,7 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
906
856
|
if let runtimeVersion = latest.runtimeVersion {
|
|
907
857
|
payload["runtimeVersion"] = runtimeVersion
|
|
908
858
|
}
|
|
909
|
-
|
|
910
|
-
payload["releaseId"] = releaseId
|
|
911
|
-
}
|
|
859
|
+
payload["releaseId"] = latest.releaseId
|
|
912
860
|
return payload
|
|
913
861
|
}
|
|
914
862
|
|
|
@@ -932,9 +880,8 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
932
880
|
return false
|
|
933
881
|
}
|
|
934
882
|
|
|
935
|
-
if let
|
|
936
|
-
|
|
937
|
-
latestReleaseId == bundleReleaseId {
|
|
883
|
+
if let bundleReleaseId = bundle.releaseId,
|
|
884
|
+
latest.releaseId == bundleReleaseId {
|
|
938
885
|
return true
|
|
939
886
|
}
|
|
940
887
|
|
|
@@ -1023,26 +970,41 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1023
970
|
return "\(normalized)-\(suffix)"
|
|
1024
971
|
}
|
|
1025
972
|
|
|
1026
|
-
private func
|
|
1027
|
-
action:
|
|
973
|
+
private func sendDeviceEvent(
|
|
974
|
+
action: DeviceEventAction,
|
|
1028
975
|
bundleVersion: String? = nil,
|
|
976
|
+
runtimeVersion: String? = nil,
|
|
1029
977
|
channel: String? = nil,
|
|
1030
978
|
releaseId: String? = nil,
|
|
1031
|
-
|
|
979
|
+
detail: String? = nil
|
|
1032
980
|
) {
|
|
1033
981
|
guard let appId else {
|
|
1034
982
|
return
|
|
1035
983
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
984
|
+
guard let bundleVersion = trimToNil(bundleVersion) else {
|
|
985
|
+
print("[OtaKit] Skipping device event without bundleVersion")
|
|
986
|
+
return
|
|
987
|
+
}
|
|
988
|
+
guard let releaseId = trimToNil(releaseId) else {
|
|
989
|
+
print("[OtaKit] Skipping device event without releaseId")
|
|
990
|
+
return
|
|
991
|
+
}
|
|
992
|
+
let nativeBuild = store.nativeBuild.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
993
|
+
guard !nativeBuild.isEmpty else {
|
|
994
|
+
print("[OtaKit] Skipping device event without nativeBuild")
|
|
995
|
+
return
|
|
996
|
+
}
|
|
997
|
+
DeviceEventClient.send(
|
|
998
|
+
ingestUrl: ingestUrl,
|
|
1038
999
|
appId: appId,
|
|
1039
1000
|
platform: "ios",
|
|
1040
1001
|
action: action,
|
|
1041
1002
|
bundleVersion: bundleVersion,
|
|
1042
1003
|
channel: channel,
|
|
1004
|
+
runtimeVersion: trimToNil(runtimeVersion),
|
|
1043
1005
|
releaseId: releaseId,
|
|
1044
|
-
nativeBuild:
|
|
1045
|
-
|
|
1006
|
+
nativeBuild: nativeBuild,
|
|
1007
|
+
detail: detail
|
|
1046
1008
|
)
|
|
1047
1009
|
}
|
|
1048
1010
|
|
|
@@ -1061,21 +1023,27 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1061
1023
|
return trimmed.isEmpty ? nil : trimmed
|
|
1062
1024
|
}
|
|
1063
1025
|
|
|
1064
|
-
private func
|
|
1026
|
+
private func resolveIngestUrl(
|
|
1027
|
+
configured: String?,
|
|
1028
|
+
env: String?
|
|
1029
|
+
) -> String {
|
|
1065
1030
|
let configuredValue = configured?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
1066
1031
|
if let configuredValue, !configuredValue.isEmpty {
|
|
1067
|
-
return
|
|
1032
|
+
return normalizeIngestUrl(configuredValue)
|
|
1068
1033
|
}
|
|
1069
1034
|
|
|
1070
1035
|
let envValue = env?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
1071
1036
|
if let envValue, !envValue.isEmpty {
|
|
1072
|
-
return
|
|
1037
|
+
return normalizeIngestUrl(envValue)
|
|
1073
1038
|
}
|
|
1074
1039
|
|
|
1075
|
-
return UpdaterPlugin.
|
|
1040
|
+
return UpdaterPlugin.defaultIngestURL
|
|
1076
1041
|
}
|
|
1077
1042
|
|
|
1078
|
-
private func resolveCdnUrl(
|
|
1043
|
+
private func resolveCdnUrl(
|
|
1044
|
+
configured: String?,
|
|
1045
|
+
env: String?
|
|
1046
|
+
) -> String {
|
|
1079
1047
|
let configuredValue = configured?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
1080
1048
|
if let configuredValue, !configuredValue.isEmpty {
|
|
1081
1049
|
return normalizeCdnUrl(configuredValue)
|
|
@@ -1086,15 +1054,10 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1086
1054
|
return normalizeCdnUrl(envValue)
|
|
1087
1055
|
}
|
|
1088
1056
|
|
|
1089
|
-
if resolvedUpdateUrl.lowercased() != UpdaterPlugin.defaultUpdateURL.lowercased(),
|
|
1090
|
-
resolvedUpdateUrl.lowercased().hasSuffix(UpdaterPlugin.apiPathSuffix) {
|
|
1091
|
-
return String(resolvedUpdateUrl.dropLast(UpdaterPlugin.apiPathSuffix.count))
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
1057
|
return UpdaterPlugin.defaultCdnURL
|
|
1095
1058
|
}
|
|
1096
1059
|
|
|
1097
|
-
private func
|
|
1060
|
+
private func normalizeIngestUrl(_ raw: String) -> String {
|
|
1098
1061
|
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
1099
1062
|
let withoutTrailingSlash = trimmed.replacingOccurrences(
|
|
1100
1063
|
of: "/+$",
|
|
@@ -1102,11 +1065,11 @@ public class UpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1102
1065
|
options: .regularExpression
|
|
1103
1066
|
)
|
|
1104
1067
|
|
|
1105
|
-
if withoutTrailingSlash.lowercased().hasSuffix(UpdaterPlugin.
|
|
1068
|
+
if withoutTrailingSlash.lowercased().hasSuffix(UpdaterPlugin.ingestPathSuffix) {
|
|
1106
1069
|
return withoutTrailingSlash
|
|
1107
1070
|
}
|
|
1108
1071
|
|
|
1109
|
-
return withoutTrailingSlash + UpdaterPlugin.
|
|
1072
|
+
return withoutTrailingSlash + UpdaterPlugin.ingestPathSuffix
|
|
1110
1073
|
}
|
|
1111
1074
|
|
|
1112
1075
|
private func normalizeCdnUrl(_ raw: String) -> String {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@otakit/capacitor-updater",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Capacitor plugin for OTA updates",
|
|
5
5
|
"main": "dist/plugin.cjs.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"dist/",
|
|
17
17
|
"ios/Sources/",
|
|
18
18
|
"ios/Tests/",
|
|
19
|
-
"
|
|
19
|
+
"OtaKitUpdater.podspec"
|
|
20
20
|
],
|
|
21
21
|
"author": "OtaKit",
|
|
22
22
|
"license": "MIT",
|