@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
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["/**\n * Bundle status enum.\n */\nexport var BundleStatus;\n(function (BundleStatus) {\n /** Factory-installed bundle */\n BundleStatus[\"BUILTIN\"] = \"builtin\";\n /** Downloaded and staged, awaiting activation */\n BundleStatus[\"PENDING\"] = \"pending\";\n /** Active but not yet confirmed */\n BundleStatus[\"TRIAL\"] = \"trial\";\n /** Confirmed working */\n BundleStatus[\"SUCCESS\"] = \"success\";\n /** Failed (hash mismatch, extraction error, rollback) */\n BundleStatus[\"ERROR\"] = \"error\";\n})(BundleStatus || (BundleStatus = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst NativeOtaKit = registerPlugin('OtaKit', {\n web: () => import('./web').then((m) => new m.OtaKitWeb()),\n});\n/**\n * Check if result is an empty object (iOS returns {} instead of null)\n */\nfunction isEmptyObject(obj) {\n return obj !== null && typeof obj === 'object' && Object.keys(obj).length === 0;\n}\n/**\n * Wrapped plugin that normalizes null returns from native code.\n * iOS call.resolve() without arguments returns {} instead of null.\n */\nfunction normalizeNullable(value) {\n return isEmptyObject(value) ? null : value;\n}\nfunction toPublicState(value) {\n return {\n current: value.current,\n staged: value.staged,\n builtinVersion: value.builtinVersion,\n };\n}\nasync function getState() {\n return toPublicState(await NativeOtaKit.debugGetState());\n}\nasync function check() {\n return normalizeNullable(await NativeOtaKit.check());\n}\nasync function download() {\n return normalizeNullable(await NativeOtaKit.download());\n}\nfunction apply() {\n return NativeOtaKit.apply();\n}\nasync function update() {\n const state = await getState();\n let latest;\n try {\n latest = await check();\n }\n catch (error) {\n if (state.staged) {\n await apply();\n return;\n }\n throw error;\n }\n if (latest) {\n if (latest.downloaded) {\n await apply();\n return;\n }\n const bundle = await download();\n if (bundle) {\n await apply();\n }\n return;\n }\n if (state.staged) {\n await apply();\n }\n}\nconst OtaKit = {\n getState,\n check,\n download,\n apply,\n update,\n notifyAppReady: () => NativeOtaKit.notifyAppReady(),\n debug: {\n
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["/**\n * Bundle status enum.\n */\nexport var BundleStatus;\n(function (BundleStatus) {\n /** Factory-installed bundle */\n BundleStatus[\"BUILTIN\"] = \"builtin\";\n /** Downloaded and staged, awaiting activation */\n BundleStatus[\"PENDING\"] = \"pending\";\n /** Active but not yet confirmed */\n BundleStatus[\"TRIAL\"] = \"trial\";\n /** Confirmed working */\n BundleStatus[\"SUCCESS\"] = \"success\";\n /** Failed (hash mismatch, extraction error, rollback) */\n BundleStatus[\"ERROR\"] = \"error\";\n})(BundleStatus || (BundleStatus = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst NativeOtaKit = registerPlugin('OtaKit', {\n web: () => import('./web').then((m) => new m.OtaKitWeb()),\n});\n/**\n * Check if result is an empty object (iOS returns {} instead of null)\n */\nfunction isEmptyObject(obj) {\n return obj !== null && typeof obj === 'object' && Object.keys(obj).length === 0;\n}\n/**\n * Wrapped plugin that normalizes null returns from native code.\n * iOS call.resolve() without arguments returns {} instead of null.\n */\nfunction normalizeNullable(value) {\n return isEmptyObject(value) ? null : value;\n}\nfunction toPublicState(value) {\n return {\n current: value.current,\n staged: value.staged,\n builtinVersion: value.builtinVersion,\n };\n}\nasync function getState() {\n return toPublicState(await NativeOtaKit.debugGetState());\n}\nasync function check() {\n return normalizeNullable(await NativeOtaKit.check());\n}\nasync function download() {\n return normalizeNullable(await NativeOtaKit.download());\n}\nfunction apply() {\n return NativeOtaKit.apply();\n}\nasync function update() {\n const state = await getState();\n let latest;\n try {\n latest = await check();\n }\n catch (error) {\n if (state.staged) {\n await apply();\n return;\n }\n throw error;\n }\n if (latest) {\n if (latest.downloaded) {\n await apply();\n return;\n }\n const bundle = await download();\n if (bundle) {\n await apply();\n }\n return;\n }\n if (state.staged) {\n await apply();\n }\n}\nconst OtaKit = {\n getState,\n check,\n download,\n apply,\n update,\n notifyAppReady: () => NativeOtaKit.notifyAppReady(),\n debug: {\n reset: () => NativeOtaKit.debugReset(),\n listBundles: () => NativeOtaKit.debugListBundles(),\n deleteBundle: (options) => NativeOtaKit.debugDeleteBundle(options),\n getLastFailure: async () => normalizeNullable(await NativeOtaKit.debugGetLastFailure()),\n },\n addListener: NativeOtaKit.addListener.bind(NativeOtaKit),\n removeAllListeners: () => NativeOtaKit.removeAllListeners(),\n};\nexport * from './definitions';\nexport { OtaKit };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\n/**\n * Web implementation of the native bridge.\n * Most methods are no-ops since OTA updates don't apply to web\n */\nexport class OtaKitWeb extends WebPlugin {\n BUILTIN_BUNDLE = {\n id: 'builtin',\n version: '0.0.0',\n status: 'builtin',\n };\n async debugGetState() {\n return {\n current: this.BUILTIN_BUNDLE,\n fallback: this.BUILTIN_BUNDLE,\n staged: null,\n builtinVersion: this.BUILTIN_BUNDLE.version,\n };\n }\n async check() {\n console.warn('OtaKit.check() is not supported on web');\n return null;\n }\n async download() {\n console.warn('OtaKit.download() is not supported on web');\n return null;\n }\n async apply() {\n console.warn('OtaKit.apply() is not supported on web');\n throw new Error('OtaKit.apply() is not supported on web');\n }\n async notifyAppReady() {\n // No-op on web, but don't warn - apps should call this unconditionally\n }\n async debugReset() {\n console.warn('OtaKit.debug.reset() is not supported on web');\n }\n async debugListBundles() {\n return { bundles: [] };\n }\n async debugDeleteBundle(_options) {\n console.warn('OtaKit.debug.deleteBundle() is not supported on web');\n }\n async debugGetLastFailure() {\n return null;\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["BundleStatus","registerPlugin","WebPlugin"],"mappings":"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"}
|
package/dist/plugin.js
CHANGED
|
@@ -89,8 +89,6 @@ var capacitorUpdater = (function (exports, core) {
|
|
|
89
89
|
update,
|
|
90
90
|
notifyAppReady: () => NativeOtaKit.notifyAppReady(),
|
|
91
91
|
debug: {
|
|
92
|
-
check: async (options) => normalizeNullable(await NativeOtaKit.debugCheck(options)),
|
|
93
|
-
download: async (options) => normalizeNullable(await NativeOtaKit.debugDownload(options)),
|
|
94
92
|
reset: () => NativeOtaKit.debugReset(),
|
|
95
93
|
listBundles: () => NativeOtaKit.debugListBundles(),
|
|
96
94
|
deleteBundle: (options) => NativeOtaKit.debugDeleteBundle(options),
|
|
@@ -130,14 +128,6 @@ var capacitorUpdater = (function (exports, core) {
|
|
|
130
128
|
console.warn('OtaKit.apply() is not supported on web');
|
|
131
129
|
throw new Error('OtaKit.apply() is not supported on web');
|
|
132
130
|
}
|
|
133
|
-
async debugCheck() {
|
|
134
|
-
console.warn('OtaKit.debug.check() is not supported on web');
|
|
135
|
-
return null;
|
|
136
|
-
}
|
|
137
|
-
async debugDownload(_options) {
|
|
138
|
-
console.warn('OtaKit.debug.download() is not supported on web');
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
131
|
async notifyAppReady() {
|
|
142
132
|
// No-op on web, but don't warn - apps should call this unconditionally
|
|
143
133
|
}
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["/**\n * Bundle status enum.\n */\nexport var BundleStatus;\n(function (BundleStatus) {\n /** Factory-installed bundle */\n BundleStatus[\"BUILTIN\"] = \"builtin\";\n /** Downloaded and staged, awaiting activation */\n BundleStatus[\"PENDING\"] = \"pending\";\n /** Active but not yet confirmed */\n BundleStatus[\"TRIAL\"] = \"trial\";\n /** Confirmed working */\n BundleStatus[\"SUCCESS\"] = \"success\";\n /** Failed (hash mismatch, extraction error, rollback) */\n BundleStatus[\"ERROR\"] = \"error\";\n})(BundleStatus || (BundleStatus = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst NativeOtaKit = registerPlugin('OtaKit', {\n web: () => import('./web').then((m) => new m.OtaKitWeb()),\n});\n/**\n * Check if result is an empty object (iOS returns {} instead of null)\n */\nfunction isEmptyObject(obj) {\n return obj !== null && typeof obj === 'object' && Object.keys(obj).length === 0;\n}\n/**\n * Wrapped plugin that normalizes null returns from native code.\n * iOS call.resolve() without arguments returns {} instead of null.\n */\nfunction normalizeNullable(value) {\n return isEmptyObject(value) ? null : value;\n}\nfunction toPublicState(value) {\n return {\n current: value.current,\n staged: value.staged,\n builtinVersion: value.builtinVersion,\n };\n}\nasync function getState() {\n return toPublicState(await NativeOtaKit.debugGetState());\n}\nasync function check() {\n return normalizeNullable(await NativeOtaKit.check());\n}\nasync function download() {\n return normalizeNullable(await NativeOtaKit.download());\n}\nfunction apply() {\n return NativeOtaKit.apply();\n}\nasync function update() {\n const state = await getState();\n let latest;\n try {\n latest = await check();\n }\n catch (error) {\n if (state.staged) {\n await apply();\n return;\n }\n throw error;\n }\n if (latest) {\n if (latest.downloaded) {\n await apply();\n return;\n }\n const bundle = await download();\n if (bundle) {\n await apply();\n }\n return;\n }\n if (state.staged) {\n await apply();\n }\n}\nconst OtaKit = {\n getState,\n check,\n download,\n apply,\n update,\n notifyAppReady: () => NativeOtaKit.notifyAppReady(),\n debug: {\n
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["/**\n * Bundle status enum.\n */\nexport var BundleStatus;\n(function (BundleStatus) {\n /** Factory-installed bundle */\n BundleStatus[\"BUILTIN\"] = \"builtin\";\n /** Downloaded and staged, awaiting activation */\n BundleStatus[\"PENDING\"] = \"pending\";\n /** Active but not yet confirmed */\n BundleStatus[\"TRIAL\"] = \"trial\";\n /** Confirmed working */\n BundleStatus[\"SUCCESS\"] = \"success\";\n /** Failed (hash mismatch, extraction error, rollback) */\n BundleStatus[\"ERROR\"] = \"error\";\n})(BundleStatus || (BundleStatus = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst NativeOtaKit = registerPlugin('OtaKit', {\n web: () => import('./web').then((m) => new m.OtaKitWeb()),\n});\n/**\n * Check if result is an empty object (iOS returns {} instead of null)\n */\nfunction isEmptyObject(obj) {\n return obj !== null && typeof obj === 'object' && Object.keys(obj).length === 0;\n}\n/**\n * Wrapped plugin that normalizes null returns from native code.\n * iOS call.resolve() without arguments returns {} instead of null.\n */\nfunction normalizeNullable(value) {\n return isEmptyObject(value) ? null : value;\n}\nfunction toPublicState(value) {\n return {\n current: value.current,\n staged: value.staged,\n builtinVersion: value.builtinVersion,\n };\n}\nasync function getState() {\n return toPublicState(await NativeOtaKit.debugGetState());\n}\nasync function check() {\n return normalizeNullable(await NativeOtaKit.check());\n}\nasync function download() {\n return normalizeNullable(await NativeOtaKit.download());\n}\nfunction apply() {\n return NativeOtaKit.apply();\n}\nasync function update() {\n const state = await getState();\n let latest;\n try {\n latest = await check();\n }\n catch (error) {\n if (state.staged) {\n await apply();\n return;\n }\n throw error;\n }\n if (latest) {\n if (latest.downloaded) {\n await apply();\n return;\n }\n const bundle = await download();\n if (bundle) {\n await apply();\n }\n return;\n }\n if (state.staged) {\n await apply();\n }\n}\nconst OtaKit = {\n getState,\n check,\n download,\n apply,\n update,\n notifyAppReady: () => NativeOtaKit.notifyAppReady(),\n debug: {\n reset: () => NativeOtaKit.debugReset(),\n listBundles: () => NativeOtaKit.debugListBundles(),\n deleteBundle: (options) => NativeOtaKit.debugDeleteBundle(options),\n getLastFailure: async () => normalizeNullable(await NativeOtaKit.debugGetLastFailure()),\n },\n addListener: NativeOtaKit.addListener.bind(NativeOtaKit),\n removeAllListeners: () => NativeOtaKit.removeAllListeners(),\n};\nexport * from './definitions';\nexport { OtaKit };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\n/**\n * Web implementation of the native bridge.\n * Most methods are no-ops since OTA updates don't apply to web\n */\nexport class OtaKitWeb extends WebPlugin {\n BUILTIN_BUNDLE = {\n id: 'builtin',\n version: '0.0.0',\n status: 'builtin',\n };\n async debugGetState() {\n return {\n current: this.BUILTIN_BUNDLE,\n fallback: this.BUILTIN_BUNDLE,\n staged: null,\n builtinVersion: this.BUILTIN_BUNDLE.version,\n };\n }\n async check() {\n console.warn('OtaKit.check() is not supported on web');\n return null;\n }\n async download() {\n console.warn('OtaKit.download() is not supported on web');\n return null;\n }\n async apply() {\n console.warn('OtaKit.apply() is not supported on web');\n throw new Error('OtaKit.apply() is not supported on web');\n }\n async notifyAppReady() {\n // No-op on web, but don't warn - apps should call this unconditionally\n }\n async debugReset() {\n console.warn('OtaKit.debug.reset() is not supported on web');\n }\n async debugListBundles() {\n return { bundles: [] };\n }\n async debugDeleteBundle(_options) {\n console.warn('OtaKit.debug.deleteBundle() is not supported on web');\n }\n async debugGetLastFailure() {\n return null;\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["BundleStatus","registerPlugin","WebPlugin"],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ import Foundation
|
|
|
3
3
|
struct BundleInfo: Codable {
|
|
4
4
|
let id: String
|
|
5
5
|
let version: String
|
|
6
|
+
let runtimeVersion: String?
|
|
6
7
|
let status: BundleStatus
|
|
7
8
|
let downloadedAt: Date?
|
|
8
9
|
let sha256: String?
|
|
@@ -21,6 +22,10 @@ struct BundleInfo: Codable {
|
|
|
21
22
|
"status": status.rawValue,
|
|
22
23
|
]
|
|
23
24
|
|
|
25
|
+
if let runtimeVersion {
|
|
26
|
+
result["runtimeVersion"] = runtimeVersion
|
|
27
|
+
}
|
|
28
|
+
|
|
24
29
|
if let downloadedAt {
|
|
25
30
|
result["downloadedAt"] = ISO8601DateFormatter().string(from: downloadedAt)
|
|
26
31
|
}
|
|
@@ -10,6 +10,7 @@ final class BundleStore {
|
|
|
10
10
|
|
|
11
11
|
private let defaults = UserDefaults.standard
|
|
12
12
|
private let fileManager = FileManager.default
|
|
13
|
+
var appRuntimeVersion: String?
|
|
13
14
|
|
|
14
15
|
private lazy var decoder: JSONDecoder = {
|
|
15
16
|
let decoder = JSONDecoder()
|
|
@@ -51,6 +52,7 @@ final class BundleStore {
|
|
|
51
52
|
BundleInfo(
|
|
52
53
|
id: "builtin",
|
|
53
54
|
version: builtinVersion,
|
|
55
|
+
runtimeVersion: appRuntimeVersion,
|
|
54
56
|
status: .builtin,
|
|
55
57
|
downloadedAt: nil,
|
|
56
58
|
sha256: nil,
|
|
@@ -202,6 +204,7 @@ final class BundleStore {
|
|
|
202
204
|
bundle = BundleInfo(
|
|
203
205
|
id: bundle.id,
|
|
204
206
|
version: bundle.version,
|
|
207
|
+
runtimeVersion: bundle.runtimeVersion,
|
|
205
208
|
status: status,
|
|
206
209
|
downloadedAt: bundle.downloadedAt,
|
|
207
210
|
sha256: bundle.sha256,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
|
|
3
3
|
enum HostedManifestKeys {
|
|
4
|
-
static let
|
|
4
|
+
static let managedCdnURL = "https://cdn.otakit.app"
|
|
5
5
|
|
|
6
6
|
static let defaults: [(kid: String, key: Data)] = [
|
|
7
7
|
(
|
|
@@ -12,12 +12,12 @@ enum HostedManifestKeys {
|
|
|
12
12
|
)
|
|
13
13
|
]
|
|
14
14
|
|
|
15
|
-
static func
|
|
16
|
-
let normalized =
|
|
15
|
+
static func matchesManagedManifestURL(_ cdnUrl: String) -> Bool {
|
|
16
|
+
let normalized = cdnUrl
|
|
17
17
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
18
18
|
.replacingOccurrences(of: "/+$", with: "", options: .regularExpression)
|
|
19
19
|
.lowercased()
|
|
20
|
-
return normalized ==
|
|
21
|
-
|| normalized == "https://otakit.app
|
|
20
|
+
return normalized == managedCdnURL
|
|
21
|
+
|| normalized == "https://www.otakit.app"
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
|
|
3
|
+
private let baseChannelKey = "__base__"
|
|
4
|
+
private let defaultRuntimeKey = "__default__"
|
|
5
|
+
|
|
3
6
|
struct LatestManifest {
|
|
4
7
|
let version: String
|
|
5
8
|
let url: String
|
|
6
9
|
let sha256: String
|
|
7
10
|
let size: Int
|
|
8
|
-
let
|
|
11
|
+
let runtimeVersion: String?
|
|
9
12
|
let releaseId: String?
|
|
10
|
-
let signature: ManifestSignature?
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
struct ManifestSignature {
|
|
@@ -39,41 +41,34 @@ enum ManifestClient {
|
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
static func fetchLatest(
|
|
42
|
-
|
|
44
|
+
cdnUrl: String,
|
|
43
45
|
appId: String,
|
|
44
46
|
channel: String?,
|
|
45
|
-
|
|
46
|
-
currentReleaseId: String?,
|
|
47
|
-
nativeBuild: String,
|
|
48
|
-
platform: String,
|
|
47
|
+
runtimeVersion: String?,
|
|
49
48
|
allowInsecureUrls: Bool = false,
|
|
50
49
|
manifestKeys: [ManifestKey] = []
|
|
51
50
|
) async throws -> LatestManifest? {
|
|
52
|
-
let sanitizedBase =
|
|
51
|
+
let sanitizedBase = cdnUrl.replacingOccurrences(
|
|
53
52
|
of: "/+$",
|
|
54
53
|
with: "",
|
|
55
54
|
options: .regularExpression
|
|
56
55
|
)
|
|
57
56
|
|
|
58
|
-
|
|
57
|
+
let channelKey = channel?.trimmingCharacters(in: .whitespacesAndNewlines).nilIfEmpty
|
|
58
|
+
?? baseChannelKey
|
|
59
|
+
let runtimeKey = runtimeVersion?.trimmingCharacters(in: .whitespacesAndNewlines).nilIfEmpty
|
|
60
|
+
?? defaultRuntimeKey
|
|
61
|
+
|
|
62
|
+
guard let url = URL(
|
|
63
|
+
string: "\(sanitizedBase)/manifests/\(appId)/\(channelKey)/\(runtimeKey)/manifest.json"
|
|
64
|
+
) else {
|
|
59
65
|
throw ManifestClientError.invalidURL
|
|
60
66
|
}
|
|
61
|
-
let url = baseURL.appendingPathComponent("manifest")
|
|
62
67
|
|
|
63
68
|
try requireHTTPS(url: url, allowInsecure: allowInsecureUrls)
|
|
64
69
|
|
|
65
70
|
var request = URLRequest(url: url)
|
|
66
71
|
request.httpMethod = "GET"
|
|
67
|
-
request.setValue(appId, forHTTPHeaderField: "X-App-Id")
|
|
68
|
-
request.setValue(platform, forHTTPHeaderField: "X-Platform")
|
|
69
|
-
if let channel, !channel.isEmpty {
|
|
70
|
-
request.setValue(channel, forHTTPHeaderField: "X-Channel")
|
|
71
|
-
}
|
|
72
|
-
request.setValue(currentVersion, forHTTPHeaderField: "X-Current-Version")
|
|
73
|
-
if let currentReleaseId, !currentReleaseId.isEmpty {
|
|
74
|
-
request.setValue(currentReleaseId, forHTTPHeaderField: "X-Release-Id")
|
|
75
|
-
}
|
|
76
|
-
request.setValue(nativeBuild, forHTTPHeaderField: "X-Native-Build")
|
|
77
72
|
request.timeoutInterval = 30
|
|
78
73
|
|
|
79
74
|
let (data, response) = try await URLSession.shared.data(for: request)
|
|
@@ -81,7 +76,7 @@ enum ManifestClient {
|
|
|
81
76
|
throw ManifestClientError.invalidResponse
|
|
82
77
|
}
|
|
83
78
|
|
|
84
|
-
if httpResponse.statusCode == 204 {
|
|
79
|
+
if httpResponse.statusCode == 404 || httpResponse.statusCode == 204 {
|
|
85
80
|
return nil
|
|
86
81
|
}
|
|
87
82
|
|
|
@@ -102,25 +97,13 @@ enum ManifestClient {
|
|
|
102
97
|
throw ManifestClientError.invalidResponse
|
|
103
98
|
}
|
|
104
99
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
} else if let stringValue = object["minNativeBuild"] as? String {
|
|
109
|
-
minNativeBuild = Int(stringValue)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
var signature: ManifestSignature?
|
|
113
|
-
if let sigObj = object["signature"] as? [String: Any],
|
|
114
|
-
let kid = sigObj["kid"] as? String,
|
|
115
|
-
let sig = sigObj["sig"] as? String,
|
|
116
|
-
let iat = sigObj["iat"] as? Int,
|
|
117
|
-
let exp = sigObj["exp"] as? Int {
|
|
118
|
-
signature = ManifestSignature(kid: kid, sig: sig, iat: iat, exp: exp)
|
|
119
|
-
}
|
|
100
|
+
let runtimeVersion = (object["runtimeVersion"] as? String)?
|
|
101
|
+
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
102
|
+
.nilIfEmpty
|
|
120
103
|
|
|
104
|
+
let signature = parseSignature(object["signature"])
|
|
121
105
|
let releaseId = object["releaseId"] as? String
|
|
122
106
|
|
|
123
|
-
// Validate download URL scheme
|
|
124
107
|
guard let dlURL = URL(string: downloadUrl) else {
|
|
125
108
|
throw ManifestClientError.invalidURL
|
|
126
109
|
}
|
|
@@ -130,20 +113,19 @@ enum ManifestClient {
|
|
|
130
113
|
print("[UpdateKit] WARNING: No manifest signing keys configured — signature verification is disabled for this request.")
|
|
131
114
|
}
|
|
132
115
|
|
|
133
|
-
// Verify manifest signature if signing keys are configured
|
|
134
116
|
if !manifestKeys.isEmpty {
|
|
135
|
-
guard let
|
|
117
|
+
guard let signature else {
|
|
136
118
|
throw ManifestVerifierError.missingSignature
|
|
137
119
|
}
|
|
120
|
+
|
|
138
121
|
try ManifestVerifier.verify(
|
|
139
122
|
appId: appId,
|
|
140
123
|
channel: channel,
|
|
141
|
-
platform: platform,
|
|
142
124
|
version: version,
|
|
143
125
|
sha256: sha256,
|
|
144
126
|
size: size,
|
|
145
|
-
|
|
146
|
-
signature:
|
|
127
|
+
runtimeVersion: runtimeVersion,
|
|
128
|
+
signature: signature,
|
|
147
129
|
trustedKeys: manifestKeys
|
|
148
130
|
)
|
|
149
131
|
}
|
|
@@ -153,9 +135,26 @@ enum ManifestClient {
|
|
|
153
135
|
url: downloadUrl,
|
|
154
136
|
sha256: sha256,
|
|
155
137
|
size: size,
|
|
156
|
-
|
|
157
|
-
releaseId: releaseId
|
|
158
|
-
signature: signature
|
|
138
|
+
runtimeVersion: runtimeVersion,
|
|
139
|
+
releaseId: releaseId
|
|
159
140
|
)
|
|
160
141
|
}
|
|
142
|
+
|
|
143
|
+
private static func parseSignature(_ rawValue: Any?) -> ManifestSignature? {
|
|
144
|
+
guard let sigObj = rawValue as? [String: Any],
|
|
145
|
+
let kid = sigObj["kid"] as? String,
|
|
146
|
+
let sig = sigObj["sig"] as? String,
|
|
147
|
+
let iat = sigObj["iat"] as? Int,
|
|
148
|
+
let exp = sigObj["exp"] as? Int else {
|
|
149
|
+
return nil
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return ManifestSignature(kid: kid, sig: sig, iat: iat, exp: exp)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private extension String {
|
|
157
|
+
var nilIfEmpty: String? {
|
|
158
|
+
isEmpty ? nil : self
|
|
159
|
+
}
|
|
161
160
|
}
|
|
@@ -18,8 +18,8 @@ enum ManifestVerifier {
|
|
|
18
18
|
/// Verify a manifest signature using ES256 (ECDSA P-256 + SHA-256).
|
|
19
19
|
///
|
|
20
20
|
/// - Parameters:
|
|
21
|
-
/// - appId, channel
|
|
22
|
-
/// - version, sha256, size,
|
|
21
|
+
/// - appId, channel: Request context (known by plugin).
|
|
22
|
+
/// - version, sha256, size, runtimeVersion: Response fields.
|
|
23
23
|
/// - signature: The signature object from the manifest response.
|
|
24
24
|
/// - trustedKeys: Array of verification keys configured in the plugin.
|
|
25
25
|
///
|
|
@@ -27,38 +27,42 @@ enum ManifestVerifier {
|
|
|
27
27
|
static func verify(
|
|
28
28
|
appId: String,
|
|
29
29
|
channel: String?,
|
|
30
|
-
platform: String,
|
|
31
30
|
version: String,
|
|
32
31
|
sha256: String,
|
|
33
32
|
size: Int,
|
|
34
|
-
|
|
33
|
+
runtimeVersion: String?,
|
|
35
34
|
signature: ManifestSignature,
|
|
36
35
|
trustedKeys: [ManifestKey]
|
|
37
36
|
) throws {
|
|
38
|
-
// Check expiry
|
|
39
|
-
let now = Int(Date().timeIntervalSince1970)
|
|
40
|
-
guard signature.exp > now else {
|
|
41
|
-
throw ManifestVerifierError.expired
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Find matching key
|
|
45
|
-
guard let keyEntry = trustedKeys.first(where: { $0.kid == signature.kid }) else {
|
|
46
|
-
throw ManifestVerifierError.unknownKid(signature.kid)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Build canonical payload (must match server exactly)
|
|
50
37
|
let payload = buildCanonicalPayload(
|
|
51
38
|
appId: appId,
|
|
52
39
|
channel: channel,
|
|
53
|
-
platform: platform,
|
|
54
40
|
version: version,
|
|
55
41
|
sha256: sha256,
|
|
56
42
|
size: size,
|
|
57
|
-
|
|
43
|
+
runtimeVersion: runtimeVersion,
|
|
58
44
|
kid: signature.kid,
|
|
59
45
|
iat: signature.iat,
|
|
60
46
|
exp: signature.exp
|
|
61
47
|
)
|
|
48
|
+
try verifyPayload(payload, signature: signature, trustedKeys: trustedKeys)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private static func verifyPayload(
|
|
52
|
+
_ payload: String,
|
|
53
|
+
signature: ManifestSignature,
|
|
54
|
+
trustedKeys: [ManifestKey]
|
|
55
|
+
) throws {
|
|
56
|
+
// Check expiry
|
|
57
|
+
let now = Int(Date().timeIntervalSince1970)
|
|
58
|
+
guard signature.exp > now else {
|
|
59
|
+
throw ManifestVerifierError.expired
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Find matching key
|
|
63
|
+
guard let keyEntry = trustedKeys.first(where: { $0.kid == signature.kid }) else {
|
|
64
|
+
throw ManifestVerifierError.unknownKid(signature.kid)
|
|
65
|
+
}
|
|
62
66
|
|
|
63
67
|
// Decode base64url signature
|
|
64
68
|
guard let sigData = base64UrlDecode(signature.sig) else {
|
|
@@ -77,25 +81,22 @@ enum ManifestVerifier {
|
|
|
77
81
|
private static func buildCanonicalPayload(
|
|
78
82
|
appId: String,
|
|
79
83
|
channel: String?,
|
|
80
|
-
platform: String,
|
|
81
84
|
version: String,
|
|
82
85
|
sha256: String,
|
|
83
86
|
size: Int,
|
|
84
|
-
|
|
87
|
+
runtimeVersion: String?,
|
|
85
88
|
kid: String,
|
|
86
89
|
iat: Int,
|
|
87
90
|
exp: Int
|
|
88
91
|
) -> String {
|
|
89
|
-
let minBuildStr = minNativeBuild.map { String($0) } ?? "null"
|
|
90
92
|
return [
|
|
91
|
-
"
|
|
93
|
+
"MANIFEST",
|
|
92
94
|
"appId:\(appId)",
|
|
93
95
|
"channel:\(channel ?? "null")",
|
|
94
|
-
"platform:\(platform)",
|
|
95
96
|
"version:\(version)",
|
|
96
97
|
"sha256:\(sha256)",
|
|
97
98
|
"size:\(size)",
|
|
98
|
-
"
|
|
99
|
+
"runtimeVersion:\(runtimeVersion ?? "null")",
|
|
99
100
|
"kid:\(kid)",
|
|
100
101
|
"iat:\(iat)",
|
|
101
102
|
"exp:\(exp)",
|
|
@@ -5,8 +5,6 @@ CAP_PLUGIN(UpdaterPlugin, "OtaKit",
|
|
|
5
5
|
CAP_PLUGIN_METHOD(download, CAPPluginReturnPromise);
|
|
6
6
|
CAP_PLUGIN_METHOD(apply, CAPPluginReturnPromise);
|
|
7
7
|
CAP_PLUGIN_METHOD(debugGetState, CAPPluginReturnPromise);
|
|
8
|
-
CAP_PLUGIN_METHOD(debugCheck, CAPPluginReturnPromise);
|
|
9
|
-
CAP_PLUGIN_METHOD(debugDownload, CAPPluginReturnPromise);
|
|
10
8
|
CAP_PLUGIN_METHOD(notifyAppReady, CAPPluginReturnPromise);
|
|
11
9
|
CAP_PLUGIN_METHOD(debugReset, CAPPluginReturnPromise);
|
|
12
10
|
CAP_PLUGIN_METHOD(debugListBundles, CAPPluginReturnPromise);
|