@capgo/capacitor-updater 7.45.10 → 7.50.2
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/Package.swift +1 -1
- package/README.md +510 -92
- package/android/src/main/java/ee/forgr/capacitor_updater/AndroidAppExitReporter.java +92 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +3192 -777
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +798 -299
- package/android/src/main/java/ee/forgr/capacitor_updater/DeviceIdHelper.java +45 -30
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +46 -28
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadWorkerManager.java +2 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeDetector.java +3 -3
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeMenu.java +359 -25
- package/android/src/main/java/ee/forgr/capacitor_updater/ThreeFingerPinchDetector.java +323 -0
- package/dist/docs.json +1283 -169
- package/dist/esm/definitions.d.ts +621 -44
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +11 -1
- package/dist/esm/web.js +59 -1
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +59 -1
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +59 -1
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/AES.swift +0 -1
- package/ios/Sources/CapacitorUpdaterPlugin/AppHealthTracker.swift +82 -0
- package/ios/Sources/CapacitorUpdaterPlugin/BigInt.swift +0 -16
- package/ios/Sources/CapacitorUpdaterPlugin/BundleInfo.swift +2 -2
- package/ios/Sources/CapacitorUpdaterPlugin/BundleStatus.swift +78 -2
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +2116 -223
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +997 -332
- package/ios/Sources/CapacitorUpdaterPlugin/CryptoCipher.swift +0 -1
- package/ios/Sources/CapacitorUpdaterPlugin/InternalUtils.swift +78 -1
- package/ios/Sources/CapacitorUpdaterPlugin/ShakeMenu.swift +418 -36
- package/ios/Sources/CapacitorUpdaterPlugin/WebViewStatsReporter.swift +276 -0
- package/package.json +15 -3
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import Capacitor
|
|
2
|
+
import Foundation
|
|
3
|
+
import WebKit
|
|
4
|
+
|
|
5
|
+
final class WebViewStatsReporter {
|
|
6
|
+
static let script = """
|
|
7
|
+
(function(){
|
|
8
|
+
if(window.__capgoWebViewErrorReporterInstalled){return;}
|
|
9
|
+
window.__capgoWebViewErrorReporterInstalled=true;
|
|
10
|
+
var maxReports=20,sentReports=0,queue=[],seen={};
|
|
11
|
+
var sessionKey='CapacitorUpdater.webViewSession';
|
|
12
|
+
var sessionId=String(Date.now())+'-'+Math.random().toString(36).slice(2);
|
|
13
|
+
function s(value){
|
|
14
|
+
try{
|
|
15
|
+
if(value===undefined){return '';}
|
|
16
|
+
if(value===null){return 'null';}
|
|
17
|
+
if(typeof value==='string'){return value;}
|
|
18
|
+
if(value&&typeof value.message==='string'){return value.message;}
|
|
19
|
+
return String(value);
|
|
20
|
+
}catch(_){return '';}
|
|
21
|
+
}
|
|
22
|
+
function stack(value){
|
|
23
|
+
try{return value&&value.stack?String(value.stack):'';}catch(_){return '';}
|
|
24
|
+
}
|
|
25
|
+
function updater(){
|
|
26
|
+
var cap=window.Capacitor;
|
|
27
|
+
if(!cap||!cap.Plugins){return null;}
|
|
28
|
+
return cap.Plugins.CapacitorUpdater||null;
|
|
29
|
+
}
|
|
30
|
+
function flush(){
|
|
31
|
+
var plugin=updater();
|
|
32
|
+
if(!plugin||typeof plugin.reportWebViewError!=='function'){return false;}
|
|
33
|
+
while(queue.length){
|
|
34
|
+
var payload=queue.shift();
|
|
35
|
+
try{
|
|
36
|
+
var result=plugin.reportWebViewError(payload);
|
|
37
|
+
if(result&&typeof result.catch==='function'){result.catch(function(){});}
|
|
38
|
+
}catch(_){}
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
var retries=0;
|
|
43
|
+
function scheduleFlush(){
|
|
44
|
+
if(flush()){return;}
|
|
45
|
+
if(retries++<40){setTimeout(scheduleFlush,250);}
|
|
46
|
+
}
|
|
47
|
+
function send(payload){
|
|
48
|
+
try{
|
|
49
|
+
if(sentReports>=maxReports){return;}
|
|
50
|
+
payload.href=payload.href||location.href||'';
|
|
51
|
+
payload.user_agent=navigator.userAgent||'';
|
|
52
|
+
payload.session_id=sessionId;
|
|
53
|
+
var key=[payload.type,payload.message,payload.source,payload.line,payload.column,payload.tag_name].join('|');
|
|
54
|
+
if(seen[key]){return;}
|
|
55
|
+
seen[key]=true;
|
|
56
|
+
sentReports+=1;
|
|
57
|
+
queue.push(payload);
|
|
58
|
+
scheduleFlush();
|
|
59
|
+
}catch(_){}
|
|
60
|
+
}
|
|
61
|
+
function readSession(){
|
|
62
|
+
try{return JSON.parse(localStorage.getItem(sessionKey)||'null')||null;}catch(_){return null;}
|
|
63
|
+
}
|
|
64
|
+
function writeSession(active){
|
|
65
|
+
try{
|
|
66
|
+
localStorage.setItem(sessionKey,JSON.stringify({
|
|
67
|
+
id:sessionId,
|
|
68
|
+
active:active,
|
|
69
|
+
href:location.href||'',
|
|
70
|
+
started_at:window.__capgoWebViewSessionStartedAt,
|
|
71
|
+
updated_at:String(Date.now())
|
|
72
|
+
}));
|
|
73
|
+
}catch(_){}
|
|
74
|
+
}
|
|
75
|
+
window.__capgoWebViewSessionStartedAt=String(Date.now());
|
|
76
|
+
var previous=readSession();
|
|
77
|
+
if(previous&&previous.active){
|
|
78
|
+
send({
|
|
79
|
+
type:'webview_unclean_restart',
|
|
80
|
+
message:'WebView restarted without a clean page unload',
|
|
81
|
+
previous_session_id:s(previous.id),
|
|
82
|
+
previous_href:s(previous.href),
|
|
83
|
+
previous_started_at:s(previous.started_at),
|
|
84
|
+
previous_updated_at:s(previous.updated_at)
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
writeSession(true);
|
|
88
|
+
setInterval(function(){writeSession(true);},15000);
|
|
89
|
+
function markClean(){writeSession(false);}
|
|
90
|
+
window.addEventListener('pagehide',markClean,true);
|
|
91
|
+
window.addEventListener('beforeunload',markClean,true);
|
|
92
|
+
window.addEventListener('error',function(event){
|
|
93
|
+
var target=event&&event.target;
|
|
94
|
+
if(target&&target!==window&&(target.src||target.href)){
|
|
95
|
+
send({
|
|
96
|
+
type:'resource_error',
|
|
97
|
+
message:'Resource failed to load',
|
|
98
|
+
source:s(target.src||target.href),
|
|
99
|
+
tag_name:s(target.tagName)
|
|
100
|
+
});
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
send({
|
|
104
|
+
type:'javascript_error',
|
|
105
|
+
message:s((event&&event.message)||(event&&event.error)),
|
|
106
|
+
source:s(event&&event.filename),
|
|
107
|
+
line:s(event&&event.lineno),
|
|
108
|
+
column:s(event&&event.colno),
|
|
109
|
+
stack:stack(event&&event.error)
|
|
110
|
+
});
|
|
111
|
+
},true);
|
|
112
|
+
window.addEventListener('unhandledrejection',function(event){
|
|
113
|
+
var reason=event&&event.reason;
|
|
114
|
+
send({type:'unhandled_rejection',message:s(reason),stack:stack(reason)});
|
|
115
|
+
},true);
|
|
116
|
+
document.addEventListener('securitypolicyviolation',function(event){
|
|
117
|
+
send({
|
|
118
|
+
type:'security_policy_violation',
|
|
119
|
+
message:s(event&&event.violatedDirective),
|
|
120
|
+
source:s(event&&event.blockedURI)
|
|
121
|
+
});
|
|
122
|
+
},true);
|
|
123
|
+
document.addEventListener('deviceready',scheduleFlush,false);
|
|
124
|
+
setTimeout(scheduleFlush,0);
|
|
125
|
+
})();
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
private let implementation: CapgoUpdater
|
|
129
|
+
private var installed = false
|
|
130
|
+
|
|
131
|
+
init(implementation: CapgoUpdater) {
|
|
132
|
+
self.implementation = implementation
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
func install(on webView: WKWebView?) {
|
|
136
|
+
guard !installed else {
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
guard let webView else {
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
installed = true
|
|
144
|
+
let userScript = WKUserScript(source: Self.script, injectionTime: .atDocumentStart, forMainFrameOnly: true)
|
|
145
|
+
webView.configuration.userContentController.addUserScript(userScript)
|
|
146
|
+
webView.evaluateJavaScript(Self.script, completionHandler: nil)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
func reportError(_ call: CAPPluginCall) {
|
|
150
|
+
let errorType = call.getString("type") ?? "javascript_error"
|
|
151
|
+
let current = implementation.getCurrentBundle()
|
|
152
|
+
implementation.sendStats(
|
|
153
|
+
action: Self.statsAction(for: errorType),
|
|
154
|
+
versionName: current.getVersionName(),
|
|
155
|
+
oldVersionName: "",
|
|
156
|
+
metadata: Self.buildMetadata([
|
|
157
|
+
"type": errorType,
|
|
158
|
+
"message": call.getString("message"),
|
|
159
|
+
"source": call.getString("source"),
|
|
160
|
+
"line": call.getString("line") ?? call.getString("lineno"),
|
|
161
|
+
"column": call.getString("column") ?? call.getString("colno"),
|
|
162
|
+
"stack": call.getString("stack"),
|
|
163
|
+
"tag_name": call.getString("tag_name"),
|
|
164
|
+
"href": call.getString("href"),
|
|
165
|
+
"user_agent": call.getString("user_agent"),
|
|
166
|
+
"session_id": call.getString("session_id"),
|
|
167
|
+
"previous_session_id": call.getString("previous_session_id"),
|
|
168
|
+
"previous_href": call.getString("previous_href"),
|
|
169
|
+
"previous_started_at": call.getString("previous_started_at"),
|
|
170
|
+
"previous_updated_at": call.getString("previous_updated_at")
|
|
171
|
+
])
|
|
172
|
+
)
|
|
173
|
+
call.resolve()
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
static func statsAction(for type: String) -> String {
|
|
177
|
+
switch type {
|
|
178
|
+
case "unhandled_rejection":
|
|
179
|
+
return "webview_unhandled_rejection"
|
|
180
|
+
case "resource_error":
|
|
181
|
+
return "webview_resource_error"
|
|
182
|
+
case "security_policy_violation":
|
|
183
|
+
return "webview_security_policy_violation"
|
|
184
|
+
case "webview_unclean_restart":
|
|
185
|
+
return "webview_unclean_restart"
|
|
186
|
+
case "render_process_gone":
|
|
187
|
+
return "webview_render_process_gone"
|
|
188
|
+
case "web_content_process_terminated":
|
|
189
|
+
return "webview_content_process_terminated"
|
|
190
|
+
case "javascript_error":
|
|
191
|
+
return "webview_javascript_error"
|
|
192
|
+
default:
|
|
193
|
+
return "webview_javascript_error"
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
static func buildMetadata(_ values: [String: String?]) -> [String: String] {
|
|
198
|
+
var metadata: [String: String] = [:]
|
|
199
|
+
put(&metadata, key: "error_type", value: payloadValue(values, "type") ?? "javascript_error", maxLength: 64)
|
|
200
|
+
put(&metadata, key: "message", value: payloadValue(values, "message"), maxLength: 1_024)
|
|
201
|
+
put(&metadata, key: "source", value: sanitizeUrl(payloadValue(values, "source")), maxLength: 512)
|
|
202
|
+
put(&metadata, key: "line", value: payloadValue(values, "line"), maxLength: 32)
|
|
203
|
+
put(&metadata, key: "column", value: payloadValue(values, "column"), maxLength: 32)
|
|
204
|
+
put(&metadata, key: "stack", value: payloadValue(values, "stack"), maxLength: 2_048)
|
|
205
|
+
put(&metadata, key: "tag_name", value: payloadValue(values, "tag_name"), maxLength: 64)
|
|
206
|
+
put(&metadata, key: "href", value: sanitizeUrl(payloadValue(values, "href")), maxLength: 512)
|
|
207
|
+
put(&metadata, key: "user_agent", value: payloadValue(values, "user_agent"), maxLength: 256)
|
|
208
|
+
put(&metadata, key: "session_id", value: payloadValue(values, "session_id"), maxLength: 128)
|
|
209
|
+
put(&metadata, key: "previous_session_id", value: payloadValue(values, "previous_session_id"), maxLength: 128)
|
|
210
|
+
put(&metadata, key: "previous_href", value: sanitizeUrl(payloadValue(values, "previous_href")), maxLength: 512)
|
|
211
|
+
put(&metadata, key: "previous_started_at", value: payloadValue(values, "previous_started_at"), maxLength: 64)
|
|
212
|
+
put(&metadata, key: "previous_updated_at", value: payloadValue(values, "previous_updated_at"), maxLength: 64)
|
|
213
|
+
return metadata
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private static func payloadValue(_ values: [String: String?], _ key: String) -> String? {
|
|
217
|
+
guard let value = values[key] else {
|
|
218
|
+
return nil
|
|
219
|
+
}
|
|
220
|
+
return value
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
static func sanitizeUrl(_ value: String?) -> String? {
|
|
224
|
+
guard let value = value, !value.isEmpty else {
|
|
225
|
+
return value
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if var components = URLComponents(string: value), components.scheme != nil, components.host != nil {
|
|
229
|
+
components.user = nil
|
|
230
|
+
components.password = nil
|
|
231
|
+
components.query = nil
|
|
232
|
+
components.fragment = nil
|
|
233
|
+
components.path = sanitizeUrlPath(components.path)
|
|
234
|
+
return components.string ?? stripUrlQueryAndFragment(value)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return stripUrlQueryAndFragment(value)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private static func sanitizeUrlPath(_ path: String) -> String {
|
|
241
|
+
guard !path.isEmpty else {
|
|
242
|
+
return path
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return path
|
|
246
|
+
.split(separator: "/", omittingEmptySubsequences: false)
|
|
247
|
+
.map { isSensitiveUrlPathSegment(String($0)) ? "redacted" : String($0) }
|
|
248
|
+
.joined(separator: "/")
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private static func isSensitiveUrlPathSegment(_ segment: String) -> Bool {
|
|
252
|
+
segment.range(of: #"^[0-9]{6,}$"#, options: .regularExpression) != nil ||
|
|
253
|
+
segment.range(of: #"^[0-9a-fA-F]{16,}$"#, options: .regularExpression) != nil ||
|
|
254
|
+
segment.range(
|
|
255
|
+
of: #"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"#,
|
|
256
|
+
options: .regularExpression
|
|
257
|
+
) != nil
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
private static func stripUrlQueryAndFragment(_ value: String) -> String {
|
|
261
|
+
let queryIndex = value.firstIndex(of: "?")
|
|
262
|
+
let fragmentIndex = value.firstIndex(of: "#")
|
|
263
|
+
let endIndexes = [queryIndex, fragmentIndex].compactMap { $0 }
|
|
264
|
+
guard let endIndex = endIndexes.min() else {
|
|
265
|
+
return value
|
|
266
|
+
}
|
|
267
|
+
return String(value[..<endIndex])
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private static func put(_ metadata: inout [String: String], key: String, value: String?, maxLength: Int) {
|
|
271
|
+
guard let value = value, !value.isEmpty else {
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
metadata[key] = String(value.prefix(maxLength))
|
|
275
|
+
}
|
|
276
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capgo/capacitor-updater",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.50.2",
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
5
|
"description": "Live update for capacitor apps",
|
|
6
6
|
"main": "dist/plugin.cjs.js",
|
|
@@ -45,12 +45,22 @@
|
|
|
45
45
|
"verify:ios": "xcodebuild -scheme CapgoCapacitorUpdater -destination generic/platform=iOS",
|
|
46
46
|
"verify:android": "cd android && ./gradlew clean build test && cd ..",
|
|
47
47
|
"verify:web": "bun run build",
|
|
48
|
+
"native:compile": "./scripts/native-platform.sh compile all",
|
|
49
|
+
"native:compile:ios": "./scripts/native-platform.sh compile ios",
|
|
50
|
+
"native:compile:android": "./scripts/native-platform.sh compile android",
|
|
51
|
+
"native:test": "./scripts/native-platform.sh test all",
|
|
52
|
+
"native:test:ios": "./scripts/native-platform.sh test ios",
|
|
53
|
+
"native:test:android": "./scripts/native-platform.sh test android",
|
|
54
|
+
"native:contract": "bun run native:contract:ios && bun run native:contract:android",
|
|
55
|
+
"native:contract:ios": "./scripts/test-ios.sh -only-testing:CapacitorUpdaterPluginTests/NativeContractTests",
|
|
56
|
+
"native:contract:android": "cd android && ./gradlew testDebugUnitTest --tests ee.forgr.capacitor_updater.NativeContractTest && cd ..",
|
|
48
57
|
"test": "bun run test:ios && bun run test:android",
|
|
49
58
|
"test:ios": "./scripts/test-ios.sh",
|
|
50
59
|
"test:android": "cd android && ./gradlew test && cd ..",
|
|
51
60
|
"test:maestro": "./scripts/maestro/run-android-live-update.sh",
|
|
52
61
|
"test:maestro:android": "./scripts/test-maestro-android.sh",
|
|
53
|
-
"test:maestro:ios": "./scripts/
|
|
62
|
+
"test:maestro:ios": "./scripts/maestro/run-ios-live-update.sh",
|
|
63
|
+
"test:maestro:ios:smoke": "./scripts/test-maestro-ios.sh",
|
|
54
64
|
"lint": "bun run eslint && bun run prettier -- --check && bun run swiftlint -- lint",
|
|
55
65
|
"fmt": "bun run eslint -- --fix && bun run prettier -- --write && bun run swiftlint -- --fix --format",
|
|
56
66
|
"eslint": "eslint . --ext .ts",
|
|
@@ -61,7 +71,9 @@
|
|
|
61
71
|
"clean": "rimraf ./dist",
|
|
62
72
|
"watch": "tsc --watch",
|
|
63
73
|
"prepublishOnly": "bun run build",
|
|
64
|
-
"check:wiring": "node scripts/check-capacitor-plugin-wiring.mjs"
|
|
74
|
+
"check:wiring": "node scripts/check-capacitor-plugin-wiring.mjs",
|
|
75
|
+
"example:install": "cd example-app && bun install --frozen-lockfile",
|
|
76
|
+
"example:build": "bun run build && cd example-app && bun install --frozen-lockfile && bun run build"
|
|
65
77
|
},
|
|
66
78
|
"devDependencies": {
|
|
67
79
|
"@capacitor/android": "^7.4.3",
|