@capgo/capacitor-webview-crash 8.0.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C,MAAM,OAAO,eAAgB,SAAQ,SAAS;IAA9C;;QACU,4BAAuB,GAAG,KAAK,CAAC;IA4E1C,CAAC;IA1EC,KAAK,CAAC,mBAAmB;QACvB,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,qBAAqB;QACzB,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC9B,IAAI,CAAC,uBAAuB,GAAG,KAAK,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,qBAAqB;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;QACpC,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,CAAC,uBAAuB,GAAG,KAAK,CAAC;QACrC,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC9B,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,SAAiB,EAAE,YAAqC;QACxE,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QAChE,IAAI,SAAS,KAAK,eAAe,CAAC,SAAS,EAAE,CAAC;YAC5C,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAChC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,sBAAsB;QAC5B,IAAI,IAAI,CAAC,uBAAuB,EAAE,CAAC;YACjC,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO;QACT,CAAC;QAED,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC;QACpC,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACzD,CAAC;IAEO,cAAc;;QACpB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,OAAO;YACL,QAAQ,EAAE,KAAK;YACf,SAAS;YACT,YAAY,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE;YAC/C,MAAM,EAAE,WAAW;YACnB,GAAG,EAAE,MAAA,UAAU,CAAC,QAAQ,0CAAE,IAAI;YAC9B,QAAQ,EAAE,QAAQ;SACnB,CAAC;IACJ,CAAC;IAEO,oBAAoB;;QAC1B,MAAM,GAAG,GAAG,MAAA,UAAU,CAAC,YAAY,0CAAE,OAAO,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;QACzE,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAqB,CAAC;QAC7C,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAEO,qBAAqB,CAAC,KAAuB;;QACnD,MAAA,UAAU,CAAC,YAAY,0CAAE,OAAO,CAAC,eAAe,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IACtF,CAAC;IAEO,sBAAsB;;QAC5B,MAAA,UAAU,CAAC,YAAY,0CAAE,UAAU,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;IAClE,CAAC;;AAEuB,yBAAS,GAAG,2BAA2B,AAA9B,CAA+B;AACxC,0BAAU,GAAG,6BAA6B,AAAhC,CAAiC","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\nimport { WebPlugin } from '@capacitor/core';\n\nimport type { PendingCrashInfoResult, WebViewCrashInfo, WebViewCrashPlugin } from './definitions';\n\nexport class WebViewCrashWeb extends WebPlugin implements WebViewCrashPlugin {\n private didDispatchPendingEvent = false;\n\n async getPendingCrashInfo(): Promise<PendingCrashInfoResult> {\n return { value: this.readPendingCrashInfo() };\n }\n\n async clearPendingCrashInfo(): Promise<void> {\n this.removePendingCrashInfo();\n this.didDispatchPendingEvent = false;\n }\n\n async simulateCrashRecovery(): Promise<PendingCrashInfoResult> {\n const value = this.buildCrashInfo();\n this.writePendingCrashInfo(value);\n this.didDispatchPendingEvent = false;\n this.flushPendingCrashEvent();\n return { value };\n }\n\n async addListener(eventName: string, listenerFunc: (...args: any[]) => any): Promise<PluginListenerHandle> {\n const handle = await super.addListener(eventName, listenerFunc);\n if (eventName === WebViewCrashWeb.eventName) {\n this.flushPendingCrashEvent();\n }\n return handle;\n }\n\n private flushPendingCrashEvent(): void {\n if (this.didDispatchPendingEvent) {\n return;\n }\n\n const value = this.readPendingCrashInfo();\n if (!value) {\n return;\n }\n\n this.didDispatchPendingEvent = true;\n this.notifyListeners(WebViewCrashWeb.eventName, value);\n }\n\n private buildCrashInfo(): WebViewCrashInfo {\n const timestamp = Date.now();\n return {\n platform: 'web',\n timestamp,\n timestampISO: new Date(timestamp).toISOString(),\n reason: 'simulated',\n url: globalThis.location?.href,\n appState: 'active',\n };\n }\n\n private readPendingCrashInfo(): WebViewCrashInfo | null {\n const raw = globalThis.localStorage?.getItem(WebViewCrashWeb.storageKey);\n if (!raw) {\n return null;\n }\n\n try {\n return JSON.parse(raw) as WebViewCrashInfo;\n } catch {\n return null;\n }\n }\n\n private writePendingCrashInfo(value: WebViewCrashInfo): void {\n globalThis.localStorage?.setItem(WebViewCrashWeb.storageKey, JSON.stringify(value));\n }\n\n private removePendingCrashInfo(): void {\n globalThis.localStorage?.removeItem(WebViewCrashWeb.storageKey);\n }\n\n private static readonly eventName = 'webViewRestoredAfterCrash';\n private static readonly storageKey = 'capgo.webview-crash.pending';\n}\n"]}
@@ -0,0 +1,89 @@
1
+ 'use strict';
2
+
3
+ var core = require('@capacitor/core');
4
+
5
+ const WebViewCrash = core.registerPlugin('WebViewCrash', {
6
+ web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.WebViewCrashWeb()),
7
+ });
8
+
9
+ class WebViewCrashWeb extends core.WebPlugin {
10
+ constructor() {
11
+ super(...arguments);
12
+ this.didDispatchPendingEvent = false;
13
+ }
14
+ async getPendingCrashInfo() {
15
+ return { value: this.readPendingCrashInfo() };
16
+ }
17
+ async clearPendingCrashInfo() {
18
+ this.removePendingCrashInfo();
19
+ this.didDispatchPendingEvent = false;
20
+ }
21
+ async simulateCrashRecovery() {
22
+ const value = this.buildCrashInfo();
23
+ this.writePendingCrashInfo(value);
24
+ this.didDispatchPendingEvent = false;
25
+ this.flushPendingCrashEvent();
26
+ return { value };
27
+ }
28
+ async addListener(eventName, listenerFunc) {
29
+ const handle = await super.addListener(eventName, listenerFunc);
30
+ if (eventName === WebViewCrashWeb.eventName) {
31
+ this.flushPendingCrashEvent();
32
+ }
33
+ return handle;
34
+ }
35
+ flushPendingCrashEvent() {
36
+ if (this.didDispatchPendingEvent) {
37
+ return;
38
+ }
39
+ const value = this.readPendingCrashInfo();
40
+ if (!value) {
41
+ return;
42
+ }
43
+ this.didDispatchPendingEvent = true;
44
+ this.notifyListeners(WebViewCrashWeb.eventName, value);
45
+ }
46
+ buildCrashInfo() {
47
+ var _a;
48
+ const timestamp = Date.now();
49
+ return {
50
+ platform: 'web',
51
+ timestamp,
52
+ timestampISO: new Date(timestamp).toISOString(),
53
+ reason: 'simulated',
54
+ url: (_a = globalThis.location) === null || _a === void 0 ? void 0 : _a.href,
55
+ appState: 'active',
56
+ };
57
+ }
58
+ readPendingCrashInfo() {
59
+ var _a;
60
+ const raw = (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.getItem(WebViewCrashWeb.storageKey);
61
+ if (!raw) {
62
+ return null;
63
+ }
64
+ try {
65
+ return JSON.parse(raw);
66
+ }
67
+ catch (_b) {
68
+ return null;
69
+ }
70
+ }
71
+ writePendingCrashInfo(value) {
72
+ var _a;
73
+ (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.setItem(WebViewCrashWeb.storageKey, JSON.stringify(value));
74
+ }
75
+ removePendingCrashInfo() {
76
+ var _a;
77
+ (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.removeItem(WebViewCrashWeb.storageKey);
78
+ }
79
+ }
80
+ WebViewCrashWeb.eventName = 'webViewRestoredAfterCrash';
81
+ WebViewCrashWeb.storageKey = 'capgo.webview-crash.pending';
82
+
83
+ var web = /*#__PURE__*/Object.freeze({
84
+ __proto__: null,
85
+ WebViewCrashWeb: WebViewCrashWeb
86
+ });
87
+
88
+ exports.WebViewCrash = WebViewCrash;
89
+ //# sourceMappingURL=plugin.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst WebViewCrash = registerPlugin('WebViewCrash', {\n web: () => import('./web').then((m) => new m.WebViewCrashWeb()),\n});\nexport * from './definitions';\nexport { WebViewCrash };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class WebViewCrashWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.didDispatchPendingEvent = false;\n }\n async getPendingCrashInfo() {\n return { value: this.readPendingCrashInfo() };\n }\n async clearPendingCrashInfo() {\n this.removePendingCrashInfo();\n this.didDispatchPendingEvent = false;\n }\n async simulateCrashRecovery() {\n const value = this.buildCrashInfo();\n this.writePendingCrashInfo(value);\n this.didDispatchPendingEvent = false;\n this.flushPendingCrashEvent();\n return { value };\n }\n async addListener(eventName, listenerFunc) {\n const handle = await super.addListener(eventName, listenerFunc);\n if (eventName === WebViewCrashWeb.eventName) {\n this.flushPendingCrashEvent();\n }\n return handle;\n }\n flushPendingCrashEvent() {\n if (this.didDispatchPendingEvent) {\n return;\n }\n const value = this.readPendingCrashInfo();\n if (!value) {\n return;\n }\n this.didDispatchPendingEvent = true;\n this.notifyListeners(WebViewCrashWeb.eventName, value);\n }\n buildCrashInfo() {\n var _a;\n const timestamp = Date.now();\n return {\n platform: 'web',\n timestamp,\n timestampISO: new Date(timestamp).toISOString(),\n reason: 'simulated',\n url: (_a = globalThis.location) === null || _a === void 0 ? void 0 : _a.href,\n appState: 'active',\n };\n }\n readPendingCrashInfo() {\n var _a;\n const raw = (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.getItem(WebViewCrashWeb.storageKey);\n if (!raw) {\n return null;\n }\n try {\n return JSON.parse(raw);\n }\n catch (_b) {\n return null;\n }\n }\n writePendingCrashInfo(value) {\n var _a;\n (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.setItem(WebViewCrashWeb.storageKey, JSON.stringify(value));\n }\n removePendingCrashInfo() {\n var _a;\n (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.removeItem(WebViewCrashWeb.storageKey);\n }\n}\nWebViewCrashWeb.eventName = 'webViewRestoredAfterCrash';\nWebViewCrashWeb.storageKey = 'capgo.webview-crash.pending';\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
package/dist/plugin.js ADDED
@@ -0,0 +1,92 @@
1
+ var capacitorWebViewCrash = (function (exports, core) {
2
+ 'use strict';
3
+
4
+ const WebViewCrash = core.registerPlugin('WebViewCrash', {
5
+ web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.WebViewCrashWeb()),
6
+ });
7
+
8
+ class WebViewCrashWeb extends core.WebPlugin {
9
+ constructor() {
10
+ super(...arguments);
11
+ this.didDispatchPendingEvent = false;
12
+ }
13
+ async getPendingCrashInfo() {
14
+ return { value: this.readPendingCrashInfo() };
15
+ }
16
+ async clearPendingCrashInfo() {
17
+ this.removePendingCrashInfo();
18
+ this.didDispatchPendingEvent = false;
19
+ }
20
+ async simulateCrashRecovery() {
21
+ const value = this.buildCrashInfo();
22
+ this.writePendingCrashInfo(value);
23
+ this.didDispatchPendingEvent = false;
24
+ this.flushPendingCrashEvent();
25
+ return { value };
26
+ }
27
+ async addListener(eventName, listenerFunc) {
28
+ const handle = await super.addListener(eventName, listenerFunc);
29
+ if (eventName === WebViewCrashWeb.eventName) {
30
+ this.flushPendingCrashEvent();
31
+ }
32
+ return handle;
33
+ }
34
+ flushPendingCrashEvent() {
35
+ if (this.didDispatchPendingEvent) {
36
+ return;
37
+ }
38
+ const value = this.readPendingCrashInfo();
39
+ if (!value) {
40
+ return;
41
+ }
42
+ this.didDispatchPendingEvent = true;
43
+ this.notifyListeners(WebViewCrashWeb.eventName, value);
44
+ }
45
+ buildCrashInfo() {
46
+ var _a;
47
+ const timestamp = Date.now();
48
+ return {
49
+ platform: 'web',
50
+ timestamp,
51
+ timestampISO: new Date(timestamp).toISOString(),
52
+ reason: 'simulated',
53
+ url: (_a = globalThis.location) === null || _a === void 0 ? void 0 : _a.href,
54
+ appState: 'active',
55
+ };
56
+ }
57
+ readPendingCrashInfo() {
58
+ var _a;
59
+ const raw = (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.getItem(WebViewCrashWeb.storageKey);
60
+ if (!raw) {
61
+ return null;
62
+ }
63
+ try {
64
+ return JSON.parse(raw);
65
+ }
66
+ catch (_b) {
67
+ return null;
68
+ }
69
+ }
70
+ writePendingCrashInfo(value) {
71
+ var _a;
72
+ (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.setItem(WebViewCrashWeb.storageKey, JSON.stringify(value));
73
+ }
74
+ removePendingCrashInfo() {
75
+ var _a;
76
+ (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.removeItem(WebViewCrashWeb.storageKey);
77
+ }
78
+ }
79
+ WebViewCrashWeb.eventName = 'webViewRestoredAfterCrash';
80
+ WebViewCrashWeb.storageKey = 'capgo.webview-crash.pending';
81
+
82
+ var web = /*#__PURE__*/Object.freeze({
83
+ __proto__: null,
84
+ WebViewCrashWeb: WebViewCrashWeb
85
+ });
86
+
87
+ exports.WebViewCrash = WebViewCrash;
88
+
89
+ return exports;
90
+
91
+ })({}, capacitorExports);
92
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst WebViewCrash = registerPlugin('WebViewCrash', {\n web: () => import('./web').then((m) => new m.WebViewCrashWeb()),\n});\nexport * from './definitions';\nexport { WebViewCrash };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class WebViewCrashWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.didDispatchPendingEvent = false;\n }\n async getPendingCrashInfo() {\n return { value: this.readPendingCrashInfo() };\n }\n async clearPendingCrashInfo() {\n this.removePendingCrashInfo();\n this.didDispatchPendingEvent = false;\n }\n async simulateCrashRecovery() {\n const value = this.buildCrashInfo();\n this.writePendingCrashInfo(value);\n this.didDispatchPendingEvent = false;\n this.flushPendingCrashEvent();\n return { value };\n }\n async addListener(eventName, listenerFunc) {\n const handle = await super.addListener(eventName, listenerFunc);\n if (eventName === WebViewCrashWeb.eventName) {\n this.flushPendingCrashEvent();\n }\n return handle;\n }\n flushPendingCrashEvent() {\n if (this.didDispatchPendingEvent) {\n return;\n }\n const value = this.readPendingCrashInfo();\n if (!value) {\n return;\n }\n this.didDispatchPendingEvent = true;\n this.notifyListeners(WebViewCrashWeb.eventName, value);\n }\n buildCrashInfo() {\n var _a;\n const timestamp = Date.now();\n return {\n platform: 'web',\n timestamp,\n timestampISO: new Date(timestamp).toISOString(),\n reason: 'simulated',\n url: (_a = globalThis.location) === null || _a === void 0 ? void 0 : _a.href,\n appState: 'active',\n };\n }\n readPendingCrashInfo() {\n var _a;\n const raw = (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.getItem(WebViewCrashWeb.storageKey);\n if (!raw) {\n return null;\n }\n try {\n return JSON.parse(raw);\n }\n catch (_b) {\n return null;\n }\n }\n writePendingCrashInfo(value) {\n var _a;\n (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.setItem(WebViewCrashWeb.storageKey, JSON.stringify(value));\n }\n removePendingCrashInfo() {\n var _a;\n (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.removeItem(WebViewCrashWeb.storageKey);\n }\n}\nWebViewCrashWeb.eventName = 'webViewRestoredAfterCrash';\nWebViewCrashWeb.storageKey = 'capgo.webview-crash.pending';\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ import Capacitor
2
+ import Foundation
3
+ import ObjectiveC.runtime
4
+ import UIKit
5
+ import WebKit
6
+
7
+ enum WebViewCrashBridge {
8
+ static let eventName = "webViewRestoredAfterCrash"
9
+
10
+ static func pendingResult(_ value: [String: Any]?) -> [String: Any] {
11
+ [
12
+ "value": value ?? NSNull()
13
+ ]
14
+ }
15
+ }
16
+
17
+ enum WebViewCrashStore {
18
+ private static let pendingCrashKey = "CapgoWebViewCrash.pendingInfo"
19
+ private static let timestampFormatter = ISO8601DateFormatter()
20
+
21
+ static func read() -> [String: Any]? {
22
+ UserDefaults.standard.dictionary(forKey: pendingCrashKey)
23
+ }
24
+
25
+ static func write(_ value: [String: Any]) {
26
+ UserDefaults.standard.set(value, forKey: pendingCrashKey)
27
+ }
28
+
29
+ static func clear() {
30
+ UserDefaults.standard.removeObject(forKey: pendingCrashKey)
31
+ }
32
+
33
+ static func buildCrashInfo(platform: String, reason: String, url: String?, appState: String? = nil) -> [String: Any] {
34
+ let date = Date()
35
+ let timestamp = Int(date.timeIntervalSince1970 * 1000)
36
+ var value: [String: Any] = [
37
+ "platform": platform,
38
+ "timestamp": timestamp,
39
+ "timestampISO": timestampFormatter.string(from: date),
40
+ "reason": reason
41
+ ]
42
+
43
+ if let url, !url.isEmpty {
44
+ value["url"] = url
45
+ }
46
+
47
+ if let appState {
48
+ value["appState"] = appState
49
+ }
50
+
51
+ return value
52
+ }
53
+ }
54
+
55
+ enum WebViewCrashSwizzler {
56
+ private static var didInstall = false
57
+
58
+ static func installIfNeeded() {
59
+ guard !didInstall else {
60
+ return
61
+ }
62
+
63
+ let originalSelector = #selector(WebViewDelegationHandler.webViewWebContentProcessDidTerminate(_:))
64
+ let swizzledSelector = #selector(WebViewDelegationHandler.capgo_webViewCrash_webViewWebContentProcessDidTerminate(_:))
65
+
66
+ guard
67
+ let originalMethod = class_getInstanceMethod(WebViewDelegationHandler.self, originalSelector),
68
+ let swizzledMethod = class_getInstanceMethod(WebViewDelegationHandler.self, swizzledSelector)
69
+ else {
70
+ return
71
+ }
72
+
73
+ method_exchangeImplementations(originalMethod, swizzledMethod)
74
+ didInstall = true
75
+ }
76
+ }
77
+
78
+ private extension WebViewDelegationHandler {
79
+ @objc func capgo_webViewCrash_webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
80
+ let crashInfo = WebViewCrashStore.buildCrashInfo(
81
+ platform: "ios",
82
+ reason: "webContentProcessDidTerminate",
83
+ url: webView.url?.absoluteString,
84
+ appState: UIApplication.shared.applicationState.capgoWebViewCrashValue
85
+ )
86
+
87
+ WebViewCrashStore.write(crashInfo)
88
+ capgo_webViewCrash_webViewWebContentProcessDidTerminate(webView)
89
+ }
90
+ }
91
+
92
+ extension UIApplication.State {
93
+ var capgoWebViewCrashValue: String {
94
+ switch self {
95
+ case .active:
96
+ return "active"
97
+ case .inactive:
98
+ return "inactive"
99
+ case .background:
100
+ return "background"
101
+ @unknown default:
102
+ return "unknown"
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,62 @@
1
+ import Capacitor
2
+ import Foundation
3
+ import UIKit
4
+
5
+ @objc(WebViewCrashPlugin)
6
+ public class WebViewCrashPlugin: CAPPlugin, CAPBridgedPlugin {
7
+ public let identifier = "WebViewCrashPlugin"
8
+ public let jsName = "WebViewCrash"
9
+ public let pluginMethods: [CAPPluginMethod] = [
10
+ CAPPluginMethod(name: "getPendingCrashInfo", returnType: CAPPluginReturnPromise),
11
+ CAPPluginMethod(name: "clearPendingCrashInfo", returnType: CAPPluginReturnPromise),
12
+ CAPPluginMethod(name: "simulateCrashRecovery", returnType: CAPPluginReturnPromise)
13
+ ]
14
+
15
+ private var didDispatchPendingEvent = false
16
+
17
+ override public func load() {
18
+ WebViewCrashSwizzler.installIfNeeded()
19
+ }
20
+
21
+ @objc override public func addListener(_ call: CAPPluginCall) {
22
+ super.addListener(call)
23
+
24
+ if call.getString("eventName") == WebViewCrashBridge.eventName {
25
+ dispatchPendingCrashIfNeeded()
26
+ }
27
+ }
28
+
29
+ @objc func getPendingCrashInfo(_ call: CAPPluginCall) {
30
+ call.resolve(WebViewCrashBridge.pendingResult(WebViewCrashStore.read()))
31
+ }
32
+
33
+ @objc func clearPendingCrashInfo(_ call: CAPPluginCall) {
34
+ WebViewCrashStore.clear()
35
+ didDispatchPendingEvent = false
36
+ call.resolve()
37
+ }
38
+
39
+ @objc func simulateCrashRecovery(_ call: CAPPluginCall) {
40
+ let crashInfo = WebViewCrashStore.buildCrashInfo(
41
+ platform: "ios",
42
+ reason: "simulated",
43
+ url: bridge?.webView?.url?.absoluteString,
44
+ appState: UIApplication.shared.applicationState.capgoWebViewCrashValue
45
+ )
46
+
47
+ WebViewCrashStore.write(crashInfo)
48
+ didDispatchPendingEvent = false
49
+ dispatchPendingCrashIfNeeded()
50
+
51
+ call.resolve(WebViewCrashBridge.pendingResult(crashInfo))
52
+ }
53
+
54
+ private func dispatchPendingCrashIfNeeded() {
55
+ guard !didDispatchPendingEvent, let crashInfo = WebViewCrashStore.read() else {
56
+ return
57
+ }
58
+
59
+ didDispatchPendingEvent = true
60
+ notifyListeners(WebViewCrashBridge.eventName, data: crashInfo)
61
+ }
62
+ }
@@ -0,0 +1,12 @@
1
+ import XCTest
2
+ @testable import WebViewCrashPlugin
3
+
4
+ final class WebViewCrashPluginTests: XCTestCase {
5
+ func testPluginMetadata() {
6
+ let plugin = WebViewCrashPlugin()
7
+
8
+ XCTAssertEqual(plugin.identifier, "WebViewCrashPlugin")
9
+ XCTAssertEqual(plugin.jsName, "WebViewCrash")
10
+ XCTAssertEqual(plugin.pluginMethods.count, 3)
11
+ }
12
+ }
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "@capgo/capacitor-webview-crash",
3
+ "version": "8.0.1",
4
+ "description": "Capacitor plugin for detecting WebView crash recovery and informing the next JS runtime.",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/Sources",
14
+ "ios/Tests",
15
+ "Package.swift",
16
+ "CapgoCapacitorWebViewCrash.podspec"
17
+ ],
18
+ "author": "Martin Donadieu <martin@capgo.app>",
19
+ "license": "MPL-2.0",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/Cap-go/capacitor-webview-crash.git"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/Cap-go/capacitor-webview-crash/issues"
26
+ },
27
+ "homepage": "https://capgo.app/docs/plugins/webview-crash/",
28
+ "keywords": [
29
+ "capacitor",
30
+ "plugin",
31
+ "webview-crash",
32
+ "crash-recovery",
33
+ "ios",
34
+ "android",
35
+ "capgo"
36
+ ],
37
+ "scripts": {
38
+ "verify": "bun run verify:ios && bun run verify:android && bun run verify:web",
39
+ "verify:ios": "xcodebuild -scheme CapgoCapacitorWebViewCrash -destination generic/platform=iOS",
40
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
41
+ "verify:web": "bun run build && bun test",
42
+ "test": "bun test",
43
+ "lint": "bun run eslint && bun run prettier -- --check && bun run swiftlint -- lint",
44
+ "fmt": "bun run eslint -- --fix && bun run prettier -- --write && bun run swiftlint -- --fix --format",
45
+ "eslint": "eslint . --ext .ts",
46
+ "prettier": "prettier-pretty-check \"**/*.{css,html,ts,js,java,json,md}\" --plugin=prettier-plugin-java",
47
+ "swiftlint": "node-swiftlint",
48
+ "docgen": "docgen --api WebViewCrashPlugin --output-readme README.md --output-json dist/docs.json",
49
+ "build": "bun run clean && bun run docgen && tsc && rollup -c rollup.config.mjs",
50
+ "clean": "rimraf ./dist",
51
+ "watch": "tsc --watch",
52
+ "prepublishOnly": "bun run build",
53
+ "check:wiring": "node scripts/check-capacitor-plugin-wiring.mjs"
54
+ },
55
+ "devDependencies": {
56
+ "@capacitor/android": "^8.0.0",
57
+ "@capacitor/cli": "^8.0.0",
58
+ "@capacitor/core": "^8.0.0",
59
+ "@capacitor/docgen": "^0.3.1",
60
+ "@capacitor/ios": "^8.0.0",
61
+ "@ionic/eslint-config": "^0.4.0",
62
+ "@ionic/prettier-config": "^4.0.0",
63
+ "@ionic/swiftlint-config": "^2.0.0",
64
+ "@types/node": "^24.10.1",
65
+ "eslint": "^8.57.1",
66
+ "eslint-plugin-import": "^2.31.0",
67
+ "husky": "^9.1.7",
68
+ "prettier": "^3.6.2",
69
+ "prettier-pretty-check": "^0.2.0",
70
+ "prettier-plugin-java": "^2.7.7",
71
+ "rimraf": "^6.1.0",
72
+ "rollup": "^4.53.2",
73
+ "swiftlint": "^2.0.0",
74
+ "typescript": "^5.9.3"
75
+ },
76
+ "peerDependencies": {
77
+ "@capacitor/core": ">=8.0.0"
78
+ },
79
+ "prettier": "@ionic/prettier-config",
80
+ "swiftlint": "@ionic/swiftlint-config",
81
+ "eslintConfig": {
82
+ "extends": "@ionic/eslint-config/recommended"
83
+ },
84
+ "capacitor": {
85
+ "ios": {
86
+ "src": "ios"
87
+ },
88
+ "android": {
89
+ "src": "android"
90
+ }
91
+ }
92
+ }