capdebug 0.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.
@@ -0,0 +1,204 @@
1
+ import Foundation
2
+ import Capacitor
3
+ import UIKit
4
+ import Network
5
+ import WebKit
6
+
7
+ @objc(CapDebugPlugin)
8
+ public class CapDebugPlugin: CAPPlugin, CAPBridgedPlugin {
9
+ public let identifier = "CapDebugPlugin"
10
+ public let jsName = "CapDebug"
11
+ public let pluginMethods: [CAPPluginMethod] = [
12
+ CAPPluginMethod(name: "getDeviceInfo", returnType: CAPPluginReturnPromise),
13
+ CAPPluginMethod(name: "getMemoryInfo", returnType: CAPPluginReturnPromise),
14
+ CAPPluginMethod(name: "getWebViewInfo", returnType: CAPPluginReturnPromise),
15
+ CAPPluginMethod(name: "getAppInfo", returnType: CAPPluginReturnPromise),
16
+ CAPPluginMethod(name: "getNetworkInfo", returnType: CAPPluginReturnPromise)
17
+ ]
18
+
19
+ private let networkMonitor = NWPathMonitor()
20
+ private let monitorQueue = DispatchQueue(label: "com.capdebug.networkMonitor")
21
+ private var lastKnownPath: NWPath?
22
+
23
+ override public func load() {
24
+ super.load()
25
+ networkMonitor.pathUpdateHandler = { [weak self] path in
26
+ self?.lastKnownPath = path
27
+ }
28
+ networkMonitor.start(queue: monitorQueue)
29
+ }
30
+
31
+ deinit {
32
+ networkMonitor.cancel()
33
+ }
34
+
35
+ @objc func getDeviceInfo(_ call: CAPPluginCall) {
36
+ var machineIdentifier = "Unknown"
37
+ var systemInfo = utsname()
38
+ if uname(&systemInfo) == 0 {
39
+ let machineMirror = Mirror(reflecting: systemInfo.machine)
40
+ let identifier = machineMirror.children.reduce("") { identifier, element in
41
+ guard let value = element.value as? Int8, value != 0 else { return identifier }
42
+ return identifier + String(UnicodeScalar(UInt8(value)))
43
+ }
44
+ if !identifier.isEmpty {
45
+ machineIdentifier = identifier
46
+ }
47
+ }
48
+
49
+ #if targetEnvironment(simulator)
50
+ let isSimulator = true
51
+ #else
52
+ let isSimulator = false
53
+ #endif
54
+
55
+ let result: [String: Any] = [
56
+ "manufacturer": "Apple",
57
+ "model": UIDevice.current.model,
58
+ "device": machineIdentifier,
59
+ "systemName": UIDevice.current.systemName,
60
+ "systemVersion": UIDevice.current.systemVersion,
61
+ "isSimulator": isSimulator,
62
+ "architecture": getCpuArchitecture(),
63
+ "availableProcessors": ProcessInfo.processInfo.activeProcessorCount,
64
+ "processorCount": ProcessInfo.processInfo.processorCount,
65
+ "platform": "ios"
66
+ ]
67
+
68
+ call.resolve(result)
69
+ }
70
+
71
+ @objc func getMemoryInfo(_ call: CAPPluginCall) {
72
+ var usedBytes: UInt64 = 0
73
+ var taskInfo = mach_task_basic_info()
74
+ var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size / 4)
75
+
76
+ let kerr: kern_return_t = withUnsafeMutablePointer(to: &taskInfo) {
77
+ $0.withMemoryRebound(to: integer_t.self, capacity: 1) {
78
+ task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), $0, &count)
79
+ }
80
+ }
81
+
82
+ if kerr == KERN_SUCCESS {
83
+ usedBytes = UInt64(taskInfo.resident_size)
84
+ }
85
+
86
+ let totalBytes = ProcessInfo.processInfo.physicalMemory
87
+
88
+ var availableBytes: UInt64 = 0
89
+ if #available(iOS 13.0, *) {
90
+ availableBytes = UInt64(os_proc_available_memory())
91
+ }
92
+
93
+ var result: [String: Any] = [
94
+ "appMemoryUsage": usedBytes,
95
+ "appMemoryUsageFormatted": formatBytes(usedBytes),
96
+ "systemTotalMemory": totalBytes,
97
+ "systemTotalMemoryFormatted": formatBytes(totalBytes),
98
+ "lowMemory": false
99
+ ]
100
+
101
+ if availableBytes > 0 {
102
+ result["systemAvailableMemory"] = availableBytes
103
+ result["systemAvailableMemoryFormatted"] = formatBytes(availableBytes)
104
+ }
105
+
106
+ call.resolve(result)
107
+ }
108
+
109
+ @objc func getWebViewInfo(_ call: CAPPluginCall) {
110
+ var userAgent = "Unknown"
111
+ if let customUA = self.bridge?.webView?.customUserAgent, !customUA.isEmpty {
112
+ userAgent = customUA
113
+ }
114
+
115
+ #if DEBUG
116
+ let isDebug = true
117
+ #else
118
+ let isDebug = false
119
+ #endif
120
+
121
+ let result: [String: Any] = [
122
+ "userAgent": userAgent,
123
+ "version": "WebKit iOS",
124
+ "packageName": Bundle.main.bundleIdentifier ?? "Unknown",
125
+ "debugEnabled": isDebug
126
+ ]
127
+
128
+ call.resolve(result)
129
+ }
130
+
131
+ @objc func getAppInfo(_ call: CAPPluginCall) {
132
+ let bundleId = Bundle.main.bundleIdentifier ?? "Unknown"
133
+ let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
134
+ let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "Unknown"
135
+
136
+ #if DEBUG
137
+ let buildType = "debug"
138
+ #else
139
+ let buildType = "release"
140
+ #endif
141
+
142
+ let result: [String: Any] = [
143
+ "applicationId": bundleId,
144
+ "bundleIdentifier": bundleId,
145
+ "version": version,
146
+ "versionName": version,
147
+ "build": build,
148
+ "versionCode": build,
149
+ "buildType": buildType
150
+ ]
151
+
152
+ call.resolve(result)
153
+ }
154
+
155
+ @objc func getNetworkInfo(_ call: CAPPluginCall) {
156
+ let path = lastKnownPath ?? networkMonitor.currentPath
157
+ let isConnected = path.status == .satisfied
158
+ let isMetered = path.isExpensive
159
+
160
+ var connectionType = "none"
161
+ if isConnected {
162
+ if path.usesInterfaceType(.wifi) {
163
+ connectionType = "wifi"
164
+ } else if path.usesInterfaceType(.cellular) {
165
+ connectionType = "cellular"
166
+ } else if path.usesInterfaceType(.wiredEthernet) {
167
+ connectionType = "ethernet"
168
+ } else {
169
+ connectionType = "other"
170
+ }
171
+ }
172
+
173
+ let result: [String: Any] = [
174
+ "connected": isConnected,
175
+ "connectionType": connectionType,
176
+ "metered": isMetered
177
+ ]
178
+
179
+ call.resolve(result)
180
+ }
181
+
182
+ private func getCpuArchitecture() -> String {
183
+ #if arch(x86_64)
184
+ return "x86_64"
185
+ #elseif arch(arm64)
186
+ return "arm64"
187
+ #elseif arch(arm)
188
+ return "arm"
189
+ #elseif arch(i386)
190
+ return "i386"
191
+ #else
192
+ return "unknown"
193
+ #endif
194
+ }
195
+
196
+ private func formatBytes(_ bytes: UInt64) -> String {
197
+ if bytes == 0 { return "0 B" }
198
+ let units = ["B", "KB", "MB", "GB", "TB"]
199
+ let digitGroups = Int(log10(Double(bytes)) / log10(1024.0))
200
+ let safeGroup = min(digitGroups, units.count - 1)
201
+ let formatted = String(format: "%.2f %@", Double(bytes) / pow(1024.0, Double(safeGroup)), units[safeGroup])
202
+ return formatted
203
+ }
204
+ }
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "capdebug",
3
+ "version": "0.1.0",
4
+ "description": "Lightweight, embedded debug viewer and diagnostics plugin for Capacitor apps",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/plugin.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "dist/",
11
+ "ios/",
12
+ "android/",
13
+ "CapDebug.podspec"
14
+ ],
15
+ "scripts": {
16
+ "build": "rollup -c rollup.config.mjs && tsc --emitDeclarationOnly",
17
+ "watch": "rollup -c rollup.config.mjs -w",
18
+ "prepublishOnly": "npm run build",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest"
21
+ },
22
+ "keywords": [
23
+ "capacitor",
24
+ "plugin",
25
+ "native",
26
+ "debug",
27
+ "devtools",
28
+ "logger",
29
+ "console",
30
+ "inspector",
31
+ "viewer",
32
+ "embedded",
33
+ "ios",
34
+ "android"
35
+ ],
36
+ "author": "CapDebug Contributors",
37
+ "license": "MIT",
38
+ "peerDependencies": {
39
+ "@capacitor/core": "^5.0.0 || ^6.0.0 || ^7.0.0"
40
+ },
41
+ "devDependencies": {
42
+ "@capacitor/core": "^6.2.0",
43
+ "@rollup/plugin-node-resolve": "^15.2.3",
44
+ "@rollup/plugin-typescript": "^11.1.6",
45
+ "@types/node": "^22.10.2",
46
+ "jsdom": "^25.0.1",
47
+ "rollup": "^4.28.1",
48
+ "tslib": "^2.8.1",
49
+ "typescript": "^5.7.2",
50
+ "vitest": "^2.1.8"
51
+ },
52
+ "capacitor": {
53
+ "ios": {
54
+ "src": "ios"
55
+ },
56
+ "android": {
57
+ "src": "android"
58
+ }
59
+ },
60
+ "exports": {
61
+ ".": {
62
+ "types": "./dist/index.d.ts",
63
+ "import": "./dist/plugin.mjs",
64
+ "require": "./dist/plugin.cjs.js",
65
+ "default": "./dist/plugin.mjs"
66
+ }
67
+ }
68
+ }