@capgo/capacitor-updater 7.13.16 → 7.13.18

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,303 @@
1
+ /*
2
+ * This Source Code Form is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
+ */
6
+
7
+ import Foundation
8
+
9
+ extension Collection {
10
+ subscript(safe index: Index) -> Element? {
11
+ return indices.contains(index) ? self[index] : nil
12
+ }
13
+ }
14
+ extension URL {
15
+ var isDirectory: Bool {
16
+ (try? resourceValues(forKeys: [.isDirectoryKey]))?.isDirectory == true
17
+ }
18
+ var exist: Bool {
19
+ return FileManager().fileExists(atPath: self.path)
20
+ }
21
+ }
22
+ struct SetChannelDec: Decodable {
23
+ let status: String?
24
+ let error: String?
25
+ let message: String?
26
+ }
27
+ public class SetChannel: NSObject {
28
+ var status: String = ""
29
+ var error: String = ""
30
+ var message: String = ""
31
+ }
32
+ extension SetChannel {
33
+ func toDict() -> [String: Any] {
34
+ var dict: [String: Any] = [String: Any]()
35
+ let otherSelf: Mirror = Mirror(reflecting: self)
36
+ for child: Mirror.Child in otherSelf.children {
37
+ if let key: String = child.label {
38
+ dict[key] = child.value
39
+ }
40
+ }
41
+ return dict
42
+ }
43
+ }
44
+ struct GetChannelDec: Decodable {
45
+ let channel: String?
46
+ let status: String?
47
+ let error: String?
48
+ let message: String?
49
+ let allowSet: Bool?
50
+ }
51
+ public class GetChannel: NSObject {
52
+ var channel: String = ""
53
+ var status: String = ""
54
+ var error: String = ""
55
+ var message: String = ""
56
+ var allowSet: Bool = true
57
+ }
58
+ extension GetChannel {
59
+ func toDict() -> [String: Any] {
60
+ var dict: [String: Any] = [String: Any]()
61
+ let otherSelf: Mirror = Mirror(reflecting: self)
62
+ for child: Mirror.Child in otherSelf.children {
63
+ if let key: String = child.label {
64
+ dict[key] = child.value
65
+ }
66
+ }
67
+ return dict
68
+ }
69
+ }
70
+ struct ChannelInfo: Codable {
71
+ let id: String?
72
+ let name: String?
73
+ let `public`: Bool?
74
+ let allow_self_set: Bool?
75
+ }
76
+ struct ListChannelsDec: Decodable {
77
+ let channels: [ChannelInfo]?
78
+ let error: String?
79
+
80
+ init(from decoder: Decoder) throws {
81
+ let container = try decoder.singleValueContainer()
82
+
83
+ if let channelsArray = try? container.decode([ChannelInfo].self) {
84
+ // Backend returns direct array
85
+ self.channels = channelsArray
86
+ self.error = nil
87
+ } else {
88
+ // Handle error response
89
+ let errorContainer = try decoder.container(keyedBy: CodingKeys.self)
90
+ self.channels = nil
91
+ self.error = try? errorContainer.decode(String.self, forKey: .error)
92
+ }
93
+ }
94
+
95
+ private enum CodingKeys: String, CodingKey {
96
+ case error
97
+ }
98
+ }
99
+ public class ListChannels: NSObject {
100
+ var channels: [[String: Any]] = []
101
+ var error: String = ""
102
+ }
103
+ extension ListChannels {
104
+ func toDict() -> [String: Any] {
105
+ var dict: [String: Any] = [String: Any]()
106
+ let otherSelf: Mirror = Mirror(reflecting: self)
107
+ for child: Mirror.Child in otherSelf.children {
108
+ if let key: String = child.label {
109
+ dict[key] = child.value
110
+ }
111
+ }
112
+ return dict
113
+ }
114
+ }
115
+ struct InfoObject: Codable {
116
+ let platform: String?
117
+ let device_id: String?
118
+ let app_id: String?
119
+ let custom_id: String?
120
+ let version_build: String?
121
+ let version_code: String?
122
+ let version_os: String?
123
+ var version_name: String?
124
+ var old_version_name: String?
125
+ let plugin_version: String?
126
+ let is_emulator: Bool?
127
+ let is_prod: Bool?
128
+ var action: String?
129
+ var channel: String?
130
+ var defaultChannel: String?
131
+ }
132
+
133
+ public struct ManifestEntry: Codable {
134
+ let file_name: String?
135
+ let file_hash: String?
136
+ let download_url: String?
137
+ }
138
+
139
+ extension ManifestEntry {
140
+ func toDict() -> [String: Any] {
141
+ var dict: [String: Any] = [String: Any]()
142
+ let mirror = Mirror(reflecting: self)
143
+ for child in mirror.children {
144
+ if let key = child.label {
145
+ dict[key] = child.value
146
+ }
147
+ }
148
+ return dict
149
+ }
150
+ }
151
+
152
+ struct AppVersionDec: Decodable {
153
+ let version: String?
154
+ let checksum: String?
155
+ let url: String?
156
+ let message: String?
157
+ let error: String?
158
+ let session_key: String?
159
+ let major: Bool?
160
+ let data: [String: String]?
161
+ let manifest: [ManifestEntry]?
162
+ }
163
+
164
+ public class AppVersion: NSObject {
165
+ var version: String = ""
166
+ var checksum: String = ""
167
+ var url: String = ""
168
+ var message: String?
169
+ var error: String?
170
+ var sessionKey: String?
171
+ var major: Bool?
172
+ var data: [String: String]?
173
+ var manifest: [ManifestEntry]?
174
+ }
175
+
176
+ extension AppVersion {
177
+ func toDict() -> [String: Any] {
178
+ var dict: [String: Any] = [String: Any]()
179
+ let otherSelf: Mirror = Mirror(reflecting: self)
180
+ for child: Mirror.Child in otherSelf.children {
181
+ if let key: String = child.label {
182
+ if key == "manifest", let manifestEntries = child.value as? [ManifestEntry] {
183
+ dict[key] = manifestEntries.map { $0.toDict() }
184
+ } else {
185
+ dict[key] = child.value
186
+ }
187
+ }
188
+ }
189
+ return dict
190
+ }
191
+ }
192
+
193
+ extension OperatingSystemVersion {
194
+ func getFullVersion(separator: String = ".") -> String {
195
+ return "\(majorVersion)\(separator)\(minorVersion)\(separator)\(patchVersion)"
196
+ }
197
+ }
198
+ extension Bundle {
199
+ var versionName: String? {
200
+ return infoDictionary?["CFBundleShortVersionString"] as? String
201
+ }
202
+ var versionCode: String? {
203
+ return infoDictionary?["CFBundleVersion"] as? String
204
+ }
205
+ }
206
+
207
+ extension ISO8601DateFormatter {
208
+ convenience init(_ formatOptions: Options) {
209
+ self.init()
210
+ self.formatOptions = formatOptions
211
+ }
212
+ }
213
+ extension Formatter {
214
+ static let iso8601withFractionalSeconds: ISO8601DateFormatter = ISO8601DateFormatter([.withInternetDateTime, .withFractionalSeconds])
215
+ }
216
+ extension Date {
217
+ var iso8601withFractionalSeconds: String { return Formatter.iso8601withFractionalSeconds.string(from: self) }
218
+ }
219
+ extension String {
220
+
221
+ var fileURL: URL {
222
+ return URL(fileURLWithPath: self)
223
+ }
224
+
225
+ var lastPathComponent: String {
226
+ get {
227
+ return fileURL.lastPathComponent
228
+ }
229
+ }
230
+ var iso8601withFractionalSeconds: Date? {
231
+ return Formatter.iso8601withFractionalSeconds.date(from: self)
232
+ }
233
+ func trim(using characterSet: CharacterSet = .whitespacesAndNewlines) -> String {
234
+ return trimmingCharacters(in: characterSet)
235
+ }
236
+ }
237
+
238
+ enum CustomError: Error {
239
+ // Throw when an unzip fail
240
+ case cannotUnzip
241
+ case cannotWrite
242
+ case cannotDecode
243
+ case cannotUnflat
244
+ case cannotCreateDirectory
245
+ case cannotDeleteDirectory
246
+ case cannotDecryptSessionKey
247
+ case invalidBase64
248
+
249
+ // Throw in all other cases
250
+ case unexpected(code: Int)
251
+ }
252
+
253
+ extension CustomError: LocalizedError {
254
+ public var errorDescription: String? {
255
+ switch self {
256
+ case .cannotUnzip:
257
+ return NSLocalizedString(
258
+ "The file cannot be unzip",
259
+ comment: "Invalid zip"
260
+ )
261
+ case .cannotCreateDirectory:
262
+ return NSLocalizedString(
263
+ "The folder cannot be created",
264
+ comment: "Invalid folder"
265
+ )
266
+ case .cannotDeleteDirectory:
267
+ return NSLocalizedString(
268
+ "The folder cannot be deleted",
269
+ comment: "Invalid folder"
270
+ )
271
+ case .cannotUnflat:
272
+ return NSLocalizedString(
273
+ "The file cannot be unflat",
274
+ comment: "Invalid folder"
275
+ )
276
+ case .unexpected:
277
+ return NSLocalizedString(
278
+ "An unexpected error occurred.",
279
+ comment: "Unexpected Error"
280
+ )
281
+ case .cannotDecode:
282
+ return NSLocalizedString(
283
+ "Decoding the zip failed with this key",
284
+ comment: "Invalid public key"
285
+ )
286
+ case .cannotWrite:
287
+ return NSLocalizedString(
288
+ "Cannot write to the destination",
289
+ comment: "Invalid destination"
290
+ )
291
+ case .cannotDecryptSessionKey:
292
+ return NSLocalizedString(
293
+ "Decrypting the session key failed",
294
+ comment: "Invalid session key"
295
+ )
296
+ case .invalidBase64:
297
+ return NSLocalizedString(
298
+ "Decrypting the base64 failed",
299
+ comment: "Invalid checksum key"
300
+ )
301
+ }
302
+ }
303
+ }
@@ -0,0 +1,310 @@
1
+ import Capacitor
2
+ import Foundation
3
+ import os.log
4
+ import WebKit
5
+
6
+ public class Logger {
7
+ public enum LogLevel: Int {
8
+ case silent = 0
9
+ case error
10
+ case warn
11
+ case info
12
+ case debug
13
+
14
+ public static subscript(_ str: String) -> LogLevel? {
15
+ var index = 0
16
+
17
+ while let item = LogLevel(rawValue: index) {
18
+ if str == "\(item)" {
19
+ return item
20
+ }
21
+
22
+ index += 1
23
+ }
24
+
25
+ return nil
26
+ }
27
+
28
+ public func asOSLogType() -> OSLogType {
29
+ switch self {
30
+ case .debug:
31
+ return OSLogType.debug
32
+ case .info:
33
+ return OSLogType.info
34
+ case .warn:
35
+ return OSLogType.default
36
+ case .error:
37
+ return OSLogType.error
38
+ case .silent:
39
+ return OSLogType.info // Compiler wants this, it will never be used
40
+ }
41
+ }
42
+ public func asString() -> String {
43
+ switch self {
44
+ case .debug:
45
+ return "debug"
46
+ case .info:
47
+ return "info"
48
+ case .warn:
49
+ return "warn"
50
+ case .error:
51
+ return "error"
52
+ case .silent:
53
+ return "info"
54
+ }
55
+ }
56
+ }
57
+
58
+ public struct Options {
59
+ public var level: LogLevel
60
+ public var labels: [String: String]
61
+ public var useSyslog: Bool
62
+
63
+ public init(level: LogLevel = LogLevel.info, labels: [String: String] = [:], useSyslog: Bool = false) {
64
+ self.level = level
65
+ self.labels = labels
66
+ self.useSyslog = useSyslog
67
+ }
68
+ }
69
+
70
+ private var _labels: [LogLevel: String] = [
71
+ LogLevel.silent: "",
72
+ LogLevel.error: "🔴",
73
+ LogLevel.warn: "🟠",
74
+ LogLevel.info: "🟢",
75
+ LogLevel.debug: "🔎"
76
+ ]
77
+
78
+ public var labels: [String: String] {
79
+ get {
80
+ var result: [String: String] = [:]
81
+
82
+ for (level, label) in _labels {
83
+ result[String(describing: level)] = label
84
+ }
85
+
86
+ return result
87
+ }
88
+
89
+ set {
90
+ for (level, label) in newValue {
91
+ if let logLevel = LogLevel[level] {
92
+ _labels[logLevel] = label
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ public var level = LogLevel.info
99
+
100
+ public var levelName: String {
101
+ get {
102
+ String(describing: level)
103
+ }
104
+ set {
105
+ if let newLevel = LogLevel[newValue] {
106
+ level = newLevel
107
+ }
108
+ }
109
+ }
110
+
111
+ private var _tag = ""
112
+
113
+ public var tag: String {
114
+ get {
115
+ _tag
116
+ }
117
+ set {
118
+ if !newValue.isEmpty {
119
+ _tag = newValue
120
+ }
121
+ }
122
+ }
123
+
124
+ private let kDefaultTimerLabel = "default"
125
+ private var timers: [String: Date] = [:]
126
+ public var useSyslog = false
127
+ public var webView: WKWebView?
128
+
129
+ public func setWebView(webView: WKWebView) {
130
+ self.webView = webView
131
+ }
132
+
133
+ public init(
134
+ withTag tag: String,
135
+ config: InstanceConfiguration? = nil,
136
+ options: Options? = nil
137
+ ) {
138
+ self.tag = tag
139
+ if let config = config {
140
+ // The logger plugin's name is LoggerBridge, we want to look at the config
141
+ // named "Logger", so we can't use plugin.getConfigValue().
142
+ if let configLevel = getConfigValue("level", from: config) as? String,
143
+ let logLevel = LogLevel[configLevel] {
144
+ level = logLevel
145
+ }
146
+
147
+ if let configLabels = getConfigValue("labels", from: config) as? [String: String] {
148
+ labels = configLabels
149
+ }
150
+
151
+ if let configSyslog = getConfigValue("useSyslog", from: config) as? Bool {
152
+ useSyslog = configSyslog
153
+ }
154
+ }
155
+
156
+ if let options = options {
157
+ self.level = options.level
158
+ self.labels = options.labels
159
+ self.useSyslog = options.useSyslog
160
+ }
161
+ }
162
+
163
+ private func getConfigValue(_ configKey: String, from config: InstanceConfiguration) -> Any? {
164
+ if let config = config.pluginConfigurations as? JSObject {
165
+ return config[keyPath: KeyPath(stringLiteral: "Logger.\(configKey)")]
166
+ }
167
+
168
+ return nil
169
+ }
170
+
171
+ public func error(_ message: String) {
172
+ log(atLevel: LogLevel.error, message: message)
173
+ }
174
+
175
+ public func warn(_ message: String) {
176
+ log(atLevel: LogLevel.warn, message: message)
177
+ }
178
+
179
+ public func info(_ message: String) {
180
+ log(atLevel: LogLevel.info, message: message)
181
+ }
182
+
183
+ public func log(_ message: String) {
184
+ log(atLevel: LogLevel.info, message: message)
185
+ }
186
+
187
+ public func debug(_ message: String) {
188
+ log(atLevel: LogLevel.debug, message: message)
189
+ }
190
+
191
+ public func dir(_ value: Any?) {
192
+ // Check the log level here to avoid conversion to string
193
+ if canLog(atLevel: LogLevel.info) {
194
+ log(atLevel: LogLevel.info, message: String(describing: value))
195
+ }
196
+ }
197
+
198
+ public func log(atLevel level: LogLevel, message: String) {
199
+ // This will never fail, but we have to keep swift happy
200
+ if let label = _labels[level] {
201
+ log(atLevel: level, label: label, tag: tag, message: message)
202
+ if let webView = self.webView {
203
+ DispatchQueue.main.async {
204
+ let combined = "\(label) \(self.tag) : \(message)"
205
+ let jsArg = self.toJSStringLiteral(combined)
206
+ webView.evaluateJavaScript("console.\(level.asString())(\(jsArg))", completionHandler: nil)
207
+ }
208
+ }
209
+ }
210
+ }
211
+
212
+ private func toJSStringLiteral(_ value: String) -> String {
213
+ // Prefer JSON encoding to produce a valid JS string literal
214
+ if let data = try? JSONEncoder().encode(value),
215
+ let encoded = String(data: data, encoding: .utf8) {
216
+ return encoded
217
+ }
218
+
219
+ // Fallback manual escaping (unlikely to be used)
220
+ var s = value
221
+ s = s.replacingOccurrences(of: "\\", with: "\\\\")
222
+ s = s.replacingOccurrences(of: "\"", with: "\\\"")
223
+ s = s.replacingOccurrences(of: "'", with: "\\'")
224
+ s = s.replacingOccurrences(of: "\n", with: "\\n")
225
+ s = s.replacingOccurrences(of: "\r", with: "\\r")
226
+ s = s.replacingOccurrences(of: "\u{2028}", with: "\\u2028")
227
+ s = s.replacingOccurrences(of: "\u{2029}", with: "\\u2029")
228
+ return "\"\(s)\""
229
+ }
230
+
231
+ public func log(atLevel level: LogLevel, label: String?, tag: String, message: String) {
232
+ // This will never fail, but we have to keep swift happy
233
+ if let label = label ?? _labels[level] {
234
+ print(atLevel: level, label: label, tag: tag, message: message)
235
+ // eval
236
+ }
237
+ }
238
+
239
+ private func canLog(atLevel level: LogLevel) -> Bool {
240
+ self.level.rawValue >= level.rawValue
241
+ }
242
+
243
+ private func print(atLevel level: LogLevel, label: String, tag: String, message: String) {
244
+ guard canLog(atLevel: level) else {
245
+ return
246
+ }
247
+
248
+ var msg = message
249
+
250
+ if !label.isEmpty {
251
+ // If the label is ASCII, put it after the tag, otherwise before.
252
+ if label[label.startIndex].isASCII {
253
+ msg = "[\(tag)] \(label): \(message)"
254
+ } else {
255
+ msg = "\(label) [\(tag)]: \(message)"
256
+ }
257
+ } else {
258
+ msg = "[\(tag)]: \(message)"
259
+ }
260
+
261
+ if useSyslog {
262
+ os_log("%{public}@", type: level.asOSLogType(), msg)
263
+ } else {
264
+ Swift.print(msg)
265
+ }
266
+ }
267
+
268
+ public func time(_ label: String?) {
269
+ timers[label ?? ""] = Date()
270
+ }
271
+
272
+ public func timeLog(_ label: String?) {
273
+ if let timer = timers[label ?? ""] {
274
+ info(formatTimeInterval(timer.timeIntervalSinceNow))
275
+ } else {
276
+ warn("timer \(label ?? kDefaultTimerLabel) does not exist")
277
+ }
278
+ }
279
+
280
+ public func timeEnd(_ label: String?) {
281
+ timeLog(label)
282
+ timers.removeValue(forKey: label ?? kDefaultTimerLabel)
283
+ }
284
+
285
+ private func formatTimeInterval(_ interval: TimeInterval) -> String {
286
+ let int = Int(interval)
287
+ let millis = Int(((1 + interval.remainder(dividingBy: 1)) * 1000).rounded())
288
+ let seconds = int % 60
289
+ let minutes = (int / 60) % 60
290
+ let hours = (int / 3600)
291
+
292
+ if seconds < 1 {
293
+ return "\(millis)ms"
294
+ }
295
+
296
+ if minutes < 1 {
297
+ return "\(seconds).\(String(format: "%0.3d", millis))s"
298
+ }
299
+
300
+ if hours < 1 {
301
+ return "\(minutes):\(String(format: "%0.2d", seconds)).\(String(format: "%0.3d", millis)) (min:sec.ms)"
302
+ }
303
+
304
+ return "\(hours):\(String(format: "%0.2d", minutes)):\(String(format: "%0.2d", seconds)) (hr:min:sec)"
305
+ }
306
+
307
+ public func trace() {
308
+ info(String(format: "%@", Thread.callStackSymbols))
309
+ }
310
+ }